예제 #1
0
bool MacroExpander::collectMacroArgs(const Macro &macro,
                                     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 &params = 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;
}
예제 #2
0
bool MacroExpander::collectMacroArgs(const Macro &macro,
                                     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 &params = 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;
}
예제 #3
0
//
// 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());
}