// 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 #include #include #include "Diagnostics.h" #include "DirectiveHandler.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 std::string kDirectiveDefine("define"); static const std::string kDirectiveUndef("undef"); static const std::string kDirectiveIf("if"); static const std::string kDirectiveIfdef("ifdef"); static const std::string kDirectiveIfndef("ifndef"); static const std::string kDirectiveElse("else"); static const std::string kDirectiveElif("elif"); static const std::string kDirectiveEndif("endif"); static const std::string kDirectiveError("error"); static const std::string kDirectivePragma("pragma"); static const std::string kDirectiveExtension("extension"); static const std::string kDirectiveVersion("version"); static const std::string 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. if (name.substr(0, 3) == "GL_") return true; // Names containing two consecutive underscores are reserved. if (name.find("__") != std::string::npos) return true; return false; } 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 { DirectiveParser::DirectiveParser(Tokenizer* tokenizer, MacroSet* macroSet, Diagnostics* diagnostics, DirectiveHandler* directiveHandler) : mPastFirstStatement(false), mTokenizer(tokenizer), mMacroSet(macroSet), mDiagnostics(diagnostics), mDirectiveHandler(directiveHandler) { } void DirectiveParser::lex(Token* token) { do { mTokenizer->lex(token); if (token->type == Token::PP_HASH) { parseDirective(token); mPastFirstStatement = true; } if (token->type == Token::LAST) { if (!mConditionalStack.empty()) { const ConditionalBlock& block = mConditionalStack.back(); mDiagnostics->report(Diagnostics::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::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::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::UNEXPECTED_TOKEN, token->location, token->text); return; } if (isMacroPredefined(token->text, *mMacroSet)) { mDiagnostics->report(Diagnostics::MACRO_PREDEFINED_REDEFINED, token->location, token->text); return; } if (isMacroNameReserved(token->text)) { mDiagnostics->report(Diagnostics::MACRO_NAME_RESERVED, token->location, token->text); return; } Macro 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::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::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::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::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::MACRO_PREDEFINED_UNDEFINED, token->location, token->text); } else { mMacroSet->erase(iter); } } mTokenizer->lex(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::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::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::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::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::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::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::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); 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::UNRECOGNIZED_PRAGMA, token->location, name); } else if (state > PRAGMA_NAME) // Do not notify for empty pragma. { mDirectiveHandler->handlePragma(token->location, name, value); } } 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::INVALID_EXTENSION_NAME, token->location, token->text); valid = false; } if (valid) name = token->text; break; case COLON: if (valid && (token->type != ':')) { mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, token->location, token->text); valid = false; } break; case EXT_BEHAVIOR: if (valid && (token->type != Token::IDENTIFIER)) { mDiagnostics->report(Diagnostics::INVALID_EXTENSION_BEHAVIOR, token->location, token->text); valid = false; } if (valid) behavior = token->text; break; default: if (valid) { mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, token->location, token->text); valid = false; } break; } mTokenizer->lex(token); } if (valid && (state != EXT_BEHAVIOR + 1)) { mDiagnostics->report(Diagnostics::INVALID_EXTENSION_DIRECTIVE, token->location, token->text); valid = false; } if (valid) mDirectiveHandler->handleExtension(token->location, name, behavior); } void DirectiveParser::parseVersion(Token* token) { assert(getDirective(token) == DIRECTIVE_VERSION); if (mPastFirstStatement) { mDiagnostics->report(Diagnostics::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::INVALID_VERSION_NUMBER, token->location, token->text); valid = false; } if (valid && !token->iValue(&version)) { mDiagnostics->report(Diagnostics::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::INVALID_VERSION_DIRECTIVE, token->location, token->text); valid = false; } state = VERSION_ENDLINE; break; default: mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, token->location, token->text); valid = false; break; } mTokenizer->lex(token); } if (valid && (state != VERSION_ENDLINE)) { mDiagnostics->report(Diagnostics::INVALID_VERSION_DIRECTIVE, token->location, token->text); valid = false; } if (valid) { mDirectiveHandler->handleVersion(token->location, version); } } void DirectiveParser::parseLine(Token* token) { assert(getDirective(token) == DIRECTIVE_LINE); enum State { LINE_NUMBER, FILE_NUMBER }; bool valid = true; int line = 0, file = 0; int state = LINE_NUMBER; MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, false); macroExpander.lex(token); while ((token->type != '\n') && (token->type != Token::LAST)) { switch (state++) { case LINE_NUMBER: if (valid && (token->type != Token::CONST_INT)) { mDiagnostics->report(Diagnostics::INVALID_LINE_NUMBER, token->location, token->text); valid = false; } if (valid && !token->iValue(&line)) { mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW, token->location, token->text); valid = false; } break; case FILE_NUMBER: if (valid && (token->type != Token::CONST_INT)) { mDiagnostics->report(Diagnostics::INVALID_FILE_NUMBER, token->location, token->text); valid = false; } if (valid && !token->iValue(&file)) { mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW, token->location, token->text); valid = false; } break; default: if (valid) { mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, token->location, token->text); valid = false; } break; } macroExpander.lex(token); } if (valid && (state != FILE_NUMBER) && (state != FILE_NUMBER + 1)) { mDiagnostics->report(Diagnostics::INVALID_LINE_DIRECTIVE, token->location, token->text); valid = false; } if (valid) { mTokenizer->setLineNumber(line); if (state == FILE_NUMBER + 1) 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)); MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics, true); ExpressionParser expressionParser(¯oExpander, mDiagnostics); int expression = 0; macroExpander.lex(token); expressionParser.parse(token, &expression); // Check if there are tokens after #if expression. if (!isEOD(token)) { mDiagnostics->report(Diagnostics::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::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::CONDITIONAL_UNEXPECTED_TOKEN, token->location, token->text); skipUntilEOD(mTokenizer, token); } return expression; } } // namespace pp