/* 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. */ }
/** * Defines the given name as a macro. * * The String value is lexed into a token stream, which is * used as the macro expansion. */ public void addMacro(String name, String value) { try { Macro m = new Macro(name); StringLexerSource s = new StringLexerSource(value); for (;;) { Token tok = s.token(); if(tok.getType() == Token.EOF) break; m.addToken(tok); } addMacro(m); } catch (IOException e) { throw new LexerException(e); } }