Browse Source

Solidity parser, can not parse much yet.

cl-refactor
Christian 10 years ago
parent
commit
961327ee7d
  1. 23
      libsolidity/AST.cpp
  2. 287
      libsolidity/AST.h
  3. 22
      libsolidity/BaseTypes.h
  4. 180
      libsolidity/Parser.cpp
  5. 64
      libsolidity/Parser.h
  6. 42
      libsolidity/Scanner.cpp
  7. 18
      libsolidity/Scanner.h
  8. 35
      libsolidity/Token.h
  9. 32
      libsolidity/grammar.txt
  10. 52
      test/solidityParser.cpp
  11. 37
      test/solidityScanner.cpp

23
libsolidity/AST.cpp

@ -0,0 +1,23 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity abstract syntax tree.
*/
#include <libsolidity/AST.h>

287
libsolidity/AST.h

@ -0,0 +1,287 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity abstract syntax tree.
*/
#pragma once
#include <string>
#include <vector>
#include <memory>
#include <libsolidity/BaseTypes.h>
#include <libsolidity/Token.h>
namespace dev {
namespace solidity {
template <class T>
using ptr = std::shared_ptr<T>;
template <class T>
using vecptr = std::vector<ptr<T>>;
class VariableDeclaration;
class StructDefinition;
class FunctionDefinition;
class TypeName;
class Block;
class Expression;
class ASTNode
{
public:
explicit ASTNode(const Location& _location)
: m_location(_location)
{}
private:
Location m_location;
};
class ContractDefinition : public ASTNode
{
public:
ContractDefinition(const Location& _location,
const std::string& _name,
const vecptr<StructDefinition>& _definedStructs,
const vecptr<VariableDeclaration>& _stateVariables,
const vecptr<FunctionDefinition>& _definedFunctions)
: ASTNode(_location),
m_name(_name),
m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions)
{}
private:
std::string m_name;
vecptr<StructDefinition> m_definedStructs;
vecptr<VariableDeclaration> m_stateVariables;
vecptr<FunctionDefinition> m_definedFunctions;
};
class StructDefinition : public ASTNode
{
private:
std::string m_name;
vecptr<VariableDeclaration> m_members;
};
class FunctionDefinition : public ASTNode
{
private:
std::string m_name;
vecptr<VariableDeclaration> m_arguments;
bool m_isDeclaredConst;
vecptr<VariableDeclaration> m_returns;
ptr<Block> m_body;
};
class VariableDeclaration : public ASTNode
{
public:
VariableDeclaration(const Location& _location,
const ptr<TypeName>& _type,
const std::string& _name)
: ASTNode(_location),
m_type(_type),
m_name(_name)
{}
private:
ptr<TypeName> m_type; ///<s can be empty ("var")
std::string m_name;
};
/// types
/// @{
class TypeName : public ASTNode
{
public:
explicit TypeName(const Location& _location)
: ASTNode(_location)
{}
};
/// any pre-defined type that is not a mapping
class ElementaryTypeName : public TypeName
{
public:
explicit ElementaryTypeName(const Location& _location, Token::Value _type)
: TypeName(_location), m_type(_type)
{}
private:
Token::Value m_type;
};
class UserDefinedTypeName : public TypeName
{
public:
UserDefinedTypeName(const Location& _location, const std::string& _name)
: TypeName(_location), m_name(_name)
{}
private:
std::string m_name;
};
class MappingTypeName : public TypeName
{
public:
explicit MappingTypeName(const Location& _location)
: TypeName(_location)
{}
private:
ptr<ElementaryTypeName> m_keyType;
ptr<TypeName> m_valueType;
};
/// @}
/// Statements
/// @{
class Statement : public ASTNode
{
};
class Block : public Statement
{
private:
vecptr<Statement> m_statements;
};
class IfStatement : public Statement
{
private:
ptr<Expression> m_condition;
ptr<Statement> m_trueBody;
ptr<Statement> m_falseBody;
};
class BreakableStatement : public Statement
{
};
class WhileStatement : public BreakableStatement
{
private:
ptr<Expression> m_condition;
ptr<Statement> m_body;
};
class Continue : public Statement
{
};
class Break : public Statement
{
};
class Return : public Statement
{
private:
ptr<Expression> m_expression;
};
class VariableAssignment : public Statement
{
private:
ptr<VariableDeclaration> m_variable;
Token::Value m_assigmentOperator;
ptr<Expression> m_rightHandSide; ///< can be missing
};
class Expression : public Statement
{
private:
};
/// @}
/// Expressions
/// @{
class Assignment : public Expression
{
private:
ptr<Expression> m_leftHandSide;
Token::Value m_assigmentOperator;
ptr<Expression> m_rightHandSide;
};
class UnaryOperation : public Expression
{
private:
Token::Value m_operator;
ptr<Expression> m_subExpression;
bool isPrefix;
};
class BinaryOperation : public Expression
{
private:
ptr<Expression> m_left;
ptr<Expression> m_right;
Token::Value m_operator;
};
class FunctionCall : public Expression
{
private:
std::string m_functionName; // TODO only calls to fixed, named functions for now
vecptr<Expression> m_arguments;
};
class MemberAccess : public Expression
{
private:
ptr<Expression> m_expression;
std::string m_memberName;
};
class IndexAccess : public Expression
{
ptr<Expression> m_base;
ptr<Expression> m_index;
};
class PrimaryExpression : public Expression
{
};
class Identifier : public PrimaryExpression
{
private:
std::string m_name;
};
class Literal : public PrimaryExpression
{
private:
std::string m_value;
};
/// @}
} }

