// | |
// Copyright (c) 2011 The ANGLE Project Authors. All rights reserved. | |
// Use of this source code is governed by a BSD-style license that can be | |
// found in the LICENSE file. | |
// | |
#include "DirectiveParser.h" | |
#include <cassert> | |
#include <cstdlib> | |
#include <sstream> | |
#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 | |
{ | |
class DefinedParser : public Lexer | |
{ | |
public: | |
DefinedParser(Lexer* lexer, | |
const MacroSet* macroSet, | |
Diagnostics* diagnostics) : | |
mLexer(lexer), | |
mMacroSet(macroSet), | |
mDiagnostics(diagnostics) | |
{ | |
} | |
protected: | |
virtual void lex(Token* token) | |
{ | |
static const std::string 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::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::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) : | |
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; | |
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; | |
// Warn 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(); | |
// Warn 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); | |
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)); | |
DefinedParser definedParser(mTokenizer, mMacroSet, mDiagnostics); | |
MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics); | |
ExpressionParser expressionParser(¯oExpander, mDiagnostics); | |
int expression = 0; | |
macroExpander.lex(token); | |
expressionParser.parse(token, &expression); | |
// Warn 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; | |
// Warn 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 |