/* processes a #define directive */ private Token define() { Token tok = source_token_nonwhite(); if (tok.getType() != Token.IDENTIFIER) { error(tok, "Expected Token.IDENTIFIER"); return source_skipline(false); } /* if predefined */ String name = tok.getText(); if ("defined" == name) { error(tok, "Cannot redefine name 'defined'"); return source_skipline(false); } Macro m = new Macro(getSource(), name); List<String> args; tok = source_token(); if (tok.getType() == '(') { tok = source_token_nonwhite(); if (tok.getType() != ')') { args = new List<String>(); for (;;) { switch (tok.getType()) { case Token.IDENTIFIER: if(m.isVariadic()) { throw new Exception(); } args.Add(tok.getText()); break; case Token.ELLIPSIS: m.setVariadic(true); args.Add("__VA_ARGS__"); break; case Token.NL: case Token.EOF: error(tok, "Unterminated macro parameter list"); return tok; default: error(tok, "error in macro parameters: " + tok.getText()); return source_skipline(false); } tok = source_token_nonwhite(); switch (tok.getType()) { case ',': break; case Token.ELLIPSIS: tok = source_token_nonwhite(); if (tok.getType() != ')') error(tok, "ellipsis must be on last argument"); m.setVariadic(true); goto BREAK_ARGS; case ')': goto BREAK_ARGS; case Token.NL: case Token.EOF: /* Do not skip line. */ error(tok, "Unterminated macro parameters"); return tok; default: error(tok, "Bad token in macro parameters: " + tok.getText()); return source_skipline(false); } tok = source_token_nonwhite(); } BREAK_ARGS:; } else { System.Diagnostics.Debug.Assert(tok.getType() == ')', "Expected ')'"); args = new List<string>(); } m.setArgs(args); } else { /* For searching. */ args = new List<string>(); source_untoken(tok); } /* Get an expansion for the macro, using IndexOf. */ bool space = false; bool paste = false; int idx; /* Ensure no space at start. */ tok = source_token_nonwhite(); for (;;) { switch (tok.getType()) { case Token.EOF: goto BREAK_EXPANSION; case Token.NL: goto BREAK_EXPANSION; case Token.CCOMMENT: case Token.CPPCOMMENT: /* XXX This is where we implement GNU's cpp -CC. */ // break; case Token.WHITESPACE: if (!paste) space = true; break; /* Paste. */ case Token.PASTE: space = false; paste = true; m.addPaste(new Token(Token.M_PASTE, tok.getLine(), tok.getColumn(), "#" + "#", null)); break; /* Stringify. */ case '#': if (space) m.addToken(Token.space); space = false; Token la = source_token_nonwhite(); if(la.getType() == Token.IDENTIFIER && ((idx = args.IndexOf(la.getText())) != -1)) { m.addToken(new Token(Token.M_STRING, la.getLine(), la.getColumn(), "#" + la.getText(), idx)); } else { m.addToken(tok); /* Allow for special processing. */ source_untoken(la); } break; case Token.IDENTIFIER: if (space) m.addToken(Token.space); space = false; paste = false; idx = args.IndexOf(tok.getText()); if (idx == -1) m.addToken(tok); else m.addToken(new Token(Token.M_ARG, tok.getLine(), tok.getColumn(), tok.getText(), idx)); break; default: if (space) m.addToken(Token.space); space = false; paste = false; m.addToken(tok); break; } tok = source_token(); } BREAK_EXPANSION: if (getFeature(Feature.DEBUG)) System.Console.Error.WriteLine("Defined macro " + m); addMacro(m); return tok; /* Token.NL or Token.EOF. */ }
/* processes and expands a macro. */ private bool macro(Macro m, Token orig) { Token tok; List<Argument> args; // System.out.println("pp: expanding " + m); if (m.isFunctionLike()) { for (;;) { tok = source_token(); // System.out.println("pp: open: token is " + tok); switch (tok.getType()) { case Token.WHITESPACE: /* XXX Really? */ case Token.CCOMMENT: case Token.CPPCOMMENT: case Token.NL: break; /* continue */ case '(': goto BREAK_OPEN; default: source_untoken(tok); return false; } } BREAK_OPEN: // tok = expanded_token_nonwhite(); tok = source_token_nonwhite(); /* We either have, or we should have args. * This deals elegantly with the case that we have * one empty arg. */ if (tok.getType() != ')' || m.getArgs() > 0) { args = new List<Argument>(); Argument arg = new Argument(); int depth = 0; bool space = false; ARGS: for (;;) { // System.out.println("pp: arg: token is " + tok); switch (tok.getType()) { case Token.EOF: error(tok, "EOF in macro args"); return false; case ',': if (depth == 0) { if (m.isVariadic() && /* We are building the last arg. */ args.Count == m.getArgs() - 1) { /* Just add the comma. */ arg.addToken(tok); } else { args.Add(arg); arg = new Argument(); } } else { arg.addToken(tok); } space = false; break; case ')': if (depth == 0) { args.Add(arg); goto BREAK_ARGS; } else { depth--; arg.addToken(tok); } space = false; break; case '(': depth++; arg.addToken(tok); space = false; break; case Token.WHITESPACE: case Token.CCOMMENT: case Token.CPPCOMMENT: /* Avoid duplicating spaces. */ space = true; break; default: /* Do not put space on the beginning of * an argument token. */ if (space && arg.Count != 0) arg.addToken(Token.space); arg.addToken(tok); space = false; break; } // tok = expanded_token(); tok = source_token(); } BREAK_ARGS: if(m.isVariadic() && args.Count < m.getArgs()) { args.Add(new Argument()); } /* space may still be true here, thus trailing space * is stripped from arguments. */ if (args.Count != m.getArgs()) { error(tok, "macro " + m.getName() + " has " + m.getArgs() + " parameters " + "but given " + args.Count + " args"); /* We could replay the arg tokens, but I * note that GNU cpp does exactly what we do, * i.e. output the macro name and chew the args. */ return false; } /* for (Argument a : args) a.expand(this); */ for (int i = 0; i < args.Count; i++) { args[i].expand(this); } // System.out.println("Macro " + m + " args " + args); } else { /* nargs == 0 and we (correctly) got () */ args = null; } } else { /* Macro without args. */ args = null; } if (m == __LINE__) { push_source(new FixedTokenSource( new Token[] { new Token(Token.INTEGER, orig.getLine(), orig.getColumn(), orig.getLine().ToString(), orig.getLine()) } ), true); } else if (m == __FILE__) { StringBuilder buf = new StringBuilder("\""); String name = getSource().getName(); if (name == null) name = "<no file>"; for (int i = 0; i < name.Length; i++) { char c = name[i]; switch (c) { case '\\': buf.Append("\\\\"); break; case '"': buf.Append("\\\""); break; default: buf.Append(c); break; } } buf.Append("\""); String text = buf.ToString(); push_source(new FixedTokenSource( new Token[] { new Token(Token.STRING, orig.getLine(), orig.getColumn(), text, text) } ), true); } else if (m == __COUNTER__) { /* This could equivalently have been done by adding * a special Macro subclass which overrides getTokens(). */ int value = this.counter++; push_source(new FixedTokenSource( new Token[] { new Token(Token.INTEGER, orig.getLine(), orig.getColumn(), value.ToString(), value) } ), true); } else { push_source(new MacroTokenSource(m, args), true); } return true; }