22
libsolidity/BaseTypes.h

@ -0,0 +1,22 @@
#pragma once
namespace dev {
namespace solidity {
// Representation of an interval of source positions.
struct Location {
Location(int b, int e) : beg_pos(b), end_pos(e) { }
Location() : beg_pos(0), end_pos(0) { }
bool IsValid() const {
return beg_pos >= 0 && end_pos >= beg_pos;
}
static Location invalid() { return Location(-1, -1); }
int beg_pos;
int end_pos;
};
} }

180
libsolidity/Parser.cpp

@ -0,0 +1,180 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity parser.
*/
#include "libsolidity/BaseTypes.h"
#include "libsolidity/Parser.h"
#include "libsolidity/Scanner.h"
namespace dev {
namespace solidity {
ptr<ASTNode> Parser::parse(Scanner& _scanner)
{
m_scanner = &_scanner;
return parseContractDefinition();
}
/// AST node factory that also tracks the begin and end position of an AST node
/// while it is being parsed
class Parser::ASTNodeFactory
{
public:
ASTNodeFactory(const Parser& _parser)
: m_parser(_parser),
m_location(_parser.getPosition(), -1)
{}
void markEndPosition()
{
m_location.end_pos = m_parser.getEndPosition();
}
/// @todo: check that this actually uses perfect forwarding
template <class NodeType, typename... Args>
ptr<NodeType> createNode(Args&&... _args)
{
if (m_location.end_pos < 0) markEndPosition();
return std::make_shared<NodeType>(m_location, std::forward<Args>(_args)...);
}
private:
const Parser& m_parser;
Location m_location;
};
int Parser::getPosition() const
{
return m_scanner->getCurrentLocation().beg_pos;
}
int Parser::getEndPosition() const
{
return m_scanner->getCurrentLocation().end_pos;
}
ptr<ContractDefinition> Parser::parseContractDefinition()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::CONTRACT);
std::string name = expectIdentifier();
expectToken(Token::LBRACE);
vecptr<StructDefinition> structs;
vecptr<VariableDeclaration> stateVariables;
vecptr<FunctionDefinition> functions;
bool visibilityIsPublic = true;
while (true) {
Token::Value currentToken = m_scanner->getCurrentToken();
if (currentToken == Token::RBRACE) {
break;
} else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE) {
visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC);
m_scanner->next();
expectToken(Token::COLON);
} else if (currentToken == Token::FUNCTION) {
functions.push_back(parseFunctionDefinition(visibilityIsPublic));
} else if (currentToken == Token::STRUCT) {
structs.push_back(parseStructDefinition());
expectToken(Token::SEMICOLON);
} else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING ||
Token::IsElementaryTypeName(currentToken)) {
stateVariables.push_back(parseVariableDeclaration());
expectToken(Token::SEMICOLON);
} else {
throwExpectationError("Function, variable or struct declaration expected.");
}
}
nodeFactory.markEndPosition();
m_scanner->next();
expectToken(Token::EOS);
return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions);
}
ptr<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
{
(void) _isPublic;
throwExpectationError("Function parsing is not yet implemented.");
}
ptr<StructDefinition> Parser::parseStructDefinition()
{
throwExpectationError("Struct definition parsing is not yet implemented.");
}
ptr<VariableDeclaration> Parser::parseVariableDeclaration()
{
ASTNodeFactory nodeFactory(*this);
ptr<TypeName> type;
Token::Value token = m_scanner->getCurrentToken();
if (Token::IsElementaryTypeName(token)) {
type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token);
m_scanner->next();
} else if (token == Token::VAR) {
type = ASTNodeFactory(*this).createNode<TypeName>();
m_scanner->next();
} else if (token == Token::MAPPING) {
// TODO
throwExpectationError("mappings are not yet implemented");
} else if (token == Token::IDENTIFIER) {
type = ASTNodeFactory(*this).createNode<UserDefinedTypeName>(m_scanner->getCurrentLiteral());
m_scanner->next();
} else {
throwExpectationError("Expected variable declaration");
}
nodeFactory.markEndPosition();
std::string name = expectIdentifier();
return nodeFactory.createNode<VariableDeclaration>(type, name);
}
void Parser::expectToken(Token::Value _value)
{
if (m_scanner->getCurrentToken() != _value)
throwExpectationError(std::string("Expected token ") + std::string(Token::Name(_value)));
m_scanner->next();
}
std::string Parser::expectIdentifier()
{
if (m_scanner->getCurrentToken() != Token::IDENTIFIER)
throwExpectationError("Expected identifier");
std::string literal = m_scanner->getCurrentLiteral();
m_scanner->next();
return literal;
}
void Parser::throwExpectationError(const std::string& _description)
{
(void) _description;
/// @todo make a proper exception hierarchy
throw std::exception();//_description);
}
} }

