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