1//
2// Copyright (c) 2011-2013 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 "compiler/preprocessor/DirectiveParser.h"
8
9#include <algorithm>
10#include <cstdlib>
11#include <sstream>
12
13#include "common/debug.h"
14#include "compiler/preprocessor/DiagnosticsBase.h"
15#include "compiler/preprocessor/DirectiveHandlerBase.h"
16#include "compiler/preprocessor/ExpressionParser.h"
17#include "compiler/preprocessor/MacroExpander.h"
18#include "compiler/preprocessor/Token.h"
19#include "compiler/preprocessor/Tokenizer.h"
20
21namespace angle
22{
23
24namespace
25{
26enum DirectiveType
27{
28 DIRECTIVE_NONE,
29 DIRECTIVE_DEFINE,
30 DIRECTIVE_UNDEF,
31 DIRECTIVE_IF,
32 DIRECTIVE_IFDEF,
33 DIRECTIVE_IFNDEF,
34 DIRECTIVE_ELSE,
35 DIRECTIVE_ELIF,
36 DIRECTIVE_ENDIF,
37 DIRECTIVE_ERROR,
38 DIRECTIVE_PRAGMA,
39 DIRECTIVE_EXTENSION,
40 DIRECTIVE_VERSION,
41 DIRECTIVE_LINE
42};
43
44DirectiveType getDirective(const pp::Token *token)
45{
46 const char kDirectiveDefine[] = "define";
47 const char kDirectiveUndef[] = "undef";
48 const char kDirectiveIf[] = "if";
49 const char kDirectiveIfdef[] = "ifdef";
50 const char kDirectiveIfndef[] = "ifndef";
51 const char kDirectiveElse[] = "else";
52 const char kDirectiveElif[] = "elif";
53 const char kDirectiveEndif[] = "endif";
54 const char kDirectiveError[] = "error";
55 const char kDirectivePragma[] = "pragma";
56 const char kDirectiveExtension[] = "extension";
57 const char kDirectiveVersion[] = "version";
58 const char kDirectiveLine[] = "line";
59
60 if (token->type != pp::Token::IDENTIFIER)
61 return DIRECTIVE_NONE;
62
63 if (token->text == kDirectiveDefine)
64 return DIRECTIVE_DEFINE;
65 if (token->text == kDirectiveUndef)
66 return DIRECTIVE_UNDEF;
67 if (token->text == kDirectiveIf)
68 return DIRECTIVE_IF;
69 if (token->text == kDirectiveIfdef)
70 return DIRECTIVE_IFDEF;
71 if (token->text == kDirectiveIfndef)
72 return DIRECTIVE_IFNDEF;
73 if (token->text == kDirectiveElse)
74 return DIRECTIVE_ELSE;
75 if (token->text == kDirectiveElif)
76 return DIRECTIVE_ELIF;
77 if (token->text == kDirectiveEndif)
78 return DIRECTIVE_ENDIF;
79 if (token->text == kDirectiveError)
80 return DIRECTIVE_ERROR;
81 if (token->text == kDirectivePragma)
82 return DIRECTIVE_PRAGMA;
83 if (token->text == kDirectiveExtension)
84 return DIRECTIVE_EXTENSION;
85 if (token->text == kDirectiveVersion)
86 return DIRECTIVE_VERSION;
87 if (token->text == kDirectiveLine)
88 return DIRECTIVE_LINE;
89
90 return DIRECTIVE_NONE;
91}
92
93bool isConditionalDirective(DirectiveType directive)
94{
95 switch (directive)
96 {
97 case DIRECTIVE_IF:
98 case DIRECTIVE_IFDEF:
99 case DIRECTIVE_IFNDEF:
100 case DIRECTIVE_ELSE:
101 case DIRECTIVE_ELIF:
102 case DIRECTIVE_ENDIF:
103 return true;
104 default:
105 return false;
106 }
107}
108
109// Returns true if the token represents End Of Directive.
110bool isEOD(const pp::Token *token)
111{
112 return (token->type == '\n') || (token->type == pp::Token::LAST);
113}
114
115void skipUntilEOD(pp::Lexer *lexer, pp::Token *token)
116{
117 while (!isEOD(token))
118 {
119 lexer->lex(token);
120 }
121}
122
123bool isMacroNameReserved(const std::string &name)
124{
125 // Names prefixed with "GL_" and the name "defined" are reserved.
126 return name == "defined" || (name.substr(0, 3) == "GL_");
127}
128
129bool hasDoubleUnderscores(const std::string &name)
130{
131 return (name.find("__") != std::string::npos);
132}
133
134bool isMacroPredefined(const std::string &name, const pp::MacroSet &macroSet)
135{
136 pp::MacroSet::const_iterator iter = macroSet.find(name);
137 return iter != macroSet.end() ? iter->second->predefined : false;
138}
139
140} // namespace
141
142namespace pp
143{
144DirectiveParser::DirectiveParser(Tokenizer *tokenizer,
145 MacroSet *macroSet,
146 Diagnostics *diagnostics,
147 DirectiveHandler *directiveHandler,
148 const PreprocessorSettings &settings)
149 : mPastFirstStatement(false),
150 mSeenNonPreprocessorToken(false),
151 mTokenizer(tokenizer),
152 mMacroSet(macroSet),
153 mDiagnostics(diagnostics),
154 mDirectiveHandler(directiveHandler),
155 mShaderVersion(100),
156 mSettings(settings)
157{}
158
159DirectiveParser::~DirectiveParser() {}
160
161void DirectiveParser::lex(Token *token)
162{
163 do
164 {
165 mTokenizer->lex(token);
166
167 if (token->type == Token::PP_HASH)
168 {
169 parseDirective(token);
170 mPastFirstStatement = true;
171 }
172 else if (!isEOD(token))
173 {
174 mSeenNonPreprocessorToken = true;
175 }
176
177 if (token->type == Token::LAST)
178 {
179 if (!mConditionalStack.empty())
180 {
181 const ConditionalBlock &block = mConditionalStack.back();
182 mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNTERMINATED, block.location,
183 block.type);
184 }
185 break;
186 }
187
188 } while (skipping() || (token->type == '\n'));
189
190 mPastFirstStatement = true;
191}
192
193void DirectiveParser::parseDirective(Token *token)
194{
195 ASSERT(token->type == Token::PP_HASH);
196
197 mTokenizer->lex(token);
198 if (isEOD(token))
199 {
200 // Empty Directive.
201 return;
202 }
203
204 DirectiveType directive = getDirective(token);
205
206 // While in an excluded conditional block/group,
207 // we only parse conditional directives.
208 if (skipping() && !isConditionalDirective(directive))
209 {
210 skipUntilEOD(mTokenizer, token);
211 return;
212 }
213
214 switch (directive)
215 {
216 case DIRECTIVE_NONE:
217 mDiagnostics->report(Diagnostics::PP_DIRECTIVE_INVALID_NAME, token->location,
218 token->text);
219 skipUntilEOD(mTokenizer, token);
220 break;
221 case DIRECTIVE_DEFINE:
222 parseDefine(token);
223 break;
224 case DIRECTIVE_UNDEF:
225 parseUndef(token);
226 break;
227 case DIRECTIVE_IF:
228 parseIf(token);
229 break;
230 case DIRECTIVE_IFDEF:
231 parseIfdef(token);
232 break;
233 case DIRECTIVE_IFNDEF:
234 parseIfndef(token);
235 break;
236 case DIRECTIVE_ELSE:
237 parseElse(token);
238 break;
239 case DIRECTIVE_ELIF:
240 parseElif(token);
241 break;
242 case DIRECTIVE_ENDIF:
243 parseEndif(token);
244 break;
245 case DIRECTIVE_ERROR:
246 parseError(token);
247 break;
248 case DIRECTIVE_PRAGMA:
249 parsePragma(token);
250 break;
251 case DIRECTIVE_EXTENSION:
252 parseExtension(token);
253 break;
254 case DIRECTIVE_VERSION:
255 parseVersion(token);
256 break;
257 case DIRECTIVE_LINE:
258 parseLine(token);
259 break;
260 default:
261 UNREACHABLE();
262 break;
263 }
264
265 skipUntilEOD(mTokenizer, token);
266 if (token->type == Token::LAST)
267 {
268 mDiagnostics->report(Diagnostics::PP_EOF_IN_DIRECTIVE, token->location, token->text);
269 }
270}
271
272void DirectiveParser::parseDefine(Token *token)
273{
274 ASSERT(getDirective(token) == DIRECTIVE_DEFINE);
275
276 mTokenizer->lex(token);
277 if (token->type != Token::IDENTIFIER)
278 {
279 mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
280 return;
281 }
282 if (isMacroPredefined(token->text, *mMacroSet))
283 {
284 mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_REDEFINED, token->location,
285 token->text);
286 return;
287 }
288 if (isMacroNameReserved(token->text))
289 {
290 mDiagnostics->report(Diagnostics::PP_MACRO_NAME_RESERVED, token->location, token->text);
291 return;
292 }
293 // Using double underscores is allowed, but may result in unintended
294 // behavior, so a warning is issued. At the time of writing this was
295 // specified in ESSL 3.10, but the intent judging from Khronos
296 // discussions and dEQP tests was that double underscores should be
297 // allowed in earlier ESSL versions too.
298 if (hasDoubleUnderscores(token->text))
299 {
300 mDiagnostics->report(Diagnostics::PP_WARNING_MACRO_NAME_RESERVED, token->location,
301 token->text);
302 }
303
304 std::shared_ptr<Macro> macro = std::make_shared<Macro>();
305 macro->type = Macro::kTypeObj;
306 macro->name = token->text;
307
308 mTokenizer->lex(token);
309 if (token->type == '(' && !token->hasLeadingSpace())
310 {
311 // Function-like macro. Collect arguments.
312 macro->type = Macro::kTypeFunc;
313 do
314 {
315 mTokenizer->lex(token);
316 if (token->type != Token::IDENTIFIER)
317 break;
318
319 if (std::find(macro->parameters.begin(), macro->parameters.end(), token->text) !=
320 macro->parameters.end())
321 {
322 mDiagnostics->report(Diagnostics::PP_MACRO_DUPLICATE_PARAMETER_NAMES,
323 token->location, token->text);
324 return;
325 }
326
327 macro->parameters.push_back(token->text);
328
329 mTokenizer->lex(token); // Get ','.
330 } while (token->type == ',');
331
332 if (token->type != ')')
333 {
334 mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
335 return;
336 }
337 mTokenizer->lex(token); // Get ')'.
338 }
339
340 while ((token->type != '\n') && (token->type != Token::LAST))
341 {
342 // Reset the token location because it is unnecessary in replacement
343 // list. Resetting it also allows us to reuse Token::equals() to
344 // compare macros.
345 token->location = SourceLocation();
346 macro->replacements.push_back(*token);
347 mTokenizer->lex(token);
348 }
349 if (!macro->replacements.empty())
350 {
351 // Whitespace preceding the replacement list is not considered part of
352 // the replacement list for either form of macro.
353 macro->replacements.front().setHasLeadingSpace(false);
354 }
355
356 // Check for macro redefinition.
357 MacroSet::const_iterator iter = mMacroSet->find(macro->name);
358 if (iter != mMacroSet->end() && !macro->equals(*iter->second))
359 {
360 mDiagnostics->report(Diagnostics::PP_MACRO_REDEFINED, token->location, macro->name);
361 return;
362 }
363 mMacroSet->insert(std::make_pair(macro->name, macro));
364}
365
366void DirectiveParser::parseUndef(Token *token)
367{
368 ASSERT(getDirective(token) == DIRECTIVE_UNDEF);
369
370 mTokenizer->lex(token);
371 if (token->type != Token::IDENTIFIER)
372 {
373 mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
374 return;
375 }
376
377 MacroSet::iterator iter = mMacroSet->find(token->text);
378 if (iter != mMacroSet->end())
379 {
380 if (iter->second->predefined)
381 {
382 mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_UNDEFINED, token->location,
383 token->text);
384 return;
385 }
386 else if (iter->second->expansionCount > 0)
387 {
388 mDiagnostics->report(Diagnostics::PP_MACRO_UNDEFINED_WHILE_INVOKED, token->location,
389 token->text);
390 return;
391 }
392 else
393 {
394 mMacroSet->erase(iter);
395 }
396 }
397
398 mTokenizer->lex(token);
399 if (!isEOD(token))
400 {
401 mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
402 skipUntilEOD(mTokenizer, token);
403 }
404}
405
406void DirectiveParser::parseIf(Token *token)
407{
408 ASSERT(getDirective(token) == DIRECTIVE_IF);
409 parseConditionalIf(token);
410}
411
412void DirectiveParser::parseIfdef(Token *token)
413{
414 ASSERT(getDirective(token) == DIRECTIVE_IFDEF);
415 parseConditionalIf(token);
416}
417
418void DirectiveParser::parseIfndef(Token *token)
419{
420 ASSERT(getDirective(token) == DIRECTIVE_IFNDEF);
421 parseConditionalIf(token);
422}
423
424void DirectiveParser::parseElse(Token *token)
425{
426 ASSERT(getDirective(token) == DIRECTIVE_ELSE);
427
428 if (mConditionalStack.empty())
429 {
430 mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELSE_WITHOUT_IF, token->location,
431 token->text);
432 skipUntilEOD(mTokenizer, token);
433 return;
434 }
435
436 ConditionalBlock &block = mConditionalStack.back();
437 if (block.skipBlock)
438 {
439 // No diagnostics. Just skip the whole line.
440 skipUntilEOD(mTokenizer, token);
441 return;
442 }
443 if (block.foundElseGroup)
444 {
445 mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELSE_AFTER_ELSE, token->location,
446 token->text);
447 skipUntilEOD(mTokenizer, token);
448 return;
449 }
450
451 block.foundElseGroup = true;
452 block.skipGroup = block.foundValidGroup;
453 block.foundValidGroup = true;
454
455 // Check if there are extra tokens after #else.
456 mTokenizer->lex(token);
457 if (!isEOD(token))
458 {
459 mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, token->location,
460 token->text);
461 skipUntilEOD(mTokenizer, token);
462 }
463}
464
465void DirectiveParser::parseElif(Token *token)
466{
467 ASSERT(getDirective(token) == DIRECTIVE_ELIF);
468
469 if (mConditionalStack.empty())
470 {
471 mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELIF_WITHOUT_IF, token->location,
472 token->text);
473 skipUntilEOD(mTokenizer, token);
474 return;
475 }
476
477 ConditionalBlock &block = mConditionalStack.back();
478 if (block.skipBlock)
479 {
480 // No diagnostics. Just skip the whole line.
481 skipUntilEOD(mTokenizer, token);
482 return;
483 }
484 if (block.foundElseGroup)
485 {
486 mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELIF_AFTER_ELSE, token->location,
487 token->text);
488 skipUntilEOD(mTokenizer, token);
489 return;
490 }
491 if (block.foundValidGroup)
492 {
493 // Do not parse the expression.
494 // Also be careful not to emit a diagnostic.
495 block.skipGroup = true;
496 skipUntilEOD(mTokenizer, token);
497 return;
498 }
499
500 int expression = parseExpressionIf(token);
501 block.skipGroup = expression == 0;
502 block.foundValidGroup = expression != 0;
503}
504
505void DirectiveParser::parseEndif(Token *token)
506{
507 ASSERT(getDirective(token) == DIRECTIVE_ENDIF);
508
509 if (mConditionalStack.empty())
510 {
511 mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ENDIF_WITHOUT_IF, token->location,
512 token->text);
513 skipUntilEOD(mTokenizer, token);
514 return;
515 }
516
517 mConditionalStack.pop_back();
518
519 // Check if there are tokens after #endif.
520 mTokenizer->lex(token);
521 if (!isEOD(token))
522 {
523 mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, token->location,
524 token->text);
525 skipUntilEOD(mTokenizer, token);
526 }
527}
528
529void DirectiveParser::parseError(Token *token)
530{
531 ASSERT(getDirective(token) == DIRECTIVE_ERROR);
532
533 std::ostringstream stream;
534 mTokenizer->lex(token);
535 while ((token->type != '\n') && (token->type != Token::LAST))
536 {
537 stream << *token;
538 mTokenizer->lex(token);
539 }
540 mDirectiveHandler->handleError(token->location, stream.str());
541}
542
543// Parses pragma of form: #pragma name[(value)].
544void DirectiveParser::parsePragma(Token *token)
545{
546 ASSERT(getDirective(token) == DIRECTIVE_PRAGMA);
547
548 enum State
549 {
550 PRAGMA_NAME,
551 LEFT_PAREN,
552 PRAGMA_VALUE,
553 RIGHT_PAREN
554 };
555
556 bool valid = true;
557 std::string name, value;
558 int state = PRAGMA_NAME;
559
560 mTokenizer->lex(token);
561 bool stdgl = token->text == "STDGL";
562 if (stdgl)
563 {
564 mTokenizer->lex(token);
565 }
566 while ((token->type != '\n') && (token->type != Token::LAST))
567 {
568 switch (state++)
569 {
570 case PRAGMA_NAME:
571 name = token->text;
572 valid = valid && (token->type == Token::IDENTIFIER);
573 break;
574 case LEFT_PAREN:
575 valid = valid && (token->type == '(');
576 break;
577 case PRAGMA_VALUE:
578 value = token->text;
579 valid = valid && (token->type == Token::IDENTIFIER);
580 break;
581 case RIGHT_PAREN:
582 valid = valid && (token->type == ')');
583 break;
584 default:
585 valid = false;
586 break;
587 }
588 mTokenizer->lex(token);
589 }
590
591 valid = valid && ((state == PRAGMA_NAME) || // Empty pragma.
592 (state == LEFT_PAREN) || // Without value.
593 (state == RIGHT_PAREN + 1)); // With value.
594 if (!valid)
595 {
596 mDiagnostics->report(Diagnostics::PP_UNRECOGNIZED_PRAGMA, token->location, name);
597 }
598 else if (state > PRAGMA_NAME) // Do not notify for empty pragma.
599 {
600 mDirectiveHandler->handlePragma(token->location, name, value, stdgl);
601 }
602}
603
604void DirectiveParser::parseExtension(Token *token)
605{
606 ASSERT(getDirective(token) == DIRECTIVE_EXTENSION);
607
608 enum State
609 {
610 EXT_NAME,
611 COLON,
612 EXT_BEHAVIOR
613 };
614
615 bool valid = true;
616 std::string name, behavior;
617 int state = EXT_NAME;
618
619 mTokenizer->lex(token);
620 while ((token->type != '\n') && (token->type != Token::LAST))
621 {
622 switch (state++)
623 {
624 case EXT_NAME:
625 if (valid && (token->type != Token::IDENTIFIER))
626 {
627 mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_NAME, token->location,
628 token->text);
629 valid = false;
630 }
631 if (valid)
632 name = token->text;
633 break;
634 case COLON:
635 if (valid && (token->type != ':'))
636 {
637 mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
638 token->text);
639 valid = false;
640 }
641 break;
642 case EXT_BEHAVIOR:
643 if (valid && (token->type != Token::IDENTIFIER))
644 {
645 mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_BEHAVIOR,
646 token->location, token->text);
647 valid = false;
648 }
649 if (valid)
650 behavior = token->text;
651 break;
652 default:
653 if (valid)
654 {
655 mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
656 token->text);
657 valid = false;
658 }
659 break;
660 }
661 mTokenizer->lex(token);
662 }
663 if (valid && (state != EXT_BEHAVIOR + 1))
664 {
665 mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_DIRECTIVE, token->location,
666 token->text);
667 valid = false;
668 }
669 if (valid && mSeenNonPreprocessorToken)
670 {
671 if (mShaderVersion >= 300)
672 {
673 mDiagnostics->report(Diagnostics::PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3,
674 token->location, token->text);
675 valid = false;
676 }
677 else
678 {
679 mDiagnostics->report(Diagnostics::PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1,
680 token->location, token->text);
681 }
682 }
683 if (valid)
684 mDirectiveHandler->handleExtension(token->location, name, behavior);
685}
686
687void DirectiveParser::parseVersion(Token *token)
688{
689 ASSERT(getDirective(token) == DIRECTIVE_VERSION);
690
691 if (mPastFirstStatement)
692 {
693 mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT, token->location,
694 token->text);
695 skipUntilEOD(mTokenizer, token);
696 return;
697 }
698
699 enum State
700 {
701 VERSION_NUMBER,
702 VERSION_PROFILE,
703 VERSION_ENDLINE
704 };
705
706 bool valid = true;
707 int version = 0;
708 int state = VERSION_NUMBER;
709
710 mTokenizer->lex(token);
711 while (valid && (token->type != '\n') && (token->type != Token::LAST))
712 {
713 switch (state)
714 {
715 case VERSION_NUMBER:
716 if (token->type != Token::CONST_INT)
717 {
718 mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_NUMBER, token->location,
719 token->text);
720 valid = false;
721 }
722 if (valid && !token->iValue(&version))
723 {
724 mDiagnostics->report(Diagnostics::PP_INTEGER_OVERFLOW, token->location,
725 token->text);
726 valid = false;
727 }
728 if (valid)
729 {
730 state = (version < 300) ? VERSION_ENDLINE : VERSION_PROFILE;
731 }
732 break;
733 case VERSION_PROFILE:
734 if (token->type != Token::IDENTIFIER || token->text != "es")
735 {
736 mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_DIRECTIVE, token->location,
737 token->text);
738 valid = false;
739 }
740 state = VERSION_ENDLINE;
741 break;
742 default:
743 mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
744 token->text);
745 valid = false;
746 break;
747 }
748
749 mTokenizer->lex(token);
750 }
751
752 if (valid && (state != VERSION_ENDLINE))
753 {
754 mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_DIRECTIVE, token->location,
755 token->text);
756 valid = false;
757 }
758
759 if (valid && version >= 300 && token->location.line > 1)
760 {
761 mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_LINE_ESSL3, token->location,
762 token->text);
763 valid = false;
764 }
765
766 if (valid)
767 {
768 mDirectiveHandler->handleVersion(token->location, version);
769 mShaderVersion = version;
770 PredefineMacro(mMacroSet, "__VERSION__", version);
771 }
772}
773
774void DirectiveParser::parseLine(Token *token)
775{
776 ASSERT(getDirective(token) == DIRECTIVE_LINE);
777
778 bool valid = true;
779 bool parsedFileNumber = false;
780 int line = 0, file = 0;
781
782 MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, mSettings, false);
783
784 // Lex the first token after "#line" so we can check it for EOD.
785 macroExpander.lex(token);
786
787 if (isEOD(token))
788 {
789 mDiagnostics->report(Diagnostics::PP_INVALID_LINE_DIRECTIVE, token->location, token->text);
790 valid = false;
791 }
792 else
793 {
794 ExpressionParser expressionParser(&macroExpander, mDiagnostics);
795 ExpressionParser::ErrorSettings errorSettings;
796
797 // See GLES3 section 12.42
798 errorSettings.integerLiteralsMustFit32BitSignedRange = true;
799
800 errorSettings.unexpectedIdentifier = Diagnostics::PP_INVALID_LINE_NUMBER;
801 // The first token was lexed earlier to check if it was EOD. Include
802 // the token in parsing for a second time by setting the
803 // parsePresetToken flag to true.
804 expressionParser.parse(token, &line, true, errorSettings, &valid);
805 if (!isEOD(token) && valid)
806 {
807 errorSettings.unexpectedIdentifier = Diagnostics::PP_INVALID_FILE_NUMBER;
808 // After parsing the line expression expressionParser has also
809 // advanced to the first token of the file expression - this is the
810 // token that makes the parser reduce the "input" rule for the line
811 // expression and stop. So we're using parsePresetToken = true here
812 // as well.
813 expressionParser.parse(token, &file, true, errorSettings, &valid);
814 parsedFileNumber = true;
815 }
816 if (!isEOD(token))
817 {
818 if (valid)
819 {
820 mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
821 token->text);
822 valid = false;
823 }
824 skipUntilEOD(mTokenizer, token);
825 }
826 }
827
828 if (valid)
829 {
830 mTokenizer->setLineNumber(line);
831 if (parsedFileNumber)
832 mTokenizer->setFileNumber(file);
833 }
834}
835
836bool DirectiveParser::skipping() const
837{
838 if (mConditionalStack.empty())
839 return false;
840
841 const ConditionalBlock &block = mConditionalStack.back();
842 return block.skipBlock || block.skipGroup;
843}
844
845void DirectiveParser::parseConditionalIf(Token *token)
846{
847 ConditionalBlock block;
848 block.type = token->text;
849 block.location = token->location;
850
851 if (skipping())
852 {
853 // This conditional block is inside another conditional group
854 // which is skipped. As a consequence this whole block is skipped.
855 // Be careful not to parse the conditional expression that might
856 // emit a diagnostic.
857 skipUntilEOD(mTokenizer, token);
858 block.skipBlock = true;
859 }
860 else
861 {
862 DirectiveType directive = getDirective(token);
863
864 int expression = 0;
865 switch (directive)
866 {
867 case DIRECTIVE_IF:
868 expression = parseExpressionIf(token);
869 break;
870 case DIRECTIVE_IFDEF:
871 expression = parseExpressionIfdef(token);
872 break;
873 case DIRECTIVE_IFNDEF:
874 expression = parseExpressionIfdef(token) == 0 ? 1 : 0;
875 break;
876 default:
877 UNREACHABLE();
878 break;
879 }
880 block.skipGroup = expression == 0;
881 block.foundValidGroup = expression != 0;
882 }
883 mConditionalStack.push_back(block);
884}
885
886int DirectiveParser::parseExpressionIf(Token *token)
887{
888 ASSERT((getDirective(token) == DIRECTIVE_IF) || (getDirective(token) == DIRECTIVE_ELIF));
889
890 MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, mSettings, true);
891 ExpressionParser expressionParser(&macroExpander, mDiagnostics);
892
893 int expression = 0;
894 ExpressionParser::ErrorSettings errorSettings;
895 errorSettings.integerLiteralsMustFit32BitSignedRange = false;
896 errorSettings.unexpectedIdentifier = Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN;
897
898 bool valid = true;
899 expressionParser.parse(token, &expression, false, errorSettings, &valid);
900
901 // Check if there are tokens after #if expression.
902 if (!isEOD(token))
903 {
904 mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, token->location,
905 token->text);
906 skipUntilEOD(mTokenizer, token);
907 }
908
909 return expression;
910}
911
912int DirectiveParser::parseExpressionIfdef(Token *token)
913{
914 ASSERT((getDirective(token) == DIRECTIVE_IFDEF) || (getDirective(token) == DIRECTIVE_IFNDEF));
915
916 mTokenizer->lex(token);
917 if (token->type != Token::IDENTIFIER)
918 {
919 mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
920 skipUntilEOD(mTokenizer, token);
921 return 0;
922 }
923
924 MacroSet::const_iterator iter = mMacroSet->find(token->text);
925 int expression = iter != mMacroSet->end() ? 1 : 0;
926
927 // Check if there are tokens after #ifdef expression.
928 mTokenizer->lex(token);
929 if (!isEOD(token))
930 {
931 mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, token->location,
932 token->text);
933 skipUntilEOD(mTokenizer, token);
934 }
935 return expression;
936}
937
938} // namespace pp
939
940} // namespace angle
941