Commit 46aa13d8 by alokp@chromium.org

Implemented line directive.

Review URL: https://codereview.appspot.com/6307083 git-svn-id: https://angleproject.googlecode.com/svn/trunk@1149 736b8ea6-26fd-11df-bfd4-992fa37f6226
parent 7fc38ddd
...@@ -19,6 +19,11 @@ struct SourceLocation; ...@@ -19,6 +19,11 @@ struct SourceLocation;
class Diagnostics class Diagnostics
{ {
public: public:
enum Severity
{
ERROR,
WARNING
};
enum ID enum ID
{ {
ERROR_BEGIN, ERROR_BEGIN,
...@@ -41,6 +46,9 @@ class Diagnostics ...@@ -41,6 +46,9 @@ class Diagnostics
INVALID_EXTENSION_DIRECTIVE, INVALID_EXTENSION_DIRECTIVE,
INVALID_VERSION_NUMBER, INVALID_VERSION_NUMBER,
INVALID_VERSION_DIRECTIVE, INVALID_VERSION_DIRECTIVE,
INVALID_LINE_NUMBER,
INVALID_FILE_NUMBER,
INVALID_LINE_DIRECTIVE,
ERROR_END, ERROR_END,
WARNING_BEGIN, WARNING_BEGIN,
...@@ -53,11 +61,6 @@ class Diagnostics ...@@ -53,11 +61,6 @@ class Diagnostics
void report(ID id, const SourceLocation& loc, const std::string& text); void report(ID id, const SourceLocation& loc, const std::string& text);
protected: protected:
enum Severity
{
ERROR,
WARNING
};
Severity severity(ID id); Severity severity(ID id);
virtual void print(ID id, virtual void print(ID id,
......
...@@ -472,10 +472,65 @@ void DirectiveParser::parseVersion(Token* token) ...@@ -472,10 +472,65 @@ void DirectiveParser::parseVersion(Token* token)
void DirectiveParser::parseLine(Token* token) void DirectiveParser::parseLine(Token* token)
{ {
// TODO(alokp): Implement me.
assert(token->value == kDirectiveLine); assert(token->value == kDirectiveLine);
enum State
{
LINE_NUMBER,
FILE_NUMBER
};
bool valid = true;
int line = 0, file = 0;
int state = LINE_NUMBER;
MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics); MacroExpander macroExpander(mTokenizer, mMacroSet, mDiagnostics);
macroExpander.lex(token); macroExpander.lex(token);
while ((token->type != '\n') && (token->type != Token::LAST))
{
switch (state++)
{
case LINE_NUMBER:
if (valid && (token->type != Token::CONST_INT))
{
mDiagnostics->report(Diagnostics::INVALID_LINE_NUMBER,
token->location, token->value);
valid = false;
}
if (valid) line = atoi(token->value.c_str());
break;
case FILE_NUMBER:
if (valid && (token->type != Token::CONST_INT))
{
mDiagnostics->report(Diagnostics::INVALID_FILE_NUMBER,
token->location, token->value);
valid = false;
}
if (valid) file = atoi(token->value.c_str());
break;
default:
if (valid)
{
mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN,
token->location, token->value);
valid = false;
}
break;
}
macroExpander.lex(token);
}
if (valid && (state != FILE_NUMBER) && (state != FILE_NUMBER + 1))
{
mDiagnostics->report(Diagnostics::INVALID_LINE_DIRECTIVE,
token->location, token->value);
valid = false;
}
if (valid)
{
mTokenizer->setLineNumber(line);
if (state == FILE_NUMBER + 1) mTokenizer->setFileNumber(file);
}
} }
} // namespace pp } // namespace pp
...@@ -2302,6 +2302,18 @@ bool Tokenizer::init(int count, const char* const string[], const int length[]) ...@@ -2302,6 +2302,18 @@ bool Tokenizer::init(int count, const char* const string[], const int length[])
return initScanner(); return initScanner();
} }
void Tokenizer::setFileNumber(int file)
{
// We use column number as file number.
// See macro yyfileno.
ppset_column(file,mHandle);
}
void Tokenizer::setLineNumber(int line)
{
ppset_lineno(line,mHandle);
}
void Tokenizer::lex(Token* token) void Tokenizer::lex(Token* token)
{ {
token->type = pplex(&token->value,&token->location,mHandle); token->type = pplex(&token->value,&token->location,mHandle);
......
...@@ -37,6 +37,10 @@ class Tokenizer : public Lexer ...@@ -37,6 +37,10 @@ class Tokenizer : public Lexer
~Tokenizer(); ~Tokenizer();
bool init(int count, const char* const string[], const int length[]); bool init(int count, const char* const string[], const int length[]);
void setFileNumber(int file);
void setLineNumber(int line);
virtual void lex(Token* token); virtual void lex(Token* token);
private: private:
......
...@@ -286,6 +286,18 @@ bool Tokenizer::init(int count, const char* const string[], const int length[]) ...@@ -286,6 +286,18 @@ bool Tokenizer::init(int count, const char* const string[], const int length[])
return initScanner(); return initScanner();
} }
void Tokenizer::setFileNumber(int file)
{
// We use column number as file number.
// See macro yyfileno.
yyset_column(file, mHandle);
}
void Tokenizer::setLineNumber(int line)
{
yyset_lineno(line, mHandle);
}
void Tokenizer::lex(Token* token) void Tokenizer::lex(Token* token)
{ {
token->type = yylex(&token->value, &token->location, mHandle); token->type = yylex(&token->value, &token->location, mHandle);
......
...@@ -30,9 +30,7 @@ protected: ...@@ -30,9 +30,7 @@ protected:
TEST_F(LocationTest, String0_Line1) TEST_F(LocationTest, String0_Line1)
{ {
const char* str = "foo"; const char* str = "foo";
pp::SourceLocation loc; pp::SourceLocation loc(0, 1);
loc.file = 0;
loc.line = 1;
SCOPED_TRACE("String0_Line1"); SCOPED_TRACE("String0_Line1");
expectLocation(1, &str, NULL, loc); expectLocation(1, &str, NULL, loc);
...@@ -41,9 +39,7 @@ TEST_F(LocationTest, String0_Line1) ...@@ -41,9 +39,7 @@ TEST_F(LocationTest, String0_Line1)
TEST_F(LocationTest, String0_Line2) TEST_F(LocationTest, String0_Line2)
{ {
const char* str = "\nfoo"; const char* str = "\nfoo";
pp::SourceLocation loc; pp::SourceLocation loc(0, 2);
loc.file = 0;
loc.line = 2;
SCOPED_TRACE("String0_Line2"); SCOPED_TRACE("String0_Line2");
expectLocation(1, &str, NULL, loc); expectLocation(1, &str, NULL, loc);
...@@ -52,9 +48,7 @@ TEST_F(LocationTest, String0_Line2) ...@@ -52,9 +48,7 @@ TEST_F(LocationTest, String0_Line2)
TEST_F(LocationTest, String1_Line1) TEST_F(LocationTest, String1_Line1)
{ {
const char* const str[] = {"\n\n", "foo"}; const char* const str[] = {"\n\n", "foo"};
pp::SourceLocation loc; pp::SourceLocation loc(1, 1);
loc.file = 1;
loc.line = 1;
SCOPED_TRACE("String1_Line1"); SCOPED_TRACE("String1_Line1");
expectLocation(2, str, NULL, loc); expectLocation(2, str, NULL, loc);
...@@ -63,9 +57,7 @@ TEST_F(LocationTest, String1_Line1) ...@@ -63,9 +57,7 @@ TEST_F(LocationTest, String1_Line1)
TEST_F(LocationTest, String1_Line2) TEST_F(LocationTest, String1_Line2)
{ {
const char* const str[] = {"\n\n", "\nfoo"}; const char* const str[] = {"\n\n", "\nfoo"};
pp::SourceLocation loc; pp::SourceLocation loc(1, 2);
loc.file = 1;
loc.line = 2;
SCOPED_TRACE("String1_Line2"); SCOPED_TRACE("String1_Line2");
expectLocation(2, str, NULL, loc); expectLocation(2, str, NULL, loc);
...@@ -74,9 +66,7 @@ TEST_F(LocationTest, String1_Line2) ...@@ -74,9 +66,7 @@ TEST_F(LocationTest, String1_Line2)
TEST_F(LocationTest, NewlineInsideCommentCounted) TEST_F(LocationTest, NewlineInsideCommentCounted)
{ {
const char* str = "/*\n\n*/foo"; const char* str = "/*\n\n*/foo";
pp::SourceLocation loc; pp::SourceLocation loc(0, 3);
loc.file = 0;
loc.line = 3;
SCOPED_TRACE("NewlineInsideCommentCounted"); SCOPED_TRACE("NewlineInsideCommentCounted");
expectLocation(1, &str, NULL, loc); expectLocation(1, &str, NULL, loc);
...@@ -101,9 +91,7 @@ TEST_F(LocationTest, ErrorLocationAfterComment) ...@@ -101,9 +91,7 @@ TEST_F(LocationTest, ErrorLocationAfterComment)
TEST_F(LocationTest, TokenStraddlingTwoStrings) TEST_F(LocationTest, TokenStraddlingTwoStrings)
{ {
const char* const str[] = {"f", "oo"}; const char* const str[] = {"f", "oo"};
pp::SourceLocation loc; pp::SourceLocation loc(0, 1);
loc.file = 0;
loc.line = 1;
SCOPED_TRACE("TokenStraddlingTwoStrings"); SCOPED_TRACE("TokenStraddlingTwoStrings");
expectLocation(2, str, NULL, loc); expectLocation(2, str, NULL, loc);
...@@ -112,9 +100,7 @@ TEST_F(LocationTest, TokenStraddlingTwoStrings) ...@@ -112,9 +100,7 @@ TEST_F(LocationTest, TokenStraddlingTwoStrings)
TEST_F(LocationTest, TokenStraddlingThreeStrings) TEST_F(LocationTest, TokenStraddlingThreeStrings)
{ {
const char* const str[] = {"f", "o", "o"}; const char* const str[] = {"f", "o", "o"};
pp::SourceLocation loc; pp::SourceLocation loc(0, 1);
loc.file = 0;
loc.line = 1;
SCOPED_TRACE("TokenStraddlingThreeStrings"); SCOPED_TRACE("TokenStraddlingThreeStrings");
expectLocation(3, str, NULL, loc); expectLocation(3, str, NULL, loc);
...@@ -174,4 +160,121 @@ TEST_F(LocationTest, EndOfFileAfterEmptyString) ...@@ -174,4 +160,121 @@ TEST_F(LocationTest, EndOfFileAfterEmptyString)
EXPECT_EQ(1, token.location.line); EXPECT_EQ(1, token.location.line);
} }
// TODO(alokp): Add tests for #line directives. TEST_F(LocationTest, ValidLineDirective1)
{
const char* str = "#line 10\n"
"foo";
pp::SourceLocation loc(0, 10);
SCOPED_TRACE("ValidLineDirective1");
expectLocation(1, &str, NULL, loc);
}
TEST_F(LocationTest, ValidLineDirective2)
{
const char* str = "#line 10 20\n"
"foo";
pp::SourceLocation loc(20, 10);
SCOPED_TRACE("ValidLineDirective2");
expectLocation(1, &str, NULL, loc);
}
TEST_F(LocationTest, LineDirectiveCommentsIgnored)
{
const char* str = "/* bar */"
"#"
"/* bar */"
"line"
"/* bar */"
"10"
"/* bar */"
"20"
"/* bar */"
"// bar "
"\n"
"foo";
pp::SourceLocation loc(20, 10);
SCOPED_TRACE("LineDirectiveCommentsIgnored");
expectLocation(1, &str, NULL, loc);
}
TEST_F(LocationTest, LineDirectiveWithMacro)
{
const char* str = "#define L 10\n"
"#define F(x) x\n"
"#line L F(20)\n"
"foo";
pp::SourceLocation loc(20, 10);
SCOPED_TRACE("LineDirectiveWithMacro");
expectLocation(1, &str, NULL, loc);
}
TEST_F(LocationTest, LineDirectiveNewlineBeforeStringBreak)
{
const char* const str[] = {"#line 10 20\n", "foo"};
// String number is incremented after it is set by the line directive.
// Also notice that line number is reset after the string break.
pp::SourceLocation loc(21, 1);
SCOPED_TRACE("LineDirectiveNewlineBeforeStringBreak");
expectLocation(2, str, NULL, loc);
}
TEST_F(LocationTest, LineDirectiveNewlineAfterStringBreak)
{
const char* const str[] = {"#line 10 20", "\nfoo"};
// String number is incremented before it is set by the line directive.
pp::SourceLocation loc(20, 10);
SCOPED_TRACE("LineDirectiveNewlineAfterStringBreak");
expectLocation(2, str, NULL, loc);
}
TEST_F(LocationTest, LineDirectiveMissingNewline)
{
const char* str = "#line 10";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_;
// Error reported about EOF.
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::EOF_IN_DIRECTIVE, _, _));
pp::Token token;
mPreprocessor.lex(&token);
}
struct LineTestParam
{
const char* str;
pp::Diagnostics::ID id;
};
class InvalidLineTest : public LocationTest,
public testing::WithParamInterface<LineTestParam>
{
};
TEST_P(InvalidLineTest, Identified)
{
LineTestParam param = GetParam();
ASSERT_TRUE(mPreprocessor.init(1, &param.str, NULL));
using testing::_;
// Invalid line directive call.
EXPECT_CALL(mDiagnostics, print(param.id, pp::SourceLocation(0, 1), _));
pp::Token token;
mPreprocessor.lex(&token);
}
static const LineTestParam kParams[] = {
{"#line\n", pp::Diagnostics::INVALID_LINE_DIRECTIVE},
{"#line foo\n", pp::Diagnostics::INVALID_LINE_NUMBER},
{"#line 10 foo\n", pp::Diagnostics::INVALID_FILE_NUMBER},
{"#line 10 20 foo\n", pp::Diagnostics::UNEXPECTED_TOKEN}
};
INSTANTIATE_TEST_CASE_P(All, InvalidLineTest, testing::ValuesIn(kParams));
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