• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
TokenLexer(TokenVector * tokens)23     TokenLexer(TokenVector* tokens)
24     {
25         tokens->swap(mTokens);
26         mIter = mTokens.begin();
27     }
28 
lex(Token * token)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 
MacroExpander(Lexer * lexer,MacroSet * macroSet,Diagnostics * diagnostics)49 MacroExpander::MacroExpander(Lexer* lexer,
50                              MacroSet* macroSet,
51                              Diagnostics* diagnostics) :
52     mLexer(lexer),
53     mMacroSet(macroSet),
54     mDiagnostics(diagnostics)
55 {
56 }
57 
~MacroExpander()58 MacroExpander::~MacroExpander()
59 {
60     for (std::size_t i = 0; i < mContextStack.size(); ++i)
61     {
62         delete mContextStack[i];
63     }
64 }
65 
lex(Token * token)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 
getToken(Token * token)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 
ungetToken(const Token & token)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 
isNextTokenLeftParen()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 
pushMacro(const Macro & macro,const Token & identifier)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 
popMacro()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 
expandMacro(const Macro & macro,const Token & identifier,std::vector<Token> * replacements)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 
collectMacroArgs(const Macro & macro,const Token & identifier,std::vector<MacroArg> * args)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) 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::PP_MACRO_TOO_FEW_ARGS :
306             Diagnostics::PP_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 
replaceMacroParams(const Macro & macro,const std::vector<MacroArg> & args,std::vector<Token> * replacements)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