Christian
10 years ago
11 changed files with 745 additions and 47 deletions
@ -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> |
@ -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; |
|||
}; |
|||
|
|||
/// @}
|
|||
|
|||
|
|||
} } |
@ -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; |
|||
}; |
|||
|
|||
} } |
@ -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);
|
|||
} |
|||
|
|||
|
|||
} } |
@ -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; |
|||
}; |
|||
|
|||
} } |
@ -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 ')' |
@ -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
|
|||
|
Loading…
Reference in new issue