Home | History | Annotate | Download | only in preprocessor
      1 //
      2 // Copyright (c) 2011 The ANGLE Project Authors. All rights reserved.
      3 // Use of this source code is governed by a BSD-style license that can be
      4 // found in the LICENSE file.
      5 //
      6 
      7 #include "MacroExpander.h"
      8 
      9 #include <algorithm>
     10 #include <sstream>
     11 
     12 #include "DiagnosticsBase.h"
     13 #include "Token.h"
     14 
     15 namespace pp
     16 {
     17 
     18 class TokenLexer : public Lexer
     19 {
     20  public:
     21     typedef std::vector<Token> TokenVector;
     22 
     23     TokenLexer(TokenVector *tokens)
     24     {
     25         tokens->swap(mTokens);
     26         mIter = mTokens.begin();
     27     }
     28 
     29     virtual void lex(Token *token)
     30     {
     31         if (mIter == mTokens.end())
     32         {
     33             token->reset();
     34             token->type = Token::LAST;
     35         }
     36         else
     37         {
     38             *token = *mIter++;
     39         }
     40     }
     41 
     42  private:
     43     PP_DISALLOW_COPY_AND_ASSIGN(TokenLexer);
     44 
     45     TokenVector mTokens;
     46     TokenVector::const_iterator mIter;
     47 };
     48 
     49 MacroExpander::MacroExpander(Lexer *lexer,
     50                              MacroSet *macroSet,
     51                              Diagnostics *diagnostics)
     52     : mLexer(lexer),
     53       mMacroSet(macroSet),
     54       mDiagnostics(diagnostics)
     55 {
     56 }
     57 
     58 MacroExpander::~MacroExpander()
     59 {
     60     for (std::size_t i = 0; i < mContextStack.size(); ++i)
     61     {
     62         delete mContextStack[i];
     63     }
     64 }
     65 
     66 void MacroExpander::lex(Token *token)
     67 {
     68     while (true)
     69     {
     70         getToken(token);
     71 
     72         if (token->type != Token::IDENTIFIER)
     73             break;
     74 
     75         if (token->expansionDisabled())
     76             break;
     77 
     78         MacroSet::const_iterator iter = mMacroSet->find(token->text);
     79         if (iter == mMacroSet->end())
     80             break;
     81 
     82         const Macro& macro = iter->second;
     83         if (macro.disabled)
     84         {
     85             // If a particular token is not expanded, it is never expanded.
     86             token->setExpansionDisabled(true);
     87             break;
     88         }
     89         if ((macro.type == Macro::kTypeFunc) && !isNextTokenLeftParen())
     90         {
     91             // If the token immediately after the macro name is not a '(',
     92             // this macro should not be expanded.
     93             break;
     94         }
     95 
     96         pushMacro(macro, *token);
     97     }
     98 }
     99 
    100 void MacroExpander::getToken(Token *token)
    101 {
    102     if (mReserveToken.get())
    103     {
    104         *token = *mReserveToken;
    105         mReserveToken.reset();
    106         return;
    107     }
    108 
    109     // First pop all empty macro contexts.
    110     while (!mContextStack.empty() && mContextStack.back()->empty())
    111     {
    112         popMacro();
    113     }
    114 
    115     if (!mContextStack.empty())
    116     {
    117         *token = mContextStack.back()->get();
    118     }
    119     else
    120     {
    121         mLexer->lex(token);
    122     }
    123 }
    124 
    125 void MacroExpander::ungetToken(const Token &token)
    126 {
    127     if (!mContextStack.empty())
    128     {
    129         MacroContext *context = mContextStack.back();
    130         context->unget();
    131         assert(context->replacements[context->index] == token);
    132     }
    133     else
    134     {
    135         assert(!mReserveToken.get());
    136         mReserveToken.reset(new Token(token));
    137     }
    138 }
    139 
    140 bool MacroExpander::isNextTokenLeftParen()
    141 {
    142     Token token;
    143     getToken(&token);
    144 
    145     bool lparen = token.type == '(';
    146     ungetToken(token);
    147 
    148     return lparen;
    149 }
    150 
    151 bool MacroExpander::pushMacro(const Macro &macro, const Token &identifier)
    152 {
    153     assert(!macro.disabled);
    154     assert(!identifier.expansionDisabled());
    155     assert(identifier.type == Token::IDENTIFIER);
    156     assert(identifier.text == macro.name);
    157 
    158     std::vector<Token> replacements;
    159     if (!expandMacro(macro, identifier, &replacements))
    160         return false;
    161 
    162     // Macro is disabled for expansion until it is popped off the stack.
    163     macro.disabled = true;
    164 
    165     MacroContext *context = new MacroContext;
    166     context->macro = &macro;
    167     context->replacements.swap(replacements);
    168     mContextStack.push_back(context);
    169     return true;
    170 }
    171 
    172 void MacroExpander::popMacro()
    173 {
    174     assert(!mContextStack.empty());
    175 
    176     MacroContext *context = mContextStack.back();
    177     mContextStack.pop_back();
    178 
    179     assert(context->empty());
    180     assert(context->macro->disabled);
    181     context->macro->disabled = false;
    182     delete context;
    183 }
    184 
    185 bool MacroExpander::expandMacro(const Macro &macro,
    186                                 const Token &identifier,
    187                                 std::vector<Token> *replacements)
    188 {
    189     replacements->clear();
    190     if (macro.type == Macro::kTypeObj)
    191     {
    192         replacements->assign(macro.replacements.begin(),
    193                              macro.replacements.end());
    194 
    195         if (macro.predefined)
    196         {
    197             static const std::string kLine = "__LINE__";
    198             static const std::string kFile = "__FILE__";
    199 
    200             assert(replacements->size() == 1);
    201             Token& repl = replacements->front();
    202             if (macro.name == kLine)
    203             {
    204                 std::ostringstream stream;
    205                 stream << identifier.location.line;
    206                 repl.text = stream.str();
    207             }
    208             else if (macro.name == kFile)
    209             {
    210                 std::ostringstream stream;
    211                 stream << identifier.location.file;
    212                 repl.text = stream.str();
    213             }
    214         }
    215     }
    216     else
    217     {
    218         assert(macro.type == Macro::kTypeFunc);
    219         std::vector<MacroArg> args;
    220         args.reserve(macro.parameters.size());
    221         if (!collectMacroArgs(macro, identifier, &args))
    222             return false;
    223 
    224         replaceMacroParams(macro, args, replacements);
    225     }
    226 
    227     for (std::size_t i = 0; i < replacements->size(); ++i)
    228     {
    229         Token& repl = replacements->at(i);
    230         if (i == 0)
    231         {
    232             // The first token in the replacement list inherits the padding
    233             // properties of the identifier token.
    234             repl.setAtStartOfLine(identifier.atStartOfLine());
    235             repl.setHasLeadingSpace(identifier.hasLeadingSpace());
    236         }
    237         repl.location = identifier.location;
    238     }
    239     return true;
    240 }
    241 
    242 bool MacroExpander::collectMacroArgs(const Macro &macro,
    243                                      const Token &identifier,
    244                                      std::vector<MacroArg> *args)
    245 {
    246     Token token;
    247     getToken(&token);
    248     assert(token.type == '(');
    249 
    250     args->push_back(MacroArg());
    251     for (int openParens = 1; openParens != 0; )
    252     {
    253         getToken(&token);
    254 
    255         if (token.type == Token::LAST)
    256         {
    257             mDiagnostics->report(Diagnostics::PP_MACRO_UNTERMINATED_INVOCATION,
    258                                  identifier.location, identifier.text);
    259             // Do not lose EOF token.
    260             ungetToken(token);
    261             return false;
    262         }
    263 
    264         bool isArg = false; // True if token is part of the current argument.
    265         switch (token.type)
    266         {
    267           case '(':
    268             ++openParens;
    269             isArg = true;
    270             break;
    271           case ')':
    272             --openParens;
    273             isArg = openParens != 0;
    274             break;
    275           case ',':
    276             // The individual arguments are separated by comma tokens, but
    277             // the comma tokens between matching inner parentheses do not
    278             // seperate arguments.
    279             if (openParens == 1)
    280                 args->push_back(MacroArg());
    281             isArg = openParens != 1;
    282             break;
    283           default:
    284             isArg = true;
    285             break;
    286         }
    287         if (isArg)
    288         {
    289             MacroArg &arg = args->back();
    290             // Initial whitespace is not part of the argument.
    291             if (arg.empty())
    292                 token.setHasLeadingSpace(false);
    293             arg.push_back(token);
    294         }
    295     }
    296 
    297     const Macro::Parameters &params = macro.parameters;
    298     // If there is only one empty argument, it is equivalent to no argument.
    299     if (params.empty() && (args->size() == 1) && args->front().empty())
    300     {
    301         args->clear();
    302     }
    303     // Validate the number of arguments.
    304     if (args->size() != params.size())
    305     {
    306         Diagnostics::ID id = args->size() < macro.parameters.size() ?
    307             Diagnostics::PP_MACRO_TOO_FEW_ARGS :
    308             Diagnostics::PP_MACRO_TOO_MANY_ARGS;
    309         mDiagnostics->report(id, identifier.location, identifier.text);
    310         return false;
    311     }
    312 
    313     // Pre-expand each argument before substitution.
    314     // This step expands each argument individually before they are
    315     // inserted into the macro body.
    316     for (std::size_t i = 0; i < args->size(); ++i)
    317     {
    318         MacroArg &arg = args->at(i);
    319         TokenLexer lexer(&arg);
    320         MacroExpander expander(&lexer, mMacroSet, mDiagnostics);
    321 
    322         arg.clear();
    323         expander.lex(&token);
    324         while (token.type != Token::LAST)
    325         {
    326             arg.push_back(token);
    327             expander.lex(&token);
    328         }
    329     }
    330     return true;
    331 }
    332 
    333 void MacroExpander::replaceMacroParams(const Macro &macro,
    334                                        const std::vector<MacroArg> &args,
    335                                        std::vector<Token> *replacements)
    336 {
    337     for (std::size_t i = 0; i < macro.replacements.size(); ++i)
    338     {
    339         const Token &repl = macro.replacements[i];
    340         if (repl.type != Token::IDENTIFIER)
    341         {
    342             replacements->push_back(repl);
    343             continue;
    344         }
    345 
    346         // TODO(alokp): Optimize this.
    347         // There is no need to search for macro params every time.
    348         // The param index can be cached with the replacement token.
    349         Macro::Parameters::const_iterator iter = std::find(
    350             macro.parameters.begin(), macro.parameters.end(), repl.text);
    351         if (iter == macro.parameters.end())
    352         {
    353             replacements->push_back(repl);
    354             continue;
    355         }
    356 
    357         std::size_t iArg = std::distance(macro.parameters.begin(), iter);
    358         const MacroArg &arg = args[iArg];
    359         if (arg.empty())
    360         {
    361             continue;
    362         }
    363         std::size_t iRepl = replacements->size();
    364         replacements->insert(replacements->end(), arg.begin(), arg.end());
    365         // The replacement token inherits padding properties from
    366         // macro replacement token.
    367         replacements->at(iRepl).setHasLeadingSpace(repl.hasLeadingSpace());
    368     }
    369 }
    370 
    371 }  // namespace pp
    372 
    373