|
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 "DirectiveParser.h" |
|
8 |
|
9 #include <cassert> |
|
10 #include <cstdlib> |
|
11 #include <sstream> |
|
12 |
|
13 #include "DiagnosticsBase.h" |
|
14 #include "DirectiveHandlerBase.h" |
|
15 #include "ExpressionParser.h" |
|
16 #include "MacroExpander.h" |
|
17 #include "Token.h" |
|
18 #include "Tokenizer.h" |
|
19 |
|
20 namespace { |
|
21 enum 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 }; |
|
38 } // namespace |
|
39 |
|
40 static 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 |
|
59 if (token->text == kDirectiveDefine) |
|
60 return DIRECTIVE_DEFINE; |
|
61 else if (token->text == kDirectiveUndef) |
|
62 return DIRECTIVE_UNDEF; |
|
63 else if (token->text == kDirectiveIf) |
|
64 return DIRECTIVE_IF; |
|
65 else if (token->text == kDirectiveIfdef) |
|
66 return DIRECTIVE_IFDEF; |
|
67 else if (token->text == kDirectiveIfndef) |
|
68 return DIRECTIVE_IFNDEF; |
|
69 else if (token->text == kDirectiveElse) |
|
70 return DIRECTIVE_ELSE; |
|
71 else if (token->text == kDirectiveElif) |
|
72 return DIRECTIVE_ELIF; |
|
73 else if (token->text == kDirectiveEndif) |
|
74 return DIRECTIVE_ENDIF; |
|
75 else if (token->text == kDirectiveError) |
|
76 return DIRECTIVE_ERROR; |
|
77 else if (token->text == kDirectivePragma) |
|
78 return DIRECTIVE_PRAGMA; |
|
79 else if (token->text == kDirectiveExtension) |
|
80 return DIRECTIVE_EXTENSION; |
|
81 else if (token->text == kDirectiveVersion) |
|
82 return DIRECTIVE_VERSION; |
|
83 else if (token->text == kDirectiveLine) |
|
84 return DIRECTIVE_LINE; |
|
85 |
|
86 return DIRECTIVE_NONE; |
|
87 } |
|
88 |
|
89 static 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. |
|
106 static bool isEOD(const pp::Token* token) |
|
107 { |
|
108 return (token->type == '\n') || (token->type == pp::Token::LAST); |
|
109 } |
|
110 |
|
111 static void skipUntilEOD(pp::Lexer* lexer, pp::Token* token) |
|
112 { |
|
113 while(!isEOD(token)) |
|
114 { |
|
115 lexer->lex(token); |
|
116 } |
|
117 } |
|
118 |
|
119 static 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 |
|
132 static 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 |
|
139 namespace pp |
|
140 { |
|
141 |
|
142 class DefinedParser : public Lexer |
|
143 { |
|
144 public: |
|
145 DefinedParser(Lexer* lexer, |
|
146 const MacroSet* macroSet, |
|
147 Diagnostics* diagnostics) : |
|
148 mLexer(lexer), |
|
149 mMacroSet(macroSet), |
|
150 mDiagnostics(diagnostics) |
|
151 { |
|
152 } |
|
153 |
|
154 protected: |
|
155 virtual void lex(Token* token) |
|
156 { |
|
157 static const std::string kDefined("defined"); |
|
158 |
|
159 mLexer->lex(token); |
|
160 if (token->type != Token::IDENTIFIER) |
|
161 return; |
|
162 if (token->text != kDefined) |
|
163 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, |
|
176 token->location, token->text); |
|
177 skipUntilEOD(mLexer, token); |
|
178 return; |
|
179 } |
|
180 MacroSet::const_iterator iter = mMacroSet->find(token->text); |
|
181 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, |
|
189 token->location, token->text); |
|
190 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; |
|
198 token->text = expression; |
|
199 } |
|
200 |
|
201 private: |
|
202 Lexer* mLexer; |
|
203 const MacroSet* mMacroSet; |
|
204 Diagnostics* mDiagnostics; |
|
205 }; |
|
206 |
|
207 DirectiveParser::DirectiveParser(Tokenizer* tokenizer, |
|
208 MacroSet* macroSet, |
|
209 Diagnostics* diagnostics, |
|
210 DirectiveHandler* directiveHandler) : |
|
211 mPastFirstStatement(false), |
|
212 mTokenizer(tokenizer), |
|
213 mMacroSet(macroSet), |
|
214 mDiagnostics(diagnostics), |
|
215 mDirectiveHandler(directiveHandler) |
|
216 { |
|
217 } |
|
218 |
|
219 void DirectiveParser::lex(Token* token) |
|
220 { |
|
221 do |
|
222 { |
|
223 mTokenizer->lex(token); |
|
224 |
|
225 if (token->type == Token::PP_HASH) |
|
226 { |
|
227 parseDirective(token); |
|
228 mPastFirstStatement = true; |
|
229 } |
|
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')); |
|
243 |
|
244 mPastFirstStatement = true; |
|
245 } |
|
246 |
|
247 void DirectiveParser::parseDirective(Token* token) |
|
248 { |
|
249 assert(token->type == Token::PP_HASH); |
|
250 |
|
251 mTokenizer->lex(token); |
|
252 if (isEOD(token)) |
|
253 { |
|
254 // Empty Directive. |
|
255 return; |
|
256 } |
|
257 |
|
258 DirectiveType directive = getDirective(token); |
|
259 |
|
260 // While in an excluded conditional block/group, |
|
261 // we only parse conditional directives. |
|
262 if (skipping() && !isConditionalDirective(directive)) |
|
263 { |
|
264 skipUntilEOD(mTokenizer, token); |
|
265 return; |
|
266 } |
|
267 |
|
268 switch(directive) |
|
269 { |
|
270 case DIRECTIVE_NONE: |
|
271 mDiagnostics->report(Diagnostics::DIRECTIVE_INVALID_NAME, |
|
272 token->location, token->text); |
|
273 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, |
|
323 token->location, token->text); |
|
324 } |
|
325 } |
|
326 |
|
327 void DirectiveParser::parseDefine(Token* token) |
|
328 { |
|
329 assert(getDirective(token) == DIRECTIVE_DEFINE); |
|
330 |
|
331 mTokenizer->lex(token); |
|
332 if (token->type != Token::IDENTIFIER) |
|
333 { |
|
334 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
|
335 token->location, token->text); |
|
336 return; |
|
337 } |
|
338 if (isMacroPredefined(token->text, *mMacroSet)) |
|
339 { |
|
340 mDiagnostics->report(Diagnostics::MACRO_PREDEFINED_REDEFINED, |
|
341 token->location, token->text); |
|
342 return; |
|
343 } |
|
344 if (isMacroNameReserved(token->text)) |
|
345 { |
|
346 mDiagnostics->report(Diagnostics::MACRO_NAME_RESERVED, |
|
347 token->location, token->text); |
|
348 return; |
|
349 } |
|
350 |
|
351 Macro macro; |
|
352 macro.type = Macro::kTypeObj; |
|
353 macro.name = token->text; |
|
354 |
|
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); |
|
362 if (token->type != Token::IDENTIFIER) |
|
363 break; |
|
364 macro.parameters.push_back(token->text); |
|
365 |
|
366 mTokenizer->lex(token); // Get ','. |
|
367 } while (token->type == ','); |
|
368 |
|
369 if (token->type != ')') |
|
370 { |
|
371 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
|
372 token->location, |
|
373 token->text); |
|
374 return; |
|
375 } |
|
376 mTokenizer->lex(token); // Get ')'. |
|
377 } |
|
378 |
|
379 while ((token->type != '\n') && (token->type != Token::LAST)) |
|
380 { |
|
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 } |
|
388 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 } |
|
394 |
|
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)); |
|
405 } |
|
406 |
|
407 void DirectiveParser::parseUndef(Token* token) |
|
408 { |
|
409 assert(getDirective(token) == DIRECTIVE_UNDEF); |
|
410 |
|
411 mTokenizer->lex(token); |
|
412 if (token->type != Token::IDENTIFIER) |
|
413 { |
|
414 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
|
415 token->location, token->text); |
|
416 return; |
|
417 } |
|
418 |
|
419 MacroSet::iterator iter = mMacroSet->find(token->text); |
|
420 if (iter != mMacroSet->end()) |
|
421 { |
|
422 if (iter->second.predefined) |
|
423 { |
|
424 mDiagnostics->report(Diagnostics::MACRO_PREDEFINED_UNDEFINED, |
|
425 token->location, token->text); |
|
426 } |
|
427 else |
|
428 { |
|
429 mMacroSet->erase(iter); |
|
430 } |
|
431 } |
|
432 |
|
433 mTokenizer->lex(token); |
|
434 } |
|
435 |
|
436 void DirectiveParser::parseIf(Token* token) |
|
437 { |
|
438 assert(getDirective(token) == DIRECTIVE_IF); |
|
439 parseConditionalIf(token); |
|
440 } |
|
441 |
|
442 void DirectiveParser::parseIfdef(Token* token) |
|
443 { |
|
444 assert(getDirective(token) == DIRECTIVE_IFDEF); |
|
445 parseConditionalIf(token); |
|
446 } |
|
447 |
|
448 void DirectiveParser::parseIfndef(Token* token) |
|
449 { |
|
450 assert(getDirective(token) == DIRECTIVE_IFNDEF); |
|
451 parseConditionalIf(token); |
|
452 } |
|
453 |
|
454 void DirectiveParser::parseElse(Token* token) |
|
455 { |
|
456 assert(getDirective(token) == DIRECTIVE_ELSE); |
|
457 |
|
458 if (mConditionalStack.empty()) |
|
459 { |
|
460 mDiagnostics->report(Diagnostics::CONDITIONAL_ELSE_WITHOUT_IF, |
|
461 token->location, token->text); |
|
462 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, |
|
476 token->location, token->text); |
|
477 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. |
|
486 mTokenizer->lex(token); |
|
487 if (!isEOD(token)) |
|
488 { |
|
489 mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN, |
|
490 token->location, token->text); |
|
491 skipUntilEOD(mTokenizer, token); |
|
492 } |
|
493 } |
|
494 |
|
495 void DirectiveParser::parseElif(Token* token) |
|
496 { |
|
497 assert(getDirective(token) == DIRECTIVE_ELIF); |
|
498 |
|
499 if (mConditionalStack.empty()) |
|
500 { |
|
501 mDiagnostics->report(Diagnostics::CONDITIONAL_ELIF_WITHOUT_IF, |
|
502 token->location, token->text); |
|
503 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, |
|
517 token->location, token->text); |
|
518 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; |
|
533 } |
|
534 |
|
535 void DirectiveParser::parseEndif(Token* token) |
|
536 { |
|
537 assert(getDirective(token) == DIRECTIVE_ENDIF); |
|
538 |
|
539 if (mConditionalStack.empty()) |
|
540 { |
|
541 mDiagnostics->report(Diagnostics::CONDITIONAL_ENDIF_WITHOUT_IF, |
|
542 token->location, token->text); |
|
543 skipUntilEOD(mTokenizer, token); |
|
544 return; |
|
545 } |
|
546 |
|
547 mConditionalStack.pop_back(); |
|
548 |
|
549 // Warn if there are tokens after #endif. |
|
550 mTokenizer->lex(token); |
|
551 if (!isEOD(token)) |
|
552 { |
|
553 mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN, |
|
554 token->location, token->text); |
|
555 skipUntilEOD(mTokenizer, token); |
|
556 } |
|
557 } |
|
558 |
|
559 void DirectiveParser::parseError(Token* token) |
|
560 { |
|
561 assert(getDirective(token) == DIRECTIVE_ERROR); |
|
562 |
|
563 std::ostringstream stream; |
|
564 mTokenizer->lex(token); |
|
565 while ((token->type != '\n') && (token->type != Token::LAST)) |
|
566 { |
|
567 stream << *token; |
|
568 mTokenizer->lex(token); |
|
569 } |
|
570 mDirectiveHandler->handleError(token->location, stream.str()); |
|
571 } |
|
572 |
|
573 // Parses pragma of form: #pragma name[(value)]. |
|
574 void DirectiveParser::parsePragma(Token* token) |
|
575 { |
|
576 assert(getDirective(token) == DIRECTIVE_PRAGMA); |
|
577 |
|
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 |
|
590 mTokenizer->lex(token); |
|
591 while ((token->type != '\n') && (token->type != Token::LAST)) |
|
592 { |
|
593 switch(state++) |
|
594 { |
|
595 case PRAGMA_NAME: |
|
596 name = token->text; |
|
597 valid = valid && (token->type == Token::IDENTIFIER); |
|
598 break; |
|
599 case LEFT_PAREN: |
|
600 valid = valid && (token->type == '('); |
|
601 break; |
|
602 case PRAGMA_VALUE: |
|
603 value = token->text; |
|
604 valid = valid && (token->type == Token::IDENTIFIER); |
|
605 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 } |
|
628 } |
|
629 |
|
630 void DirectiveParser::parseExtension(Token* token) |
|
631 { |
|
632 assert(getDirective(token) == DIRECTIVE_EXTENSION); |
|
633 |
|
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 |
|
645 mTokenizer->lex(token); |
|
646 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, |
|
654 token->location, token->text); |
|
655 valid = false; |
|
656 } |
|
657 if (valid) name = token->text; |
|
658 break; |
|
659 case COLON: |
|
660 if (valid && (token->type != ':')) |
|
661 { |
|
662 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
|
663 token->location, token->text); |
|
664 valid = false; |
|
665 } |
|
666 break; |
|
667 case EXT_BEHAVIOR: |
|
668 if (valid && (token->type != Token::IDENTIFIER)) |
|
669 { |
|
670 mDiagnostics->report(Diagnostics::INVALID_EXTENSION_BEHAVIOR, |
|
671 token->location, token->text); |
|
672 valid = false; |
|
673 } |
|
674 if (valid) behavior = token->text; |
|
675 break; |
|
676 default: |
|
677 if (valid) |
|
678 { |
|
679 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
|
680 token->location, token->text); |
|
681 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, |
|
690 token->location, token->text); |
|
691 valid = false; |
|
692 } |
|
693 if (valid) |
|
694 mDirectiveHandler->handleExtension(token->location, name, behavior); |
|
695 } |
|
696 |
|
697 void DirectiveParser::parseVersion(Token* token) |
|
698 { |
|
699 assert(getDirective(token) == DIRECTIVE_VERSION); |
|
700 |
|
701 if (mPastFirstStatement) |
|
702 { |
|
703 mDiagnostics->report(Diagnostics::VERSION_NOT_FIRST_STATEMENT, |
|
704 token->location, token->text); |
|
705 skipUntilEOD(mTokenizer, token); |
|
706 return; |
|
707 } |
|
708 |
|
709 enum State |
|
710 { |
|
711 VERSION_NUMBER |
|
712 }; |
|
713 |
|
714 bool valid = true; |
|
715 int version = 0; |
|
716 int state = VERSION_NUMBER; |
|
717 |
|
718 mTokenizer->lex(token); |
|
719 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, |
|
727 token->location, token->text); |
|
728 valid = false; |
|
729 } |
|
730 if (valid && !token->iValue(&version)) |
|
731 { |
|
732 mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW, |
|
733 token->location, token->text); |
|
734 valid = false; |
|
735 } |
|
736 break; |
|
737 default: |
|
738 if (valid) |
|
739 { |
|
740 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
|
741 token->location, token->text); |
|
742 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, |
|
751 token->location, token->text); |
|
752 valid = false; |
|
753 } |
|
754 if (valid) |
|
755 mDirectiveHandler->handleVersion(token->location, version); |
|
756 } |
|
757 |
|
758 void DirectiveParser::parseLine(Token* token) |
|
759 { |
|
760 assert(getDirective(token) == DIRECTIVE_LINE); |
|
761 |
|
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 |
|
772 MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics); |
|
773 macroExpander.lex(token); |
|
774 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, |
|
782 token->location, token->text); |
|
783 valid = false; |
|
784 } |
|
785 if (valid && !token->iValue(&line)) |
|
786 { |
|
787 mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW, |
|
788 token->location, token->text); |
|
789 valid = false; |
|
790 } |
|
791 break; |
|
792 case FILE_NUMBER: |
|
793 if (valid && (token->type != Token::CONST_INT)) |
|
794 { |
|
795 mDiagnostics->report(Diagnostics::INVALID_FILE_NUMBER, |
|
796 token->location, token->text); |
|
797 valid = false; |
|
798 } |
|
799 if (valid && !token->iValue(&file)) |
|
800 { |
|
801 mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW, |
|
802 token->location, token->text); |
|
803 valid = false; |
|
804 } |
|
805 break; |
|
806 default: |
|
807 if (valid) |
|
808 { |
|
809 mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, |
|
810 token->location, token->text); |
|
811 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, |
|
821 token->location, token->text); |
|
822 valid = false; |
|
823 } |
|
824 if (valid) |
|
825 { |
|
826 mTokenizer->setLineNumber(line); |
|
827 if (state == FILE_NUMBER + 1) mTokenizer->setFileNumber(file); |
|
828 } |
|
829 } |
|
830 |
|
831 bool 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 |
|
839 void DirectiveParser::parseConditionalIf(Token* token) |
|
840 { |
|
841 ConditionalBlock block; |
|
842 block.type = token->text; |
|
843 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 |
|
880 int 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(¯oExpander, 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, |
|
897 token->location, token->text); |
|
898 skipUntilEOD(mTokenizer, token); |
|
899 } |
|
900 |
|
901 return expression; |
|
902 } |
|
903 |
|
904 int 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, |
|
913 token->location, token->text); |
|
914 skipUntilEOD(mTokenizer, token); |
|
915 return 0; |
|
916 } |
|
917 |
|
918 MacroSet::const_iterator iter = mMacroSet->find(token->text); |
|
919 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, |
|
926 token->location, token->text); |
|
927 skipUntilEOD(mTokenizer, token); |
|
928 } |
|
929 return expression; |
|
930 } |
|
931 |
|
932 } // namespace pp |