Commit d39ec4c1 by alokp@chromium.org

Implemented conditional processing.

Review URL: https://codereview.appspot.com/6333046 git-svn-id: https://angleproject.googlecode.com/svn/trunk@1168 736b8ea6-26fd-11df-bfd4-992fa37f6226
parent 8b8f02dc
...@@ -36,6 +36,7 @@ class Diagnostics ...@@ -36,6 +36,7 @@ class Diagnostics
EOF_IN_COMMENT, EOF_IN_COMMENT,
EOF_IN_DIRECTIVE, EOF_IN_DIRECTIVE,
UNEXPECTED_TOKEN, UNEXPECTED_TOKEN,
DIRECTIVE_INVALID_NAME,
MACRO_NAME_RESERVED, MACRO_NAME_RESERVED,
MACRO_REDEFINED, MACRO_REDEFINED,
MACRO_PREDEFINED_REDEFINED, MACRO_PREDEFINED_REDEFINED,
...@@ -43,6 +44,12 @@ class Diagnostics ...@@ -43,6 +44,12 @@ class Diagnostics
MACRO_UNTERMINATED_INVOCATION, MACRO_UNTERMINATED_INVOCATION,
MACRO_TOO_FEW_ARGS, MACRO_TOO_FEW_ARGS,
MACRO_TOO_MANY_ARGS, MACRO_TOO_MANY_ARGS,
CONDITIONAL_ENDIF_WITHOUT_IF,
CONDITIONAL_ELSE_WITHOUT_IF,
CONDITIONAL_ELSE_AFTER_ELSE,
CONDITIONAL_ELIF_WITHOUT_IF,
CONDITIONAL_ELIF_AFTER_ELSE,
CONDITIONAL_UNTERMINATED,
INVALID_EXTENSION_NAME, INVALID_EXTENSION_NAME,
INVALID_EXTENSION_BEHAVIOR, INVALID_EXTENSION_BEHAVIOR,
INVALID_EXTENSION_DIRECTIVE, INVALID_EXTENSION_DIRECTIVE,
...@@ -54,6 +61,7 @@ class Diagnostics ...@@ -54,6 +61,7 @@ class Diagnostics
ERROR_END, ERROR_END,
WARNING_BEGIN, WARNING_BEGIN,
CONDITIONAL_UNEXPECTED_TOKEN,
UNRECOGNIZED_PRAGMA, UNRECOGNIZED_PRAGMA,
WARNING_END WARNING_END
}; };
......
...@@ -18,21 +18,104 @@ ...@@ -18,21 +18,104 @@
#include "Tokenizer.h" #include "Tokenizer.h"
namespace { namespace {
static const std::string kDirectiveDefine("define"); enum DirectiveType
static const std::string kDirectiveUndef("undef"); {
static const std::string kDirectiveIf("if"); DIRECTIVE_NONE,
static const std::string kDirectiveIfdef("ifdef"); DIRECTIVE_DEFINE,
static const std::string kDirectiveIfndef("ifndef"); DIRECTIVE_UNDEF,
static const std::string kDirectiveElse("else"); DIRECTIVE_IF,
static const std::string kDirectiveElif("elif"); DIRECTIVE_IFDEF,
static const std::string kDirectiveEndif("endif"); DIRECTIVE_IFNDEF,
static const std::string kDirectiveError("error"); DIRECTIVE_ELSE,
static const std::string kDirectivePragma("pragma"); DIRECTIVE_ELIF,
static const std::string kDirectiveExtension("extension"); DIRECTIVE_ENDIF,
static const std::string kDirectiveVersion("version"); DIRECTIVE_ERROR,
static const std::string kDirectiveLine("line"); DIRECTIVE_PRAGMA,
DIRECTIVE_EXTENSION,
DIRECTIVE_VERSION,
DIRECTIVE_LINE
};
} // namespace } // 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->value == kDirectiveDefine)
return DIRECTIVE_DEFINE;
else if (token->value == kDirectiveUndef)
return DIRECTIVE_UNDEF;
else if (token->value == kDirectiveIf)
return DIRECTIVE_IF;
else if (token->value == kDirectiveIfdef)
return DIRECTIVE_IFDEF;
else if (token->value == kDirectiveIfndef)
return DIRECTIVE_IFNDEF;
else if (token->value == kDirectiveElse)
return DIRECTIVE_ELSE;
else if (token->value == kDirectiveElif)
return DIRECTIVE_ELIF;
else if (token->value == kDirectiveEndif)
return DIRECTIVE_ENDIF;
else if (token->value == kDirectiveError)
return DIRECTIVE_ERROR;
else if (token->value == kDirectivePragma)
return DIRECTIVE_PRAGMA;
else if (token->value == kDirectiveExtension)
return DIRECTIVE_EXTENSION;
else if (token->value == kDirectiveVersion)
return DIRECTIVE_VERSION;
else if (token->value == 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) static bool isMacroNameReserved(const std::string& name)
{ {
// Names prefixed with "GL_" are reserved. // Names prefixed with "GL_" are reserved.
...@@ -59,17 +142,66 @@ namespace pp ...@@ -59,17 +142,66 @@ namespace pp
class DefinedParser : public Lexer class DefinedParser : public Lexer
{ {
public: public:
DefinedParser(Lexer* lexer) : mLexer(lexer) { } DefinedParser(Lexer* lexer,
const MacroSet* macroSet,
Diagnostics* diagnostics) :
mLexer(lexer),
mMacroSet(macroSet),
mDiagnostics(diagnostics)
{
}
protected: protected:
virtual void lex(Token* token) virtual void lex(Token* token)
{ {
// TODO(alokp): Implement me. static const std::string kDefined("defined");
mLexer->lex(token); mLexer->lex(token);
if (token->type != Token::IDENTIFIER)
return;
if (token->value != 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->value);
skipUntilEOD(mLexer, token);
return;
}
MacroSet::const_iterator iter = mMacroSet->find(token->value);
std::string expression = iter != mMacroSet->end() ? "1" : "0";
if (paren)
{
mLexer->lex(token);
if (token->type != ')')
{
mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
token->location, token->value);
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->value = expression;
} }
private: private:
Lexer* mLexer; Lexer* mLexer;
const MacroSet* mMacroSet;
Diagnostics* mDiagnostics;
}; };
DirectiveParser::DirectiveParser(Tokenizer* tokenizer, DirectiveParser::DirectiveParser(Tokenizer* tokenizer,
...@@ -88,8 +220,24 @@ void DirectiveParser::lex(Token* token) ...@@ -88,8 +220,24 @@ void DirectiveParser::lex(Token* token)
do do
{ {
mTokenizer->lex(token); mTokenizer->lex(token);
if (token->type == '#') parseDirective(token);
} while (token->type == '\n'); if (token->type == '#')
{
parseDirective(token);
}
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'));
} }
void DirectiveParser::parseDirective(Token* token) void DirectiveParser::parseDirective(Token* token)
...@@ -97,51 +245,78 @@ void DirectiveParser::parseDirective(Token* token) ...@@ -97,51 +245,78 @@ void DirectiveParser::parseDirective(Token* token)
assert(token->type == '#'); assert(token->type == '#');
mTokenizer->lex(token); mTokenizer->lex(token);
if (token->type == Token::IDENTIFIER) DirectiveType directive = getDirective(token);
{
if (token->value == kDirectiveDefine) // While in an excluded conditional block/group,
parseDefine(token); // we only parse conditional directives.
else if (token->value == kDirectiveUndef) if (skipping() && !isConditionalDirective(directive))
parseUndef(token); {
else if (token->value == kDirectiveIf) skipUntilEOD(mTokenizer, token);
parseIf(token); return;
else if (token->value == kDirectiveIfdef) }
parseIfdef(token);
else if (token->value == kDirectiveIfndef) switch(directive)
parseIfndef(token); {
else if (token->value == kDirectiveElse) case DIRECTIVE_NONE:
parseElse(token); mDiagnostics->report(Diagnostics::DIRECTIVE_INVALID_NAME,
else if (token->value == kDirectiveElif) token->location, token->value);
parseElif(token); skipUntilEOD(mTokenizer, token);
else if (token->value == kDirectiveEndif) break;
parseEndif(token); case DIRECTIVE_DEFINE:
else if (token->value == kDirectiveError) parseDefine(token);
parseError(token); break;
else if (token->value == kDirectivePragma) case DIRECTIVE_UNDEF:
parsePragma(token); parseUndef(token);
else if (token->value == kDirectiveExtension) break;
parseExtension(token); case DIRECTIVE_IF:
else if (token->value == kDirectiveVersion) parseIf(token);
parseVersion(token); break;
else if (token->value == kDirectiveLine) case DIRECTIVE_IFDEF:
parseLine(token); parseIfdef(token);
} break;
case DIRECTIVE_IFNDEF:
while (token->type != '\n') parseIfndef(token);
{ break;
if (token->type == 0) { case DIRECTIVE_ELSE:
mDiagnostics->report(Diagnostics::EOF_IN_DIRECTIVE, parseElse(token);
token->location, break;
token->value); case DIRECTIVE_ELIF:
break; parseElif(token);
} break;
mTokenizer->lex(token); 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->value);
} }
} }
void DirectiveParser::parseDefine(Token* token) void DirectiveParser::parseDefine(Token* token)
{ {
assert(token->value == kDirectiveDefine); assert(getDirective(token) == DIRECTIVE_DEFINE);
mTokenizer->lex(token); mTokenizer->lex(token);
if (token->type != Token::IDENTIFIER) if (token->type != Token::IDENTIFIER)
...@@ -221,7 +396,7 @@ void DirectiveParser::parseDefine(Token* token) ...@@ -221,7 +396,7 @@ void DirectiveParser::parseDefine(Token* token)
void DirectiveParser::parseUndef(Token* token) void DirectiveParser::parseUndef(Token* token)
{ {
assert(token->value == kDirectiveUndef); assert(getDirective(token) == DIRECTIVE_UNDEF);
mTokenizer->lex(token); mTokenizer->lex(token);
if (token->type != Token::IDENTIFIER) if (token->type != Token::IDENTIFIER)
...@@ -250,63 +425,130 @@ void DirectiveParser::parseUndef(Token* token) ...@@ -250,63 +425,130 @@ void DirectiveParser::parseUndef(Token* token)
void DirectiveParser::parseIf(Token* token) void DirectiveParser::parseIf(Token* token)
{ {
// TODO(alokp): Implement me. assert(getDirective(token) == DIRECTIVE_IF);
assert(token->value == kDirectiveIf); parseConditionalIf(token);
DefinedParser definedParser(mTokenizer);
MacroExpander macroExpander(&definedParser, mMacroSet, mDiagnostics);
ExpressionParser expressionParser(&macroExpander, mDiagnostics);
macroExpander.lex(token);
int expression = 0;
if (!expressionParser.parse(token, &expression))
{
// TODO(alokp): Report diagnostic.
return;
}
// We have a valid #if directive. Handle it.
// TODO(alokp): Push conditional block.
} }
void DirectiveParser::parseIfdef(Token* token) void DirectiveParser::parseIfdef(Token* token)
{ {
// TODO(alokp): Implement me. assert(getDirective(token) == DIRECTIVE_IFDEF);
assert(token->value == kDirectiveIfdef); parseConditionalIf(token);
mTokenizer->lex(token);
} }
void DirectiveParser::parseIfndef(Token* token) void DirectiveParser::parseIfndef(Token* token)
{ {
// TODO(alokp): Implement me. assert(getDirective(token) == DIRECTIVE_IFNDEF);
assert(token->value == kDirectiveIfndef); parseConditionalIf(token);
mTokenizer->lex(token);
} }
void DirectiveParser::parseElse(Token* token) void DirectiveParser::parseElse(Token* token)
{ {
// TODO(alokp): Implement me. assert(getDirective(token) == DIRECTIVE_ELSE);
assert(token->value == kDirectiveElse);
if (mConditionalStack.empty())
{
mDiagnostics->report(Diagnostics::CONDITIONAL_ELSE_WITHOUT_IF,
token->location, token->value);
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->value);
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); mTokenizer->lex(token);
if (!isEOD(token))
{
mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN,
token->location, token->value);
skipUntilEOD(mTokenizer, token);
}
} }
void DirectiveParser::parseElif(Token* token) void DirectiveParser::parseElif(Token* token)
{ {
// TODO(alokp): Implement me. assert(getDirective(token) == DIRECTIVE_ELIF);
assert(token->value == kDirectiveElif);
mTokenizer->lex(token); if (mConditionalStack.empty())
{
mDiagnostics->report(Diagnostics::CONDITIONAL_ELIF_WITHOUT_IF,
token->location, token->value);
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->value);
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) void DirectiveParser::parseEndif(Token* token)
{ {
// TODO(alokp): Implement me. assert(getDirective(token) == DIRECTIVE_ENDIF);
assert(token->value == kDirectiveEndif);
if (mConditionalStack.empty())
{
mDiagnostics->report(Diagnostics::CONDITIONAL_ENDIF_WITHOUT_IF,
token->location, token->value);
skipUntilEOD(mTokenizer, token);
return;
}
mConditionalStack.pop_back();
// Warn if there are tokens after #endif.
mTokenizer->lex(token); mTokenizer->lex(token);
if (!isEOD(token))
{
mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN,
token->location, token->value);
skipUntilEOD(mTokenizer, token);
}
} }
void DirectiveParser::parseError(Token* token) void DirectiveParser::parseError(Token* token)
{ {
assert(token->value == kDirectiveError); assert(getDirective(token) == DIRECTIVE_ERROR);
std::stringstream stream; std::stringstream stream;
mTokenizer->lex(token); mTokenizer->lex(token);
...@@ -321,7 +563,7 @@ void DirectiveParser::parseError(Token* token) ...@@ -321,7 +563,7 @@ void DirectiveParser::parseError(Token* token)
// Parses pragma of form: #pragma name[(value)]. // Parses pragma of form: #pragma name[(value)].
void DirectiveParser::parsePragma(Token* token) void DirectiveParser::parsePragma(Token* token)
{ {
assert(token->value == kDirectivePragma); assert(getDirective(token) == DIRECTIVE_PRAGMA);
enum State enum State
{ {
...@@ -377,7 +619,7 @@ void DirectiveParser::parsePragma(Token* token) ...@@ -377,7 +619,7 @@ void DirectiveParser::parsePragma(Token* token)
void DirectiveParser::parseExtension(Token* token) void DirectiveParser::parseExtension(Token* token)
{ {
assert(token->value == kDirectiveExtension); assert(getDirective(token) == DIRECTIVE_EXTENSION);
enum State enum State
{ {
...@@ -444,7 +686,7 @@ void DirectiveParser::parseExtension(Token* token) ...@@ -444,7 +686,7 @@ void DirectiveParser::parseExtension(Token* token)
void DirectiveParser::parseVersion(Token* token) void DirectiveParser::parseVersion(Token* token)
{ {
assert(token->value == kDirectiveVersion); assert(getDirective(token) == DIRECTIVE_VERSION);
enum State enum State
{ {
...@@ -492,7 +734,7 @@ void DirectiveParser::parseVersion(Token* token) ...@@ -492,7 +734,7 @@ void DirectiveParser::parseVersion(Token* token)
void DirectiveParser::parseLine(Token* token) void DirectiveParser::parseLine(Token* token)
{ {
assert(token->value == kDirectiveLine); assert(getDirective(token) == DIRECTIVE_LINE);
enum State enum State
{ {
...@@ -553,4 +795,105 @@ void DirectiveParser::parseLine(Token* token) ...@@ -553,4 +795,105 @@ void DirectiveParser::parseLine(Token* token)
} }
} }
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->value;
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(&macroExpander, 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->value);
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->value);
skipUntilEOD(mTokenizer, token);
return 0;
}
MacroSet::const_iterator iter = mMacroSet->find(token->value);
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->value);
skipUntilEOD(mTokenizer, token);
}
return expression;
}
} // namespace pp } // namespace pp
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "Lexer.h" #include "Lexer.h"
#include "Macro.h" #include "Macro.h"
#include "pp_utils.h" #include "pp_utils.h"
#include "SourceLocation.h"
namespace pp namespace pp
{ {
...@@ -46,6 +47,29 @@ class DirectiveParser : public Lexer ...@@ -46,6 +47,29 @@ class DirectiveParser : public Lexer
void parseVersion(Token* token); void parseVersion(Token* token);
void parseLine(Token* token); void parseLine(Token* token);
bool skipping() const;
void parseConditionalIf(Token* token);
int parseExpressionIf(Token* token);
int parseExpressionIfdef(Token* token);
struct ConditionalBlock
{
std::string type;
SourceLocation location;
bool skipBlock;
bool skipGroup;
bool foundValidGroup;
bool foundElseGroup;
ConditionalBlock() :
skipBlock(false),
skipGroup(false),
foundValidGroup(false),
foundElseGroup(false)
{
}
};
std::vector<ConditionalBlock> mConditionalStack;
Tokenizer* mTokenizer; Tokenizer* mTokenizer;
MacroSet* mMacroSet; MacroSet* mMacroSet;
Diagnostics* mDiagnostics; Diagnostics* mDiagnostics;
......
...@@ -91,12 +91,23 @@ ...@@ -91,12 +91,23 @@
#include "ExpressionParser.h" #include "ExpressionParser.h"
#include <cassert> #include <cassert>
#include <cstdlib>
#include <sstream> #include <sstream>
#include "Diagnostics.h" #include "Diagnostics.h"
#include "Lexer.h" #include "Lexer.h"
#include "Token.h" #include "Token.h"
#if defined(_MSC_VER)
typedef __int64 YYSTYPE;
#define strtoll _strtoi64
#else
#include <stdint.h>
typedef intmax_t YYSTYPE;
#endif // _MSC_VER
#define YYSTYPE_IS_TRIVIAL 1
#define YYSTYPE_IS_DECLARED 1
namespace { namespace {
struct Context struct Context
{ {
...@@ -108,7 +119,7 @@ struct Context ...@@ -108,7 +119,7 @@ struct Context
} // namespace } // namespace
static int yylex(int* lvalp, Context* context); static int yylex(YYSTYPE* lvalp, Context* context);
static void yyerror(Context* context, const char* reason); static void yyerror(Context* context, const char* reason);
...@@ -456,9 +467,9 @@ static const yytype_int8 yyrhs[] = ...@@ -456,9 +467,9 @@ static const yytype_int8 yyrhs[] =
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ /* YYRLINE[YYN] -- source line where rule number YYN was defined. */
static const yytype_uint8 yyrline[] = static const yytype_uint8 yyrline[] =
{ {
0, 74, 74, 81, 82, 85, 88, 91, 94, 97, 0, 85, 85, 92, 93, 96, 99, 102, 105, 108,
100, 103, 106, 109, 112, 115, 118, 121, 124, 127, 111, 114, 117, 120, 123, 126, 129, 132, 135, 138,
140, 153, 156, 159, 162, 165, 168 151, 164, 167, 170, 173, 176, 179
}; };
#endif #endif
...@@ -1425,7 +1436,7 @@ yyreduce: ...@@ -1425,7 +1436,7 @@ yyreduce:
case 2: case 2:
{ {
*(context->result) = (yyvsp[(1) - (1)]); *(context->result) = static_cast<int>((yyvsp[(1) - (1)]));
YYACCEPT; YYACCEPT;
} }
break; break;
...@@ -1825,7 +1836,7 @@ yyreturn: ...@@ -1825,7 +1836,7 @@ yyreturn:
int yylex(int* lvalp, Context* context) int yylex(YYSTYPE* lvalp, Context* context)
{ {
int type = 0; int type = 0;
...@@ -1833,7 +1844,7 @@ int yylex(int* lvalp, Context* context) ...@@ -1833,7 +1844,7 @@ int yylex(int* lvalp, Context* context)
switch (token->type) switch (token->type)
{ {
case pp::Token::CONST_INT: case pp::Token::CONST_INT:
*lvalp = atoi(token->value.c_str()); *lvalp = strtoll(token->value.c_str(), NULL, 0);
type = CONST_INT; type = CONST_INT;
break; break;
......
...@@ -28,12 +28,23 @@ WHICH GENERATES THE GLSL ES preprocessor expression parser. ...@@ -28,12 +28,23 @@ WHICH GENERATES THE GLSL ES preprocessor expression parser.
#include "ExpressionParser.h" #include "ExpressionParser.h"
#include <cassert> #include <cassert>
#include <cstdlib>
#include <sstream> #include <sstream>
#include "Diagnostics.h" #include "Diagnostics.h"
#include "Lexer.h" #include "Lexer.h"
#include "Token.h" #include "Token.h"
#if defined(_MSC_VER)
typedef __int64 YYSTYPE;
#define strtoll _strtoi64
#else
#include <stdint.h>
typedef intmax_t YYSTYPE;
#endif // _MSC_VER
#define YYSTYPE_IS_TRIVIAL 1
#define YYSTYPE_IS_DECLARED 1
namespace { namespace {
struct Context struct Context
{ {
...@@ -51,7 +62,7 @@ struct Context ...@@ -51,7 +62,7 @@ struct Context
%lex-param {Context *context} %lex-param {Context *context}
%{ %{
static int yylex(int* lvalp, Context* context); static int yylex(YYSTYPE* lvalp, Context* context);
static void yyerror(Context* context, const char* reason); static void yyerror(Context* context, const char* reason);
%} %}
...@@ -72,7 +83,7 @@ static void yyerror(Context* context, const char* reason); ...@@ -72,7 +83,7 @@ static void yyerror(Context* context, const char* reason);
input input
: expression { : expression {
*(context->result) = $1; *(context->result) = static_cast<int>($1);
YYACCEPT; YYACCEPT;
} }
; ;
...@@ -172,7 +183,7 @@ expression ...@@ -172,7 +183,7 @@ expression
%% %%
int yylex(int* lvalp, Context* context) int yylex(YYSTYPE* lvalp, Context* context)
{ {
int type = 0; int type = 0;
...@@ -180,7 +191,7 @@ int yylex(int* lvalp, Context* context) ...@@ -180,7 +191,7 @@ int yylex(int* lvalp, Context* context)
switch (token->type) switch (token->type)
{ {
case pp::Token::CONST_INT: case pp::Token::CONST_INT:
*lvalp = atoi(token->value.c_str()); *lvalp = strtoll(token->value.c_str(), NULL, 0);
type = CONST_INT; type = CONST_INT;
break; break;
......
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
'preprocessor_tests/error_test.cpp', 'preprocessor_tests/error_test.cpp',
'preprocessor_tests/extension_test.cpp', 'preprocessor_tests/extension_test.cpp',
'preprocessor_tests/identifier_test.cpp', 'preprocessor_tests/identifier_test.cpp',
'preprocessor_tests/if_test.cpp',
'preprocessor_tests/input_test.cpp', 'preprocessor_tests/input_test.cpp',
'preprocessor_tests/location_test.cpp', 'preprocessor_tests/location_test.cpp',
'preprocessor_tests/MockDiagnostics.h', 'preprocessor_tests/MockDiagnostics.h',
......
//
// Copyright (c) 2012 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 "PreprocessorTest.h"
#include "Token.h"
class IfTest : public PreprocessorTest
{
};
TEST_F(IfTest, If_0)
{
const char* str = "pass_1\n"
"#if 0\n"
"fail\n"
"#endif\n"
"pass_2\n";
const char* expected = "pass_1\n"
"\n"
"\n"
"\n"
"pass_2\n";
preprocess(str, expected);
}
TEST_F(IfTest, If_1)
{
const char* str = "pass_1\n"
"#if 1\n"
"pass_2\n"
"#endif\n"
"pass_3\n";
const char* expected = "pass_1\n"
"\n"
"pass_2\n"
"\n"
"pass_3\n";
preprocess(str, expected);
}
TEST_F(IfTest, If_0_Else)
{
const char* str = "pass_1\n"
"#if 0\n"
"fail\n"
"#else\n"
"pass_2\n"
"#endif\n"
"pass_3\n";
const char* expected = "pass_1\n"
"\n"
"\n"
"\n"
"pass_2\n"
"\n"
"pass_3\n";
preprocess(str, expected);
}
TEST_F(IfTest, If_1_Else)
{
const char* str = "pass_1\n"
"#if 1\n"
"pass_2\n"
"#else\n"
"fail\n"
"#endif\n"
"pass_3\n";
const char* expected = "pass_1\n"
"\n"
"pass_2\n"
"\n"
"\n"
"\n"
"pass_3\n";
preprocess(str, expected);
}
TEST_F(IfTest, If_0_Elif)
{
const char* str = "pass_1\n"
"#if 0\n"
"fail_1\n"
"#elif 0\n"
"fail_2\n"
"#elif 1\n"
"pass_2\n"
"#elif 1\n"
"fail_3\n"
"#else\n"
"fail_4\n"
"#endif\n"
"pass_3\n";
const char* expected = "pass_1\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"pass_2\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"pass_3\n";
preprocess(str, expected);
}
TEST_F(IfTest, If_1_Elif)
{
const char* str = "pass_1\n"
"#if 1\n"
"pass_2\n"
"#elif 0\n"
"fail_1\n"
"#elif 1\n"
"fail_2\n"
"#else\n"
"fail_4\n"
"#endif\n"
"pass_3\n";
const char* expected = "pass_1\n"
"\n"
"pass_2\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"pass_3\n";
preprocess(str, expected);
}
TEST_F(IfTest, If_Elif_Else)
{
const char* str = "pass_1\n"
"#if 0\n"
"fail_1\n"
"#elif 0\n"
"fail_2\n"
"#elif 0\n"
"fail_3\n"
"#else\n"
"pass_2\n"
"#endif\n"
"pass_3\n";
const char* expected = "pass_1\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"pass_2\n"
"\n"
"pass_3\n";
preprocess(str, expected);
}
TEST_F(IfTest, If_0_Nested)
{
const char* str = "pass_1\n"
"#if 0\n"
"fail_1\n"
"#if 1\n"
"fail_2\n"
"#else\n"
"fail_3\n"
"#endif\n"
"#else\n"
"pass_2\n"
"#endif\n"
"pass_3\n";
const char* expected = "pass_1\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"pass_2\n"
"\n"
"pass_3\n";
preprocess(str, expected);
}
TEST_F(IfTest, If_1_Nested)
{
const char* str = "pass_1\n"
"#if 1\n"
"pass_2\n"
"#if 1\n"
"pass_3\n"
"#else\n"
"fail_1\n"
"#endif\n"
"#else\n"
"fail_2\n"
"#endif\n"
"pass_4\n";
const char* expected = "pass_1\n"
"\n"
"pass_2\n"
"\n"
"pass_3\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"pass_4\n";
preprocess(str, expected);
}
TEST_F(IfTest, OperatorPrecedence)
{
const char* str = "#if 1 + 2 * 3 + - (26 % 17 - + 4 / 2)\n"
"fail_1\n"
"#else\n"
"pass_1\n"
"#endif\n";
const char* expected = "\n"
"\n"
"\n"
"pass_1\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, OperatorDefined)
{
const char* str = "#if defined foo\n"
"fail_1\n"
"#else\n"
"pass_1\n"
"#endif\n"
"#define foo\n"
"#if defined(foo)\n"
"pass_2\n"
"#else\n"
"fail_2\n"
"#endif\n"
"#undef foo\n"
"#if defined ( foo ) \n"
"fail_3\n"
"#else\n"
"pass_3\n"
"#endif\n";
const char* expected = "\n"
"\n"
"\n"
"pass_1\n"
"\n"
"\n"
"\n"
"pass_2\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"pass_3\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, OperatorEQ)
{
const char* str = "#if 4 - 1 == 2 + 1\n"
"pass\n"
"#else\n"
"fail\n"
"#endif\n";
const char* expected = "\n"
"pass\n"
"\n"
"\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, OperatorNE)
{
const char* str = "#if 1 != 2\n"
"pass\n"
"#else\n"
"fail\n"
"#endif\n";
const char* expected = "\n"
"pass\n"
"\n"
"\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, OperatorLess)
{
const char* str = "#if 1 < 2\n"
"pass\n"
"#else\n"
"fail\n"
"#endif\n";
const char* expected = "\n"
"pass\n"
"\n"
"\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, OperatorGreater)
{
const char* str = "#if 2 > 1\n"
"pass\n"
"#else\n"
"fail\n"
"#endif\n";
const char* expected = "\n"
"pass\n"
"\n"
"\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, OperatorLE)
{
const char* str = "#if 1 <= 2\n"
"pass_1\n"
"#else\n"
"fail_1\n"
"#endif\n"
"#if 2 <= 2\n"
"pass_2\n"
"#else\n"
"fail_2\n"
"#endif\n";
const char* expected = "\n"
"pass_1\n"
"\n"
"\n"
"\n"
"\n"
"pass_2\n"
"\n"
"\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, OperatorGE)
{
const char* str = "#if 2 >= 1\n"
"pass_1\n"
"#else\n"
"fail_1\n"
"#endif\n"
"#if 2 >= 2\n"
"pass_2\n"
"#else\n"
"fail_2\n"
"#endif\n";
const char* expected = "\n"
"pass_1\n"
"\n"
"\n"
"\n"
"\n"
"pass_2\n"
"\n"
"\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, OperatorBitwiseOR)
{
const char* str = "#if (0xaaaaaaaa | 0x55555555) == 0xffffffff\n"
"pass\n"
"#else\n"
"fail\n"
"#endif\n";
const char* expected = "\n"
"pass\n"
"\n"
"\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, OperatorBitwiseAND)
{
const char* str = "#if (0xaaaaaaa & 0x5555555) == 0\n"
"pass\n"
"#else\n"
"fail\n"
"#endif\n";
const char* expected = "\n"
"pass\n"
"\n"
"\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, OperatorBitwiseXOR)
{
const char* str = "#if (0xaaaaaaa ^ 0x5555555) == 0xfffffff\n"
"pass\n"
"#else\n"
"fail\n"
"#endif\n";
const char* expected = "\n"
"pass\n"
"\n"
"\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, OperatorBitwiseComplement)
{
const char* str = "#if (~ 0xdeadbeef) == -3735928560\n"
"pass\n"
"#else\n"
"fail\n"
"#endif\n";
const char* expected = "\n"
"pass\n"
"\n"
"\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, OperatorLeft)
{
const char* str = "#if (1 << 12) == 4096\n"
"pass\n"
"#else\n"
"fail\n"
"#endif\n";
const char* expected = "\n"
"pass\n"
"\n"
"\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, OperatorRight)
{
const char* str = "#if (31762 >> 8) == 124\n"
"pass\n"
"#else\n"
"fail\n"
"#endif\n";
const char* expected = "\n"
"pass\n"
"\n"
"\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, ExpressionWithMacros)
{
const char* str = "#define one 1\n"
"#define two 2\n"
"#define three 3\n"
"#if one + two == three\n"
"pass\n"
"#else\n"
"fail\n"
"#endif\n";
const char* expected = "\n"
"\n"
"\n"
"\n"
"pass\n"
"\n"
"\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, JunkInsideExcludedBlockIgnored)
{
const char* str = "#if 0\n"
"foo !@#$%^&* .1bar\n"
"#foo\n"
"#if bar\n"
"fail\n"
"#endif\n"
"#else\n"
"pass\n"
"#endif\n";
const char* expected = "\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"pass\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, Ifdef)
{
const char* str = "#define foo\n"
"#ifdef foo\n"
"pass_1\n"
"#else\n"
"fail_1\n"
"#endif\n"
"#undef foo\n"
"#ifdef foo\n"
"fail_2\n"
"#else\n"
"pass_2\n"
"#endif\n";
const char* expected = "\n"
"\n"
"pass_1\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"pass_2\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, Ifndef)
{
const char* str = "#define foo\n"
"#ifndef foo\n"
"fail_1\n"
"#else\n"
"pass_1\n"
"#endif\n"
"#undef foo\n"
"#ifndef foo\n"
"pass_2\n"
"#else\n"
"fail_2\n"
"#endif\n";
const char* expected = "\n"
"\n"
"\n"
"\n"
"pass_1\n"
"\n"
"\n"
"\n"
"pass_2\n"
"\n"
"\n"
"\n";
preprocess(str, expected);
}
TEST_F(IfTest, MissingExpression)
{
const char* str = "#if\n"
"#endif\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::INVALID_EXPRESSION,
pp::SourceLocation(0, 1),
"syntax error"));
pp::Token token;
mPreprocessor.lex(&token);
}
TEST_F(IfTest, UndefinedMacro)
{
const char* str = "#if UNDEFINED\n"
"#endif\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::INVALID_EXPRESSION,
pp::SourceLocation(0, 1),
"syntax error"));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN,
pp::SourceLocation(0, 1),
"UNDEFINED"));
pp::Token token;
mPreprocessor.lex(&token);
}
TEST_F(IfTest, InvalidExpressionIgnoredForExcludedElif)
{
const char* str = "#if 1\n"
"pass\n"
"#elif UNDEFINED\n"
"fail\n"
"#endif\n";
const char* expected = "\n"
"pass\n"
"\n"
"\n"
"\n";
// No error or warning.
using testing::_;
EXPECT_CALL(mDiagnostics, print(_, _, _)).Times(0);
preprocess(str, expected);
}
TEST_F(IfTest, ElseWithoutIf)
{
const char* str = "#else\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::CONDITIONAL_ELSE_WITHOUT_IF,
pp::SourceLocation(0, 1),
"else"));
pp::Token token;
mPreprocessor.lex(&token);
}
TEST_F(IfTest, ElifWithoutIf)
{
const char* str = "#elif 1\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::CONDITIONAL_ELIF_WITHOUT_IF,
pp::SourceLocation(0, 1),
"elif"));
pp::Token token;
mPreprocessor.lex(&token);
}
TEST_F(IfTest, EndifWithoutIf)
{
const char* str = "#endif\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::CONDITIONAL_ENDIF_WITHOUT_IF,
pp::SourceLocation(0, 1),
"endif"));
pp::Token token;
mPreprocessor.lex(&token);
}
TEST_F(IfTest, ElseAfterElse)
{
const char* str = "#if 1\n"
"#else\n"
"#else\n"
"#endif\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::CONDITIONAL_ELSE_AFTER_ELSE,
pp::SourceLocation(0, 3),
"else"));
pp::Token token;
mPreprocessor.lex(&token);
}
TEST_F(IfTest, ElifAfterElse)
{
const char* str = "#if 1\n"
"#else\n"
"#elif 0\n"
"#endif\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::CONDITIONAL_ELIF_AFTER_ELSE,
pp::SourceLocation(0, 3),
"elif"));
pp::Token token;
mPreprocessor.lex(&token);
}
TEST_F(IfTest, UnterminatedIf)
{
const char* str = "#if 1\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::CONDITIONAL_UNTERMINATED,
pp::SourceLocation(0, 1),
"if"));
pp::Token token;
mPreprocessor.lex(&token);
}
TEST_F(IfTest, UnterminatedIfdef)
{
const char* str = "#ifdef foo\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, 0));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::CONDITIONAL_UNTERMINATED,
pp::SourceLocation(0, 1),
"ifdef"));
pp::Token token;
mPreprocessor.lex(&token);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment