blob: ed2ec074686ba7efd085667ab56c53fcca3182a0 [file] [log] [blame]
// 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(&macroExpander, 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(&macroExpander, 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