64
libsolidity/Parser.h

@ -0,0 +1,64 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Solidity parser.
*/
#pragma once
#include "libsolidity/AST.h"
namespace dev {
namespace solidity {
class Scanner;
class Parser
{
public:
ptr<ASTNode> parse(Scanner& _scanner);
private:
class ASTNodeFactory;
/// Start position of the current token
int getPosition() const;
/// End position of the current token
int getEndPosition() const;
/// Parsing functions for the AST nodes
/// @{
ptr<ContractDefinition> parseContractDefinition();
ptr<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
ptr<StructDefinition> parseStructDefinition();
ptr<VariableDeclaration> parseVariableDeclaration();
/// @}
/// Helper functions
/// @{
/// If current token value is not _value, throw exception otherwise advance token.
void expectToken(Token::Value _value);
std::string expectIdentifier();
void throwExpectationError(const std::string& _description);
/// @}
Scanner* m_scanner;
};
} }

42
libsolidity/Scanner.cpp

@ -82,16 +82,10 @@ void Scanner::reset(const CharStream& _source)
{
m_source = _source;
// Initialize current_ to not refer to a literal.
m_current_token.token = Token::ILLEGAL;
m_current_token.literal.clear();
m_hasLineTerminatorBeforeNext = true;
m_hasMultilineCommentBeforeNext = false;
m_char = m_source.get();
skipWhitespace();
scanToken();
next();
}
@ -466,7 +460,7 @@ Token::Value Scanner::scanString()
literal.Complete();
advance(); // consume quote
return Token::STRING;
return Token::STRING_LITERAL;
}
@ -551,13 +545,17 @@ Token::Value Scanner::scanNumber(bool _periodSeen)
// Keyword Matcher
#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \
KEYWORD_GROUP('a') \
KEYWORD("address", Token::BREAK) \
KEYWORD_GROUP('b') \
KEYWORD("break", Token::BREAK) \
KEYWORD("bool", Token::BOOL) \
KEYWORD_GROUP('c') \
KEYWORD("case", Token::CASE) \
KEYWORD("catch", Token::CATCH) \
KEYWORD("const", Token::CONST) \
KEYWORD("continue", Token::CONTINUE) \
KEYWORD("contract", Token::CONTRACT) \
KEYWORD_GROUP('d') \
KEYWORD("debugger", Token::DEBUGGER) \
KEYWORD("default", Token::DEFAULT) \
@ -571,31 +569,55 @@ Token::Value Scanner::scanNumber(bool _periodSeen)
KEYWORD("finally", Token::FINALLY) \
KEYWORD("for", Token::FOR) \
KEYWORD("function", Token::FUNCTION) \
KEYWORD_GROUP('h') \
KEYWORD("hash", Token::HASH) \
KEYWORD("hash32", Token::HASH32) \
KEYWORD("hash64", Token::HASH64) \
KEYWORD("hash128", Token::HASH128) \
KEYWORD("hash256", Token::HASH256) \
KEYWORD_GROUP('i') \
KEYWORD("if", Token::IF) \
KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \
KEYWORD("in", Token::IN) \
KEYWORD("instanceof", Token::INSTANCEOF) \
KEYWORD("int", Token::INT) \
KEYWORD("int32", Token::INT32) \
KEYWORD("int64", Token::INT64) \
KEYWORD("int128", Token::INT128) \
KEYWORD("int256", Token::INT256) \
KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \
KEYWORD_GROUP('l') \
KEYWORD_GROUP('m') \
KEYWORD_GROUP('n') \
KEYWORD("mapping", Token::MAPPING) \
KEYWORD("new", Token::NEW) \
KEYWORD("null", Token::NULL_LITERAL) \
KEYWORD_GROUP('p') \
KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \
KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD) \
KEYWORD("private", Token::PRIVATE) \
KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \
KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD) \
KEYWORD("public", Token::PUBLIC) \
KEYWORD_GROUP('r') \
KEYWORD("real", Token::REAL) \
KEYWORD("return", Token::RETURN) \
KEYWORD_GROUP('s') \
KEYWORD("string", Token::STRING_TYPE) \
KEYWORD("struct", Token::STRUCT) \
KEYWORD("switch", Token::SWITCH) \
KEYWORD_GROUP('t') \
KEYWORD("text", Token::TEXT) \
KEYWORD("this", Token::THIS) \
KEYWORD("throw", Token::THROW) \
KEYWORD("true", Token::TRUE_LITERAL) \
KEYWORD("try", Token::TRY) \
KEYWORD("typeof", Token::TYPEOF) \
KEYWORD_GROUP('u') \
KEYWORD("uint", Token::UINT) \
KEYWORD("uint32", Token::UINT32) \
KEYWORD("uint64", Token::UINT64) \
KEYWORD("uint128", Token::UINT128) \
KEYWORD("uint256", Token::UINT256) \
KEYWORD("ureal", Token::UREAL) \
KEYWORD_GROUP('v') \
KEYWORD("var", Token::VAR) \
KEYWORD("void", Token::VOID) \

