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