Commit f3cdb460 by alokp@chromium.org

Added support for pre-defined macros.

Review URL: https://codereview.appspot.com/6301084 git-svn-id: https://angleproject.googlecode.com/svn/trunk@1157 736b8ea6-26fd-11df-bfd4-992fa37f6226
parent 4f677306
...@@ -38,6 +38,8 @@ class Diagnostics ...@@ -38,6 +38,8 @@ class Diagnostics
UNEXPECTED_TOKEN, UNEXPECTED_TOKEN,
MACRO_NAME_RESERVED, MACRO_NAME_RESERVED,
MACRO_REDEFINED, MACRO_REDEFINED,
MACRO_PREDEFINED_REDEFINED,
MACRO_PREDEFINED_UNDEFINED,
MACRO_UNTERMINATED_INVOCATION, MACRO_UNTERMINATED_INVOCATION,
MACRO_TOO_FEW_ARGS, MACRO_TOO_FEW_ARGS,
MACRO_TOO_MANY_ARGS, MACRO_TOO_MANY_ARGS,
......
...@@ -46,6 +46,13 @@ static bool isMacroNameReserved(const std::string& name) ...@@ -46,6 +46,13 @@ static bool isMacroNameReserved(const std::string& name)
return false; 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 namespace pp
{ {
...@@ -140,15 +147,19 @@ void DirectiveParser::parseDefine(Token* token) ...@@ -140,15 +147,19 @@ void DirectiveParser::parseDefine(Token* token)
if (token->type != Token::IDENTIFIER) if (token->type != Token::IDENTIFIER)
{ {
mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
token->location, token->location, token->value);
token->value); return;
}
if (isMacroPredefined(token->value, *mMacroSet))
{
mDiagnostics->report(Diagnostics::MACRO_PREDEFINED_REDEFINED,
token->location, token->value);
return; return;
} }
if (isMacroNameReserved(token->value)) if (isMacroNameReserved(token->value))
{ {
mDiagnostics->report(Diagnostics::MACRO_NAME_RESERVED, mDiagnostics->report(Diagnostics::MACRO_NAME_RESERVED,
token->location, token->location, token->value);
token->value);
return; return;
} }
...@@ -216,14 +227,23 @@ void DirectiveParser::parseUndef(Token* token) ...@@ -216,14 +227,23 @@ void DirectiveParser::parseUndef(Token* token)
if (token->type != Token::IDENTIFIER) if (token->type != Token::IDENTIFIER)
{ {
mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN, mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
token->location, token->location, token->value);
token->value);
return; return;
} }
MacroSet::iterator iter = mMacroSet->find(token->value); MacroSet::iterator iter = mMacroSet->find(token->value);
if (iter != mMacroSet->end()) if (iter != mMacroSet->end())
mMacroSet->erase(iter); {
if (iter->second.predefined)
{
mDiagnostics->report(Diagnostics::MACRO_PREDEFINED_UNDEFINED,
token->location, token->value);
}
else
{
mMacroSet->erase(iter);
}
}
mTokenizer->lex(token); mTokenizer->lex(token);
} }
......
...@@ -26,10 +26,12 @@ struct Macro ...@@ -26,10 +26,12 @@ struct Macro
typedef std::vector<std::string> Parameters; typedef std::vector<std::string> Parameters;
typedef std::vector<Token> Replacements; typedef std::vector<Token> Replacements;
Macro() : disabled(false), type(kTypeObj) { } Macro() : predefined(false), disabled(false), type(kTypeObj) { }
bool equals(const Macro& other) const; bool equals(const Macro& other) const;
bool predefined;
mutable bool disabled; mutable bool disabled;
Type type; Type type;
std::string name; std::string name;
Parameters parameters; Parameters parameters;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <sstream>
#include "Diagnostics.h" #include "Diagnostics.h"
#include "Token.h" #include "Token.h"
...@@ -57,8 +58,7 @@ MacroExpander::MacroExpander(Lexer* lexer, ...@@ -57,8 +58,7 @@ MacroExpander::MacroExpander(Lexer* lexer,
MacroExpander::~MacroExpander() MacroExpander::~MacroExpander()
{ {
assert(!mReserveToken.get()); while (!mContextStack.empty()) popMacro();
assert(mContextStack.empty());
} }
void MacroExpander::lex(Token* token) void MacroExpander::lex(Token* token)
...@@ -185,10 +185,32 @@ bool MacroExpander::expandMacro(const Macro& macro, ...@@ -185,10 +185,32 @@ bool MacroExpander::expandMacro(const Macro& macro,
const Token& identifier, const Token& identifier,
std::vector<Token>* replacements) std::vector<Token>* replacements)
{ {
replacements->clear();
if (macro.type == Macro::kTypeObj) if (macro.type == Macro::kTypeObj)
{ {
replacements->assign(macro.replacements.begin(), replacements->assign(macro.replacements.begin(),
macro.replacements.end()); macro.replacements.end());
if (macro.predefined)
{
static const std::string kLine = "__LINE__";
static const std::string kFile = "__FILE__";
assert(replacements->size() == 1);
Token& repl = replacements->front();
if (macro.name == kLine)
{
std::stringstream stream;
stream << identifier.location.line;
repl.value = stream.str();
}
else if (macro.name == kFile)
{
std::stringstream stream;
stream << identifier.location.file;
repl.value = stream.str();
}
}
} }
else else
{ {
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#include "Preprocessor.h" #include "Preprocessor.h"
#include <sstream>
#include "Token.h" #include "Token.h"
namespace pp namespace pp
...@@ -23,9 +25,35 @@ bool Preprocessor::init(int count, ...@@ -23,9 +25,35 @@ bool Preprocessor::init(int count,
const char* const string[], const char* const string[],
const int length[]) const int length[])
{ {
static const int kGLSLVersion = 100;
// Add standard pre-defined macros.
predefineMacro("__LINE__", 0);
predefineMacro("__FILE__", 0);
predefineMacro("__VERSION__", kGLSLVersion);
predefineMacro("GL_ES", 1);
return mTokenizer.init(count, string, length); return mTokenizer.init(count, string, length);
} }
void Preprocessor::predefineMacro(const std::string& name, int value)
{
std::stringstream stream;
stream << value;
Token token;
token.type = Token::CONST_INT;
token.value = stream.str();
Macro macro;
macro.predefined = true;
macro.type = Macro::kTypeObj;
macro.name = name;
macro.replacements.push_back(token);
mMacroSet[name] = macro;
}
void Preprocessor::lex(Token* token) void Preprocessor::lex(Token* token)
{ {
mMacroExpander.lex(token); mMacroExpander.lex(token);
......
...@@ -33,6 +33,8 @@ class Preprocessor ...@@ -33,6 +33,8 @@ class Preprocessor
// corresponding string or a value less than 0 to indicate that the string // corresponding string or a value less than 0 to indicate that the string
// is null terminated. // is null terminated.
bool init(int count, const char* const string[], const int length[]); bool init(int count, const char* const string[], const int length[]);
// Adds a pre-defined macro.
void predefineMacro(const std::string& name, int value);
void lex(Token* token); void lex(Token* token);
......
...@@ -26,6 +26,45 @@ TEST_F(DefineTest, NonIdentifier) ...@@ -26,6 +26,45 @@ TEST_F(DefineTest, NonIdentifier)
preprocess(input, expected); preprocess(input, expected);
}; };
TEST_F(DefineTest, RedefinePredefined)
{
const char* input = "#define __LINE__ 10\n"
"__LINE__\n"
"#define __FILE__ 20\n"
"__FILE__\n"
"#define __VERSION__ 200\n"
"__VERSION__\n"
"#define GL_ES 0\n"
"GL_ES\n";
const char* expected = "\n"
"2\n"
"\n"
"0\n"
"\n"
"100\n"
"\n"
"1\n";
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::MACRO_PREDEFINED_REDEFINED,
pp::SourceLocation(0, 1),
"__LINE__"));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::MACRO_PREDEFINED_REDEFINED,
pp::SourceLocation(0, 3),
"__FILE__"));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::MACRO_PREDEFINED_REDEFINED,
pp::SourceLocation(0, 5),
"__VERSION__"));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::MACRO_PREDEFINED_REDEFINED,
pp::SourceLocation(0, 7),
"GL_ES"));
preprocess(input, expected);
}
TEST_F(DefineTest, ReservedUnderScore1) TEST_F(DefineTest, ReservedUnderScore1)
{ {
const char* input = "#define __foo bar\n" const char* input = "#define __foo bar\n"
...@@ -693,6 +732,45 @@ TEST_F(DefineTest, Undef) ...@@ -693,6 +732,45 @@ TEST_F(DefineTest, Undef)
preprocess(input, expected); preprocess(input, expected);
} }
TEST_F(DefineTest, UndefPredefined)
{
const char* input = "#undef __LINE__\n"
"__LINE__\n"
"#undef __FILE__\n"
"__FILE__\n"
"#undef __VERSION__\n"
"__VERSION__\n"
"#undef GL_ES\n"
"GL_ES\n";
const char* expected = "\n"
"2\n"
"\n"
"0\n"
"\n"
"100\n"
"\n"
"1\n";
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::MACRO_PREDEFINED_UNDEFINED,
pp::SourceLocation(0, 1),
"__LINE__"));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::MACRO_PREDEFINED_UNDEFINED,
pp::SourceLocation(0, 3),
"__FILE__"));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::MACRO_PREDEFINED_UNDEFINED,
pp::SourceLocation(0, 5),
"__VERSION__"));
EXPECT_CALL(mDiagnostics,
print(pp::Diagnostics::MACRO_PREDEFINED_UNDEFINED,
pp::SourceLocation(0, 7),
"GL_ES"));
preprocess(input, expected);
}
TEST_F(DefineTest, UndefRedefine) TEST_F(DefineTest, UndefRedefine)
{ {
const char* input = "#define foo 1\n" const char* input = "#define foo 1\n"
...@@ -753,3 +831,63 @@ TEST_F(DefineTest, C99Example) ...@@ -753,3 +831,63 @@ TEST_F(DefineTest, C99Example)
preprocess(input, expected); preprocess(input, expected);
} }
TEST_F(DefineTest, Predefined_GL_ES)
{
const char* input = "GL_ES\n";
const char* expected = "1\n";
preprocess(input, expected);
}
TEST_F(DefineTest, Predefined_VERSION)
{
const char* input = "__VERSION__\n";
const char* expected = "100\n";
preprocess(input, expected);
}
TEST_F(DefineTest, Predefined_LINE1)
{
const char* str = "\n\n__LINE__";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
pp::Token token;
mPreprocessor.lex(&token);
EXPECT_EQ(pp::Token::CONST_INT, token.type);
EXPECT_EQ("3", token.value);
}
TEST_F(DefineTest, Predefined_LINE2)
{
const char* str = "#line 10\n"
"__LINE__\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
pp::Token token;
mPreprocessor.lex(&token);
EXPECT_EQ(pp::Token::CONST_INT, token.type);
EXPECT_EQ("10", token.value);
}
TEST_F(DefineTest, Predefined_FILE1)
{
const char* const str[] = {"", "", "__FILE__"};
ASSERT_TRUE(mPreprocessor.init(3, str, NULL));
pp::Token token;
mPreprocessor.lex(&token);
EXPECT_EQ(pp::Token::CONST_INT, token.type);
EXPECT_EQ("2", token.value);
}
TEST_F(DefineTest, Predefined_FILE2)
{
const char* const str[] = {"#line 10 20\n", "__FILE__"};
ASSERT_TRUE(mPreprocessor.init(2, str, NULL));
pp::Token token;
mPreprocessor.lex(&token);
EXPECT_EQ(pp::Token::CONST_INT, token.type);
EXPECT_EQ("21", token.value);
}
...@@ -200,7 +200,7 @@ TEST_F(LocationTest, LineDirectiveCommentsIgnored) ...@@ -200,7 +200,7 @@ TEST_F(LocationTest, LineDirectiveCommentsIgnored)
expectLocation(1, &str, NULL, loc); expectLocation(1, &str, NULL, loc);
} }
TEST_F(LocationTest, LineDirectiveWithMacro) TEST_F(LocationTest, LineDirectiveWithMacro1)
{ {
const char* str = "#define L 10\n" const char* str = "#define L 10\n"
"#define F(x) x\n" "#define F(x) x\n"
...@@ -208,6 +208,27 @@ TEST_F(LocationTest, LineDirectiveWithMacro) ...@@ -208,6 +208,27 @@ TEST_F(LocationTest, LineDirectiveWithMacro)
"foo"; "foo";
pp::SourceLocation loc(20, 10); pp::SourceLocation loc(20, 10);
SCOPED_TRACE("LineDirectiveWithMacro1");
expectLocation(1, &str, NULL, loc);
}
TEST_F(LocationTest, LineDirectiveWithMacro2)
{
const char* str = "#define LOC 10 20\n"
"#line LOC\n"
"foo";
pp::SourceLocation loc(20, 10);
SCOPED_TRACE("LineDirectiveWithMacro2");
expectLocation(1, &str, NULL, loc);
}
TEST_F(LocationTest, LineDirectiveWithPredefinedMacro)
{
const char* str = "#line __LINE__ __FILE__\n"
"foo";
pp::SourceLocation loc(0, 1);
SCOPED_TRACE("LineDirectiveWithMacro"); SCOPED_TRACE("LineDirectiveWithMacro");
expectLocation(1, &str, NULL, loc); expectLocation(1, &str, NULL, loc);
} }
......
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