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::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) args->push_back(MacroArg());
    280             isArg = openParens != 1;
    281             break;
    282           default:
    283             isArg = true;
    284             break;
    285         }
    286         if (isArg)
    287         {
    288             MacroArg& arg = args->back();
    289             // Initial whitespace is not part of the argument.
    290             if (arg.empty()) token.setHasLeadingSpace(false);
    291             arg.push_back(token);
    292         }
    293     }
    294 
    295     const Macro::Parameters& params = macro.parameters;
    296     // If there is only one empty argument, it is equivalent to no argument.
    297     if (params.empty() && (args->size() == 1) && args->front().empty())
    298     {
    299         args->clear();
    300     }
    301     // Validate the number of arguments.
    302     if (args->size() != params.size())
    303     {
    304         Diagnostics::ID id = args->size() < macro.parameters.size() ?
    305             Diagnostics::MACRO_TOO_FEW_ARGS :
    306             Diagnostics::MACRO_TOO_MANY_ARGS;
    307         mDiagnostics->report(id, identifier.location, identifier.text);
    308         return false;
    309     }
    310 
    311     // Pre-expand each argument before substitution.
    312     // This step expands each argument individually before they are
    313     // inserted into the macro body.
    314     for (std::size_t i = 0; i < args->size(); ++i)
    315     {
    316         MacroArg& arg = args->at(i);
    317         TokenLexer lexer(&arg);
    318         MacroExpander expander(&lexer, mMacroSet, mDiagnostics);
    319 
    320         arg.clear();
    321         expander.lex(&token);
    322         while (token.type != Token::LAST)
    323         {
    324             arg.push_back(token);
    325             expander.lex(&token);
    326         }
    327     }
    328     return true;
    329 }
    330 
    331 void MacroExpander::replaceMacroParams(const Macro& macro,
    332                                        const std::vector<MacroArg>& args,
    333                                        std::vector<Token>* replacements)
    334 {
    335     for (std::size_t i = 0; i < macro.replacements.size(); ++i)
    336     {
    337         const Token& repl = macro.replacements[i];
    338         if (repl.type != Token::IDENTIFIER)
    339         {
    340             replacements->push_back(repl);
    341             continue;
    342         }
    343 
    344         // TODO(alokp): Optimize this.
    345         // There is no need to search for macro params every time.
    346         // The param index can be cached with the replacement token.
    347         Macro::Parameters::const_iterator iter = std::find(
    348             macro.parameters.begin(), macro.parameters.end(), repl.text);
    349         if (iter == macro.parameters.end())
    350         {
    351             replacements->push_back(repl);
    352             continue;
    353         }
    354 
    355         std::size_t iArg = std::distance(macro.parameters.begin(), iter);
    356         const MacroArg& arg = args[iArg];
    357         if (arg.empty())
    358         {
    359             continue;
    360         }
    361         std::size_t iRepl = replacements->size();
    362         replacements->insert(replacements->end(), arg.begin(), arg.end());
    363         // The replacement token inherits padding properties from
    364         // macro replacement token.
    365         replacements->at(iRepl).setHasLeadingSpace(repl.hasLeadingSpace());
    366     }
    367 }
    368 
    369 }  // namespace pp
    370 
    371