blob: f2e42d06bf40f98bf165deeceea7db6824d8c611 [file] [log] [blame]
alokp@chromium.org04d7d222012-05-16 19:24:07 +00001//
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 "DirectiveParser.h"
8
9#include <cassert>
alokp@chromium.org51b96852012-05-30 20:25:05 +000010#include <cstdlib>
alokp@chromium.org36124de82012-05-24 02:17:43 +000011#include <sstream>
alokp@chromium.org04d7d222012-05-16 19:24:07 +000012
alokp@chromium.org2c958ee2012-05-17 20:35:42 +000013#include "Diagnostics.h"
alokp@chromium.org36124de82012-05-24 02:17:43 +000014#include "DirectiveHandler.h"
alokp@chromium.org04d7d222012-05-16 19:24:07 +000015#include "ExpressionParser.h"
16#include "MacroExpander.h"
17#include "Token.h"
18#include "Tokenizer.h"
19
20namespace {
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000021enum DirectiveType
22{
23 DIRECTIVE_NONE,
24 DIRECTIVE_DEFINE,
25 DIRECTIVE_UNDEF,
26 DIRECTIVE_IF,
27 DIRECTIVE_IFDEF,
28 DIRECTIVE_IFNDEF,
29 DIRECTIVE_ELSE,
30 DIRECTIVE_ELIF,
31 DIRECTIVE_ENDIF,
32 DIRECTIVE_ERROR,
33 DIRECTIVE_PRAGMA,
34 DIRECTIVE_EXTENSION,
35 DIRECTIVE_VERSION,
36 DIRECTIVE_LINE
37};
alokp@chromium.org04d7d222012-05-16 19:24:07 +000038} // namespace
39
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000040static DirectiveType getDirective(const pp::Token* token)
41{
42 static const std::string kDirectiveDefine("define");
43 static const std::string kDirectiveUndef("undef");
44 static const std::string kDirectiveIf("if");
45 static const std::string kDirectiveIfdef("ifdef");
46 static const std::string kDirectiveIfndef("ifndef");
47 static const std::string kDirectiveElse("else");
48 static const std::string kDirectiveElif("elif");
49 static const std::string kDirectiveEndif("endif");
50 static const std::string kDirectiveError("error");
51 static const std::string kDirectivePragma("pragma");
52 static const std::string kDirectiveExtension("extension");
53 static const std::string kDirectiveVersion("version");
54 static const std::string kDirectiveLine("line");
55
56 if (token->type != pp::Token::IDENTIFIER)
57 return DIRECTIVE_NONE;
58
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +000059 if (token->text == kDirectiveDefine)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000060 return DIRECTIVE_DEFINE;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +000061 else if (token->text == kDirectiveUndef)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000062 return DIRECTIVE_UNDEF;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +000063 else if (token->text == kDirectiveIf)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000064 return DIRECTIVE_IF;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +000065 else if (token->text == kDirectiveIfdef)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000066 return DIRECTIVE_IFDEF;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +000067 else if (token->text == kDirectiveIfndef)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000068 return DIRECTIVE_IFNDEF;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +000069 else if (token->text == kDirectiveElse)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000070 return DIRECTIVE_ELSE;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +000071 else if (token->text == kDirectiveElif)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000072 return DIRECTIVE_ELIF;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +000073 else if (token->text == kDirectiveEndif)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000074 return DIRECTIVE_ENDIF;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +000075 else if (token->text == kDirectiveError)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000076 return DIRECTIVE_ERROR;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +000077 else if (token->text == kDirectivePragma)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000078 return DIRECTIVE_PRAGMA;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +000079 else if (token->text == kDirectiveExtension)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000080 return DIRECTIVE_EXTENSION;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +000081 else if (token->text == kDirectiveVersion)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000082 return DIRECTIVE_VERSION;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +000083 else if (token->text == kDirectiveLine)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +000084 return DIRECTIVE_LINE;
85
86 return DIRECTIVE_NONE;
87}
88
89static bool isConditionalDirective(DirectiveType directive)
90{
91 switch (directive)
92 {
93 case DIRECTIVE_IF:
94 case DIRECTIVE_IFDEF:
95 case DIRECTIVE_IFNDEF:
96 case DIRECTIVE_ELSE:
97 case DIRECTIVE_ELIF:
98 case DIRECTIVE_ENDIF:
99 return true;
100 default:
101 return false;
102 }
103}
104
105// Returns true if the token represents End Of Directive.
106static bool isEOD(const pp::Token* token)
107{
108 return (token->type == '\n') || (token->type == pp::Token::LAST);
109}
110
111static void skipUntilEOD(pp::Lexer* lexer, pp::Token* token)
112{
113 while(!isEOD(token))
114 {
115 lexer->lex(token);
116 }
117}
118
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000119static bool isMacroNameReserved(const std::string& name)
120{
121 // Names prefixed with "GL_" are reserved.
122 if (name.substr(0, 3) == "GL_")
123 return true;
124
125 // Names containing two consecutive underscores are reserved.
126 if (name.find("__") != std::string::npos)
127 return true;
128
129 return false;
130}
131
alokp@chromium.orgf3cdb462012-06-19 18:39:48 +0000132static bool isMacroPredefined(const std::string& name,
133 const pp::MacroSet& macroSet)
134{
135 pp::MacroSet::const_iterator iter = macroSet.find(name);
136 return iter != macroSet.end() ? iter->second.predefined : false;
137}
138
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000139namespace pp
140{
141
142class DefinedParser : public Lexer
143{
144 public:
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000145 DefinedParser(Lexer* lexer,
146 const MacroSet* macroSet,
147 Diagnostics* diagnostics) :
148 mLexer(lexer),
149 mMacroSet(macroSet),
150 mDiagnostics(diagnostics)
151 {
152 }
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000153
154 protected:
155 virtual void lex(Token* token)
156 {
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000157 static const std::string kDefined("defined");
158
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000159 mLexer->lex(token);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000160 if (token->type != Token::IDENTIFIER)
161 return;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000162 if (token->text != kDefined)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000163 return;
164
165 bool paren = false;
166 mLexer->lex(token);
167 if (token->type == '(')
168 {
169 paren = true;
170 mLexer->lex(token);
171 }
172
173 if (token->type != Token::IDENTIFIER)
174 {
175 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000176 token->location, token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000177 skipUntilEOD(mLexer, token);
178 return;
179 }
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000180 MacroSet::const_iterator iter = mMacroSet->find(token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000181 std::string expression = iter != mMacroSet->end() ? "1" : "0";
182
183 if (paren)
184 {
185 mLexer->lex(token);
186 if (token->type != ')')
187 {
188 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000189 token->location, token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000190 skipUntilEOD(mLexer, token);
191 return;
192 }
193 }
194
195 // We have a valid defined operator.
196 // Convert the current token into a CONST_INT token.
197 token->type = Token::CONST_INT;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000198 token->text = expression;
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000199 }
200
201 private:
202 Lexer* mLexer;
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000203 const MacroSet* mMacroSet;
204 Diagnostics* mDiagnostics;
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000205};
206
alokp@chromium.org2c958ee2012-05-17 20:35:42 +0000207DirectiveParser::DirectiveParser(Tokenizer* tokenizer,
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000208 MacroSet* macroSet,
alokp@chromium.org36124de82012-05-24 02:17:43 +0000209 Diagnostics* diagnostics,
210 DirectiveHandler* directiveHandler) :
alokp@chromium.orgd0d9f872012-07-03 16:06:40 +0000211 mPastFirstStatement(false),
alokp@chromium.org2c958ee2012-05-17 20:35:42 +0000212 mTokenizer(tokenizer),
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000213 mMacroSet(macroSet),
alokp@chromium.org36124de82012-05-24 02:17:43 +0000214 mDiagnostics(diagnostics),
215 mDirectiveHandler(directiveHandler)
alokp@chromium.org2c958ee2012-05-17 20:35:42 +0000216{
217}
218
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000219void DirectiveParser::lex(Token* token)
220{
221 do
222 {
223 mTokenizer->lex(token);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000224
alokp@chromium.org432d6fc2012-06-27 22:13:21 +0000225 if (token->type == Token::PP_HASH)
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000226 {
227 parseDirective(token);
alokp@chromium.orgd0d9f872012-07-03 16:06:40 +0000228 mPastFirstStatement = true;
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000229 }
230
231 if (token->type == Token::LAST)
232 {
233 if (!mConditionalStack.empty())
234 {
235 const ConditionalBlock& block = mConditionalStack.back();
236 mDiagnostics->report(Diagnostics::CONDITIONAL_UNTERMINATED,
237 block.location, block.type);
238 }
239 break;
240 }
241
242 } while (skipping() || (token->type == '\n'));
alokp@chromium.orgd0d9f872012-07-03 16:06:40 +0000243
244 mPastFirstStatement = true;
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000245}
246
247void DirectiveParser::parseDirective(Token* token)
248{
alokp@chromium.org432d6fc2012-06-27 22:13:21 +0000249 assert(token->type == Token::PP_HASH);
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000250
251 mTokenizer->lex(token);
alokp@chromium.orgd0d9f872012-07-03 16:06:40 +0000252 if (isEOD(token))
253 {
254 // Empty Directive.
255 return;
256 }
257
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000258 DirectiveType directive = getDirective(token);
259
260 // While in an excluded conditional block/group,
261 // we only parse conditional directives.
262 if (skipping() && !isConditionalDirective(directive))
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000263 {
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000264 skipUntilEOD(mTokenizer, token);
265 return;
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000266 }
267
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000268 switch(directive)
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000269 {
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000270 case DIRECTIVE_NONE:
271 mDiagnostics->report(Diagnostics::DIRECTIVE_INVALID_NAME,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000272 token->location, token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000273 skipUntilEOD(mTokenizer, token);
274 break;
275 case DIRECTIVE_DEFINE:
276 parseDefine(token);
277 break;
278 case DIRECTIVE_UNDEF:
279 parseUndef(token);
280 break;
281 case DIRECTIVE_IF:
282 parseIf(token);
283 break;
284 case DIRECTIVE_IFDEF:
285 parseIfdef(token);
286 break;
287 case DIRECTIVE_IFNDEF:
288 parseIfndef(token);
289 break;
290 case DIRECTIVE_ELSE:
291 parseElse(token);
292 break;
293 case DIRECTIVE_ELIF:
294 parseElif(token);
295 break;
296 case DIRECTIVE_ENDIF:
297 parseEndif(token);
298 break;
299 case DIRECTIVE_ERROR:
300 parseError(token);
301 break;
302 case DIRECTIVE_PRAGMA:
303 parsePragma(token);
304 break;
305 case DIRECTIVE_EXTENSION:
306 parseExtension(token);
307 break;
308 case DIRECTIVE_VERSION:
309 parseVersion(token);
310 break;
311 case DIRECTIVE_LINE:
312 parseLine(token);
313 break;
314 default:
315 assert(false);
316 break;
317 }
318
319 skipUntilEOD(mTokenizer, token);
320 if (token->type == Token::LAST)
321 {
322 mDiagnostics->report(Diagnostics::EOF_IN_DIRECTIVE,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000323 token->location, token->text);
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000324 }
325}
326
327void DirectiveParser::parseDefine(Token* token)
328{
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000329 assert(getDirective(token) == DIRECTIVE_DEFINE);
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000330
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000331 mTokenizer->lex(token);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000332 if (token->type != Token::IDENTIFIER)
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000333 {
alokp@chromium.orge5dfefa2012-05-24 19:29:37 +0000334 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000335 token->location, token->text);
alokp@chromium.orgf3cdb462012-06-19 18:39:48 +0000336 return;
337 }
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000338 if (isMacroPredefined(token->text, *mMacroSet))
alokp@chromium.orgf3cdb462012-06-19 18:39:48 +0000339 {
340 mDiagnostics->report(Diagnostics::MACRO_PREDEFINED_REDEFINED,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000341 token->location, token->text);
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000342 return;
343 }
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000344 if (isMacroNameReserved(token->text))
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000345 {
346 mDiagnostics->report(Diagnostics::MACRO_NAME_RESERVED,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000347 token->location, token->text);
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000348 return;
349 }
350
351 Macro macro;
352 macro.type = Macro::kTypeObj;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000353 macro.name = token->text;
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000354
355 mTokenizer->lex(token);
356 if (token->type == '(' && !token->hasLeadingSpace())
357 {
358 // Function-like macro. Collect arguments.
359 macro.type = Macro::kTypeFunc;
360 do {
361 mTokenizer->lex(token);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000362 if (token->type != Token::IDENTIFIER)
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000363 break;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000364 macro.parameters.push_back(token->text);
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000365
alokp@chromium.org7fc38dd2012-06-14 18:23:23 +0000366 mTokenizer->lex(token); // Get ','.
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000367 } while (token->type == ',');
368
369 if (token->type != ')')
370 {
alokp@chromium.orge5dfefa2012-05-24 19:29:37 +0000371 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000372 token->location,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000373 token->text);
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000374 return;
375 }
alokp@chromium.org7fc38dd2012-06-14 18:23:23 +0000376 mTokenizer->lex(token); // Get ')'.
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000377 }
378
alokp@chromium.org7c884542012-05-24 19:13:03 +0000379 while ((token->type != '\n') && (token->type != Token::LAST))
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000380 {
381 // Reset the token location because it is unnecessary in replacement
382 // list. Resetting it also allows us to reuse Token::equals() to
383 // compare macros.
384 token->location = SourceLocation();
385 macro.replacements.push_back(*token);
386 mTokenizer->lex(token);
387 }
alokp@chromium.org7fc38dd2012-06-14 18:23:23 +0000388 if (!macro.replacements.empty())
389 {
390 // Whitespace preceding the replacement list is not considered part of
391 // the replacement list for either form of macro.
392 macro.replacements.front().setHasLeadingSpace(false);
393 }
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000394
395 // Check for macro redefinition.
396 MacroSet::const_iterator iter = mMacroSet->find(macro.name);
397 if (iter != mMacroSet->end() && !macro.equals(iter->second))
398 {
399 mDiagnostics->report(Diagnostics::MACRO_REDEFINED,
400 token->location,
401 macro.name);
402 return;
403 }
404 mMacroSet->insert(std::make_pair(macro.name, macro));
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000405}
406
407void DirectiveParser::parseUndef(Token* token)
408{
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000409 assert(getDirective(token) == DIRECTIVE_UNDEF);
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000410
411 mTokenizer->lex(token);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000412 if (token->type != Token::IDENTIFIER)
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000413 {
alokp@chromium.orge5dfefa2012-05-24 19:29:37 +0000414 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000415 token->location, token->text);
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000416 return;
417 }
418
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000419 MacroSet::iterator iter = mMacroSet->find(token->text);
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000420 if (iter != mMacroSet->end())
alokp@chromium.orgf3cdb462012-06-19 18:39:48 +0000421 {
422 if (iter->second.predefined)
423 {
424 mDiagnostics->report(Diagnostics::MACRO_PREDEFINED_UNDEFINED,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000425 token->location, token->text);
alokp@chromium.orgf3cdb462012-06-19 18:39:48 +0000426 }
427 else
428 {
429 mMacroSet->erase(iter);
430 }
431 }
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000432
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000433 mTokenizer->lex(token);
434}
435
436void DirectiveParser::parseIf(Token* token)
437{
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000438 assert(getDirective(token) == DIRECTIVE_IF);
439 parseConditionalIf(token);
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000440}
441
442void DirectiveParser::parseIfdef(Token* token)
443{
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000444 assert(getDirective(token) == DIRECTIVE_IFDEF);
445 parseConditionalIf(token);
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000446}
447
448void DirectiveParser::parseIfndef(Token* token)
449{
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000450 assert(getDirective(token) == DIRECTIVE_IFNDEF);
451 parseConditionalIf(token);
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000452}
453
454void DirectiveParser::parseElse(Token* token)
455{
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000456 assert(getDirective(token) == DIRECTIVE_ELSE);
457
458 if (mConditionalStack.empty())
459 {
460 mDiagnostics->report(Diagnostics::CONDITIONAL_ELSE_WITHOUT_IF,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000461 token->location, token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000462 skipUntilEOD(mTokenizer, token);
463 return;
464 }
465
466 ConditionalBlock& block = mConditionalStack.back();
467 if (block.skipBlock)
468 {
469 // No diagnostics. Just skip the whole line.
470 skipUntilEOD(mTokenizer, token);
471 return;
472 }
473 if (block.foundElseGroup)
474 {
475 mDiagnostics->report(Diagnostics::CONDITIONAL_ELSE_AFTER_ELSE,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000476 token->location, token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000477 skipUntilEOD(mTokenizer, token);
478 return;
479 }
480
481 block.foundElseGroup = true;
482 block.skipGroup = block.foundValidGroup;
483 block.foundValidGroup = true;
484
485 // Warn if there are extra tokens after #else.
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000486 mTokenizer->lex(token);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000487 if (!isEOD(token))
488 {
489 mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000490 token->location, token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000491 skipUntilEOD(mTokenizer, token);
492 }
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000493}
494
495void DirectiveParser::parseElif(Token* token)
496{
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000497 assert(getDirective(token) == DIRECTIVE_ELIF);
498
499 if (mConditionalStack.empty())
500 {
501 mDiagnostics->report(Diagnostics::CONDITIONAL_ELIF_WITHOUT_IF,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000502 token->location, token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000503 skipUntilEOD(mTokenizer, token);
504 return;
505 }
506
507 ConditionalBlock& block = mConditionalStack.back();
508 if (block.skipBlock)
509 {
510 // No diagnostics. Just skip the whole line.
511 skipUntilEOD(mTokenizer, token);
512 return;
513 }
514 if (block.foundElseGroup)
515 {
516 mDiagnostics->report(Diagnostics::CONDITIONAL_ELIF_AFTER_ELSE,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000517 token->location, token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000518 skipUntilEOD(mTokenizer, token);
519 return;
520 }
521 if (block.foundValidGroup)
522 {
523 // Do not parse the expression.
524 // Also be careful not to emit a diagnostic.
525 block.skipGroup = true;
526 skipUntilEOD(mTokenizer, token);
527 return;
528 }
529
530 int expression = parseExpressionIf(token);
531 block.skipGroup = expression == 0;
532 block.foundValidGroup = expression != 0;
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000533}
534
535void DirectiveParser::parseEndif(Token* token)
536{
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000537 assert(getDirective(token) == DIRECTIVE_ENDIF);
538
539 if (mConditionalStack.empty())
540 {
541 mDiagnostics->report(Diagnostics::CONDITIONAL_ENDIF_WITHOUT_IF,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000542 token->location, token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000543 skipUntilEOD(mTokenizer, token);
544 return;
545 }
546
547 mConditionalStack.pop_back();
548
549 // Warn if there are tokens after #endif.
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000550 mTokenizer->lex(token);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000551 if (!isEOD(token))
552 {
553 mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000554 token->location, token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000555 skipUntilEOD(mTokenizer, token);
556 }
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000557}
558
559void DirectiveParser::parseError(Token* token)
560{
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000561 assert(getDirective(token) == DIRECTIVE_ERROR);
alokp@chromium.org36124de82012-05-24 02:17:43 +0000562
alokp@chromium.org2e818912012-06-29 21:26:03 +0000563 std::ostringstream stream;
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000564 mTokenizer->lex(token);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000565 while ((token->type != '\n') && (token->type != Token::LAST))
alokp@chromium.org36124de82012-05-24 02:17:43 +0000566 {
567 stream << *token;
568 mTokenizer->lex(token);
569 }
570 mDirectiveHandler->handleError(token->location, stream.str());
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000571}
572
alokp@chromium.org36124de82012-05-24 02:17:43 +0000573// Parses pragma of form: #pragma name[(value)].
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000574void DirectiveParser::parsePragma(Token* token)
575{
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000576 assert(getDirective(token) == DIRECTIVE_PRAGMA);
alokp@chromium.org36124de82012-05-24 02:17:43 +0000577
578 enum State
579 {
580 PRAGMA_NAME,
581 LEFT_PAREN,
582 PRAGMA_VALUE,
583 RIGHT_PAREN
584 };
585
586 bool valid = true;
587 std::string name, value;
588 int state = PRAGMA_NAME;
589
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000590 mTokenizer->lex(token);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000591 while ((token->type != '\n') && (token->type != Token::LAST))
alokp@chromium.org36124de82012-05-24 02:17:43 +0000592 {
593 switch(state++)
594 {
595 case PRAGMA_NAME:
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000596 name = token->text;
alokp@chromium.org7c884542012-05-24 19:13:03 +0000597 valid = valid && (token->type == Token::IDENTIFIER);
alokp@chromium.org36124de82012-05-24 02:17:43 +0000598 break;
599 case LEFT_PAREN:
600 valid = valid && (token->type == '(');
601 break;
602 case PRAGMA_VALUE:
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000603 value = token->text;
alokp@chromium.org7c884542012-05-24 19:13:03 +0000604 valid = valid && (token->type == Token::IDENTIFIER);
alokp@chromium.org36124de82012-05-24 02:17:43 +0000605 break;
606 case RIGHT_PAREN:
607 valid = valid && (token->type == ')');
608 break;
609 default:
610 valid = false;
611 break;
612 }
613 mTokenizer->lex(token);
614 }
615
616 valid = valid && ((state == PRAGMA_NAME) || // Empty pragma.
617 (state == LEFT_PAREN) || // Without value.
618 (state == RIGHT_PAREN + 1)); // With value.
619 if (!valid)
620 {
621 mDiagnostics->report(Diagnostics::UNRECOGNIZED_PRAGMA,
622 token->location, name);
623 }
624 else if (state > PRAGMA_NAME) // Do not notify for empty pragma.
625 {
626 mDirectiveHandler->handlePragma(token->location, name, value);
627 }
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000628}
629
630void DirectiveParser::parseExtension(Token* token)
631{
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000632 assert(getDirective(token) == DIRECTIVE_EXTENSION);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000633
634 enum State
635 {
636 EXT_NAME,
637 COLON,
638 EXT_BEHAVIOR
639 };
640
641 bool valid = true;
642 std::string name, behavior;
643 int state = EXT_NAME;
644
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000645 mTokenizer->lex(token);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000646 while ((token->type != '\n') && (token->type != Token::LAST))
647 {
648 switch (state++)
649 {
650 case EXT_NAME:
651 if (valid && (token->type != Token::IDENTIFIER))
652 {
653 mDiagnostics->report(Diagnostics::INVALID_EXTENSION_NAME,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000654 token->location, token->text);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000655 valid = false;
656 }
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000657 if (valid) name = token->text;
alokp@chromium.org7c884542012-05-24 19:13:03 +0000658 break;
659 case COLON:
660 if (valid && (token->type != ':'))
661 {
alokp@chromium.orge5dfefa2012-05-24 19:29:37 +0000662 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000663 token->location, token->text);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000664 valid = false;
665 }
666 break;
667 case EXT_BEHAVIOR:
668 if (valid && (token->type != Token::IDENTIFIER))
669 {
670 mDiagnostics->report(Diagnostics::INVALID_EXTENSION_BEHAVIOR,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000671 token->location, token->text);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000672 valid = false;
673 }
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000674 if (valid) behavior = token->text;
alokp@chromium.org7c884542012-05-24 19:13:03 +0000675 break;
676 default:
677 if (valid)
678 {
alokp@chromium.orge5dfefa2012-05-24 19:29:37 +0000679 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000680 token->location, token->text);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000681 valid = false;
682 }
683 break;
684 }
685 mTokenizer->lex(token);
686 }
687 if (valid && (state != EXT_BEHAVIOR + 1))
688 {
689 mDiagnostics->report(Diagnostics::INVALID_EXTENSION_DIRECTIVE,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000690 token->location, token->text);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000691 valid = false;
692 }
693 if (valid)
694 mDirectiveHandler->handleExtension(token->location, name, behavior);
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000695}
696
697void DirectiveParser::parseVersion(Token* token)
698{
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000699 assert(getDirective(token) == DIRECTIVE_VERSION);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000700
alokp@chromium.orgd0d9f872012-07-03 16:06:40 +0000701 if (mPastFirstStatement)
702 {
703 mDiagnostics->report(Diagnostics::VERSION_NOT_FIRST_STATEMENT,
704 token->location, token->text);
705 skipUntilEOD(mTokenizer, token);
706 return;
707 }
708
alokp@chromium.org7c884542012-05-24 19:13:03 +0000709 enum State
710 {
711 VERSION_NUMBER
712 };
713
714 bool valid = true;
715 int version = 0;
716 int state = VERSION_NUMBER;
717
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000718 mTokenizer->lex(token);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000719 while ((token->type != '\n') && (token->type != Token::LAST))
720 {
721 switch (state++)
722 {
723 case VERSION_NUMBER:
724 if (valid && (token->type != Token::CONST_INT))
725 {
726 mDiagnostics->report(Diagnostics::INVALID_VERSION_NUMBER,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000727 token->location, token->text);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000728 valid = false;
729 }
alokp@chromium.org2e818912012-06-29 21:26:03 +0000730 if (valid && !token->iValue(&version))
731 {
732 mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW,
733 token->location, token->text);
734 valid = false;
735 }
alokp@chromium.org7c884542012-05-24 19:13:03 +0000736 break;
737 default:
738 if (valid)
739 {
alokp@chromium.orge5dfefa2012-05-24 19:29:37 +0000740 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000741 token->location, token->text);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000742 valid = false;
743 }
744 break;
745 }
746 mTokenizer->lex(token);
747 }
748 if (valid && (state != VERSION_NUMBER + 1))
749 {
750 mDiagnostics->report(Diagnostics::INVALID_VERSION_DIRECTIVE,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000751 token->location, token->text);
alokp@chromium.org7c884542012-05-24 19:13:03 +0000752 valid = false;
753 }
754 if (valid)
755 mDirectiveHandler->handleVersion(token->location, version);
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000756}
757
758void DirectiveParser::parseLine(Token* token)
759{
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000760 assert(getDirective(token) == DIRECTIVE_LINE);
alokp@chromium.org46aa13d2012-06-15 15:40:27 +0000761
762 enum State
763 {
764 LINE_NUMBER,
765 FILE_NUMBER
766 };
767
768 bool valid = true;
769 int line = 0, file = 0;
770 int state = LINE_NUMBER;
771
alokp@chromium.org98d04ec2012-05-21 22:47:20 +0000772 MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics);
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000773 macroExpander.lex(token);
alokp@chromium.org46aa13d2012-06-15 15:40:27 +0000774 while ((token->type != '\n') && (token->type != Token::LAST))
775 {
776 switch (state++)
777 {
778 case LINE_NUMBER:
779 if (valid && (token->type != Token::CONST_INT))
780 {
781 mDiagnostics->report(Diagnostics::INVALID_LINE_NUMBER,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000782 token->location, token->text);
alokp@chromium.org46aa13d2012-06-15 15:40:27 +0000783 valid = false;
784 }
alokp@chromium.org2e818912012-06-29 21:26:03 +0000785 if (valid && !token->iValue(&line))
786 {
787 mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW,
788 token->location, token->text);
789 valid = false;
790 }
alokp@chromium.org46aa13d2012-06-15 15:40:27 +0000791 break;
792 case FILE_NUMBER:
793 if (valid && (token->type != Token::CONST_INT))
794 {
795 mDiagnostics->report(Diagnostics::INVALID_FILE_NUMBER,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000796 token->location, token->text);
alokp@chromium.org46aa13d2012-06-15 15:40:27 +0000797 valid = false;
798 }
alokp@chromium.org2e818912012-06-29 21:26:03 +0000799 if (valid && !token->iValue(&file))
800 {
801 mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW,
802 token->location, token->text);
803 valid = false;
804 }
alokp@chromium.org46aa13d2012-06-15 15:40:27 +0000805 break;
806 default:
807 if (valid)
808 {
809 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000810 token->location, token->text);
alokp@chromium.org46aa13d2012-06-15 15:40:27 +0000811 valid = false;
812 }
813 break;
814 }
815 macroExpander.lex(token);
816 }
817
818 if (valid && (state != FILE_NUMBER) && (state != FILE_NUMBER + 1))
819 {
820 mDiagnostics->report(Diagnostics::INVALID_LINE_DIRECTIVE,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000821 token->location, token->text);
alokp@chromium.org46aa13d2012-06-15 15:40:27 +0000822 valid = false;
823 }
824 if (valid)
825 {
826 mTokenizer->setLineNumber(line);
827 if (state == FILE_NUMBER + 1) mTokenizer->setFileNumber(file);
828 }
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000829}
830
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000831bool DirectiveParser::skipping() const
832{
833 if (mConditionalStack.empty()) return false;
834
835 const ConditionalBlock& block = mConditionalStack.back();
836 return block.skipBlock || block.skipGroup;
837}
838
839void DirectiveParser::parseConditionalIf(Token* token)
840{
841 ConditionalBlock block;
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000842 block.type = token->text;
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000843 block.location = token->location;
844
845 if (skipping())
846 {
847 // This conditional block is inside another conditional group
848 // which is skipped. As a consequence this whole block is skipped.
849 // Be careful not to parse the conditional expression that might
850 // emit a diagnostic.
851 skipUntilEOD(mTokenizer, token);
852 block.skipBlock = true;
853 }
854 else
855 {
856 DirectiveType directive = getDirective(token);
857
858 int expression = 0;
859 switch (directive)
860 {
861 case DIRECTIVE_IF:
862 expression = parseExpressionIf(token);
863 break;
864 case DIRECTIVE_IFDEF:
865 expression = parseExpressionIfdef(token);
866 break;
867 case DIRECTIVE_IFNDEF:
868 expression = parseExpressionIfdef(token) == 0 ? 1 : 0;
869 break;
870 default:
871 assert(false);
872 break;
873 }
874 block.skipGroup = expression == 0;
875 block.foundValidGroup = expression != 0;
876 }
877 mConditionalStack.push_back(block);
878}
879
880int DirectiveParser::parseExpressionIf(Token* token)
881{
882 assert((getDirective(token) == DIRECTIVE_IF) ||
883 (getDirective(token) == DIRECTIVE_ELIF));
884
885 DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics);
886 MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics);
887 ExpressionParser expressionParser(&macroExpander, mDiagnostics);
888
889 int expression = 0;
890 macroExpander.lex(token);
891 expressionParser.parse(token, &expression);
892
893 // Warn if there are tokens after #if expression.
894 if (!isEOD(token))
895 {
896 mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000897 token->location, token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000898 skipUntilEOD(mTokenizer, token);
899 }
900
901 return expression;
902}
903
904int DirectiveParser::parseExpressionIfdef(Token* token)
905{
906 assert((getDirective(token) == DIRECTIVE_IFDEF) ||
907 (getDirective(token) == DIRECTIVE_IFNDEF));
908
909 mTokenizer->lex(token);
910 if (token->type != Token::IDENTIFIER)
911 {
912 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000913 token->location, token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000914 skipUntilEOD(mTokenizer, token);
915 return 0;
916 }
917
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000918 MacroSet::const_iterator iter = mMacroSet->find(token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000919 int expression = iter != mMacroSet->end() ? 1 : 0;
920
921 // Warn if there are tokens after #ifdef expression.
922 mTokenizer->lex(token);
923 if (!isEOD(token))
924 {
925 mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN,
alokp@chromium.org5b6a68e2012-06-28 20:29:13 +0000926 token->location, token->text);
alokp@chromium.orgd39ec4c2012-06-26 04:37:55 +0000927 skipUntilEOD(mTokenizer, token);
928 }
929 return expression;
930}
931
alokp@chromium.org04d7d222012-05-16 19:24:07 +0000932} // namespace pp