001/* 002 * Anarres C Preprocessor 003 * Copyright (c) 2007-2015, Shevek 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 014 * or implied. See the License for the specific language governing 015 * permissions and limitations under the License. 016 */ 017package org.anarres.cpp; 018 019import java.util.ArrayList; 020import java.util.Iterator; 021import java.util.List; 022 023/** 024 * A macro object. 025 * 026 * This encapsulates a name, an argument count, and a token stream 027 * for replacement. The replacement token stream may contain the 028 * extra tokens {@link Token#M_ARG} and {@link Token#M_STRING}. 029 */ 030public class Macro { 031 032 private Source source; 033 private String name; 034 /* It's an explicit decision to keep these around here. We don't 035 * need to; the argument token type is M_ARG and the value 036 * is the index. The strings themselves are only used in 037 * stringification of the macro, for debugging. */ 038 private List<String> args; 039 private boolean variadic; 040 private List<Token> tokens; 041 042 public Macro(Source source, String name) { 043 this.source = source; 044 this.name = name; 045 this.args = null; 046 this.variadic = false; 047 this.tokens = new ArrayList<Token>(); 048 } 049 050 public Macro(String name) { 051 this(null, name); 052 } 053 054 /** 055 * Sets the Source from which this macro was parsed. 056 */ 057 public void setSource(Source s) { 058 this.source = s; 059 } 060 061 /** 062 * Returns the Source from which this macro was parsed. 063 * 064 * This method may return null if the macro was not parsed 065 * from a regular file. 066 */ 067 public Source getSource() { 068 return source; 069 } 070 071 /** 072 * Returns the name of this macro. 073 */ 074 public String getName() { 075 return name; 076 } 077 078 /** 079 * Sets the arguments to this macro. 080 */ 081 public void setArgs(List<String> args) { 082 this.args = args; 083 } 084 085 /** 086 * Returns true if this is a function-like macro. 087 */ 088 public boolean isFunctionLike() { 089 return args != null; 090 } 091 092 /** 093 * Returns the number of arguments to this macro. 094 */ 095 public int getArgs() { 096 return args.size(); 097 } 098 099 /** 100 * Sets the variadic flag on this Macro. 101 */ 102 public void setVariadic(boolean b) { 103 this.variadic = b; 104 } 105 106 /** 107 * Returns true if this is a variadic function-like macro. 108 */ 109 public boolean isVariadic() { 110 return variadic; 111 } 112 113 /** 114 * Adds a token to the expansion of this macro. 115 */ 116 public void addToken(Token tok) { 117 this.tokens.add(tok); 118 } 119 120 /** 121 * Adds a "paste" operator to the expansion of this macro. 122 * 123 * A paste operator causes the next token added to be pasted 124 * to the previous token when the macro is expanded. 125 * It is an error for a macro to end with a paste token. 126 */ 127 public void addPaste(Token tok) { 128 /* 129 * Given: tok0 ## tok1 130 * We generate: M_PASTE, tok0, tok1 131 * This extends as per a stack language: 132 * tok0 ## tok1 ## tok2 -> 133 * M_PASTE, tok0, M_PASTE, tok1, tok2 134 */ 135 this.tokens.add(tokens.size() - 1, tok); 136 } 137 138 /* pp */ List<Token> getTokens() { 139 return tokens; 140 } 141 142 /* Paste tokens are inserted before the first of the two pasted 143 * tokens, so it's a kind of bytecode notation. This method 144 * swaps them around again. We know that there will never be two 145 * sequential paste tokens, so a boolean is sufficient. */ 146 public String getText() { 147 StringBuilder buf = new StringBuilder(); 148 boolean paste = false; 149 for (Token tok : tokens) { 150 if (tok.getType() == Token.M_PASTE) { 151 assert paste == false : "Two sequential pastes."; 152 paste = true; 153 continue; 154 } else { 155 buf.append(tok.getText()); 156 } 157 if (paste) { 158 buf.append(" #" + "# "); 159 paste = false; 160 } 161 // buf.append(tokens.get(i)); 162 } 163 return buf.toString(); 164 } 165 166 @Override 167 public String toString() { 168 StringBuilder buf = new StringBuilder(name); 169 if (args != null) { 170 buf.append('('); 171 Iterator<String> it = args.iterator(); 172 while (it.hasNext()) { 173 buf.append(it.next()); 174 if (it.hasNext()) 175 buf.append(", "); 176 else if (isVariadic()) 177 buf.append("..."); 178 } 179 buf.append(')'); 180 } 181 if (!tokens.isEmpty()) { 182 buf.append(" => ").append(getText()); 183 } 184 return buf.toString(); 185 } 186 187}