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 | |
29 | namespace { |
30 | enum 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 | |
49 | static 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 | |
98 | static 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. |
115 | static bool isEOD(const pp::Token *token) |
116 | { |
117 | return (token->type == '\n') || (token->type == pp::Token::LAST); |
118 | } |
119 | |
120 | static void skipUntilEOD(pp::Lexer *lexer, pp::Token *token) |
121 | { |
122 | while(!isEOD(token)) |
123 | { |
124 | lexer->lex(token); |
125 | } |
126 | } |
127 | |
128 | static bool isMacroNameReserved(const std::string& name) |
129 | { |
130 | // Names prefixed with "GL_" are reserved. |
131 | return (name.substr(0, 3) == "GL_" ); |
132 | } |
133 | |
134 | bool hasDoubleUnderscores(const std::string &name) |
135 | { |
136 | return (name.find("__" ) != std::string::npos); |
137 | } |
138 | |
139 | static 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 | |
146 | namespace pp |
147 | { |
148 | |
149 | class DefinedParser : public Lexer |
150 | { |
151 | public: |
152 | DefinedParser(Lexer *lexer, const MacroSet *macroSet, Diagnostics *diagnostics) |
153 | : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics) |
154 | { |
155 | } |
156 | |
157 | protected: |
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 | |
203 | private: |
204 | Lexer *mLexer; |
205 | const MacroSet *mMacroSet; |
206 | Diagnostics *mDiagnostics; |
207 | }; |
208 | |
209 | DirectiveParser::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 | |
225 | DirectiveParser::~DirectiveParser() |
226 | { |
227 | } |
228 | |
229 | void 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 | |
261 | void 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 | |
341 | void 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 | |
437 | void 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 | |
478 | void DirectiveParser::parseIf(Token *token) |
479 | { |
480 | assert(getDirective(token) == DIRECTIVE_IF); |
481 | parseConditionalIf(token); |
482 | } |
483 | |
484 | void DirectiveParser::parseIfdef(Token *token) |
485 | { |
486 | assert(getDirective(token) == DIRECTIVE_IFDEF); |
487 | parseConditionalIf(token); |
488 | } |
489 | |
490 | void DirectiveParser::parseIfndef(Token *token) |
491 | { |
492 | assert(getDirective(token) == DIRECTIVE_IFNDEF); |
493 | parseConditionalIf(token); |
494 | } |
495 | |
496 | void 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 | |
537 | void 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 | |
577 | void 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 | |
601 | void 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)]. |
616 | void 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 | |
676 | void 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 | |
759 | void 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 | |
846 | void 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(¯oExpander, 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 | |
908 | bool 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 | |
917 | void 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 | |
958 | int 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(¯oExpander, 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 | |
985 | int 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 | |