/* * Anarres C Preprocessor * Copyright (c) 2007-2008, Shevek * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing * permissions and limitations under the License. */ package org.anarres.cpp; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * A macro object. * * This encapsulates a name, an argument count, and a token stream * for replacement. The replacement token stream may contain the * extra tokens {@link Token#M_ARG} and {@link Token#M_STRING}. */ public class Macro { private Source source; private String name; /* It's an explicit decision to keep these around here. We don't * need to; the argument token type is M_ARG and the value * is the index. The strings themselves are only used in * stringification of the macro, for debugging. */ private List<String> args; private boolean variadic; private List<Token> tokens; public Macro(Source source, String name) { this.source = source; this.name = name; this.args = null; this.variadic = false; this.tokens = new ArrayList<Token>(); } public Macro(String name) { this(null, name); } /** * Sets the Source from which this macro was parsed. */ public void setSource(Source s) { this.source = s; } /** * Returns the Source from which this macro was parsed. * * This method may return null if the macro was not parsed * from a regular file. */ public Source getSource() { return source; } /** * Returns the name of this macro. */ public String getName() { return name; } /** * Sets the arguments to this macro. */ public void setArgs(List<String> args) { this.args = args; } /** * Returns true if this is a function-like macro. */ public boolean isFunctionLike() { return args != null; } /** * Returns the number of arguments to this macro. */ public int getArgs() { return args.size(); } /** * Sets the variadic flag on this Macro. */ public void setVariadic(boolean b) { this.variadic = b; } /** * Returns true if this is a variadic function-like macro. */ public boolean isVariadic() { return variadic; } /** * Adds a token to the expansion of this macro. */ public void addToken(Token tok) { this.tokens.add(tok); } /** * Adds a "paste" operator to the expansion of this macro. * * A paste operator causes the next token added to be pasted * to the previous token when the macro is expanded. * It is an error for a macro to end with a paste token. */ public void addPaste(Token tok) { /* * Given: tok0 ## tok1 * We generate: M_PASTE, tok0, tok1 * This extends as per a stack language: * tok0 ## tok1 ## tok2 -> * M_PASTE, tok0, M_PASTE, tok1, tok2 */ this.tokens.add(tokens.size() - 1, tok); } /* pp */ List<Token> getTokens() { return tokens; } /* Paste tokens are inserted before the first of the two pasted * tokens, so it's a kind of bytecode notation. This method * swaps them around again. We know that there will never be two * sequential paste tokens, so a boolean is sufficient. */ public String getText() { StringBuilder buf = new StringBuilder(); boolean paste = false; for (int i = 0; i < tokens.size(); i++) { Token tok = tokens.get(i); if (tok.getType() == Token.M_PASTE) { assert paste == false : "Two sequential pastes."; paste = true; continue; } else { buf.append(tok.getText()); } if (paste) { buf.append(" #" + "# "); paste = false; } // buf.append(tokens.get(i)); } return buf.toString(); } public String toString() { StringBuilder buf = new StringBuilder(name); if (args != null) { buf.append('('); Iterator<String> it = args.iterator(); while (it.hasNext()) { buf.append(it.next()); if (it.hasNext()) buf.append(", "); else if (isVariadic()) buf.append("..."); } buf.append(')'); } if (!tokens.isEmpty()) { buf.append(" => ").append(getText()); } return buf.toString(); } }