Commit 7c884540 by alokp@chromium.org

Implemented #extension and #version directives.

Review URL: https://codereview.appspot.com/6242045 git-svn-id: https://angleproject.googlecode.com/svn/trunk@1095 736b8ea6-26fd-11df-bfd4-992fa37f6226
parent 36124de8
...@@ -33,6 +33,11 @@ class Diagnostics ...@@ -33,6 +33,11 @@ class Diagnostics
UNEXPECTED_TOKEN_IN_DIRECTIVE, UNEXPECTED_TOKEN_IN_DIRECTIVE,
MACRO_NAME_RESERVED, MACRO_NAME_RESERVED,
MACRO_REDEFINED, MACRO_REDEFINED,
INVALID_EXTENSION_NAME,
INVALID_EXTENSION_BEHAVIOR,
INVALID_EXTENSION_DIRECTIVE,
INVALID_VERSION_NUMBER,
INVALID_VERSION_DIRECTIVE,
ERROR_END, ERROR_END,
WARNING_BEGIN, WARNING_BEGIN,
......
...@@ -30,6 +30,13 @@ class DirectiveHandler ...@@ -30,6 +30,13 @@ class DirectiveHandler
virtual void handlePragma(const SourceLocation& loc, virtual void handlePragma(const SourceLocation& loc,
const std::string& name, const std::string& name,
const std::string& value) = 0; const std::string& value) = 0;
virtual void handleExtension(const SourceLocation& loc,
const std::string& name,
const std::string& behavior) = 0;
virtual void handleVersion(const SourceLocation& loc,
int version) = 0;
}; };
} // namespace pp } // namespace pp
......
...@@ -89,7 +89,7 @@ void DirectiveParser::parseDirective(Token* token) ...@@ -89,7 +89,7 @@ void DirectiveParser::parseDirective(Token* token)
assert(token->type == '#'); assert(token->type == '#');
mTokenizer->lex(token); mTokenizer->lex(token);
if (token->type == pp::Token::IDENTIFIER) if (token->type == Token::IDENTIFIER)
{ {
if (token->value == kDirectiveDefine) if (token->value == kDirectiveDefine)
parseDefine(token); parseDefine(token);
...@@ -141,7 +141,7 @@ void DirectiveParser::parseDefine(Token* token) ...@@ -141,7 +141,7 @@ void DirectiveParser::parseDefine(Token* token)
assert(token->value == kDirectiveDefine); assert(token->value == kDirectiveDefine);
mTokenizer->lex(token); mTokenizer->lex(token);
if (token->type != pp::Token::IDENTIFIER) if (token->type != Token::IDENTIFIER)
{ {
mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN_IN_DIRECTIVE, mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN_IN_DIRECTIVE,
token->location, token->location,
...@@ -167,7 +167,7 @@ void DirectiveParser::parseDefine(Token* token) ...@@ -167,7 +167,7 @@ void DirectiveParser::parseDefine(Token* token)
macro.type = Macro::kTypeFunc; macro.type = Macro::kTypeFunc;
do { do {
mTokenizer->lex(token); mTokenizer->lex(token);
if (token->type != pp::Token::IDENTIFIER) if (token->type != Token::IDENTIFIER)
break; break;
macro.parameters.push_back(token->value); macro.parameters.push_back(token->value);
...@@ -183,7 +183,7 @@ void DirectiveParser::parseDefine(Token* token) ...@@ -183,7 +183,7 @@ void DirectiveParser::parseDefine(Token* token)
} }
} }
while ((token->type != '\n') && (token->type != pp::Token::LAST)) while ((token->type != '\n') && (token->type != Token::LAST))
{ {
// Reset the token location because it is unnecessary in replacement // Reset the token location because it is unnecessary in replacement
// list. Resetting it also allows us to reuse Token::equals() to // list. Resetting it also allows us to reuse Token::equals() to
...@@ -210,7 +210,7 @@ void DirectiveParser::parseUndef(Token* token) ...@@ -210,7 +210,7 @@ void DirectiveParser::parseUndef(Token* token)
assert(token->value == kDirectiveUndef); assert(token->value == kDirectiveUndef);
mTokenizer->lex(token); mTokenizer->lex(token);
if (token->type != pp::Token::IDENTIFIER) if (token->type != Token::IDENTIFIER)
{ {
mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN_IN_DIRECTIVE, mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN_IN_DIRECTIVE,
token->location, token->location,
...@@ -287,7 +287,7 @@ void DirectiveParser::parseError(Token* token) ...@@ -287,7 +287,7 @@ void DirectiveParser::parseError(Token* token)
std::stringstream stream; std::stringstream stream;
mTokenizer->lex(token); mTokenizer->lex(token);
while ((token->type != '\n') && (token->type != pp::Token::LAST)) while ((token->type != '\n') && (token->type != Token::LAST))
{ {
stream << *token; stream << *token;
mTokenizer->lex(token); mTokenizer->lex(token);
...@@ -313,20 +313,20 @@ void DirectiveParser::parsePragma(Token* token) ...@@ -313,20 +313,20 @@ void DirectiveParser::parsePragma(Token* token)
int state = PRAGMA_NAME; int state = PRAGMA_NAME;
mTokenizer->lex(token); mTokenizer->lex(token);
while ((token->type != '\n') && (token->type != pp::Token::LAST)) while ((token->type != '\n') && (token->type != Token::LAST))
{ {
switch(state++) switch(state++)
{ {
case PRAGMA_NAME: case PRAGMA_NAME:
name = token->value; name = token->value;
valid = valid && (token->type == pp::Token::IDENTIFIER); valid = valid && (token->type == Token::IDENTIFIER);
break; break;
case LEFT_PAREN: case LEFT_PAREN:
valid = valid && (token->type == '('); valid = valid && (token->type == '(');
break; break;
case PRAGMA_VALUE: case PRAGMA_VALUE:
value = token->value; value = token->value;
valid = valid && (token->type == pp::Token::IDENTIFIER); valid = valid && (token->type == Token::IDENTIFIER);
break; break;
case RIGHT_PAREN: case RIGHT_PAREN:
valid = valid && (token->type == ')'); valid = valid && (token->type == ')');
...@@ -354,16 +354,117 @@ void DirectiveParser::parsePragma(Token* token) ...@@ -354,16 +354,117 @@ void DirectiveParser::parsePragma(Token* token)
void DirectiveParser::parseExtension(Token* token) void DirectiveParser::parseExtension(Token* token)
{ {
// TODO(alokp): Implement me.
assert(token->value == kDirectiveExtension); assert(token->value == kDirectiveExtension);
enum State
{
EXT_NAME,
COLON,
EXT_BEHAVIOR
};
bool valid = true;
std::string name, behavior;
int state = EXT_NAME;
mTokenizer->lex(token); 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::INVALID_EXTENSION_NAME,
token->location, token->value);
valid = false;
}
if (valid) name = token->value;
break;
case COLON:
if (valid && (token->type != ':'))
{
mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN_IN_DIRECTIVE,
token->location, token->value);
valid = false;
}
break;
case EXT_BEHAVIOR:
if (valid && (token->type != Token::IDENTIFIER))
{
mDiagnostics->report(Diagnostics::INVALID_EXTENSION_BEHAVIOR,
token->location, token->value);
valid = false;
}
if (valid) behavior = token->value;
break;
default:
if (valid)
{
mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN_IN_DIRECTIVE,
token->location, token->value);
valid = false;
}
break;
}
mTokenizer->lex(token);
}
if (valid && (state != EXT_BEHAVIOR + 1))
{
mDiagnostics->report(Diagnostics::INVALID_EXTENSION_DIRECTIVE,
token->location, token->value);
valid = false;
}
if (valid)
mDirectiveHandler->handleExtension(token->location, name, behavior);
} }
void DirectiveParser::parseVersion(Token* token) void DirectiveParser::parseVersion(Token* token)
{ {
// TODO(alokp): Implement me.
assert(token->value == kDirectiveVersion); assert(token->value == kDirectiveVersion);
enum State
{
VERSION_NUMBER
};
bool valid = true;
int version = 0;
int state = VERSION_NUMBER;
mTokenizer->lex(token); mTokenizer->lex(token);
while ((token->type != '\n') && (token->type != Token::LAST))
{
switch (state++)
{
case VERSION_NUMBER:
if (valid && (token->type != Token::CONST_INT))
{
mDiagnostics->report(Diagnostics::INVALID_VERSION_NUMBER,
token->location, token->value);
valid = false;
}
if (valid) version = atoi(token->value.c_str());
break;
default:
if (valid)
{
mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN_IN_DIRECTIVE,
token->location, token->value);
valid = false;
}
break;
}
mTokenizer->lex(token);
}
if (valid && (state != VERSION_NUMBER + 1))
{
mDiagnostics->report(Diagnostics::INVALID_VERSION_DIRECTIVE,
token->location, token->value);
valid = false;
}
if (valid)
mDirectiveHandler->handleVersion(token->location, version);
} }
void DirectiveParser::parseLine(Token* token) void DirectiveParser::parseLine(Token* token)
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
'preprocessor_tests/char_test.cpp', 'preprocessor_tests/char_test.cpp',
'preprocessor_tests/comment_test.cpp', 'preprocessor_tests/comment_test.cpp',
'preprocessor_tests/error_test.cpp', 'preprocessor_tests/error_test.cpp',
'preprocessor_tests/extension_test.cpp',
'preprocessor_tests/identifier_test.cpp', 'preprocessor_tests/identifier_test.cpp',
'preprocessor_tests/input_test.cpp', 'preprocessor_tests/input_test.cpp',
'preprocessor_tests/location_test.cpp', 'preprocessor_tests/location_test.cpp',
...@@ -53,8 +54,9 @@ ...@@ -53,8 +54,9 @@
'preprocessor_tests/number_test.cpp', 'preprocessor_tests/number_test.cpp',
'preprocessor_tests/operator_test.cpp', 'preprocessor_tests/operator_test.cpp',
'preprocessor_tests/pragma_test.cpp', 'preprocessor_tests/pragma_test.cpp',
'preprocessor_tests/token_test.cpp',
'preprocessor_tests/space_test.cpp', 'preprocessor_tests/space_test.cpp',
'preprocessor_tests/token_test.cpp',
'preprocessor_tests/version_test.cpp',
], ],
}, },
], ],
......
...@@ -20,6 +20,14 @@ class MockDirectiveHandler : public pp::DirectiveHandler ...@@ -20,6 +20,14 @@ class MockDirectiveHandler : public pp::DirectiveHandler
void(const pp::SourceLocation& loc, void(const pp::SourceLocation& loc,
const std::string& name, const std::string& name,
const std::string& value)); const std::string& value));
MOCK_METHOD3(handleExtension,
void(const pp::SourceLocation& loc,
const std::string& name,
const std::string& behavior));
MOCK_METHOD2(handleVersion,
void(const pp::SourceLocation& loc, int version));
}; };
#endif // PREPROCESSOR_TESTS_MOCK_DIRECTIVE_HANDLER_H_ #endif // PREPROCESSOR_TESTS_MOCK_DIRECTIVE_HANDLER_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 "gtest/gtest.h"
#include "MockDiagnostics.h"
#include "MockDirectiveHandler.h"
#include "Preprocessor.h"
#include "Token.h"
class ExtensionTest : public testing::Test
{
protected:
ExtensionTest() : mPreprocessor(&mDiagnostics, &mDirectiveHandler) { }
void lex()
{
pp::Token token;
mPreprocessor.lex(&token);
EXPECT_EQ(pp::Token::LAST, token.type);
EXPECT_EQ("", token.value);
}
MockDiagnostics mDiagnostics;
MockDirectiveHandler mDirectiveHandler;
pp::Preprocessor mPreprocessor;
};
TEST_F(ExtensionTest, Valid)
{
const char* str = "#extension foo : bar\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_;
EXPECT_CALL(mDirectiveHandler,
handleExtension(pp::SourceLocation(0, 1), "foo", "bar"));
// No error or warning.
EXPECT_CALL(mDiagnostics, print(_, _, _)).Times(0);
lex();
}
TEST_F(ExtensionTest, Comments)
{
const char* str = "/*foo*/"
"#"
"/*foo*/"
"extension"
"/*foo*/"
"foo"
"/*foo*/"
":"
"/*foo*/"
"bar"
"/*foo*/"
"//foo"
"\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_;
EXPECT_CALL(mDirectiveHandler,
handleExtension(pp::SourceLocation(0, 1), "foo", "bar"));
// No error or warning.
EXPECT_CALL(mDiagnostics, print(_, _, _)).Times(0);
lex();
}
TEST_F(ExtensionTest, MissingNewline)
{
const char* str = "#extension foo : bar";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_;
// Directive successfully parsed.
EXPECT_CALL(mDirectiveHandler,
handleExtension(pp::SourceLocation(0, 1), "foo", "bar"));
// Error reported about EOF.
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::EOF_IN_DIRECTIVE, _, _));
lex();
}
struct ExtensionTestParam
{
const char* str;
pp::Diagnostics::ID id;
};
using testing::WithParamInterface;
class InvalidExtensionTest : public ExtensionTest,
public WithParamInterface<ExtensionTestParam>
{
};
TEST_P(InvalidExtensionTest, Identified)
{
ExtensionTestParam param = GetParam();
ASSERT_TRUE(mPreprocessor.init(1, &param.str, NULL));
using testing::_;
// No handleExtension call.
EXPECT_CALL(mDirectiveHandler, handleExtension(_, _, _)).Times(0);
// Invalid extension directive call.
EXPECT_CALL(mDiagnostics, print(param.id, pp::SourceLocation(0, 1), _));
lex();
}
static const ExtensionTestParam kParams[] = {
{"#extension\n", pp::Diagnostics::INVALID_EXTENSION_DIRECTIVE},
{"#extension 1\n", pp::Diagnostics::INVALID_EXTENSION_NAME},
{"#extension foo bar\n", pp::Diagnostics::UNEXPECTED_TOKEN_IN_DIRECTIVE},
{"#extension foo : \n", pp::Diagnostics::INVALID_EXTENSION_DIRECTIVE},
{"#extension foo : 1\n", pp::Diagnostics::INVALID_EXTENSION_BEHAVIOR},
{"#extension foo : bar baz\n", pp::Diagnostics::UNEXPECTED_TOKEN_IN_DIRECTIVE}
};
INSTANTIATE_TEST_CASE_P(All, InvalidExtensionTest, testing::ValuesIn(kParams));
//
// 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 "gtest/gtest.h"
#include "MockDiagnostics.h"
#include "MockDirectiveHandler.h"
#include "Preprocessor.h"
#include "Token.h"
class VersionTest : public testing::Test
{
protected:
VersionTest() : mPreprocessor(&mDiagnostics, &mDirectiveHandler) { }
void lex()
{
pp::Token token;
mPreprocessor.lex(&token);
EXPECT_EQ(pp::Token::LAST, token.type);
EXPECT_EQ("", token.value);
}
MockDiagnostics mDiagnostics;
MockDirectiveHandler mDirectiveHandler;
pp::Preprocessor mPreprocessor;
};
TEST_F(VersionTest, Valid)
{
const char* str = "#version 200\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_;
EXPECT_CALL(mDirectiveHandler,
handleVersion(pp::SourceLocation(0, 1), 200));
// No error or warning.
EXPECT_CALL(mDiagnostics, print(_, _, _)).Times(0);
lex();
}
TEST_F(VersionTest, CommentsIgnored)
{
const char* str = "/*foo*/"
"#"
"/*foo*/"
"version"
"/*foo*/"
"200"
"/*foo*/"
"//foo"
"\n";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_;
EXPECT_CALL(mDirectiveHandler,
handleVersion(pp::SourceLocation(0, 1), 200));
// No error or warning.
EXPECT_CALL(mDiagnostics, print(_, _, _)).Times(0);
lex();
}
TEST_F(VersionTest, MissingNewline)
{
const char* str = "#version 200";
ASSERT_TRUE(mPreprocessor.init(1, &str, NULL));
using testing::_;
// Directive successfully parsed.
EXPECT_CALL(mDirectiveHandler,
handleVersion(pp::SourceLocation(0, 1), 200));
// Error reported about EOF.
EXPECT_CALL(mDiagnostics, print(pp::Diagnostics::EOF_IN_DIRECTIVE, _, _));
lex();
}
struct VersionTestParam
{
const char* str;
pp::Diagnostics::ID id;
};
class InvalidVersionTest : public VersionTest,
public testing::WithParamInterface<VersionTestParam>
{
};
TEST_P(InvalidVersionTest, Identified)
{
VersionTestParam param = GetParam();
ASSERT_TRUE(mPreprocessor.init(1, &param.str, NULL));
using testing::_;
// No handleVersion call.
EXPECT_CALL(mDirectiveHandler, handleVersion(_, _)).Times(0);
// Invalid version directive call.
EXPECT_CALL(mDiagnostics, print(param.id, pp::SourceLocation(0, 1), _));
lex();
}
static const VersionTestParam kParams[] = {
{"#version\n", pp::Diagnostics::INVALID_VERSION_DIRECTIVE},
{"#version foo\n", pp::Diagnostics::INVALID_VERSION_NUMBER},
{"#version 100 foo\n", pp::Diagnostics::UNEXPECTED_TOKEN_IN_DIRECTIVE}
};
INSTANTIATE_TEST_CASE_P(All, InvalidVersionTest, 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