| // Copyright 2016 The SwiftShader Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "DirectiveParser.h" |
| |
| #include <algorithm> |
| #include <cassert> |
| #include <cstdlib> |
| #include <sstream> |
| |
| #include "DiagnosticsBase.h" |
| #include "DirectiveHandlerBase.h" |
| #include "ExpressionParser.h" |
| #include "MacroExpander.h" |
| #include "Token.h" |
| #include "Tokenizer.h" |
| |
| namespace { |
| enum DirectiveType |
| { |
| DIRECTIVE_NONE, |
| DIRECTIVE_DEFINE, |
| DIRECTIVE_UNDEF, |
| DIRECTIVE_IF, |
| DIRECTIVE_IFDEF, |
| DIRECTIVE_IFNDEF, |
| DIRECTIVE_ELSE, |
| DIRECTIVE_ELIF, |
| DIRECTIVE_ENDIF, |
| DIRECTIVE_ERROR, |
| DIRECTIVE_PRAGMA, |
| DIRECTIVE_EXTENSION, |
| DIRECTIVE_VERSION, |
| DIRECTIVE_LINE |
| }; |
| } // namespace |
| |
| static DirectiveType getDirective(const pp::Token *token) |
| { |
| static const char kDirectiveDefine[] = "define"; |
| static const char kDirectiveUndef[] = "undef"; |
| static const char kDirectiveIf[] = "if"; |
| static const char kDirectiveIfdef[] = "ifdef"; |
| static const char kDirectiveIfndef[] = "ifndef"; |
| static const char kDirectiveElse[] = "else"; |
| static const char kDirectiveElif[] = "elif"; |
| static const char kDirectiveEndif[] = "endif"; |
| static const char kDirectiveError[] = "error"; |
| static const char kDirectivePragma[] = "pragma"; |
| static const char kDirectiveExtension[] = "extension"; |
| static const char kDirectiveVersion[] = "version"; |
| static const char kDirectiveLine[] = "line"; |
| |
| if (token->type != pp::Token::IDENTIFIER) |
| return DIRECTIVE_NONE; |
| |
| if (token->text == kDirectiveDefine) |
| return DIRECTIVE_DEFINE; |
| else if (token->text == kDirectiveUndef) |
| return DIRECTIVE_UNDEF; |
| else if (token->text == kDirectiveIf) |
| return DIRECTIVE_IF; |
| else if (token->text == kDirectiveIfdef) |
| return DIRECTIVE_IFDEF; |
| else if (token->text == kDirectiveIfndef) |
| return DIRECTIVE_IFNDEF; |
| else if (token->text == kDirectiveElse) |
| return DIRECTIVE_ELSE; |
| else if (token->text == kDirectiveElif) |
| return DIRECTIVE_ELIF; |
| else if (token->text == kDirectiveEndif) |
| return DIRECTIVE_ENDIF; |
| else if (token->text == kDirectiveError) |
| return DIRECTIVE_ERROR; |
| else if (token->text == kDirectivePragma) |
| return DIRECTIVE_PRAGMA; |
| else if (token->text == kDirectiveExtension) |
| return DIRECTIVE_EXTENSION; |
| else if (token->text == kDirectiveVersion) |
| return DIRECTIVE_VERSION; |
| else if (token->text == kDirectiveLine) |
| return DIRECTIVE_LINE; |
| |
| return DIRECTIVE_NONE; |
| } |
| |
| static bool isConditionalDirective(DirectiveType directive) |
| { |
| switch (directive) |
| { |
| case DIRECTIVE_IF: |
| case DIRECTIVE_IFDEF: |
| case DIRECTIVE_IFNDEF: |
| case DIRECTIVE_ELSE: |
| case DIRECTIVE_ELIF: |
| case DIRECTIVE_ENDIF: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| // Returns true if the token represents End Of Directive. |
| static bool isEOD(const pp::Token *token) |
| { |
| return (token->type == '\n') || (token->type == pp::Token::LAST); |
| } |
| |
| static void skipUntilEOD(pp::Lexer *lexer, pp::Token *token) |
| { |
| while(!isEOD(token)) |
| { |
| lexer->lex(token); |
| } |
| } |
| |
| static bool isMacroNameReserved(const std::string& name) |
| { |
| // Names prefixed with "GL_" are reserved. |
| return (name.substr(0, 3) == "GL_"); |
| } |
| |
| bool hasDoubleUnderscores(const std::string &name) |
| { |
| return (name.find("__") != std::string::npos); |
| } |
| |
| static bool isMacroPredefined(const std::string& name, |
| const pp::MacroSet& macroSet) |
| { |
| pp::MacroSet::const_iterator iter = macroSet.find(name); |
| return iter != macroSet.end() ? iter->second->predefined : false; |
| } |
| |
| namespace pp |
| { |
| |
| class DefinedParser : public Lexer |
| { |
| public: |
| DefinedParser(Lexer *lexer, const MacroSet *macroSet, Diagnostics *diagnostics) |
| : mLexer(lexer), mMacroSet(macroSet), mDiagnostics(diagnostics) |
| { |
| } |
| |
| protected: |
| void lex(Token *token) override |
| { |
| const char kDefined[] = "defined"; |
| |
| mLexer->lex(token); |
| if (token->type != Token::IDENTIFIER) |
| return; |
| if (token->text != kDefined) |
| return; |
| |
| bool paren = false; |
| mLexer->lex(token); |
| if (token->type == '(') |
| { |
| paren = true; |
| mLexer->lex(token); |
| } |
| |
| if (token->type != Token::IDENTIFIER) |
| { |
| mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text); |
| skipUntilEOD(mLexer, token); |
| return; |
| } |
| MacroSet::const_iterator iter = mMacroSet->find(token->text); |
| std::string expression = iter != mMacroSet->end() ? "1" : "0"; |
| |
| if (paren) |
| { |
| mLexer->lex(token); |
| if (token->type != ')') |
| { |
| mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, |
| token->location, token->text); |
| skipUntilEOD(mLexer, token); |
| return; |
| } |
| } |
| |
| // We have a valid defined operator. |
| // Convert the current token into a CONST_INT token. |
| token->type = Token::CONST_INT; |
| token->text = expression; |
| } |
| |
| private: |
| Lexer *mLexer; |
| const MacroSet *mMacroSet; |
| Diagnostics *mDiagnostics; |
| }; |
| |
| DirectiveParser::DirectiveParser(Tokenizer *tokenizer, |
| MacroSet *macroSet, |
| Diagnostics *diagnostics, |
| DirectiveHandler *directiveHandler, |
| int maxMacroExpansionDepth) |
| : mPastFirstStatement(false), |
| mSeenNonPreprocessorToken(false), |
| mTokenizer(tokenizer), |
| mMacroSet(macroSet), |
| mDiagnostics(diagnostics), |
| mDirectiveHandler(directiveHandler), |
| mShaderVersion(100), |
| mMaxMacroExpansionDepth(maxMacroExpansionDepth) |
| { |
| } |
| |
| DirectiveParser::~DirectiveParser() |
| { |
| } |
| |
| void DirectiveParser::lex(Token *token) |
| { |
| do |
| { |
| mTokenizer->lex(token); |
| |
| if (token->type == Token::PP_HASH) |
| { |
| parseDirective(token); |
| mPastFirstStatement = true; |
| } |
| else if (!isEOD(token)) |
| { |
| mSeenNonPreprocessorToken = true; |
| } |
| |
| if (token->type == Token::LAST) |
| { |
| if (!mConditionalStack.empty()) |
| { |
| const ConditionalBlock &block = mConditionalStack.back(); |
| mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNTERMINATED, |
| block.location, block.type); |
| } |
| break; |
| } |
| |
| } while (skipping() || (token->type == '\n')); |
| |
| mPastFirstStatement = true; |
| } |
| |
| void DirectiveParser::parseDirective(Token *token) |
| { |
| assert(token->type == Token::PP_HASH); |
| |
| mTokenizer->lex(token); |
| if (isEOD(token)) |
| { |
| // Empty Directive. |
| return; |
| } |
| |
| DirectiveType directive = getDirective(token); |
| |
| // While in an excluded conditional block/group, |
| // we only parse conditional directives. |
| if (skipping() && !isConditionalDirective(directive)) |
| { |
| skipUntilEOD(mTokenizer, token); |
| return; |
| } |
| |
| switch(directive) |
| { |
| case DIRECTIVE_NONE: |
| mDiagnostics->report(Diagnostics::PP_DIRECTIVE_INVALID_NAME, |
| token->location, token->text); |
| skipUntilEOD(mTokenizer, token); |
| break; |
| case DIRECTIVE_DEFINE: |
| parseDefine(token); |
| break; |
| case DIRECTIVE_UNDEF: |
| parseUndef(token); |
| break; |
| case DIRECTIVE_IF: |
| parseIf(token); |
| break; |
| case DIRECTIVE_IFDEF: |
| parseIfdef(token); |
| break; |
| case DIRECTIVE_IFNDEF: |
| parseIfndef(token); |
| break; |
| case DIRECTIVE_ELSE: |
| parseElse(token); |
| break; |
| case DIRECTIVE_ELIF: |
| parseElif(token); |
| break; |
| case DIRECTIVE_ENDIF: |
| parseEndif(token); |
| break; |
| case DIRECTIVE_ERROR: |
| parseError(token); |
| break; |
| case DIRECTIVE_PRAGMA: |
| parsePragma(token); |
| break; |
| case DIRECTIVE_EXTENSION: |
| parseExtension(token); |
| break; |
| case DIRECTIVE_VERSION: |
| parseVersion(token); |
| break; |
| case DIRECTIVE_LINE: |
| parseLine(token); |
| break; |
| default: |
| assert(false); |
| break; |
| } |
| |
| skipUntilEOD(mTokenizer, token); |
| if (token->type == Token::LAST) |
| { |
| mDiagnostics->report(Diagnostics::PP_EOF_IN_DIRECTIVE, |
| token->location, token->text); |
| } |
| } |
| |
| void DirectiveParser::parseDefine(Token *token) |
| { |
| assert(getDirective(token) == DIRECTIVE_DEFINE); |
| |
| mTokenizer->lex(token); |
| if (token->type != Token::IDENTIFIER) |
| { |
| mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, |
| token->location, token->text); |
| return; |
| } |
| if (isMacroPredefined(token->text, *mMacroSet)) |
| { |
| mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_REDEFINED, |
| token->location, token->text); |
| return; |
| } |
| if (isMacroNameReserved(token->text)) |
| { |
| mDiagnostics->report(Diagnostics::PP_MACRO_NAME_RESERVED, |
| token->location, token->text); |
| return; |
| } |
| // Using double underscores is allowed, but may result in unintended |
| // behavior, so a warning is issued. At the time of writing this was |
| // specified in ESSL 3.10, but the intent judging from Khronos |
| // discussions and dEQP tests was that double underscores should be |
| // allowed in earlier ESSL versions too. |
| if (hasDoubleUnderscores(token->text)) |
| { |
| mDiagnostics->report(Diagnostics::PP_WARNING_MACRO_NAME_RESERVED, token->location, |
| token->text); |
| } |
| |
| std::shared_ptr<Macro> macro = std::make_shared<Macro>(); |
| macro->type = Macro::kTypeObj; |
| macro->name = token->text; |
| |
| mTokenizer->lex(token); |
| if (token->type == '(' && !token->hasLeadingSpace()) |
| { |
| // Function-like macro. Collect arguments. |
| macro->type = Macro::kTypeFunc; |
| do { |
| mTokenizer->lex(token); |
| if (token->type != Token::IDENTIFIER) |
| break; |
| |
| if (std::find(macro->parameters.begin(), macro->parameters.end(), token->text) != macro->parameters.end()) |
| { |
| mDiagnostics->report(Diagnostics::PP_MACRO_DUPLICATE_PARAMETER_NAMES, |
| token->location, token->text); |
| return; |
| } |
| |
| macro->parameters.push_back(token->text); |
| |
| mTokenizer->lex(token); // Get ','. |
| } while (token->type == ','); |
| |
| if (token->type != ')') |
| { |
| mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, |
| token->location, |
| token->text); |
| return; |
| } |
| mTokenizer->lex(token); // Get ')'. |
| } |
| |
| while ((token->type != '\n') && (token->type != Token::LAST)) |
| { |
| // Reset the token location because it is unnecessary in replacement |
| // list. Resetting it also allows us to reuse Token::equals() to |
| // compare macros. |
| token->location = SourceLocation(); |
| macro->replacements.push_back(*token); |
| mTokenizer->lex(token); |
| } |
| if (!macro->replacements.empty()) |
| { |
| // Whitespace preceding the replacement list is not considered part of |
| // the replacement list for either form of macro. |
| macro->replacements.front().setHasLeadingSpace(false); |
| } |
| |
| // Check for macro redefinition. |
| MacroSet::const_iterator iter = mMacroSet->find(macro->name); |
| if (iter != mMacroSet->end() && !macro->equals(*iter->second)) |
| { |
| mDiagnostics->report(Diagnostics::PP_MACRO_REDEFINED, token->location, macro->name); |
| return; |
| } |
| mMacroSet->insert(std::make_pair(macro->name, macro)); |
| } |
| |
| void DirectiveParser::parseUndef(Token *token) |
| { |
| assert(getDirective(token) == DIRECTIVE_UNDEF); |
| |
| mTokenizer->lex(token); |
| if (token->type != Token::IDENTIFIER) |
| { |
| mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, |
| token->location, token->text); |
| return; |
| } |
| |
| MacroSet::iterator iter = mMacroSet->find(token->text); |
| if (iter != mMacroSet->end()) |
| { |
| if (iter->second->predefined) |
| { |
| mDiagnostics->report(Diagnostics::PP_MACRO_PREDEFINED_UNDEFINED, |
| token->location, token->text); |
| return; |
| } |
| else if (iter->second->expansionCount > 0) |
| { |
| mDiagnostics->report(Diagnostics::PP_MACRO_UNDEFINED_WHILE_INVOKED, |
| token->location, token->text); |
| return; |
| } |
| else |
| { |
| mMacroSet->erase(iter); |
| } |
| } |
| |
| mTokenizer->lex(token); |
| if (!isEOD(token)) |
| { |
| mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, token->text); |
| skipUntilEOD(mTokenizer, token); |
| } |
| } |
| |
| void DirectiveParser::parseIf(Token *token) |
| { |
| assert(getDirective(token) == DIRECTIVE_IF); |
| parseConditionalIf(token); |
| } |
| |
| void DirectiveParser::parseIfdef(Token *token) |
| { |
| assert(getDirective(token) == DIRECTIVE_IFDEF); |
| parseConditionalIf(token); |
| } |
| |
| void DirectiveParser::parseIfndef(Token *token) |
| { |
| assert(getDirective(token) == DIRECTIVE_IFNDEF); |
| parseConditionalIf(token); |
| } |
| |
| void DirectiveParser::parseElse(Token *token) |
| { |
| assert(getDirective(token) == DIRECTIVE_ELSE); |
| |
| if (mConditionalStack.empty()) |
| { |
| mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELSE_WITHOUT_IF, |
| token->location, token->text); |
| skipUntilEOD(mTokenizer, token); |
| return; |
| } |
| |
| ConditionalBlock &block = mConditionalStack.back(); |
| if (block.skipBlock) |
| { |
| // No diagnostics. Just skip the whole line. |
| skipUntilEOD(mTokenizer, token); |
| return; |
| } |
| if (block.foundElseGroup) |
| { |
| mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELSE_AFTER_ELSE, |
| token->location, token->text); |
| skipUntilEOD(mTokenizer, token); |
| return; |
| } |
| |
| block.foundElseGroup = true; |
| block.skipGroup = block.foundValidGroup; |
| block.foundValidGroup = true; |
| |
| // Check if there are extra tokens after #else. |
| mTokenizer->lex(token); |
| if (!isEOD(token)) |
| { |
| mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, |
| token->location, token->text); |
| skipUntilEOD(mTokenizer, token); |
| } |
| } |
| |
| void DirectiveParser::parseElif(Token *token) |
| { |
| assert(getDirective(token) == DIRECTIVE_ELIF); |
| |
| if (mConditionalStack.empty()) |
| { |
| mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELIF_WITHOUT_IF, |
| token->location, token->text); |
| skipUntilEOD(mTokenizer, token); |
| return; |
| } |
| |
| ConditionalBlock &block = mConditionalStack.back(); |
| if (block.skipBlock) |
| { |
| // No diagnostics. Just skip the whole line. |
| skipUntilEOD(mTokenizer, token); |
| return; |
| } |
| if (block.foundElseGroup) |
| { |
| mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ELIF_AFTER_ELSE, |
| token->location, token->text); |
| skipUntilEOD(mTokenizer, token); |
| return; |
| } |
| if (block.foundValidGroup) |
| { |
| // Do not parse the expression. |
| // Also be careful not to emit a diagnostic. |
| block.skipGroup = true; |
| skipUntilEOD(mTokenizer, token); |
| return; |
| } |
| |
| int expression = parseExpressionIf(token); |
| block.skipGroup = expression == 0; |
| block.foundValidGroup = expression != 0; |
| } |
| |
| void DirectiveParser::parseEndif(Token *token) |
| { |
| assert(getDirective(token) == DIRECTIVE_ENDIF); |
| |
| if (mConditionalStack.empty()) |
| { |
| mDiagnostics->report(Diagnostics::PP_CONDITIONAL_ENDIF_WITHOUT_IF, |
| token->location, token->text); |
| skipUntilEOD(mTokenizer, token); |
| return; |
| } |
| |
| mConditionalStack.pop_back(); |
| |
| // Check if there are tokens after #endif. |
| mTokenizer->lex(token); |
| if (!isEOD(token)) |
| { |
| mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, |
| token->location, token->text); |
| skipUntilEOD(mTokenizer, token); |
| } |
| } |
| |
| void DirectiveParser::parseError(Token *token) |
| { |
| assert(getDirective(token) == DIRECTIVE_ERROR); |
| |
| std::ostringstream stream; |
| mTokenizer->lex(token); |
| while ((token->type != '\n') && (token->type != Token::LAST)) |
| { |
| stream << *token; |
| mTokenizer->lex(token); |
| } |
| mDirectiveHandler->handleError(token->location, stream.str()); |
| } |
| |
| // Parses pragma of form: #pragma name[(value)]. |
| void DirectiveParser::parsePragma(Token *token) |
| { |
| assert(getDirective(token) == DIRECTIVE_PRAGMA); |
| |
| enum State |
| { |
| PRAGMA_NAME, |
| LEFT_PAREN, |
| PRAGMA_VALUE, |
| RIGHT_PAREN |
| }; |
| |
| bool valid = true; |
| std::string name, value; |
| int state = PRAGMA_NAME; |
| |
| mTokenizer->lex(token); |
| bool stdgl = token->text == "STDGL"; |
| if (stdgl) |
| { |
| mTokenizer->lex(token); |
| } |
| while ((token->type != '\n') && (token->type != Token::LAST)) |
| { |
| switch(state++) |
| { |
| case PRAGMA_NAME: |
| name = token->text; |
| valid = valid && (token->type == Token::IDENTIFIER); |
| break; |
| case LEFT_PAREN: |
| valid = valid && (token->type == '('); |
| break; |
| case PRAGMA_VALUE: |
| value = token->text; |
| valid = valid && (token->type == Token::IDENTIFIER); |
| break; |
| case RIGHT_PAREN: |
| valid = valid && (token->type == ')'); |
| break; |
| default: |
| valid = false; |
| break; |
| } |
| mTokenizer->lex(token); |
| } |
| |
| valid = valid && ((state == PRAGMA_NAME) || // Empty pragma. |
| (state == LEFT_PAREN) || // Without value. |
| (state == RIGHT_PAREN + 1)); // With value. |
| if (!valid) |
| { |
| mDiagnostics->report(Diagnostics::PP_UNRECOGNIZED_PRAGMA, token->location, name); |
| } |
| else if (state > PRAGMA_NAME) // Do not notify for empty pragma. |
| { |
| mDirectiveHandler->handlePragma(token->location, name, value, stdgl); |
| } |
| } |
| |
| void DirectiveParser::parseExtension(Token *token) |
| { |
| assert(getDirective(token) == DIRECTIVE_EXTENSION); |
| |
| enum State |
| { |
| EXT_NAME, |
| COLON, |
| EXT_BEHAVIOR |
| }; |
| |
| bool valid = true; |
| std::string name, behavior; |
| int state = EXT_NAME; |
| |
| mTokenizer->lex(token); |
| while ((token->type != '\n') && (token->type != Token::LAST)) |
| { |
| switch (state++) |
| { |
| case EXT_NAME: |
| if (valid && (token->type != Token::IDENTIFIER)) |
| { |
| mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_NAME, token->location, |
| token->text); |
| valid = false; |
| } |
| if (valid) |
| name = token->text; |
| break; |
| case COLON: |
| if (valid && (token->type != ':')) |
| { |
| mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, |
| token->text); |
| valid = false; |
| } |
| break; |
| case EXT_BEHAVIOR: |
| if (valid && (token->type != Token::IDENTIFIER)) |
| { |
| mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_BEHAVIOR, |
| token->location, token->text); |
| valid = false; |
| } |
| if (valid) |
| behavior = token->text; |
| break; |
| default: |
| if (valid) |
| { |
| mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, token->location, |
| token->text); |
| valid = false; |
| } |
| break; |
| } |
| mTokenizer->lex(token); |
| } |
| if (valid && (state != EXT_BEHAVIOR + 1)) |
| { |
| mDiagnostics->report(Diagnostics::PP_INVALID_EXTENSION_DIRECTIVE, token->location, |
| token->text); |
| valid = false; |
| } |
| if (valid && mSeenNonPreprocessorToken) |
| { |
| if (mShaderVersion >= 300) |
| { |
| mDiagnostics->report(Diagnostics::PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL3, |
| token->location, token->text); |
| valid = false; |
| } |
| else |
| { |
| mDiagnostics->report(Diagnostics::PP_NON_PP_TOKEN_BEFORE_EXTENSION_ESSL1, |
| token->location, token->text); |
| } |
| } |
| if (valid) |
| mDirectiveHandler->handleExtension(token->location, name, behavior); |
| } |
| |
| void DirectiveParser::parseVersion(Token *token) |
| { |
| assert(getDirective(token) == DIRECTIVE_VERSION); |
| |
| if (mPastFirstStatement) |
| { |
| mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_STATEMENT, token->location, |
| token->text); |
| skipUntilEOD(mTokenizer, token); |
| return; |
| } |
| |
| enum State |
| { |
| VERSION_NUMBER, |
| VERSION_PROFILE, |
| VERSION_ENDLINE |
| }; |
| |
| bool valid = true; |
| int version = 0; |
| int state = VERSION_NUMBER; |
| |
| mTokenizer->lex(token); |
| while (valid && (token->type != '\n') && (token->type != Token::LAST)) |
| { |
| switch (state) |
| { |
| case VERSION_NUMBER: |
| if (token->type != Token::CONST_INT) |
| { |
| mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_NUMBER, |
| token->location, token->text); |
| valid = false; |
| } |
| if (valid && !token->iValue(&version)) |
| { |
| mDiagnostics->report(Diagnostics::PP_INTEGER_OVERFLOW, |
| token->location, token->text); |
| valid = false; |
| } |
| if (valid) |
| { |
| state = (version < 300) ? VERSION_ENDLINE : VERSION_PROFILE; |
| } |
| break; |
| case VERSION_PROFILE: |
| if (token->type != Token::IDENTIFIER || token->text != "es") |
| { |
| mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_DIRECTIVE, |
| token->location, token->text); |
| valid = false; |
| } |
| state = VERSION_ENDLINE; |
| break; |
| default: |
| mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, |
| token->location, token->text); |
| valid = false; |
| break; |
| } |
| |
| mTokenizer->lex(token); |
| } |
| |
| if (valid && (state != VERSION_ENDLINE)) |
| { |
| mDiagnostics->report(Diagnostics::PP_INVALID_VERSION_DIRECTIVE, |
| token->location, token->text); |
| valid = false; |
| } |
| |
| if (valid && version >= 300 && token->location.line > 1) |
| { |
| mDiagnostics->report(Diagnostics::PP_VERSION_NOT_FIRST_LINE_ESSL3, token->location, |
| token->text); |
| valid = false; |
| } |
| |
| if (valid) |
| { |
| mDirectiveHandler->handleVersion(token->location, version); |
| mShaderVersion = version; |
| PredefineMacro(mMacroSet, "__VERSION__", version); |
| } |
| } |
| |
| void DirectiveParser::parseLine(Token *token) |
| { |
| assert(getDirective(token) == DIRECTIVE_LINE); |
| |
| bool valid = true; |
| bool parsedFileNumber = false; |
| int line = 0, file = 0; |
| |
| MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, false, mMaxMacroExpansionDepth); |
| |
| // Lex the first token after "#line" so we can check it for EOD. |
| macroExpander.lex(token); |
| |
| if (isEOD(token)) |
| { |
| mDiagnostics->report(Diagnostics::PP_INVALID_LINE_DIRECTIVE, token->location, token->text); |
| valid = false; |
| } |
| else |
| { |
| ExpressionParser expressionParser(¯oExpander, mDiagnostics); |
| ExpressionParser::ErrorSettings errorSettings; |
| |
| // See GLES3 section 12.42 |
| errorSettings.integerLiteralsMustFit32BitSignedRange = true; |
| |
| errorSettings.unexpectedIdentifier = Diagnostics::PP_INVALID_LINE_NUMBER; |
| // The first token was lexed earlier to check if it was EOD. Include |
| // the token in parsing for a second time by setting the |
| // parsePresetToken flag to true. |
| expressionParser.parse(token, &line, true, errorSettings, &valid); |
| if (!isEOD(token) && valid) |
| { |
| errorSettings.unexpectedIdentifier = Diagnostics::PP_INVALID_FILE_NUMBER; |
| // After parsing the line expression expressionParser has also |
| // advanced to the first token of the file expression - this is the |
| // token that makes the parser reduce the "input" rule for the line |
| // expression and stop. So we're using parsePresetToken = true here |
| // as well. |
| expressionParser.parse(token, &file, true, errorSettings, &valid); |
| parsedFileNumber = true; |
| } |
| if (!isEOD(token)) |
| { |
| if (valid) |
| { |
| mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, |
| token->location, token->text); |
| valid = false; |
| } |
| skipUntilEOD(mTokenizer, token); |
| } |
| } |
| |
| if (valid) |
| { |
| mTokenizer->setLineNumber(line); |
| if (parsedFileNumber) |
| mTokenizer->setFileNumber(file); |
| } |
| } |
| |
| bool DirectiveParser::skipping() const |
| { |
| if (mConditionalStack.empty()) |
| return false; |
| |
| const ConditionalBlock &block = mConditionalStack.back(); |
| return block.skipBlock || block.skipGroup; |
| } |
| |
| void DirectiveParser::parseConditionalIf(Token *token) |
| { |
| ConditionalBlock block; |
| block.type = token->text; |
| block.location = token->location; |
| |
| if (skipping()) |
| { |
| // This conditional block is inside another conditional group |
| // which is skipped. As a consequence this whole block is skipped. |
| // Be careful not to parse the conditional expression that might |
| // emit a diagnostic. |
| skipUntilEOD(mTokenizer, token); |
| block.skipBlock = true; |
| } |
| else |
| { |
| DirectiveType directive = getDirective(token); |
| |
| int expression = 0; |
| switch (directive) |
| { |
| case DIRECTIVE_IF: |
| expression = parseExpressionIf(token); |
| break; |
| case DIRECTIVE_IFDEF: |
| expression = parseExpressionIfdef(token); |
| break; |
| case DIRECTIVE_IFNDEF: |
| expression = parseExpressionIfdef(token) == 0 ? 1 : 0; |
| break; |
| default: |
| assert(false); |
| break; |
| } |
| block.skipGroup = expression == 0; |
| block.foundValidGroup = expression != 0; |
| } |
| mConditionalStack.push_back(block); |
| } |
| |
| int DirectiveParser::parseExpressionIf(Token *token) |
| { |
| assert((getDirective(token) == DIRECTIVE_IF) || (getDirective(token) == DIRECTIVE_ELIF)); |
| |
| DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics); |
| MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics, true, mMaxMacroExpansionDepth); |
| ExpressionParser expressionParser(¯oExpander, mDiagnostics); |
| |
| int expression = 0; |
| ExpressionParser::ErrorSettings errorSettings; |
| errorSettings.integerLiteralsMustFit32BitSignedRange = false; |
| errorSettings.unexpectedIdentifier = Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN; |
| |
| bool valid = true; |
| expressionParser.parse(token, &expression, false, errorSettings, &valid); |
| |
| // Check if there are tokens after #if expression. |
| if (!isEOD(token)) |
| { |
| mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, |
| token->location, token->text); |
| skipUntilEOD(mTokenizer, token); |
| } |
| |
| return expression; |
| } |
| |
| int DirectiveParser::parseExpressionIfdef(Token* token) |
| { |
| assert((getDirective(token) == DIRECTIVE_IFDEF) || |
| (getDirective(token) == DIRECTIVE_IFNDEF)); |
| |
| mTokenizer->lex(token); |
| if (token->type != Token::IDENTIFIER) |
| { |
| mDiagnostics->report(Diagnostics::PP_UNEXPECTED_TOKEN, |
| token->location, token->text); |
| skipUntilEOD(mTokenizer, token); |
| return 0; |
| } |
| |
| MacroSet::const_iterator iter = mMacroSet->find(token->text); |
| int expression = iter != mMacroSet->end() ? 1 : 0; |
| |
| // Check if there are tokens after #ifdef expression. |
| mTokenizer->lex(token); |
| if (!isEOD(token)) |
| { |
| mDiagnostics->report(Diagnostics::PP_CONDITIONAL_UNEXPECTED_TOKEN, |
| token->location, token->text); |
| skipUntilEOD(mTokenizer, token); |
| } |
| return expression; |
| } |
| |
| } // namespace pp |