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 if (mSettings.shaderSpec == SH_WEBGL_SPEC)
680 {
681 mDiagnostics->report(Diagnostics::PP_NON_PP_TOKEN_BEFORE_EXTENSION_WEBGL,
682 token->location, token->text);
683 }
684 else
685 {
686 mDiagnostics->report(Diagnostics::PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1,
687 token->location, token->text);
688 }
689 }
690 }
691 if (valid)
692 mDirectiveHandler->handleExtension(token->location, name, behavior);
693}
694
695void DirectiveParser::parseVersion(Token *token)
696{
697 ASSERT(getDirective(token) == DIRECTIVE_VERSION);
698
699 if (mPastFirstStatement)
700 {
701 mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT, token->location,
702 token->text);
703 skipUntilEOD(mTokenizer, token);
704 return;
705 }
706
707 enum State
708 {
709 VERSION_NUMBER,
710 VERSION_PROFILE,
711 VERSION_ENDLINE
712 };
713
714 bool valid = true;
715 int version = 0;
716 int state = VERSION_NUMBER;
717
718 mTokenizer->lex(token);
719 while (valid && (token->type != '\n') && (token->type != Token::LAST))
720 {
721 switch (state)
722 {
723 case VERSION_NUMBER:
724 if (token->type != Token::CONST_INT)
725 {
726 mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_NUMBER, token->location,
727 token->text);
728 valid = false;
729 }
730 if (valid && !token->iValue(&version))
731 {
732 mDiagnostics->report(Diagnostics::PP_INTEGER_OVERFLOW, token->location,
733 token->text);
734 valid = false;
735 }
736 if (valid)
737 {
738 state = (version < 300) ? VERSION_ENDLINE : VERSION_PROFILE;
739 }
740 break;
741 case VERSION_PROFILE:
742 if (token->type != Token::IDENTIFIER || token->text != "es")
743 {
744 mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_DIRECTIVE, token->location,
745 token->text);
746 valid = false;
747 }
748 state = VERSION_ENDLINE;
749 break;
750 default:
751 mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
752 token->text);
753 valid = false;
754 break;
755 }
756
757 mTokenizer->lex(token);
758 }
759
760 if (valid && (state != VERSION_ENDLINE))
761 {
762 mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_DIRECTIVE, token->location,
763 token->text);
764 valid = false;
765 }
766
767 if (valid && version >= 300 && token->location.line > 1)
768 {
769 mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_LINE_ESSL3, token->location,
770 token->text);
771 valid = false;
772 }
773
774 if (valid)
775 {
776 mDirectiveHandler->handleVersion(token->location, version);
777 mShaderVersion = version;
778 PredefineMacro(mMacroSet, "__VERSION__", version);
779 }
780}
781
782void DirectiveParser::parseLine(Token *token)
783{
784 ASSERT(getDirective(token) == DIRECTIVE_LINE);
785
786 bool valid = true;
787 bool parsedFileNumber = false;
788 int line = 0, file = 0;
789
790 MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, mSettings, false);
791
792 // Lex the first token after "#line" so we can check it for EOD.
793 macroExpander.lex(token);
794
795 if (isEOD(token))
796 {
797 mDiagnostics->report(Diagnostics::PP_INVALID_LINE_DIRECTIVE, token->location, token->text);
798 valid = false;
799 }
800 else
801 {
802 ExpressionParser expressionParser(&macroExpander, mDiagnostics);
803 ExpressionParser::ErrorSettings errorSettings;
804
805 // See GLES3 section 12.42
806 errorSettings.integerLiteralsMustFit32BitSignedRange = true;
807
808 errorSettings.unexpectedIdentifier = Diagnostics::PP_INVALID_LINE_NUMBER;
809 // The first token was lexed earlier to check if it was EOD. Include
810 // the token in parsing for a second time by setting the
811 // parsePresetToken flag to true.
812 expressionParser.parse(token, &line, true, errorSettings, &valid);
813 if (!isEOD(token) && valid)
814 {
815 errorSettings.unexpectedIdentifier = Diagnostics::PP_INVALID_FILE_NUMBER;
816 // After parsing the line expression expressionParser has also
817 // advanced to the first token of the file expression - this is the
818 // token that makes the parser reduce the "input" rule for the line
819 // expression and stop. So we're using parsePresetToken = true here
820 // as well.
821 expressionParser.parse(token, &file, true, errorSettings, &valid);
822 parsedFileNumber = true;
823 }
824 if (!isEOD(token))
825 {
826 if (valid)
827 {
828 mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location,
829 token->text);
830 valid = false;
831 }
832 skipUntilEOD(mTokenizer, token);
833 }
834 }
835
836 if (valid)
837 {
838 mTokenizer->setLineNumber(line);
839 if (parsedFileNumber)
840 mTokenizer->setFileNumber(file);
841 }
842}
843
844bool DirectiveParser::skipping() const
845{
846 if (mConditionalStack.empty())
847 return false;
848
849 const ConditionalBlock &block = mConditionalStack.back();
850 return block.skipBlock || block.skipGroup;
851}
852
853void DirectiveParser::parseConditionalIf(Token *token)
854{
855 ConditionalBlock block;
856 block.type = token->text;
857 block.location = token->location;
858
859 if (skipping())
860 {
861 // This conditional block is inside another conditional group
862 // which is skipped. As a consequence this whole block is skipped.
863 // Be careful not to parse the conditional expression that might
864 // emit a diagnostic.
865 skipUntilEOD(mTokenizer, token);
866 block.skipBlock = true;
867 }
868 else
869 {
870 DirectiveType directive = getDirective(token);
871
872 int expression = 0;
873 switch (directive)
874 {
875 case DIRECTIVE_IF:
876 expression = parseExpressionIf(token);
877 break;
878 case DIRECTIVE_IFDEF:
879 expression = parseExpressionIfdef(token);
880 break;
881 case DIRECTIVE_IFNDEF:
882 expression = parseExpressionIfdef(token) == 0 ? 1 : 0;
883 break;
884 default:
885 UNREACHABLE();
886 break;
887 }
888 block.skipGroup = expression == 0;
889 block.foundValidGroup = expression != 0;
890 }
891 mConditionalStack.push_back(block);
892}
893
894int DirectiveParser::parseExpressionIf(Token *token)
895{
896 ASSERT((getDirective(token) == DIRECTIVE_IF) || (getDirective(token) == DIRECTIVE_ELIF));
897
898 MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, mSettings, true);
899 ExpressionParser expressionParser(&macroExpander, mDiagnostics);
900
901 int expression = 0;
902 ExpressionParser::ErrorSettings errorSettings;
903 errorSettings.integerLiteralsMustFit32BitSignedRange = false;
904 errorSettings.unexpectedIdentifier = Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN;
905
906 bool valid = true;
907 expressionParser.parse(token, &expression, false, errorSettings, &valid);
908
909 // Check if there are tokens after #if expression.
910 if (!isEOD(token))
911 {
912 mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, token->location,
913 token->text);
914 skipUntilEOD(mTokenizer, token);
915 }
916
917 return expression;
918}
919
920int DirectiveParser::parseExpressionIfdef(Token *token)
921{
922 ASSERT((getDirective(token) == DIRECTIVE_IFDEF) || (getDirective(token) == DIRECTIVE_IFNDEF));
923
924 mTokenizer->lex(token);
925 if (token->type != Token::IDENTIFIER)
926 {
927 mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text);
928 skipUntilEOD(mTokenizer, token);
929 return 0;
930 }
931
932 MacroSet::const_iterator iter = mMacroSet->find(token->text);
933 int expression = iter != mMacroSet->end() ? 1 : 0;
934
935 // Check if there are tokens after #ifdef expression.
936 mTokenizer->lex(token);
937 if (!isEOD(token))
938 {
939 mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, token->location,
940 token->text);
941 skipUntilEOD(mTokenizer, token);
942 }
943 return expression;
944}
945
946} // namespace pp
947
948} // namespace angle
949