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