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 = ¯o;
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