xref: /aosp_15_r20/external/angle/src/compiler/preprocessor/DirectiveParser.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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     : 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(&macroExpander, 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(&macroExpander, 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