michael@0: // michael@0: // Copyright (c) 2011 The ANGLE Project Authors. All rights reserved. michael@0: // Use of this source code is governed by a BSD-style license that can be michael@0: // found in the LICENSE file. michael@0: // michael@0: michael@0: #include "DirectiveParser.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "DiagnosticsBase.h" michael@0: #include "DirectiveHandlerBase.h" michael@0: #include "ExpressionParser.h" michael@0: #include "MacroExpander.h" michael@0: #include "Token.h" michael@0: #include "Tokenizer.h" michael@0: michael@0: namespace { michael@0: enum DirectiveType michael@0: { michael@0: DIRECTIVE_NONE, michael@0: DIRECTIVE_DEFINE, michael@0: DIRECTIVE_UNDEF, michael@0: DIRECTIVE_IF, michael@0: DIRECTIVE_IFDEF, michael@0: DIRECTIVE_IFNDEF, michael@0: DIRECTIVE_ELSE, michael@0: DIRECTIVE_ELIF, michael@0: DIRECTIVE_ENDIF, michael@0: DIRECTIVE_ERROR, michael@0: DIRECTIVE_PRAGMA, michael@0: DIRECTIVE_EXTENSION, michael@0: DIRECTIVE_VERSION, michael@0: DIRECTIVE_LINE michael@0: }; michael@0: } // namespace michael@0: michael@0: static DirectiveType getDirective(const pp::Token* token) michael@0: { michael@0: static const std::string kDirectiveDefine("define"); michael@0: static const std::string kDirectiveUndef("undef"); michael@0: static const std::string kDirectiveIf("if"); michael@0: static const std::string kDirectiveIfdef("ifdef"); michael@0: static const std::string kDirectiveIfndef("ifndef"); michael@0: static const std::string kDirectiveElse("else"); michael@0: static const std::string kDirectiveElif("elif"); michael@0: static const std::string kDirectiveEndif("endif"); michael@0: static const std::string kDirectiveError("error"); michael@0: static const std::string kDirectivePragma("pragma"); michael@0: static const std::string kDirectiveExtension("extension"); michael@0: static const std::string kDirectiveVersion("version"); michael@0: static const std::string kDirectiveLine("line"); michael@0: michael@0: if (token->type != pp::Token::IDENTIFIER) michael@0: return DIRECTIVE_NONE; michael@0: michael@0: if (token->text == kDirectiveDefine) michael@0: return DIRECTIVE_DEFINE; michael@0: else if (token->text == kDirectiveUndef) michael@0: return DIRECTIVE_UNDEF; michael@0: else if (token->text == kDirectiveIf) michael@0: return DIRECTIVE_IF; michael@0: else if (token->text == kDirectiveIfdef) michael@0: return DIRECTIVE_IFDEF; michael@0: else if (token->text == kDirectiveIfndef) michael@0: return DIRECTIVE_IFNDEF; michael@0: else if (token->text == kDirectiveElse) michael@0: return DIRECTIVE_ELSE; michael@0: else if (token->text == kDirectiveElif) michael@0: return DIRECTIVE_ELIF; michael@0: else if (token->text == kDirectiveEndif) michael@0: return DIRECTIVE_ENDIF; michael@0: else if (token->text == kDirectiveError) michael@0: return DIRECTIVE_ERROR; michael@0: else if (token->text == kDirectivePragma) michael@0: return DIRECTIVE_PRAGMA; michael@0: else if (token->text == kDirectiveExtension) michael@0: return DIRECTIVE_EXTENSION; michael@0: else if (token->text == kDirectiveVersion) michael@0: return DIRECTIVE_VERSION; michael@0: else if (token->text == kDirectiveLine) michael@0: return DIRECTIVE_LINE; michael@0: michael@0: return DIRECTIVE_NONE; michael@0: } michael@0: michael@0: static bool isConditionalDirective(DirectiveType directive) michael@0: { michael@0: switch (directive) michael@0: { michael@0: case DIRECTIVE_IF: michael@0: case DIRECTIVE_IFDEF: michael@0: case DIRECTIVE_IFNDEF: michael@0: case DIRECTIVE_ELSE: michael@0: case DIRECTIVE_ELIF: michael@0: case DIRECTIVE_ENDIF: michael@0: return true; michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: // Returns true if the token represents End Of Directive. michael@0: static bool isEOD(const pp::Token* token) michael@0: { michael@0: return (token->type == '\n') || (token->type == pp::Token::LAST); michael@0: } michael@0: michael@0: static void skipUntilEOD(pp::Lexer* lexer, pp::Token* token) michael@0: { michael@0: while(!isEOD(token)) michael@0: { michael@0: lexer->lex(token); michael@0: } michael@0: } michael@0: michael@0: static bool isMacroNameReserved(const std::string& name) michael@0: { michael@0: // Names prefixed with "GL_" are reserved. michael@0: if (name.substr(0, 3) == "GL_") michael@0: return true; michael@0: michael@0: // Names containing two consecutive underscores are reserved. michael@0: if (name.find("__") != std::string::npos) michael@0: return true; michael@0: michael@0: return false; michael@0: } michael@0: michael@0: static bool isMacroPredefined(const std::string& name, michael@0: const pp::MacroSet& macroSet) michael@0: { michael@0: pp::MacroSet::const_iterator iter = macroSet.find(name); michael@0: return iter != macroSet.end() ? iter->second.predefined : false; michael@0: } michael@0: michael@0: namespace pp michael@0: { michael@0: michael@0: class DefinedParser : public Lexer michael@0: { michael@0: public: michael@0: DefinedParser(Lexer* lexer, michael@0: const MacroSet* macroSet, michael@0: Diagnostics* diagnostics) : michael@0: mLexer(lexer), michael@0: mMacroSet(macroSet), michael@0: mDiagnostics(diagnostics) michael@0: { michael@0: } michael@0: michael@0: protected: michael@0: virtual void lex(Token* token) michael@0: { michael@0: static const std::string kDefined("defined"); michael@0: michael@0: mLexer->lex(token); michael@0: if (token->type != Token::IDENTIFIER) michael@0: return; michael@0: if (token->text != kDefined) michael@0: return; michael@0: michael@0: bool paren = false; michael@0: mLexer->lex(token); michael@0: if (token->type == '(') michael@0: { michael@0: paren = true; michael@0: mLexer->lex(token); michael@0: } michael@0: michael@0: if (token->type != Token::IDENTIFIER) michael@0: { michael@0: mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, michael@0: token->location, token->text); michael@0: skipUntilEOD(mLexer, token); michael@0: return; michael@0: } michael@0: MacroSet::const_iterator iter = mMacroSet->find(token->text); michael@0: std::string expression = iter != mMacroSet->end() ? "1" : "0"; michael@0: michael@0: if (paren) michael@0: { michael@0: mLexer->lex(token); michael@0: if (token->type != ')') michael@0: { michael@0: mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, michael@0: token->location, token->text); michael@0: skipUntilEOD(mLexer, token); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // We have a valid defined operator. michael@0: // Convert the current token into a CONST_INT token. michael@0: token->type = Token::CONST_INT; michael@0: token->text = expression; michael@0: } michael@0: michael@0: private: michael@0: Lexer* mLexer; michael@0: const MacroSet* mMacroSet; michael@0: Diagnostics* mDiagnostics; michael@0: }; michael@0: michael@0: DirectiveParser::DirectiveParser(Tokenizer* tokenizer, michael@0: MacroSet* macroSet, michael@0: Diagnostics* diagnostics, michael@0: DirectiveHandler* directiveHandler) : michael@0: mPastFirstStatement(false), michael@0: mTokenizer(tokenizer), michael@0: mMacroSet(macroSet), michael@0: mDiagnostics(diagnostics), michael@0: mDirectiveHandler(directiveHandler) michael@0: { michael@0: } michael@0: michael@0: void DirectiveParser::lex(Token* token) michael@0: { michael@0: do michael@0: { michael@0: mTokenizer->lex(token); michael@0: michael@0: if (token->type == Token::PP_HASH) michael@0: { michael@0: parseDirective(token); michael@0: mPastFirstStatement = true; michael@0: } michael@0: michael@0: if (token->type == Token::LAST) michael@0: { michael@0: if (!mConditionalStack.empty()) michael@0: { michael@0: const ConditionalBlock& block = mConditionalStack.back(); michael@0: mDiagnostics->report(Diagnostics::CONDITIONAL_UNTERMINATED, michael@0: block.location, block.type); michael@0: } michael@0: break; michael@0: } michael@0: michael@0: } while (skipping() || (token->type == '\n')); michael@0: michael@0: mPastFirstStatement = true; michael@0: } michael@0: michael@0: void DirectiveParser::parseDirective(Token* token) michael@0: { michael@0: assert(token->type == Token::PP_HASH); michael@0: michael@0: mTokenizer->lex(token); michael@0: if (isEOD(token)) michael@0: { michael@0: // Empty Directive. michael@0: return; michael@0: } michael@0: michael@0: DirectiveType directive = getDirective(token); michael@0: michael@0: // While in an excluded conditional block/group, michael@0: // we only parse conditional directives. michael@0: if (skipping() && !isConditionalDirective(directive)) michael@0: { michael@0: skipUntilEOD(mTokenizer, token); michael@0: return; michael@0: } michael@0: michael@0: switch(directive) michael@0: { michael@0: case DIRECTIVE_NONE: michael@0: mDiagnostics->report(Diagnostics::DIRECTIVE_INVALID_NAME, michael@0: token->location, token->text); michael@0: skipUntilEOD(mTokenizer, token); michael@0: break; michael@0: case DIRECTIVE_DEFINE: michael@0: parseDefine(token); michael@0: break; michael@0: case DIRECTIVE_UNDEF: michael@0: parseUndef(token); michael@0: break; michael@0: case DIRECTIVE_IF: michael@0: parseIf(token); michael@0: break; michael@0: case DIRECTIVE_IFDEF: michael@0: parseIfdef(token); michael@0: break; michael@0: case DIRECTIVE_IFNDEF: michael@0: parseIfndef(token); michael@0: break; michael@0: case DIRECTIVE_ELSE: michael@0: parseElse(token); michael@0: break; michael@0: case DIRECTIVE_ELIF: michael@0: parseElif(token); michael@0: break; michael@0: case DIRECTIVE_ENDIF: michael@0: parseEndif(token); michael@0: break; michael@0: case DIRECTIVE_ERROR: michael@0: parseError(token); michael@0: break; michael@0: case DIRECTIVE_PRAGMA: michael@0: parsePragma(token); michael@0: break; michael@0: case DIRECTIVE_EXTENSION: michael@0: parseExtension(token); michael@0: break; michael@0: case DIRECTIVE_VERSION: michael@0: parseVersion(token); michael@0: break; michael@0: case DIRECTIVE_LINE: michael@0: parseLine(token); michael@0: break; michael@0: default: michael@0: assert(false); michael@0: break; michael@0: } michael@0: michael@0: skipUntilEOD(mTokenizer, token); michael@0: if (token->type == Token::LAST) michael@0: { michael@0: mDiagnostics->report(Diagnostics::EOF_IN_DIRECTIVE, michael@0: token->location, token->text); michael@0: } michael@0: } michael@0: michael@0: void DirectiveParser::parseDefine(Token* token) michael@0: { michael@0: assert(getDirective(token) == DIRECTIVE_DEFINE); michael@0: michael@0: mTokenizer->lex(token); michael@0: if (token->type != Token::IDENTIFIER) michael@0: { michael@0: mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, michael@0: token->location, token->text); michael@0: return; michael@0: } michael@0: if (isMacroPredefined(token->text, *mMacroSet)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::MACRO_PREDEFINED_REDEFINED, michael@0: token->location, token->text); michael@0: return; michael@0: } michael@0: if (isMacroNameReserved(token->text)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::MACRO_NAME_RESERVED, michael@0: token->location, token->text); michael@0: return; michael@0: } michael@0: michael@0: Macro macro; michael@0: macro.type = Macro::kTypeObj; michael@0: macro.name = token->text; michael@0: michael@0: mTokenizer->lex(token); michael@0: if (token->type == '(' && !token->hasLeadingSpace()) michael@0: { michael@0: // Function-like macro. Collect arguments. michael@0: macro.type = Macro::kTypeFunc; michael@0: do { michael@0: mTokenizer->lex(token); michael@0: if (token->type != Token::IDENTIFIER) michael@0: break; michael@0: macro.parameters.push_back(token->text); michael@0: michael@0: mTokenizer->lex(token); // Get ','. michael@0: } while (token->type == ','); michael@0: michael@0: if (token->type != ')') michael@0: { michael@0: mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, michael@0: token->location, michael@0: token->text); michael@0: return; michael@0: } michael@0: mTokenizer->lex(token); // Get ')'. michael@0: } michael@0: michael@0: while ((token->type != '\n') && (token->type != Token::LAST)) michael@0: { michael@0: // Reset the token location because it is unnecessary in replacement michael@0: // list. Resetting it also allows us to reuse Token::equals() to michael@0: // compare macros. michael@0: token->location = SourceLocation(); michael@0: macro.replacements.push_back(*token); michael@0: mTokenizer->lex(token); michael@0: } michael@0: if (!macro.replacements.empty()) michael@0: { michael@0: // Whitespace preceding the replacement list is not considered part of michael@0: // the replacement list for either form of macro. michael@0: macro.replacements.front().setHasLeadingSpace(false); michael@0: } michael@0: michael@0: // Check for macro redefinition. michael@0: MacroSet::const_iterator iter = mMacroSet->find(macro.name); michael@0: if (iter != mMacroSet->end() && !macro.equals(iter->second)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::MACRO_REDEFINED, michael@0: token->location, michael@0: macro.name); michael@0: return; michael@0: } michael@0: mMacroSet->insert(std::make_pair(macro.name, macro)); michael@0: } michael@0: michael@0: void DirectiveParser::parseUndef(Token* token) michael@0: { michael@0: assert(getDirective(token) == DIRECTIVE_UNDEF); michael@0: michael@0: mTokenizer->lex(token); michael@0: if (token->type != Token::IDENTIFIER) michael@0: { michael@0: mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, michael@0: token->location, token->text); michael@0: return; michael@0: } michael@0: michael@0: MacroSet::iterator iter = mMacroSet->find(token->text); michael@0: if (iter != mMacroSet->end()) michael@0: { michael@0: if (iter->second.predefined) michael@0: { michael@0: mDiagnostics->report(Diagnostics::MACRO_PREDEFINED_UNDEFINED, michael@0: token->location, token->text); michael@0: } michael@0: else michael@0: { michael@0: mMacroSet->erase(iter); michael@0: } michael@0: } michael@0: michael@0: mTokenizer->lex(token); michael@0: } michael@0: michael@0: void DirectiveParser::parseIf(Token* token) michael@0: { michael@0: assert(getDirective(token) == DIRECTIVE_IF); michael@0: parseConditionalIf(token); michael@0: } michael@0: michael@0: void DirectiveParser::parseIfdef(Token* token) michael@0: { michael@0: assert(getDirective(token) == DIRECTIVE_IFDEF); michael@0: parseConditionalIf(token); michael@0: } michael@0: michael@0: void DirectiveParser::parseIfndef(Token* token) michael@0: { michael@0: assert(getDirective(token) == DIRECTIVE_IFNDEF); michael@0: parseConditionalIf(token); michael@0: } michael@0: michael@0: void DirectiveParser::parseElse(Token* token) michael@0: { michael@0: assert(getDirective(token) == DIRECTIVE_ELSE); michael@0: michael@0: if (mConditionalStack.empty()) michael@0: { michael@0: mDiagnostics->report(Diagnostics::CONDITIONAL_ELSE_WITHOUT_IF, michael@0: token->location, token->text); michael@0: skipUntilEOD(mTokenizer, token); michael@0: return; michael@0: } michael@0: michael@0: ConditionalBlock& block = mConditionalStack.back(); michael@0: if (block.skipBlock) michael@0: { michael@0: // No diagnostics. Just skip the whole line. michael@0: skipUntilEOD(mTokenizer, token); michael@0: return; michael@0: } michael@0: if (block.foundElseGroup) michael@0: { michael@0: mDiagnostics->report(Diagnostics::CONDITIONAL_ELSE_AFTER_ELSE, michael@0: token->location, token->text); michael@0: skipUntilEOD(mTokenizer, token); michael@0: return; michael@0: } michael@0: michael@0: block.foundElseGroup = true; michael@0: block.skipGroup = block.foundValidGroup; michael@0: block.foundValidGroup = true; michael@0: michael@0: // Warn if there are extra tokens after #else. michael@0: mTokenizer->lex(token); michael@0: if (!isEOD(token)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN, michael@0: token->location, token->text); michael@0: skipUntilEOD(mTokenizer, token); michael@0: } michael@0: } michael@0: michael@0: void DirectiveParser::parseElif(Token* token) michael@0: { michael@0: assert(getDirective(token) == DIRECTIVE_ELIF); michael@0: michael@0: if (mConditionalStack.empty()) michael@0: { michael@0: mDiagnostics->report(Diagnostics::CONDITIONAL_ELIF_WITHOUT_IF, michael@0: token->location, token->text); michael@0: skipUntilEOD(mTokenizer, token); michael@0: return; michael@0: } michael@0: michael@0: ConditionalBlock& block = mConditionalStack.back(); michael@0: if (block.skipBlock) michael@0: { michael@0: // No diagnostics. Just skip the whole line. michael@0: skipUntilEOD(mTokenizer, token); michael@0: return; michael@0: } michael@0: if (block.foundElseGroup) michael@0: { michael@0: mDiagnostics->report(Diagnostics::CONDITIONAL_ELIF_AFTER_ELSE, michael@0: token->location, token->text); michael@0: skipUntilEOD(mTokenizer, token); michael@0: return; michael@0: } michael@0: if (block.foundValidGroup) michael@0: { michael@0: // Do not parse the expression. michael@0: // Also be careful not to emit a diagnostic. michael@0: block.skipGroup = true; michael@0: skipUntilEOD(mTokenizer, token); michael@0: return; michael@0: } michael@0: michael@0: int expression = parseExpressionIf(token); michael@0: block.skipGroup = expression == 0; michael@0: block.foundValidGroup = expression != 0; michael@0: } michael@0: michael@0: void DirectiveParser::parseEndif(Token* token) michael@0: { michael@0: assert(getDirective(token) == DIRECTIVE_ENDIF); michael@0: michael@0: if (mConditionalStack.empty()) michael@0: { michael@0: mDiagnostics->report(Diagnostics::CONDITIONAL_ENDIF_WITHOUT_IF, michael@0: token->location, token->text); michael@0: skipUntilEOD(mTokenizer, token); michael@0: return; michael@0: } michael@0: michael@0: mConditionalStack.pop_back(); michael@0: michael@0: // Warn if there are tokens after #endif. michael@0: mTokenizer->lex(token); michael@0: if (!isEOD(token)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN, michael@0: token->location, token->text); michael@0: skipUntilEOD(mTokenizer, token); michael@0: } michael@0: } michael@0: michael@0: void DirectiveParser::parseError(Token* token) michael@0: { michael@0: assert(getDirective(token) == DIRECTIVE_ERROR); michael@0: michael@0: std::ostringstream stream; michael@0: mTokenizer->lex(token); michael@0: while ((token->type != '\n') && (token->type != Token::LAST)) michael@0: { michael@0: stream << *token; michael@0: mTokenizer->lex(token); michael@0: } michael@0: mDirectiveHandler->handleError(token->location, stream.str()); michael@0: } michael@0: michael@0: // Parses pragma of form: #pragma name[(value)]. michael@0: void DirectiveParser::parsePragma(Token* token) michael@0: { michael@0: assert(getDirective(token) == DIRECTIVE_PRAGMA); michael@0: michael@0: enum State michael@0: { michael@0: PRAGMA_NAME, michael@0: LEFT_PAREN, michael@0: PRAGMA_VALUE, michael@0: RIGHT_PAREN michael@0: }; michael@0: michael@0: bool valid = true; michael@0: std::string name, value; michael@0: int state = PRAGMA_NAME; michael@0: michael@0: mTokenizer->lex(token); michael@0: while ((token->type != '\n') && (token->type != Token::LAST)) michael@0: { michael@0: switch(state++) michael@0: { michael@0: case PRAGMA_NAME: michael@0: name = token->text; michael@0: valid = valid && (token->type == Token::IDENTIFIER); michael@0: break; michael@0: case LEFT_PAREN: michael@0: valid = valid && (token->type == '('); michael@0: break; michael@0: case PRAGMA_VALUE: michael@0: value = token->text; michael@0: valid = valid && (token->type == Token::IDENTIFIER); michael@0: break; michael@0: case RIGHT_PAREN: michael@0: valid = valid && (token->type == ')'); michael@0: break; michael@0: default: michael@0: valid = false; michael@0: break; michael@0: } michael@0: mTokenizer->lex(token); michael@0: } michael@0: michael@0: valid = valid && ((state == PRAGMA_NAME) || // Empty pragma. michael@0: (state == LEFT_PAREN) || // Without value. michael@0: (state == RIGHT_PAREN + 1)); // With value. michael@0: if (!valid) michael@0: { michael@0: mDiagnostics->report(Diagnostics::UNRECOGNIZED_PRAGMA, michael@0: token->location, name); michael@0: } michael@0: else if (state > PRAGMA_NAME) // Do not notify for empty pragma. michael@0: { michael@0: mDirectiveHandler->handlePragma(token->location, name, value); michael@0: } michael@0: } michael@0: michael@0: void DirectiveParser::parseExtension(Token* token) michael@0: { michael@0: assert(getDirective(token) == DIRECTIVE_EXTENSION); michael@0: michael@0: enum State michael@0: { michael@0: EXT_NAME, michael@0: COLON, michael@0: EXT_BEHAVIOR michael@0: }; michael@0: michael@0: bool valid = true; michael@0: std::string name, behavior; michael@0: int state = EXT_NAME; michael@0: michael@0: mTokenizer->lex(token); michael@0: while ((token->type != '\n') && (token->type != Token::LAST)) michael@0: { michael@0: switch (state++) michael@0: { michael@0: case EXT_NAME: michael@0: if (valid && (token->type != Token::IDENTIFIER)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::INVALID_EXTENSION_NAME, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: if (valid) name = token->text; michael@0: break; michael@0: case COLON: michael@0: if (valid && (token->type != ':')) michael@0: { michael@0: mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: break; michael@0: case EXT_BEHAVIOR: michael@0: if (valid && (token->type != Token::IDENTIFIER)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::INVALID_EXTENSION_BEHAVIOR, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: if (valid) behavior = token->text; michael@0: break; michael@0: default: michael@0: if (valid) michael@0: { michael@0: mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: break; michael@0: } michael@0: mTokenizer->lex(token); michael@0: } michael@0: if (valid && (state != EXT_BEHAVIOR + 1)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::INVALID_EXTENSION_DIRECTIVE, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: if (valid) michael@0: mDirectiveHandler->handleExtension(token->location, name, behavior); michael@0: } michael@0: michael@0: void DirectiveParser::parseVersion(Token* token) michael@0: { michael@0: assert(getDirective(token) == DIRECTIVE_VERSION); michael@0: michael@0: if (mPastFirstStatement) michael@0: { michael@0: mDiagnostics->report(Diagnostics::VERSION_NOT_FIRST_STATEMENT, michael@0: token->location, token->text); michael@0: skipUntilEOD(mTokenizer, token); michael@0: return; michael@0: } michael@0: michael@0: enum State michael@0: { michael@0: VERSION_NUMBER michael@0: }; michael@0: michael@0: bool valid = true; michael@0: int version = 0; michael@0: int state = VERSION_NUMBER; michael@0: michael@0: mTokenizer->lex(token); michael@0: while ((token->type != '\n') && (token->type != Token::LAST)) michael@0: { michael@0: switch (state++) michael@0: { michael@0: case VERSION_NUMBER: michael@0: if (valid && (token->type != Token::CONST_INT)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::INVALID_VERSION_NUMBER, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: if (valid && !token->iValue(&version)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: break; michael@0: default: michael@0: if (valid) michael@0: { michael@0: mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: break; michael@0: } michael@0: mTokenizer->lex(token); michael@0: } michael@0: if (valid && (state != VERSION_NUMBER + 1)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::INVALID_VERSION_DIRECTIVE, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: if (valid) michael@0: mDirectiveHandler->handleVersion(token->location, version); michael@0: } michael@0: michael@0: void DirectiveParser::parseLine(Token* token) michael@0: { michael@0: assert(getDirective(token) == DIRECTIVE_LINE); michael@0: michael@0: enum State michael@0: { michael@0: LINE_NUMBER, michael@0: FILE_NUMBER michael@0: }; michael@0: michael@0: bool valid = true; michael@0: int line = 0, file = 0; michael@0: int state = LINE_NUMBER; michael@0: michael@0: MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics); michael@0: macroExpander.lex(token); michael@0: while ((token->type != '\n') && (token->type != Token::LAST)) michael@0: { michael@0: switch (state++) michael@0: { michael@0: case LINE_NUMBER: michael@0: if (valid && (token->type != Token::CONST_INT)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::INVALID_LINE_NUMBER, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: if (valid && !token->iValue(&line)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: break; michael@0: case FILE_NUMBER: michael@0: if (valid && (token->type != Token::CONST_INT)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::INVALID_FILE_NUMBER, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: if (valid && !token->iValue(&file)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::INTEGER_OVERFLOW, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: break; michael@0: default: michael@0: if (valid) michael@0: { michael@0: mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: break; michael@0: } michael@0: macroExpander.lex(token); michael@0: } michael@0: michael@0: if (valid && (state != FILE_NUMBER) && (state != FILE_NUMBER + 1)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::INVALID_LINE_DIRECTIVE, michael@0: token->location, token->text); michael@0: valid = false; michael@0: } michael@0: if (valid) michael@0: { michael@0: mTokenizer->setLineNumber(line); michael@0: if (state == FILE_NUMBER + 1) mTokenizer->setFileNumber(file); michael@0: } michael@0: } michael@0: michael@0: bool DirectiveParser::skipping() const michael@0: { michael@0: if (mConditionalStack.empty()) return false; michael@0: michael@0: const ConditionalBlock& block = mConditionalStack.back(); michael@0: return block.skipBlock || block.skipGroup; michael@0: } michael@0: michael@0: void DirectiveParser::parseConditionalIf(Token* token) michael@0: { michael@0: ConditionalBlock block; michael@0: block.type = token->text; michael@0: block.location = token->location; michael@0: michael@0: if (skipping()) michael@0: { michael@0: // This conditional block is inside another conditional group michael@0: // which is skipped. As a consequence this whole block is skipped. michael@0: // Be careful not to parse the conditional expression that might michael@0: // emit a diagnostic. michael@0: skipUntilEOD(mTokenizer, token); michael@0: block.skipBlock = true; michael@0: } michael@0: else michael@0: { michael@0: DirectiveType directive = getDirective(token); michael@0: michael@0: int expression = 0; michael@0: switch (directive) michael@0: { michael@0: case DIRECTIVE_IF: michael@0: expression = parseExpressionIf(token); michael@0: break; michael@0: case DIRECTIVE_IFDEF: michael@0: expression = parseExpressionIfdef(token); michael@0: break; michael@0: case DIRECTIVE_IFNDEF: michael@0: expression = parseExpressionIfdef(token) == 0 ? 1 : 0; michael@0: break; michael@0: default: michael@0: assert(false); michael@0: break; michael@0: } michael@0: block.skipGroup = expression == 0; michael@0: block.foundValidGroup = expression != 0; michael@0: } michael@0: mConditionalStack.push_back(block); michael@0: } michael@0: michael@0: int DirectiveParser::parseExpressionIf(Token* token) michael@0: { michael@0: assert((getDirective(token) == DIRECTIVE_IF) || michael@0: (getDirective(token) == DIRECTIVE_ELIF)); michael@0: michael@0: DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics); michael@0: MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics); michael@0: ExpressionParser expressionParser(¯oExpander, mDiagnostics); michael@0: michael@0: int expression = 0; michael@0: macroExpander.lex(token); michael@0: expressionParser.parse(token, &expression); michael@0: michael@0: // Warn if there are tokens after #if expression. michael@0: if (!isEOD(token)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN, michael@0: token->location, token->text); michael@0: skipUntilEOD(mTokenizer, token); michael@0: } michael@0: michael@0: return expression; michael@0: } michael@0: michael@0: int DirectiveParser::parseExpressionIfdef(Token* token) michael@0: { michael@0: assert((getDirective(token) == DIRECTIVE_IFDEF) || michael@0: (getDirective(token) == DIRECTIVE_IFNDEF)); michael@0: michael@0: mTokenizer->lex(token); michael@0: if (token->type != Token::IDENTIFIER) michael@0: { michael@0: mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, michael@0: token->location, token->text); michael@0: skipUntilEOD(mTokenizer, token); michael@0: return 0; michael@0: } michael@0: michael@0: MacroSet::const_iterator iter = mMacroSet->find(token->text); michael@0: int expression = iter != mMacroSet->end() ? 1 : 0; michael@0: michael@0: // Warn if there are tokens after #ifdef expression. michael@0: mTokenizer->lex(token); michael@0: if (!isEOD(token)) michael@0: { michael@0: mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN, michael@0: token->location, token->text); michael@0: skipUntilEOD(mTokenizer, token); michael@0: } michael@0: return expression; michael@0: } michael@0: michael@0: } // namespace pp