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