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 &macroSet)
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(&macroExpander, 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(&macroExpander, 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