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
UNEXPECTED_TOKEN_IN_DIRECTIVE,
MACRO_NAME_RESERVED,
MACRO_REDEFINED,
INVALID_EXTENSION_NAME,
INVALID_EXTENSION_BEHAVIOR,
INVALID_EXTENSION_DIRECTIVE,
INVALID_VERSION_NUMBER,
INVALID_VERSION_DIRECTIVE,
ERROR_END,
WARNING_BEGIN,
......
......@@ -30,6 +30,13 @@ class DirectiveHandler
virtual void handlePragma(const SourceLocation& loc,
const std::string& name,
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
......
......@@ -89,7 +89,7 @@ void DirectiveParser::parseDirective(Token* token)
assert(token->type == '#');
mTokenizer->lex(token);
if (token->type == pp::Token::IDENTIFIER)
if (token->type == Token::IDENTIFIER)
{
if (token->value == kDirectiveDefine)
parseDefine(token);
......@@ -141,7 +141,7 @@ void DirectiveParser::parseDefine(Token* token)
assert(token->value == kDirectiveDefine);
mTokenizer->lex(token);
if (token->type != pp::Token::IDENTIFIER)
if (token->type != Token::IDENTIFIER)
{
mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN_IN_DIRECTIVE,
token->location,
......@@ -167,7 +167,7 @@ void DirectiveParser::parseDefine(Token* token)
macro.type = Macro::kTypeFunc;
do {
mTokenizer->lex(token);
if (token->type != pp::Token::IDENTIFIER)
if (token->type != Token::IDENTIFIER)
break;
macro.parameters.push_back(token->value);
......@@ -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
// list. Resetting it also allows us to reuse Token::equals() to
......@@ -210,7 +210,7 @@ void DirectiveParser::parseUndef(Token* token)
assert(token->value == kDirectiveUndef);
mTokenizer->lex(token);
if (token->type != pp::Token::IDENTIFIER)
if (token->type != Token::IDENTIFIER)
{
mDiagnostics->report(Diagnostics::UNEXPECTED_TOKEN_IN_DIRECTIVE,
token->location,
......@@ -287,7 +287,7 @@ void DirectiveParser::parseError(Token* token)
std::stringstream stream;
mTokenizer->lex(token);
while ((token->type != '\n') && (token->type != pp::Token::LAST))
while ((token->type != '\n') && (token->type != Token::LAST))
{
stream << *token;
mTokenizer->lex(token);
......@@ -313,20 +313,20 @@ void DirectiveParser::parsePragma(Token* token)
int state = PRAGMA_NAME;
mTokenizer->lex(token);
while ((token->type != '\n') && (token->type != pp::Token::LAST))
while ((token->type != '\n') && (token->type != Token::LAST))
{
switch(state++)
{
case PRAGMA_NAME:
name = token->value;
valid = valid && (token->type == pp::Token::IDENTIFIER);
valid = valid && (token->type == Token::IDENTIFIER);
break;
case LEFT_PAREN:
valid = valid && (token->type == '(');
break;
case PRAGMA_VALUE:
value = token->value;
valid = valid && (token->type == pp::Token::IDENTIFIER);
valid = valid && (token->type == Token::IDENTIFIER);
break;
case RIGHT_PAREN:
valid = valid && (token->type == ')');
......@@ -354,16 +354,117 @@ void DirectiveParser::parsePragma(Token* token)
void DirectiveParser::parseExtension(Token* token)
{
// TODO(alokp): Implement me.
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);
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)
{
// TODO(alokp): Implement me.
assert(token->value == kDirectiveVersion);
enum State
{
VERSION_NUMBER
};
bool valid = true;
int version = 0;
int state = VERSION_NUMBER;
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)
......
......@@ -45,6 +45,7 @@
'preprocessor_tests/char_test.cpp',
'preprocessor_tests/comment_test.cpp',
'preprocessor_tests/error_test.cpp',
'preprocessor_tests/extension_test.cpp',
'preprocessor_tests/identifier_test.cpp',
'preprocessor_tests/input_test.cpp',
'preprocessor_tests/location_test.cpp',
......@@ -53,8 +54,9 @@
'preprocessor_tests/number_test.cpp',
'preprocessor_tests/operator_test.cpp',
'preprocessor_tests/pragma_test.cpp',
'preprocessor_tests/token_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
void(const pp::SourceLocation& loc,
const std::string& name,
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_
//
// 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