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
EOF_IN_COMMENT,
EOF_IN_DIRECTIVE,
UNEXPECTED_TOKEN,
DIRECTIVE_INVALID_NAME,
MACRO_NAME_RESERVED,
MACRO_REDEFINED,
MACRO_PREDEFINED_REDEFINED,
......@@ -43,6 +44,12 @@ class Diagnostics
MACRO_UNTERMINATED_INVOCATION,
MACRO_TOO_FEW_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_BEHAVIOR,
INVALID_EXTENSION_DIRECTIVE,
......@@ -54,6 +61,7 @@ class Diagnostics
ERROR_END,
WARNING_BEGIN,
CONDITIONAL_UNEXPECTED_TOKEN,
UNRECOGNIZED_PRAGMA,
WARNING_END
};
......
......@@ -18,21 +18,104 @@
#include "Tokenizer.h"
namespace {
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");
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->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)
{
// Names prefixed with "GL_" are reserved.
......@@ -59,17 +142,66 @@ namespace pp
class DefinedParser : public Lexer
{
public:
DefinedParser(Lexer* lexer) : mLexer(lexer) { }
DefinedParser(Lexer* lexer,
const MacroSet* macroSet,
Diagnostics* diagnostics) :
mLexer(lexer),
mMacroSet(macroSet),
mDiagnostics(diagnostics)
{
}
protected:
virtual void lex(Token* token)
{
// TODO(alokp): Implement me.
static const std::string kDefined("defined");
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:
Lexer* mLexer;
const MacroSet* mMacroSet;
Diagnostics* mDiagnostics;
};
DirectiveParser::DirectiveParser(Tokenizer* tokenizer,
......@@ -88,8 +220,24 @@ void DirectiveParser::lex(Token* token)
do
{
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)
......@@ -97,51 +245,78 @@ void DirectiveParser::parseDirective(Token* token)
assert(token->type == '#');
mTokenizer->lex(token);
if (token->type == Token::IDENTIFIER)
{
if (token->value == kDirectiveDefine)
parseDefine(token);
else if (token->value == kDirectiveUndef)
parseUndef(token);
else if (token->value == kDirectiveIf)
parseIf(token);
else if (token->value == kDirectiveIfdef)
parseIfdef(token);
else if (token->value == kDirectiveIfndef)
parseIfndef(token);
else if (token->value == kDirectiveElse)
parseElse(token);
else if (token->value == kDirectiveElif)
parseElif(token);
else if (token->value == kDirectiveEndif)
parseEndif(token);
else if (token->value == kDirectiveError)
parseError(token);
else if (token->value == kDirectivePragma)
parsePragma(token);
else if (token->value == kDirectiveExtension)
parseExtension(token);
else if (token->value == kDirectiveVersion)
parseVersion(token);
else if (token->value == kDirectiveLine)
parseLine(token);
}
while (token->type != '\n')
{
if (token->type == 0) {
mDiagnostics->report(Diagnostics::EOF_IN_DIRECTIVE,
token->location,
token->value);
break;
}
mTokenizer->lex(token);
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->value);
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->value);
}
}
void DirectiveParser::parseDefine(Token* token)
{
assert(token->value == kDirectiveDefine);
assert(getDirective(token) == DIRECTIVE_DEFINE);
mTokenizer->lex(token);
if (token->type != Token::IDENTIFIER)
......@@ -221,7 +396,7 @@ void DirectiveParser::parseDefine(Token* token)
void DirectiveParser::parseUndef(Token* token)
{
assert(token->value == kDirectiveUndef);
assert(getDirective(token) == DIRECTIVE_UNDEF);
mTokenizer->lex(token);
if (token->type != Token::IDENTIFIER)
......@@ -250,63 +425,130 @@ void DirectiveParser::parseUndef(Token* token)
void DirectiveParser::parseIf(Token* token)
{
// TODO(alokp): Implement me.
assert(token->value == kDirectiveIf);
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.
assert(getDirective(token) == DIRECTIVE_IF);
parseConditionalIf(token);
}
void DirectiveParser::parseIfdef(Token* token)
{
// TODO(alokp): Implement me.
assert(token->value == kDirectiveIfdef);
mTokenizer->lex(token);
assert(getDirective(token) == DIRECTIVE_IFDEF);
parseConditionalIf(token);
}
void DirectiveParser::parseIfndef(Token* token)
{
// TODO(alokp): Implement me.
assert(token->value == kDirectiveIfndef);
mTokenizer->lex(token);
assert(getDirective(token) == DIRECTIVE_IFNDEF);
parseConditionalIf(token);
}
void DirectiveParser::parseElse(Token* token)
{
// TODO(alokp): Implement me.
assert(token->value == kDirectiveElse);
assert(getDirective(token) == DIRECTIVE_ELSE);
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);
if (!isEOD(token))
{
mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN,
token->location, token->value);
skipUntilEOD(mTokenizer, token);
}
}
void DirectiveParser::parseElif(Token* token)
{
// TODO(alokp): Implement me.
assert(token->value == kDirectiveElif);
mTokenizer->lex(token);
assert(getDirective(token) == DIRECTIVE_ELIF);
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)
{
// TODO(alokp): Implement me.
assert(token->value == kDirectiveEndif);
assert(getDirective(token) == DIRECTIVE_ENDIF);
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);
if (!isEOD(token))
{
mDiagnostics->report(Diagnostics::CONDITIONAL_UNEXPECTED_TOKEN,
token->location, token->value);
skipUntilEOD(mTokenizer, token);
}
}
void DirectiveParser::parseError(Token* token)
{
assert(token->value == kDirectiveError);
assert(getDirective(token) == DIRECTIVE_ERROR);
std::stringstream stream;
mTokenizer->lex(token);
......@@ -321,7 +563,7 @@ void DirectiveParser::parseError(Token* token)
// Parses pragma of form: #pragma name[(value)].
void DirectiveParser::parsePragma(Token* token)
{
assert(token->value == kDirectivePragma);
assert(getDirective(token) == DIRECTIVE_PRAGMA);
enum State
{
......@@ -377,7 +619,7 @@ void DirectiveParser::parsePragma(Token* token)
void DirectiveParser::parseExtension(Token* token)
{
assert(token->value == kDirectiveExtension);
assert(getDirective(token) == DIRECTIVE_EXTENSION);
enum State
{
......@@ -444,7 +686,7 @@ void DirectiveParser::parseExtension(Token* token)
void DirectiveParser::parseVersion(Token* token)
{
assert(token->value == kDirectiveVersion);
assert(getDirective(token) == DIRECTIVE_VERSION);
enum State
{
......@@ -492,7 +734,7 @@ void DirectiveParser::parseVersion(Token* token)
void DirectiveParser::parseLine(Token* token)
{
assert(token->value == kDirectiveLine);
assert(getDirective(token) == DIRECTIVE_LINE);
enum State
{
......@@ -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
......@@ -10,6 +10,7 @@
#include "Lexer.h"
#include "Macro.h"
#include "pp_utils.h"
#include "SourceLocation.h"
namespace pp
{
......@@ -46,6 +47,29 @@ class DirectiveParser : public Lexer
void parseVersion(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;
MacroSet* mMacroSet;
Diagnostics* mDiagnostics;
......
......@@ -91,12 +91,23 @@
#include "ExpressionParser.h"
#include <cassert>
#include <cstdlib>
#include <sstream>
#include "Diagnostics.h"
#include "Lexer.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 {
struct Context
{
......@@ -108,7 +119,7 @@ struct Context
} // namespace
static int yylex(int* lvalp, Context* context);
static int yylex(YYSTYPE* lvalp, Context* context);
static void yyerror(Context* context, const char* reason);
......@@ -456,9 +467,9 @@ static const yytype_int8 yyrhs[] =
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
static const yytype_uint8 yyrline[] =
{
0, 74, 74, 81, 82, 85, 88, 91, 94, 97,
100, 103, 106, 109, 112, 115, 118, 121, 124, 127,
140, 153, 156, 159, 162, 165, 168
0, 85, 85, 92, 93, 96, 99, 102, 105, 108,
111, 114, 117, 120, 123, 126, 129, 132, 135, 138,
151, 164, 167, 170, 173, 176, 179
};
#endif
......@@ -1425,7 +1436,7 @@ yyreduce:
case 2:
{
*(context->result) = (yyvsp[(1) - (1)]);
*(context->result) = static_cast<int>((yyvsp[(1) - (1)]));
YYACCEPT;
}
break;
......@@ -1825,7 +1836,7 @@ yyreturn:
int yylex(int* lvalp, Context* context)
int yylex(YYSTYPE* lvalp, Context* context)
{
int type = 0;
......@@ -1833,7 +1844,7 @@ int yylex(int* lvalp, Context* context)
switch (token->type)
{
case pp::Token::CONST_INT:
*lvalp = atoi(token->value.c_str());
*lvalp = strtoll(token->value.c_str(), NULL, 0);
type = CONST_INT;
break;
......
......@@ -28,12 +28,23 @@ WHICH GENERATES THE GLSL ES preprocessor expression parser.
#include "ExpressionParser.h"
#include <cassert>
#include <cstdlib>
#include <sstream>
#include "Diagnostics.h"
#include "Lexer.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 {
struct Context
{
......@@ -51,7 +62,7 @@ struct 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);
%}
......@@ -72,7 +83,7 @@ static void yyerror(Context* context, const char* reason);
input
: expression {
*(context->result) = $1;
*(context->result) = static_cast<int>($1);
YYACCEPT;
}
;
......@@ -172,7 +183,7 @@ expression
%%
int yylex(int* lvalp, Context* context)
int yylex(YYSTYPE* lvalp, Context* context)
{
int type = 0;
......@@ -180,7 +191,7 @@ int yylex(int* lvalp, Context* context)
switch (token->type)
{
case pp::Token::CONST_INT:
*lvalp = atoi(token->value.c_str());
*lvalp = strtoll(token->value.c_str(), NULL, 0);
type = CONST_INT;
break;
......
......@@ -48,6 +48,7 @@
'preprocessor_tests/error_test.cpp',
'preprocessor_tests/extension_test.cpp',
'preprocessor_tests/identifier_test.cpp',
'preprocessor_tests/if_test.cpp',
'preprocessor_tests/input_test.cpp',
'preprocessor_tests/location_test.cpp',
'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