18
libsolidity/Scanner.h

@ -47,6 +47,7 @@
#include <libdevcore/Common.h>
#include <libdevcore/Log.h>
#include <libdevcore/CommonData.h>
#include <libsolidity/BaseTypes.h>
#include <libsolidity/Token.h>
namespace dev {
@ -111,21 +112,6 @@ public:
bool complete_;
};
// Representation of an interval of source positions.
struct Location {
Location(int b, int e) : beg_pos(b), end_pos(e) { }
Location() : beg_pos(0), end_pos(0) { }
bool IsValid() const {
return beg_pos >= 0 && end_pos >= beg_pos;
}
static Location invalid() { return Location(-1, -1); }
int beg_pos;
int end_pos;
};
explicit Scanner(const CharStream& _source);
// Resets the scanner as if newly constructed with _input as input.
@ -163,8 +149,6 @@ private:
std::string literal;
};
static const int kCharacterLookaheadBufferSize = 1;
// Literal buffer support
inline void startNewLiteral() {
m_next_token.literal.clear();

35
libsolidity/Token.h

@ -152,6 +152,7 @@ namespace solidity {
K(CASE, "case", 0) \
K(CATCH, "catch", 0) \
K(CONTINUE, "continue", 0) \
K(CONTRACT, "contract", 0) \
K(DEBUGGER, "debugger", 0) \
K(DEFAULT, "default", 0) \
/* DELETE */ \
@ -163,8 +164,12 @@ namespace solidity {
K(IF, "if", 0) \
/* IN */ \
/* INSTANCEOF */ \
K(MAPPING, "mapping", 0) \
K(NEW, "new", 0) \
K(PUBLIC, "public", 0) \
K(PRIVATE, "private", 0) \
K(RETURN, "return", 0) \
K(STRUCT, "struct", 0) \
K(SWITCH, "switch", 0) \
K(THIS, "this", 0) \
K(THROW, "throw", 0) \
@ -175,12 +180,36 @@ namespace solidity {
K(WHILE, "while", 0) \
K(WITH, "with", 0) \
\
/* type keywords, keep them in this order, keep int as first keyword TODO more to be added */ \
K(INT, "int", 0) \
K(INT32, "int32", 0) \
K(INT64, "int64", 0) \
K(INT128, "int128", 0) \
K(INT256, "int256", 0) \
K(UINT, "uint", 0) \
K(UINT32, "uint32", 0) \
K(UINT64, "uint64", 0) \
K(UINT128, "uint128", 0) \
K(UINT256, "uint256", 0) \
K(HASH, "hash", 0) \
K(HASH32, "hash32", 0) \
K(HASH64, "hash64", 0) \
K(HASH128, "hash128", 0) \
K(HASH256, "hash256", 0) \
K(ADDRESS, "address", 0) \
K(BOOL, "bool", 0) \
K(STRING_TYPE, "string", 0) \
K(TEXT, "text", 0) \
K(REAL, "real", 0) \
K(UREAL, "ureal", 0) \
T(TYPES_END, NULL, 0) /* used as type enum end marker */ \
\
/* Literals (ECMA-262, section 7.8, page 16). */ \
K(NULL_LITERAL, "null", 0) \
K(TRUE_LITERAL, "true", 0) \
K(FALSE_LITERAL, "false", 0) \
T(NUMBER, NULL, 0) \
T(STRING, NULL, 0) \
T(STRING_LITERAL, NULL, 0) \
\
/* Identifiers (not keywords or future reserved words). */ \
T(IDENTIFIER, NULL, 0) \
@ -231,6 +260,10 @@ class Token {
return tok == IDENTIFIER;
}
static bool IsElementaryTypeName(Value tok) {
return INT <= tok && tok < TYPES_END;
}
static bool IsAssignmentOp(Value tok) {
return INIT_VAR <= tok && tok <= ASSIGN_MOD;
}

32
libsolidity/grammar.txt

@ -0,0 +1,32 @@
ContractDefinition = 'contract' Identifier '{' ContractPart* '}'
ContractPart = VariableDeclaration ';' | StructDefinition ';' |
FunctionDefinition ';' | 'public:' | 'private:'
StructDefinition = 'struct' Identifier '{'
( VariableDeclaration (';' VariableDeclaration)* )? '}
FunctionDefinition = 'function' Identifier ArgumentList 'const'?
'returns' ArgumentList Block
ArgumentList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')'
// semantic restriction: mappings and structs (recursively) containing mappings
// are not allowed in argument lists
VariableDeclaration = TypeName Identifier
TypeName = PredefinedType | Identifier | MappingType
MappingType = 'mapping' '(' SimplePredefinedType '=>' TypeName ')'
Block = '{' Statement* '}'
Statement = IfStatement | WhileStatement | Continue | Break | Return | VariableAssignment | Expression ';' | Block
IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )?
WhileStatement = 'while' '(' Expression ')' Statement
Continue = 'continue' ';'
Break = 'break' ';'
Return = 'return' Expression? ';'
VariableAssignment = VariableDeclaration ( AssignmentOp Expression )? ';'
Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | IndexAccess | MemberAccess | PrimaryExpression
Assignment = Expression (AssignmentOp Expression)
FunctionCall = Identifier '(' ( Expression ( ',' Expression )* ) ')'
MemberAccess = Expression '.' Identifier
IndexAccess = Expression '[' Expresison ']'
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | '(' Expression ')'

52
test/solidityParser.cpp

@ -0,0 +1,52 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
* @date 2014
* Unit tests for the solidity parser.
*/
#include <string>
#include <libsolidity/Scanner.h>
#include <libsolidity/Parser.h>
#include <boost/test/unit_test.hpp>
namespace dev {
namespace solidity {
namespace test {
BOOST_AUTO_TEST_SUITE(SolidityParser)
BOOST_AUTO_TEST_CASE(smoke_test)
{
std::string text = "contract test123 {\n"
"\tuint256 stateVariable1;\n"
"}\n";
Parser parser;
CharStream str(text);
// @todo: figure out why this does not compile
//Scanner scanner(CharStream(text));
Scanner scanner(str);
BOOST_CHECK_NO_THROW(parser.parse(scanner));
}
BOOST_AUTO_TEST_SUITE_END()
} } } // end namespaces

37
test/solidityScanner.cpp

@ -27,21 +27,26 @@ namespace dev {
namespace solidity {
namespace test {
BOOST_AUTO_TEST_SUITE(solidity)
BOOST_AUTO_TEST_SUITE(SolidityScanner)
BOOST_AUTO_TEST_CASE(test_empty)
{
Scanner scanner(CharStream(""));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::EOS);
}
BOOST_AUTO_TEST_CASE(smoke_test)
{
Scanner scanner(CharStream("function break;765 \t \"string1\",'string2'\nidentifier1"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL);
BOOST_CHECK_EQUAL(scanner.next(), Token::FUNCTION);
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::FUNCTION);
BOOST_CHECK_EQUAL(scanner.next(), Token::BREAK);
BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON);
BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER);
BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "765");
BOOST_CHECK_EQUAL(scanner.next(), Token::STRING);
BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL);
BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string1");
BOOST_CHECK_EQUAL(scanner.next(), Token::COMMA);
BOOST_CHECK_EQUAL(scanner.next(), Token::STRING);
BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL);
BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string2");
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "identifier1");
@ -51,26 +56,23 @@ BOOST_AUTO_TEST_CASE(smoke_test)
BOOST_AUTO_TEST_CASE(string_escapes)
{
Scanner scanner(CharStream(" { \"a\\x61\""));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL);
BOOST_CHECK_EQUAL(scanner.next(), Token::LBRACE);
BOOST_CHECK_EQUAL(scanner.next(), Token::STRING);
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LBRACE);
BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL);
BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "aa");
}
BOOST_AUTO_TEST_CASE(string_escapes_with_zero)
{
Scanner scanner(CharStream(" { \"a\\x61\\x00abc\""));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL);
BOOST_CHECK_EQUAL(scanner.next(), Token::LBRACE);
BOOST_CHECK_EQUAL(scanner.next(), Token::STRING);
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LBRACE);
BOOST_CHECK_EQUAL(scanner.next(), Token::STRING_LITERAL);
BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), std::string("aa\0abc", 6));
}
BOOST_AUTO_TEST_CASE(string_escape_illegal)
{
Scanner scanner(CharStream(" bla \"\\x6rf\" (illegalescape)"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL);
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL);
BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "");
// TODO recovery from illegal tokens should be improved
@ -83,8 +85,7 @@ BOOST_AUTO_TEST_CASE(string_escape_illegal)
BOOST_AUTO_TEST_CASE(hex_numbers)
{
Scanner scanner(CharStream("var x = 0x765432536763762734623472346;"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL);
BOOST_CHECK_EQUAL(scanner.next(), Token::VAR);
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::VAR);
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.next(), Token::ASSIGN);
BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER);
@ -96,8 +97,7 @@ BOOST_AUTO_TEST_CASE(hex_numbers)
BOOST_AUTO_TEST_CASE(locations)
{
Scanner scanner(CharStream("function_identifier has ; -0x743/*comment*/\n ident //comment"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL);
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER);
BOOST_CHECK_EQUAL(scanner.getCurrentLocation().beg_pos, 0);
BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end_pos, 19);
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER);
@ -122,8 +122,7 @@ BOOST_AUTO_TEST_CASE(ambiguities)
{
// test scanning of some operators which need look-ahead
Scanner scanner(CharStream("<=""<""+ +=a++ =>""<<"));
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::ILLEGAL);
BOOST_CHECK_EQUAL(scanner.next(), Token::LTE);
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::LTE);
BOOST_CHECK_EQUAL(scanner.next(), Token::LT);
BOOST_CHECK_EQUAL(scanner.next(), Token::ADD);
BOOST_CHECK_EQUAL(scanner.next(), Token::ASSIGN_ADD);

Loading…
Cancel
Save