bool MacroExpander::collectMacroArgs(const Macro ¯o, const Token &identifier, std::vector<MacroArg> *args, SourceLocation *closingParenthesisLocation) { Token token; getToken(&token); ASSERT(token.type == '('); args->push_back(MacroArg()); // Defer reenabling macros until args collection is finished to avoid the possibility of // infinite recursion. Otherwise infinite recursion might happen when expanding the args after // macros have been popped from the context stack when parsing the args. ScopedMacroReenabler deferReenablingMacros(this); int openParens = 1; while (openParens != 0) { getToken(&token); if (token.type == Token::LAST) { mDiagnostics->report(Diagnostics::PP_MACRO_UNTERMINATED_INVOCATION, identifier.location, identifier.text); // Do not lose EOF token. ungetToken(token); return false; } bool isArg = false; // True if token is part of the current argument. switch (token.type) { case '(': ++openParens; isArg = true; break; case ')': --openParens; isArg = openParens != 0; *closingParenthesisLocation = token.location; break; case ',': // The individual arguments are separated by comma tokens, but // the comma tokens between matching inner parentheses do not // seperate arguments. if (openParens == 1) args->push_back(MacroArg()); isArg = openParens != 1; break; default: isArg = true; break; } if (isArg) { MacroArg &arg = args->back(); // Initial whitespace is not part of the argument. if (arg.empty()) token.setHasLeadingSpace(false); arg.push_back(token); } } const Macro::Parameters ¶ms = macro.parameters; // If there is only one empty argument, it is equivalent to no argument. if (params.empty() && (args->size() == 1) && args->front().empty()) { args->clear(); } // Validate the number of arguments. if (args->size() != params.size()) { Diagnostics::ID id = args->size() < macro.parameters.size() ? Diagnostics::PP_MACRO_TOO_FEW_ARGS : Diagnostics::PP_MACRO_TOO_MANY_ARGS; mDiagnostics->report(id, identifier.location, identifier.text); return false; } // Pre-expand each argument before substitution. // This step expands each argument individually before they are // inserted into the macro body. size_t numTokens = 0; for (auto &arg : *args) { TokenLexer lexer(&arg); if (mAllowedMacroExpansionDepth < 1) { mDiagnostics->report(Diagnostics::PP_MACRO_INVOCATION_CHAIN_TOO_DEEP, token.location, token.text); return false; } MacroExpander expander(&lexer, mMacroSet, mDiagnostics, mAllowedMacroExpansionDepth - 1); arg.clear(); expander.lex(&token); while (token.type != Token::LAST) { arg.push_back(token); expander.lex(&token); numTokens++; if (numTokens + mTotalTokensInContexts > kMaxContextTokens) { mDiagnostics->report(Diagnostics::PP_OUT_OF_MEMORY, token.location, token.text); return false; } } } return true; }
bool MacroExpander::collectMacroArgs(const Macro ¯o, const Token &identifier, std::vector<MacroArg> *args, SourceLocation *closingParenthesisLocation) { Token token; getToken(&token); assert(token.type == '('); args->push_back(MacroArg()); for (int openParens = 1; openParens != 0; ) { getToken(&token); if (token.type == Token::LAST) { mDiagnostics->report(Diagnostics::PP_MACRO_UNTERMINATED_INVOCATION, identifier.location, identifier.text); // Do not lose EOF token. ungetToken(token); return false; } bool isArg = false; // True if token is part of the current argument. switch (token.type) { case '(': ++openParens; isArg = true; break; case ')': --openParens; isArg = openParens != 0; *closingParenthesisLocation = token.location; break; case ',': // The individual arguments are separated by comma tokens, but // the comma tokens between matching inner parentheses do not // seperate arguments. if (openParens == 1) args->push_back(MacroArg()); isArg = openParens != 1; break; default: isArg = true; break; } if (isArg) { MacroArg &arg = args->back(); // Initial whitespace is not part of the argument. if (arg.empty()) token.setHasLeadingSpace(false); arg.push_back(token); } } const Macro::Parameters ¶ms = macro.parameters; // If there is only one empty argument, it is equivalent to no argument. if (params.empty() && (args->size() == 1) && args->front().empty()) { args->clear(); } // Validate the number of arguments. if (args->size() != params.size()) { Diagnostics::ID id = args->size() < macro.parameters.size() ? Diagnostics::PP_MACRO_TOO_FEW_ARGS : Diagnostics::PP_MACRO_TOO_MANY_ARGS; mDiagnostics->report(id, identifier.location, identifier.text); return false; } // Pre-expand each argument before substitution. // This step expands each argument individually before they are // inserted into the macro body. for (std::size_t i = 0; i < args->size(); ++i) { MacroArg &arg = args->at(i); TokenLexer lexer(&arg); MacroExpander expander(&lexer, mMacroSet, mDiagnostics, mParseDefined); arg.clear(); expander.lex(&token); while (token.type != Token::LAST) { arg.push_back(token); expander.lex(&token); } } return true; }
// // SourceTokenizerC::readArgs // // Process function-like macro arguments, preserving whitespace in TT_NONEs. // void SourceTokenizerC::readArgs(MacroArgs &args, SourceStream *in, MacroData const &data, SourcePosition const &pos, MacroArgs const *altArgs, MacroParm const *altParm) { MacroArg const *arg; MacroParm const &parm = data.first; int pdepth = 0; // If there are no arguments expected, then this is just a simple assertion. if(parm.empty()) { doAssert(SourceTokenC::create(in), SourceTokenC::TT_PAREN_C); } // Otherwise, start reading args. Each arg being a vector of tokens. // First thing is to create the first vector, though. else for(args.push_back(MacroArg());;) { // Convert each whitespace character into a TT_NONE token. // These are used by operator # to add spaces. while (std::isspace(in->peek())) { in->get(); args.back().push_back(SourceTokenC::tt_none()); } // Read the token. SourceTokenC::Reference tok = SourceTokenC::create(in); // If it's a parenthesis, terminate on the close-parenthesis that matches // the initial one that started the expansion. if(tok->type == SourceTokenC::TT_PAREN_O) ++pdepth; if(tok->type == SourceTokenC::TT_PAREN_C && !pdepth--) break; // A comma not in parentheses starts a new argument... if(tok->type == SourceTokenC::TT_COMMA && !pdepth && // ... Unless we've reached the __VA_ARGS__ segment. (args.size() < parm.size() || !parm.back().empty())) { args.push_back(MacroArg()); continue; } // If we're being called from a function-like macro expansion and we see // one of its arguments, expand it. if(tok->type == SourceTokenC::TT_NAM && altArgs && altParm && (arg = find_arg(*altArgs, *altParm, tok->data))) { for(MacroArg::const_iterator tokEnd = arg->end(), tokItr = arg->begin(); tokItr != tokEnd; ++tokItr) { args.back().push_back(*tokItr); } } else args.back().push_back(tok); } // Must not have empty args. for(MacroArgs::iterator argsEnd = args.end(), argsItr = args.begin(); argsItr != argsEnd; ++argsItr) { if(argsItr->empty()) argsItr->push_back(SourceTokenC::tt_none()); } // And of course, must have the right number of arguments. if(args.size() != parm.size()) Error_P("incorrect arg count for macro, expected %i got %i", (int)parm.size(), (int)args.size()); }