Marek Kotewicz
10 years ago
37 changed files with 5378 additions and 37 deletions
@ -0,0 +1,483 @@ |
|||
/*
|
|||
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 <algorithm> |
|||
|
|||
#include <libsolidity/AST.h> |
|||
#include <libsolidity/ASTVisitor.h> |
|||
#include <libsolidity/Exceptions.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
void ContractDefinition::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
{ |
|||
listAccept(m_definedStructs, _visitor); |
|||
listAccept(m_stateVariables, _visitor); |
|||
listAccept(m_definedFunctions, _visitor); |
|||
} |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void StructDefinition::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
listAccept(m_members, _visitor); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void ParameterList::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
listAccept(m_parameters, _visitor); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void FunctionDefinition::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
{ |
|||
m_parameters->accept(_visitor); |
|||
if (m_returnParameters) |
|||
m_returnParameters->accept(_visitor); |
|||
m_body->accept(_visitor); |
|||
} |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void VariableDeclaration::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
if (m_typeName) |
|||
m_typeName->accept(_visitor); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void TypeName::accept(ASTVisitor& _visitor) |
|||
{ |
|||
_visitor.visit(*this); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void ElementaryTypeName::accept(ASTVisitor& _visitor) |
|||
{ |
|||
_visitor.visit(*this); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void UserDefinedTypeName::accept(ASTVisitor& _visitor) |
|||
{ |
|||
_visitor.visit(*this); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void Mapping::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
{ |
|||
m_keyType->accept(_visitor); |
|||
m_valueType->accept(_visitor); |
|||
} |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void Statement::accept(ASTVisitor& _visitor) |
|||
{ |
|||
_visitor.visit(*this); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void Block::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
listAccept(m_statements, _visitor); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void IfStatement::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
{ |
|||
m_condition->accept(_visitor); |
|||
m_trueBody->accept(_visitor); |
|||
if (m_falseBody) |
|||
m_falseBody->accept(_visitor); |
|||
} |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void BreakableStatement::accept(ASTVisitor& _visitor) |
|||
{ |
|||
_visitor.visit(*this); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void WhileStatement::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
{ |
|||
m_condition->accept(_visitor); |
|||
m_body->accept(_visitor); |
|||
} |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void Continue::accept(ASTVisitor& _visitor) |
|||
{ |
|||
_visitor.visit(*this); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void Break::accept(ASTVisitor& _visitor) |
|||
{ |
|||
_visitor.visit(*this); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void Return::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
if (m_expression) |
|||
m_expression->accept(_visitor); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void VariableDefinition::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
{ |
|||
m_variable->accept(_visitor); |
|||
if (m_value) |
|||
m_value->accept(_visitor); |
|||
} |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void Assignment::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
{ |
|||
m_leftHandSide->accept(_visitor); |
|||
m_rightHandSide->accept(_visitor); |
|||
} |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void UnaryOperation::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
m_subExpression->accept(_visitor); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void BinaryOperation::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
{ |
|||
m_left->accept(_visitor); |
|||
m_right->accept(_visitor); |
|||
} |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void FunctionCall::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
{ |
|||
m_expression->accept(_visitor); |
|||
listAccept(m_arguments, _visitor); |
|||
} |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void MemberAccess::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
m_expression->accept(_visitor); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void IndexAccess::accept(ASTVisitor& _visitor) |
|||
{ |
|||
if (_visitor.visit(*this)) |
|||
{ |
|||
m_base->accept(_visitor); |
|||
m_index->accept(_visitor); |
|||
} |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void Identifier::accept(ASTVisitor& _visitor) |
|||
{ |
|||
_visitor.visit(*this); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void ElementaryTypeNameExpression::accept(ASTVisitor& _visitor) |
|||
{ |
|||
_visitor.visit(*this); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void Literal::accept(ASTVisitor& _visitor) |
|||
{ |
|||
_visitor.visit(*this); |
|||
_visitor.endVisit(*this); |
|||
} |
|||
|
|||
void Statement::expectType(Expression& _expression, const Type& _expectedType) |
|||
{ |
|||
_expression.checkTypeRequirements(); |
|||
if (!_expression.getType()->isImplicitlyConvertibleTo(_expectedType)) |
|||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Type not implicitly convertible " |
|||
"to expected type.")); |
|||
//@todo provide more information to the exception
|
|||
} |
|||
|
|||
void Block::checkTypeRequirements() |
|||
{ |
|||
for (std::shared_ptr<Statement> const& statement: m_statements) |
|||
statement->checkTypeRequirements(); |
|||
} |
|||
|
|||
void IfStatement::checkTypeRequirements() |
|||
{ |
|||
expectType(*m_condition, BoolType()); |
|||
m_trueBody->checkTypeRequirements(); |
|||
if (m_falseBody) |
|||
m_falseBody->checkTypeRequirements(); |
|||
} |
|||
|
|||
void WhileStatement::checkTypeRequirements() |
|||
{ |
|||
expectType(*m_condition, BoolType()); |
|||
m_body->checkTypeRequirements(); |
|||
} |
|||
|
|||
void Continue::checkTypeRequirements() |
|||
{ |
|||
} |
|||
|
|||
void Break::checkTypeRequirements() |
|||
{ |
|||
} |
|||
|
|||
void Return::checkTypeRequirements() |
|||
{ |
|||
BOOST_ASSERT(m_returnParameters != nullptr); |
|||
if (m_returnParameters->getParameters().size() != 1) |
|||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Different number of arguments in " |
|||
"return statement than in returns " |
|||
"declaration.")); |
|||
// this could later be changed such that the paramaters type is an anonymous struct type,
|
|||
// but for now, we only allow one return parameter
|
|||
expectType(*m_expression, *m_returnParameters->getParameters().front()->getType()); |
|||
} |
|||
|
|||
void VariableDefinition::checkTypeRequirements() |
|||
{ |
|||
// Variables can be declared without type (with "var"), in which case the first assignment
|
|||
// setsthe type.
|
|||
// Note that assignments before the first declaration are legal because of the special scoping
|
|||
// rules inherited from JavaScript.
|
|||
if (m_value) |
|||
{ |
|||
if (m_variable->getType()) |
|||
expectType(*m_value, *m_variable->getType()); |
|||
else |
|||
{ |
|||
// no type declared and no previous assignment, infer the type
|
|||
m_value->checkTypeRequirements(); |
|||
m_variable->setType(m_value->getType()); |
|||
} |
|||
} |
|||
} |
|||
|
|||
void Assignment::checkTypeRequirements() |
|||
{ |
|||
//@todo lefthandside actually has to be assignable
|
|||
// add a feature to the type system to check that
|
|||
m_leftHandSide->checkTypeRequirements(); |
|||
expectType(*m_rightHandSide, *m_leftHandSide->getType()); |
|||
m_type = m_leftHandSide->getType(); |
|||
if (m_assigmentOperator != Token::ASSIGN) |
|||
{ |
|||
// complex assignment
|
|||
if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator))) |
|||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type.")); |
|||
} |
|||
} |
|||
|
|||
void UnaryOperation::checkTypeRequirements() |
|||
{ |
|||
// INC, DEC, NOT, BIT_NOT, DELETE
|
|||
m_subExpression->checkTypeRequirements(); |
|||
m_type = m_subExpression->getType(); |
|||
if (m_type->acceptsUnaryOperator(m_operator)) |
|||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Unary operator not compatible with type.")); |
|||
} |
|||
|
|||
void BinaryOperation::checkTypeRequirements() |
|||
{ |
|||
m_right->checkTypeRequirements(); |
|||
m_left->checkTypeRequirements(); |
|||
if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType())) |
|||
m_commonType = m_left->getType(); |
|||
else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType())) |
|||
m_commonType = m_right->getType(); |
|||
else |
|||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("No common type found in binary operation.")); |
|||
if (Token::isCompareOp(m_operator)) |
|||
m_type = std::make_shared<BoolType>(); |
|||
else |
|||
{ |
|||
BOOST_ASSERT(Token::isBinaryOp(m_operator)); |
|||
m_type = m_commonType; |
|||
if (!m_commonType->acceptsBinaryOperator(m_operator)) |
|||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type.")); |
|||
} |
|||
} |
|||
|
|||
void FunctionCall::checkTypeRequirements() |
|||
{ |
|||
m_expression->checkTypeRequirements(); |
|||
for (ASTPointer<Expression> const& argument: m_arguments) |
|||
argument->checkTypeRequirements(); |
|||
Type const& expressionType = *m_expression->getType(); |
|||
Type::Category const category = expressionType.getCategory(); |
|||
if (category == Type::Category::TYPE) |
|||
{ |
|||
TypeType const* type = dynamic_cast<TypeType const*>(&expressionType); |
|||
BOOST_ASSERT(type != nullptr); |
|||
//@todo for structs, we have to check the number of arguments to be equal to the
|
|||
// number of non-mapping members
|
|||
if (m_arguments.size() != 1) |
|||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("More than one argument for " |
|||
"explicit type conersion.")); |
|||
if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type->getActualType())) |
|||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Explicit type conversion not " |
|||
"allowed.")); |
|||
m_type = type->getActualType(); |
|||
} |
|||
else if (category == Type::Category::FUNCTION) |
|||
{ |
|||
//@todo would be nice to create a struct type from the arguments
|
|||
// and then ask if that is implicitly convertible to the struct represented by the
|
|||
// function parameters
|
|||
FunctionType const* function = dynamic_cast<FunctionType const*>(&expressionType); |
|||
BOOST_ASSERT(function != nullptr); |
|||
FunctionDefinition const& fun = function->getFunction(); |
|||
std::vector<ASTPointer<VariableDeclaration>> const& parameters = fun.getParameters(); |
|||
if (parameters.size() != m_arguments.size()) |
|||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Wrong argument count for " |
|||
"function call.")); |
|||
for (size_t i = 0; i < m_arguments.size(); ++i) |
|||
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType())) |
|||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Invalid type for argument in " |
|||
"function call.")); |
|||
// @todo actually the return type should be an anonymous struct,
|
|||
// but we change it to the type of the first return value until we have structs
|
|||
if (fun.getReturnParameterList()->getParameters().empty()) |
|||
m_type = std::make_shared<VoidType>(); |
|||
else |
|||
m_type = fun.getReturnParameterList()->getParameters().front()->getType(); |
|||
} |
|||
else |
|||
{ |
|||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Type does not support invocation.")); |
|||
} |
|||
} |
|||
|
|||
void MemberAccess::checkTypeRequirements() |
|||
{ |
|||
BOOST_ASSERT(false); // not yet implemented
|
|||
// m_type = ;
|
|||
} |
|||
|
|||
void IndexAccess::checkTypeRequirements() |
|||
{ |
|||
BOOST_ASSERT(false); // not yet implemented
|
|||
// m_type = ;
|
|||
} |
|||
|
|||
void Identifier::checkTypeRequirements() |
|||
{ |
|||
BOOST_ASSERT(m_referencedDeclaration != nullptr); |
|||
//@todo these dynamic casts here are not really nice...
|
|||
// is i useful to have an AST visitor here?
|
|||
// or can this already be done in NameAndTypeResolver?
|
|||
// the only problem we get there is that in
|
|||
// var x;
|
|||
// x = 2;
|
|||
// var y = x;
|
|||
// the type of x is not yet determined.
|
|||
VariableDeclaration* variable = dynamic_cast<VariableDeclaration*>(m_referencedDeclaration); |
|||
if (variable != nullptr) |
|||
{ |
|||
if (!variable->getType()) |
|||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Variable referenced before type " |
|||
"could be determined.")); |
|||
m_type = variable->getType(); |
|||
return; |
|||
} |
|||
//@todo can we unify these with TypeName::toType()?
|
|||
StructDefinition* structDef = dynamic_cast<StructDefinition*>(m_referencedDeclaration); |
|||
if (structDef != nullptr) |
|||
{ |
|||
// note that we do not have a struct type here
|
|||
m_type = std::make_shared<TypeType>(std::make_shared<StructType>(*structDef)); |
|||
return; |
|||
} |
|||
FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(m_referencedDeclaration); |
|||
if (functionDef != nullptr) |
|||
{ |
|||
// a function reference is not a TypeType, because calling a TypeType converts to the type.
|
|||
// Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
|
|||
// conversion.
|
|||
m_type = std::make_shared<FunctionType>(*functionDef); |
|||
return; |
|||
} |
|||
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration); |
|||
if (contractDef != nullptr) |
|||
{ |
|||
m_type = std::make_shared<TypeType>(std::make_shared<ContractType>(*contractDef)); |
|||
return; |
|||
} |
|||
BOOST_ASSERT(false); // declaration reference of unknown/forbidden type
|
|||
} |
|||
|
|||
void ElementaryTypeNameExpression::checkTypeRequirements() |
|||
{ |
|||
m_type = std::make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken)); |
|||
} |
|||
|
|||
void Literal::checkTypeRequirements() |
|||
{ |
|||
m_type = Type::forLiteral(*this); |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,533 @@ |
|||
/*
|
|||
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 <boost/noncopyable.hpp> |
|||
|
|||
#include <string> |
|||
#include <vector> |
|||
#include <memory> |
|||
|
|||
#include <libsolidity/ASTForward.h> |
|||
#include <libsolidity/BaseTypes.h> |
|||
#include <libsolidity/Token.h> |
|||
#include <libsolidity/Types.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
class ASTVisitor; |
|||
|
|||
class ASTNode: private boost::noncopyable |
|||
{ |
|||
public: |
|||
explicit ASTNode(Location const& _location): m_location(_location) {} |
|||
|
|||
virtual ~ASTNode() {} |
|||
|
|||
virtual void accept(ASTVisitor& _visitor) = 0; |
|||
template <class T> |
|||
static void listAccept(std::vector<ASTPointer<T>>& _list, ASTVisitor& _visitor) |
|||
{ |
|||
for (ASTPointer<T>& element: _list) |
|||
element->accept(_visitor); |
|||
} |
|||
|
|||
Location const& getLocation() const { return m_location; } |
|||
|
|||
private: |
|||
Location m_location; |
|||
}; |
|||
|
|||
class Declaration: public ASTNode |
|||
{ |
|||
public: |
|||
Declaration(Location const& _location, ASTPointer<ASTString> const& _name): |
|||
ASTNode(_location), m_name(_name) {} |
|||
|
|||
const ASTString& getName() const { return *m_name; } |
|||
|
|||
private: |
|||
ASTPointer<ASTString> m_name; |
|||
}; |
|||
|
|||
class ContractDefinition: public Declaration |
|||
{ |
|||
public: |
|||
ContractDefinition(Location const& _location, |
|||
ASTPointer<ASTString> const& _name, |
|||
std::vector<ASTPointer<StructDefinition>> const& _definedStructs, |
|||
std::vector<ASTPointer<VariableDeclaration>> const& _stateVariables, |
|||
std::vector<ASTPointer<FunctionDefinition>> const& _definedFunctions): |
|||
Declaration(_location, _name), |
|||
m_definedStructs(_definedStructs), |
|||
m_stateVariables(_stateVariables), |
|||
m_definedFunctions(_definedFunctions) |
|||
{} |
|||
|
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
|
|||
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() { return m_definedStructs; } |
|||
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() { return m_stateVariables; } |
|||
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() { return m_definedFunctions; } |
|||
|
|||
private: |
|||
std::vector<ASTPointer<StructDefinition>> m_definedStructs; |
|||
std::vector<ASTPointer<VariableDeclaration>> m_stateVariables; |
|||
std::vector<ASTPointer<FunctionDefinition>> m_definedFunctions; |
|||
}; |
|||
|
|||
class StructDefinition: public Declaration |
|||
{ |
|||
public: |
|||
StructDefinition(Location const& _location, |
|||
ASTPointer<ASTString> const& _name, |
|||
std::vector<ASTPointer<VariableDeclaration>> const& _members): |
|||
Declaration(_location, _name), m_members(_members) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
|
|||
private: |
|||
std::vector<ASTPointer<VariableDeclaration>> m_members; |
|||
}; |
|||
|
|||
/// Used as function parameter list and return list
|
|||
/// None of the parameters is allowed to contain mappings (not even recursively
|
|||
/// inside structs)
|
|||
class ParameterList: public ASTNode |
|||
{ |
|||
public: |
|||
ParameterList(Location const& _location, |
|||
std::vector<ASTPointer<VariableDeclaration>> const& _parameters): |
|||
ASTNode(_location), m_parameters(_parameters) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
|
|||
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() { return m_parameters; } |
|||
|
|||
private: |
|||
std::vector<ASTPointer<VariableDeclaration>> m_parameters; |
|||
}; |
|||
|
|||
class FunctionDefinition: public Declaration |
|||
{ |
|||
public: |
|||
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name, bool _isPublic, |
|||
ASTPointer<ParameterList> const& _parameters, |
|||
bool _isDeclaredConst, |
|||
ASTPointer<ParameterList> const& _returnParameters, |
|||
ASTPointer<Block> const& _body): |
|||
Declaration(_location, _name), m_isPublic(_isPublic), m_parameters(_parameters), |
|||
m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters), |
|||
m_body(_body) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
|
|||
bool isPublic() const { return m_isPublic; } |
|||
bool isDeclaredConst() const { return m_isDeclaredConst; } |
|||
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); } |
|||
ParameterList& getParameterList() { return *m_parameters; } |
|||
ASTPointer<ParameterList> const& getReturnParameterList() const { return m_returnParameters; } |
|||
Block& getBody() { return *m_body; } |
|||
|
|||
private: |
|||
bool m_isPublic; |
|||
ASTPointer<ParameterList> m_parameters; |
|||
bool m_isDeclaredConst; |
|||
ASTPointer<ParameterList> m_returnParameters; |
|||
ASTPointer<Block> m_body; |
|||
}; |
|||
|
|||
class VariableDeclaration: public Declaration |
|||
{ |
|||
public: |
|||
VariableDeclaration(Location const& _location, ASTPointer<TypeName> const& _type, |
|||
ASTPointer<ASTString> const& _name): |
|||
Declaration(_location, _name), m_typeName(_type) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
|
|||
bool isTypeGivenExplicitly() const { return m_typeName.get() != nullptr; } |
|||
TypeName* getTypeName() const { return m_typeName.get(); } |
|||
|
|||
//! Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
|
|||
//! declared and there is no assignment to the variable that fixes the type.
|
|||
std::shared_ptr<Type const> const& getType() const { return m_type; } |
|||
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; } |
|||
|
|||
private: |
|||
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
|
|||
|
|||
std::shared_ptr<Type const> m_type; |
|||
}; |
|||
|
|||
/// types
|
|||
/// @{
|
|||
|
|||
class TypeName: public ASTNode |
|||
{ |
|||
public: |
|||
explicit TypeName(Location const& _location): ASTNode(_location) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
|
|||
virtual std::shared_ptr<Type> toType() = 0; |
|||
}; |
|||
|
|||
/// any pre-defined type that is not a mapping
|
|||
class ElementaryTypeName: public TypeName |
|||
{ |
|||
public: |
|||
explicit ElementaryTypeName(Location const& _location, Token::Value _type): |
|||
TypeName(_location), m_type(_type) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual std::shared_ptr<Type> toType() override { return Type::fromElementaryTypeName(m_type); } |
|||
|
|||
Token::Value getType() const { return m_type; } |
|||
|
|||
private: |
|||
Token::Value m_type; |
|||
}; |
|||
|
|||
class UserDefinedTypeName: public TypeName |
|||
{ |
|||
public: |
|||
UserDefinedTypeName(Location const& _location, ASTPointer<ASTString> const& _name): |
|||
TypeName(_location), m_name(_name) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual std::shared_ptr<Type> toType() override { return Type::fromUserDefinedTypeName(*this); } |
|||
|
|||
const ASTString& getName() const { return *m_name; } |
|||
void setReferencedStruct(StructDefinition& _referencedStruct) { m_referencedStruct = &_referencedStruct; } |
|||
StructDefinition const* getReferencedStruct() const { return m_referencedStruct; } |
|||
|
|||
private: |
|||
ASTPointer<ASTString> m_name; |
|||
|
|||
StructDefinition* m_referencedStruct; |
|||
}; |
|||
|
|||
class Mapping: public TypeName |
|||
{ |
|||
public: |
|||
Mapping(Location const& _location, ASTPointer<ElementaryTypeName> const& _keyType, |
|||
ASTPointer<TypeName> const& _valueType): |
|||
TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual std::shared_ptr<Type> toType() override { return Type::fromMapping(*this); } |
|||
|
|||
private: |
|||
ASTPointer<ElementaryTypeName> m_keyType; |
|||
ASTPointer<TypeName> m_valueType; |
|||
}; |
|||
|
|||
/// @}
|
|||
|
|||
/// Statements
|
|||
/// @{
|
|||
|
|||
class Statement: public ASTNode |
|||
{ |
|||
public: |
|||
explicit Statement(Location const& _location): ASTNode(_location) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
|
|||
//! Check all type requirements, throws exception if some requirement is not met.
|
|||
//! For expressions, this also returns the inferred type of the expression. For other
|
|||
//! statements, returns the empty pointer.
|
|||
virtual void checkTypeRequirements() = 0; |
|||
|
|||
protected: |
|||
//! Check that the inferred type for _expression is _expectedType or at least implicitly
|
|||
//! convertible to _expectedType. If not, throw exception.
|
|||
void expectType(Expression& _expression, Type const& _expectedType); |
|||
}; |
|||
|
|||
class Block: public Statement |
|||
{ |
|||
public: |
|||
Block(Location const& _location, std::vector<ASTPointer<Statement>> const& _statements): |
|||
Statement(_location), m_statements(_statements) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
|
|||
virtual void checkTypeRequirements() override; |
|||
|
|||
private: |
|||
std::vector<ASTPointer<Statement>> m_statements; |
|||
}; |
|||
|
|||
class IfStatement: public Statement |
|||
{ |
|||
public: |
|||
IfStatement(Location const& _location, ASTPointer<Expression> const& _condition, |
|||
ASTPointer<Statement> const& _trueBody, ASTPointer<Statement> const& _falseBody): |
|||
Statement(_location), |
|||
m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual void checkTypeRequirements() override; |
|||
|
|||
private: |
|||
ASTPointer<Expression> m_condition; |
|||
ASTPointer<Statement> m_trueBody; |
|||
ASTPointer<Statement> m_falseBody; //< "else" part, optional
|
|||
}; |
|||
|
|||
class BreakableStatement: public Statement |
|||
{ |
|||
public: |
|||
BreakableStatement(Location const& _location): Statement(_location) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
}; |
|||
|
|||
class WhileStatement: public BreakableStatement |
|||
{ |
|||
public: |
|||
WhileStatement(Location const& _location, ASTPointer<Expression> const& _condition, |
|||
ASTPointer<Statement> const& _body): |
|||
BreakableStatement(_location), m_condition(_condition), m_body(_body) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual void checkTypeRequirements() override; |
|||
|
|||
private: |
|||
ASTPointer<Expression> m_condition; |
|||
ASTPointer<Statement> m_body; |
|||
}; |
|||
|
|||
class Continue: public Statement |
|||
{ |
|||
public: |
|||
Continue(Location const& _location): Statement(_location) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual void checkTypeRequirements() override; |
|||
}; |
|||
|
|||
class Break: public Statement |
|||
{ |
|||
public: |
|||
Break(Location const& _location): Statement(_location) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual void checkTypeRequirements() override; |
|||
}; |
|||
|
|||
class Return: public Statement |
|||
{ |
|||
public: |
|||
Return(Location const& _location, ASTPointer<Expression> _expression): |
|||
Statement(_location), m_expression(_expression) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual void checkTypeRequirements() override; |
|||
|
|||
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } |
|||
|
|||
private: |
|||
ASTPointer<Expression> m_expression; //< value to return, optional
|
|||
|
|||
ParameterList* m_returnParameters; //< extracted from the function declaration
|
|||
}; |
|||
|
|||
class VariableDefinition: public Statement |
|||
{ |
|||
public: |
|||
VariableDefinition(Location const& _location, ASTPointer<VariableDeclaration> _variable, |
|||
ASTPointer<Expression> _value): |
|||
Statement(_location), m_variable(_variable), m_value(_value) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual void checkTypeRequirements() override; |
|||
|
|||
private: |
|||
ASTPointer<VariableDeclaration> m_variable; |
|||
ASTPointer<Expression> m_value; ///< can be missing
|
|||
}; |
|||
|
|||
class Expression: public Statement |
|||
{ |
|||
public: |
|||
Expression(Location const& _location): Statement(_location) {} |
|||
std::shared_ptr<Type const> const& getType() const { return m_type; } |
|||
|
|||
protected: |
|||
//! Inferred type of the expression, only filled after a call to checkTypeRequirements().
|
|||
std::shared_ptr<Type const> m_type; |
|||
}; |
|||
|
|||
/// @}
|
|||
|
|||
/// Expressions
|
|||
/// @{
|
|||
|
|||
class Assignment: public Expression |
|||
{ |
|||
public: |
|||
Assignment(Location const& _location, ASTPointer<Expression> const& _leftHandSide, |
|||
Token::Value _assignmentOperator, ASTPointer<Expression> const& _rightHandSide): |
|||
Expression(_location), m_leftHandSide(_leftHandSide), |
|||
m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual void checkTypeRequirements() override; |
|||
|
|||
Token::Value getAssignmentOperator() const { return m_assigmentOperator; } |
|||
|
|||
private: |
|||
ASTPointer<Expression> m_leftHandSide; |
|||
Token::Value m_assigmentOperator; |
|||
ASTPointer<Expression> m_rightHandSide; |
|||
}; |
|||
|
|||
class UnaryOperation: public Expression |
|||
{ |
|||
public: |
|||
UnaryOperation(Location const& _location, Token::Value _operator, |
|||
ASTPointer<Expression> const& _subExpression, bool _isPrefix): |
|||
Expression(_location), m_operator(_operator), |
|||
m_subExpression(_subExpression), m_isPrefix(_isPrefix) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual void checkTypeRequirements() override; |
|||
|
|||
Token::Value getOperator() const { return m_operator; } |
|||
bool isPrefixOperation() const { return m_isPrefix; } |
|||
|
|||
private: |
|||
Token::Value m_operator; |
|||
ASTPointer<Expression> m_subExpression; |
|||
bool m_isPrefix; |
|||
}; |
|||
|
|||
class BinaryOperation: public Expression |
|||
{ |
|||
public: |
|||
BinaryOperation(Location const& _location, ASTPointer<Expression> const& _left, |
|||
Token::Value _operator, ASTPointer<Expression> const& _right): |
|||
Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual void checkTypeRequirements() override; |
|||
|
|||
Token::Value getOperator() const { return m_operator; } |
|||
|
|||
private: |
|||
ASTPointer<Expression> m_left; |
|||
Token::Value m_operator; |
|||
ASTPointer<Expression> m_right; |
|||
|
|||
std::shared_ptr<Type const> m_commonType; |
|||
}; |
|||
|
|||
/// Can be ordinary function call, type cast or struct construction.
|
|||
class FunctionCall: public Expression |
|||
{ |
|||
public: |
|||
FunctionCall(Location const& _location, ASTPointer<Expression> const& _expression, |
|||
std::vector<ASTPointer<Expression>> const& _arguments): |
|||
Expression(_location), m_expression(_expression), m_arguments(_arguments) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual void checkTypeRequirements() override; |
|||
|
|||
private: |
|||
ASTPointer<Expression> m_expression; |
|||
std::vector<ASTPointer<Expression>> m_arguments; |
|||
}; |
|||
|
|||
class MemberAccess: public Expression |
|||
{ |
|||
public: |
|||
MemberAccess(Location const& _location, ASTPointer<Expression> _expression, |
|||
ASTPointer<ASTString> const& _memberName): |
|||
Expression(_location), m_expression(_expression), m_memberName(_memberName) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
const ASTString& getMemberName() const { return *m_memberName; } |
|||
virtual void checkTypeRequirements() override; |
|||
|
|||
private: |
|||
ASTPointer<Expression> m_expression; |
|||
ASTPointer<ASTString> m_memberName; |
|||
}; |
|||
|
|||
class IndexAccess: public Expression |
|||
{ |
|||
public: |
|||
IndexAccess(Location const& _location, ASTPointer<Expression> const& _base, |
|||
ASTPointer<Expression> const& _index): |
|||
Expression(_location), m_base(_base), m_index(_index) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual void checkTypeRequirements() override; |
|||
|
|||
private: |
|||
ASTPointer<Expression> m_base; |
|||
ASTPointer<Expression> m_index; |
|||
}; |
|||
|
|||
class PrimaryExpression: public Expression |
|||
{ |
|||
public: |
|||
PrimaryExpression(Location const& _location): Expression(_location) {} |
|||
}; |
|||
|
|||
class Identifier: public PrimaryExpression |
|||
{ |
|||
public: |
|||
Identifier(Location const& _location, ASTPointer<ASTString> const& _name): |
|||
PrimaryExpression(_location), m_name(_name) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual void checkTypeRequirements() override; |
|||
|
|||
ASTString const& getName() const { return *m_name; } |
|||
void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } |
|||
Declaration* getReferencedDeclaration() { return m_referencedDeclaration; } |
|||
|
|||
private: |
|||
ASTPointer<ASTString> m_name; |
|||
|
|||
//! Declaration the name refers to.
|
|||
Declaration* m_referencedDeclaration; |
|||
}; |
|||
|
|||
class ElementaryTypeNameExpression: public PrimaryExpression |
|||
{ |
|||
public: |
|||
ElementaryTypeNameExpression(Location const& _location, Token::Value _typeToken): |
|||
PrimaryExpression(_location), m_typeToken(_typeToken) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual void checkTypeRequirements() override; |
|||
|
|||
Token::Value getTypeToken() const { return m_typeToken; } |
|||
|
|||
private: |
|||
Token::Value m_typeToken; |
|||
}; |
|||
|
|||
class Literal: public PrimaryExpression |
|||
{ |
|||
public: |
|||
Literal(Location const& _location, Token::Value _token, ASTPointer<ASTString> const& _value): |
|||
PrimaryExpression(_location), m_token(_token), m_value(_value) {} |
|||
virtual void accept(ASTVisitor& _visitor) override; |
|||
virtual void checkTypeRequirements() override; |
|||
|
|||
Token::Value getToken() const { return m_token; } |
|||
ASTString const& getValue() const { return *m_value; } |
|||
|
|||
private: |
|||
Token::Value m_token; |
|||
ASTPointer<ASTString> m_value; |
|||
}; |
|||
|
|||
/// @}
|
|||
|
|||
} |
|||
} |
@ -0,0 +1,78 @@ |
|||
/*
|
|||
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 |
|||
* Forward-declarations of AST classes. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <string> |
|||
#include <memory> |
|||
#include <vector> |
|||
|
|||
// Forward-declare all AST node types
|
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
class ASTNode; |
|||
class Declaration; |
|||
class ContractDefinition; |
|||
class StructDefinition; |
|||
class ParameterList; |
|||
class FunctionDefinition; |
|||
class VariableDeclaration; |
|||
class TypeName; |
|||
class ElementaryTypeName; |
|||
class UserDefinedTypeName; |
|||
class Mapping; |
|||
class Statement; |
|||
class Block; |
|||
class IfStatement; |
|||
class BreakableStatement; |
|||
class WhileStatement; |
|||
class Continue; |
|||
class Break; |
|||
class Return; |
|||
class VariableDefinition; |
|||
class Expression; |
|||
class Assignment; |
|||
class UnaryOperation; |
|||
class BinaryOperation; |
|||
class FunctionCall; |
|||
class MemberAccess; |
|||
class IndexAccess; |
|||
class PrimaryExpression; |
|||
class Identifier; |
|||
class ElementaryTypeNameExpression; |
|||
class Literal; |
|||
|
|||
// Used as pointers to AST nodes, to be replaced by more clever pointers, e.g. pointers which do
|
|||
// not do reference counting but point to a special memory area that is completely released
|
|||
// explicitly.
|
|||
template <class T> |
|||
using ASTPointer = std::shared_ptr<T>; |
|||
|
|||
using ASTString = std::string; |
|||
|
|||
|
|||
} |
|||
} |
@ -0,0 +1,424 @@ |
|||
/*
|
|||
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 |
|||
* Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging. |
|||
*/ |
|||
|
|||
#include <libsolidity/ASTPrinter.h> |
|||
#include <libsolidity/AST.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
ASTPrinter::ASTPrinter(ASTPointer<ASTNode> const& _ast, std::string const& _source): |
|||
m_indentation(0), m_source(_source), m_ast(_ast) |
|||
{ |
|||
} |
|||
|
|||
void ASTPrinter::print(std::ostream& _stream) |
|||
{ |
|||
m_ostream = &_stream; |
|||
m_ast->accept(*this); |
|||
m_ostream = nullptr; |
|||
} |
|||
|
|||
|
|||
bool ASTPrinter::visit(ContractDefinition& _node) |
|||
{ |
|||
writeLine("ContractDefinition \"" + _node.getName() + "\""); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(StructDefinition& _node) |
|||
{ |
|||
writeLine("StructDefinition \"" + _node.getName() + "\""); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(ParameterList& _node) |
|||
{ |
|||
writeLine("ParameterList"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(FunctionDefinition& _node) |
|||
{ |
|||
writeLine("FunctionDefinition \"" + _node.getName() + "\"" + |
|||
(_node.isPublic() ? " - public" : "") + |
|||
(_node.isDeclaredConst() ? " - const" : "")); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(VariableDeclaration& _node) |
|||
{ |
|||
writeLine("VariableDeclaration \"" + _node.getName() + "\""); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(TypeName& _node) |
|||
{ |
|||
writeLine("TypeName"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(ElementaryTypeName& _node) |
|||
{ |
|||
writeLine(std::string("ElementaryTypeName ") + Token::toString(_node.getType())); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(UserDefinedTypeName& _node) |
|||
{ |
|||
writeLine("UserDefinedTypeName \"" + _node.getName() + "\""); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(Mapping& _node) |
|||
{ |
|||
writeLine("Mapping"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(Statement& _node) |
|||
{ |
|||
writeLine("Statement"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(Block& _node) |
|||
{ |
|||
writeLine("Block"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(IfStatement& _node) |
|||
{ |
|||
writeLine("IfStatement"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(BreakableStatement& _node) |
|||
{ |
|||
writeLine("BreakableStatement"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(WhileStatement& _node) |
|||
{ |
|||
writeLine("WhileStatement"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(Continue& _node) |
|||
{ |
|||
writeLine("Continue"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(Break& _node) |
|||
{ |
|||
writeLine("Break"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(Return& _node) |
|||
{ |
|||
writeLine("Return"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(VariableDefinition& _node) |
|||
{ |
|||
writeLine("VariableDefinition"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(Expression& _node) |
|||
{ |
|||
writeLine("Expression"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(Assignment& _node) |
|||
{ |
|||
writeLine(std::string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator())); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(UnaryOperation& _node) |
|||
{ |
|||
writeLine(std::string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + |
|||
") " + Token::toString(_node.getOperator())); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(BinaryOperation& _node) |
|||
{ |
|||
writeLine(std::string("BinaryOperation using operator ") + Token::toString(_node.getOperator())); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(FunctionCall& _node) |
|||
{ |
|||
writeLine("FunctionCall"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(MemberAccess& _node) |
|||
{ |
|||
writeLine("MemberAccess to member " + _node.getMemberName()); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(IndexAccess& _node) |
|||
{ |
|||
writeLine("IndexAccess"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(PrimaryExpression& _node) |
|||
{ |
|||
writeLine("PrimaryExpression"); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(Identifier& _node) |
|||
{ |
|||
writeLine(std::string("Identifier ") + _node.getName()); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) |
|||
{ |
|||
writeLine(std::string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken())); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
bool ASTPrinter::visit(Literal& _node) |
|||
{ |
|||
char const* tokenString = Token::toString(_node.getToken()); |
|||
if (tokenString == nullptr) |
|||
tokenString = "[no token]"; |
|||
writeLine(std::string("Literal, token: ") + tokenString + " value: " + _node.getValue()); |
|||
printSourcePart(_node); |
|||
return goDeeper(); |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(ASTNode&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(ContractDefinition&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(StructDefinition&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(ParameterList&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(FunctionDefinition&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(VariableDeclaration&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(TypeName&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(ElementaryTypeName&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(UserDefinedTypeName&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(Mapping&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(Statement&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(Block&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(IfStatement&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(BreakableStatement&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(WhileStatement&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(Continue&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(Break&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(Return&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(VariableDefinition&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(Expression&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(Assignment&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(UnaryOperation&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(BinaryOperation&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(FunctionCall&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(MemberAccess&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(IndexAccess&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(PrimaryExpression&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(Identifier&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(ElementaryTypeNameExpression&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::endVisit(Literal&) |
|||
{ |
|||
m_indentation--; |
|||
} |
|||
|
|||
void ASTPrinter::printSourcePart(ASTNode const& _node) |
|||
{ |
|||
if (!m_source.empty()) |
|||
{ |
|||
Location const& location(_node.getLocation()); |
|||
*m_ostream << getIndentation() << " Source: |" |
|||
<< m_source.substr(location.start, location.end - location.start) << "|\n"; |
|||
} |
|||
} |
|||
|
|||
std::string ASTPrinter::getIndentation() const |
|||
{ |
|||
return std::string(m_indentation * 2, ' '); |
|||
} |
|||
|
|||
void ASTPrinter::writeLine(std::string const& _line) |
|||
{ |
|||
*m_ostream << getIndentation() << _line << '\n'; |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,116 @@ |
|||
/*
|
|||
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 |
|||
* Pretty-printer for the abstract syntax tree (the "pretty" is arguable), used for debugging. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <ostream> |
|||
#include <libsolidity/ASTVisitor.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
class ASTPrinter: public ASTVisitor |
|||
{ |
|||
public: |
|||
/// Create a printer for the given abstract syntax tree. If the source is specified,
|
|||
/// the corresponding parts of the source are printed with each node.
|
|||
ASTPrinter(ASTPointer<ASTNode> const& _ast, const std::string& _source = std::string()); |
|||
/// Output the string representation of the AST to _stream.
|
|||
void print(std::ostream& _stream); |
|||
|
|||
bool visit(ContractDefinition& _node) override; |
|||
bool visit(StructDefinition& _node) override; |
|||
bool visit(ParameterList& _node) override; |
|||
bool visit(FunctionDefinition& _node) override; |
|||
bool visit(VariableDeclaration& _node) override; |
|||
bool visit(TypeName& _node) override; |
|||
bool visit(ElementaryTypeName& _node) override; |
|||
bool visit(UserDefinedTypeName& _node) override; |
|||
bool visit(Mapping& _node) override; |
|||
bool visit(Statement& _node) override; |
|||
bool visit(Block& _node) override; |
|||
bool visit(IfStatement& _node) override; |
|||
bool visit(BreakableStatement& _node) override; |
|||
bool visit(WhileStatement& _node) override; |
|||
bool visit(Continue& _node) override; |
|||
bool visit(Break& _node) override; |
|||
bool visit(Return& _node) override; |
|||
bool visit(VariableDefinition& _node) override; |
|||
bool visit(Expression& _node) override; |
|||
bool visit(Assignment& _node) override; |
|||
bool visit(UnaryOperation& _node) override; |
|||
bool visit(BinaryOperation& _node) override; |
|||
bool visit(FunctionCall& _node) override; |
|||
bool visit(MemberAccess& _node) override; |
|||
bool visit(IndexAccess& _node) override; |
|||
bool visit(PrimaryExpression& _node) override; |
|||
bool visit(Identifier& _node) override; |
|||
bool visit(ElementaryTypeNameExpression& _node) override; |
|||
bool visit(Literal& _node) override; |
|||
|
|||
void endVisit(ASTNode& _node) override; |
|||
void endVisit(ContractDefinition&) override; |
|||
void endVisit(StructDefinition&) override; |
|||
void endVisit(ParameterList&) override; |
|||
void endVisit(FunctionDefinition&) override; |
|||
void endVisit(VariableDeclaration&) override; |
|||
void endVisit(TypeName&) override; |
|||
void endVisit(ElementaryTypeName&) override; |
|||
void endVisit(UserDefinedTypeName&) override; |
|||
void endVisit(Mapping&) override; |
|||
void endVisit(Statement&) override; |
|||
void endVisit(Block&) override; |
|||
void endVisit(IfStatement&) override; |
|||
void endVisit(BreakableStatement&) override; |
|||
void endVisit(WhileStatement&) override; |
|||
void endVisit(Continue&) override; |
|||
void endVisit(Break&) override; |
|||
void endVisit(Return&) override; |
|||
void endVisit(VariableDefinition&) override; |
|||
void endVisit(Expression&) override; |
|||
void endVisit(Assignment&) override; |
|||
void endVisit(UnaryOperation&) override; |
|||
void endVisit(BinaryOperation&) override; |
|||
void endVisit(FunctionCall&) override; |
|||
void endVisit(MemberAccess&) override; |
|||
void endVisit(IndexAccess&) override; |
|||
void endVisit(PrimaryExpression&) override; |
|||
void endVisit(Identifier&) override; |
|||
void endVisit(ElementaryTypeNameExpression&) override; |
|||
void endVisit(Literal&) override; |
|||
|
|||
private: |
|||
void printSourcePart(ASTNode const& _node); |
|||
std::string getIndentation() const; |
|||
void writeLine(std::string const& _line); |
|||
bool goDeeper() { m_indentation++; return true; } |
|||
|
|||
int m_indentation; |
|||
std::string m_source; |
|||
ASTPointer<ASTNode> m_ast; |
|||
std::ostream* m_ostream; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,104 @@ |
|||
/*
|
|||
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 |
|||
* AST visitor base class. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <libsolidity/ASTForward.h> |
|||
#include <string> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
class ASTVisitor |
|||
{ |
|||
public: |
|||
/// These functions are called after a call to ASTNode::accept,
|
|||
/// first visit, then (if visit returns true) recursively for all
|
|||
/// child nodes in document order (exception for contracts) and then
|
|||
/// endVisit.
|
|||
virtual bool visit(ASTNode&) { return true; } |
|||
virtual bool visit(ContractDefinition&) { return true; } |
|||
virtual bool visit(StructDefinition&) { return true; } |
|||
virtual bool visit(ParameterList&) { return true; } |
|||
virtual bool visit(FunctionDefinition&) { return true; } |
|||
virtual bool visit(VariableDeclaration&) { return true; } |
|||
virtual bool visit(TypeName&) { return true; } |
|||
virtual bool visit(ElementaryTypeName&) { return true; } |
|||
virtual bool visit(UserDefinedTypeName&) { return true; } |
|||
virtual bool visit(Mapping&) { return true; } |
|||
virtual bool visit(Statement&) { return true; } |
|||
virtual bool visit(Block&) { return true; } |
|||
virtual bool visit(IfStatement&) { return true; } |
|||
virtual bool visit(BreakableStatement&) { return true; } |
|||
virtual bool visit(WhileStatement&) { return true; } |
|||
virtual bool visit(Continue&) { return true; } |
|||
virtual bool visit(Break&) { return true; } |
|||
virtual bool visit(Return&) { return true; } |
|||
virtual bool visit(VariableDefinition&) { return true; } |
|||
virtual bool visit(Expression&) { return true; } |
|||
virtual bool visit(Assignment&) { return true; } |
|||
virtual bool visit(UnaryOperation&) { return true; } |
|||
virtual bool visit(BinaryOperation&) { return true; } |
|||
virtual bool visit(FunctionCall&) { return true; } |
|||
virtual bool visit(MemberAccess&) { return true; } |
|||
virtual bool visit(IndexAccess&) { return true; } |
|||
virtual bool visit(PrimaryExpression&) { return true; } |
|||
virtual bool visit(Identifier&) { return true; } |
|||
virtual bool visit(ElementaryTypeNameExpression&) { return true; } |
|||
virtual bool visit(Literal&) { return true; } |
|||
|
|||
virtual void endVisit(ASTNode&) { } |
|||
virtual void endVisit(ContractDefinition&) { } |
|||
virtual void endVisit(StructDefinition&) { } |
|||
virtual void endVisit(ParameterList&) { } |
|||
virtual void endVisit(FunctionDefinition&) { } |
|||
virtual void endVisit(VariableDeclaration&) { } |
|||
virtual void endVisit(TypeName&) { } |
|||
virtual void endVisit(ElementaryTypeName&) { } |
|||
virtual void endVisit(UserDefinedTypeName&) { } |
|||
virtual void endVisit(Mapping&) { } |
|||
virtual void endVisit(Statement&) { } |
|||
virtual void endVisit(Block&) { } |
|||
virtual void endVisit(IfStatement&) { } |
|||
virtual void endVisit(BreakableStatement&) { } |
|||
virtual void endVisit(WhileStatement&) { } |
|||
virtual void endVisit(Continue&) { } |
|||
virtual void endVisit(Break&) { } |
|||
virtual void endVisit(Return&) { } |
|||
virtual void endVisit(VariableDefinition&) { } |
|||
virtual void endVisit(Expression&) { } |
|||
virtual void endVisit(Assignment&) { } |
|||
virtual void endVisit(UnaryOperation&) { } |
|||
virtual void endVisit(BinaryOperation&) { } |
|||
virtual void endVisit(FunctionCall&) { } |
|||
virtual void endVisit(MemberAccess&) { } |
|||
virtual void endVisit(IndexAccess&) { } |
|||
virtual void endVisit(PrimaryExpression&) { } |
|||
virtual void endVisit(Identifier&) { } |
|||
virtual void endVisit(ElementaryTypeNameExpression&) { } |
|||
virtual void endVisit(Literal&) { } |
|||
}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,45 @@ |
|||
/*
|
|||
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 |
|||
* Some elementary types for the parser. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
/// Representation of an interval of source positions.
|
|||
/// The interval includes start and excludes end.
|
|||
struct Location |
|||
{ |
|||
Location(int _start, int _end): start(_start), end(_end) { } |
|||
Location(): start(-1), end(-1) { } |
|||
|
|||
bool IsValid() const { return start >= 0 && end >= start; } |
|||
|
|||
int start; |
|||
int end; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,49 @@ |
|||
cmake_policy(SET CMP0015 NEW) |
|||
|
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") |
|||
|
|||
aux_source_directory(. SRC_LIST) |
|||
|
|||
set(EXECUTABLE solidity) |
|||
|
|||
if(ETH_STATIC) |
|||
add_library(${EXECUTABLE} STATIC ${SRC_LIST}) |
|||
else() |
|||
add_library(${EXECUTABLE} SHARED ${SRC_LIST}) |
|||
endif() |
|||
|
|||
file(GLOB HEADERS "*.h") |
|||
|
|||
include_directories(..) |
|||
|
|||
target_link_libraries(${EXECUTABLE} evmface) |
|||
target_link_libraries(${EXECUTABLE} devcore) |
|||
|
|||
|
|||
if("${TARGET_PLATFORM}" STREQUAL "w64") |
|||
target_link_libraries(${EXECUTABLE} boost_system-mt-s) |
|||
target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s) |
|||
target_link_libraries(${EXECUTABLE} iphlpapi) |
|||
target_link_libraries(${EXECUTABLE} ws2_32) |
|||
target_link_libraries(${EXECUTABLE} mswsock) |
|||
target_link_libraries(${EXECUTABLE} shlwapi) |
|||
elseif (APPLE) |
|||
# Latest mavericks boost libraries only come with -mt |
|||
target_link_libraries(${EXECUTABLE} boost_system-mt) |
|||
target_link_libraries(${EXECUTABLE} boost_thread-mt) |
|||
find_package(Threads REQUIRED) |
|||
target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) |
|||
elseif (UNIX) |
|||
target_link_libraries(${EXECUTABLE} ${Boost_SYSTEM_LIBRARY}) |
|||
target_link_libraries(${EXECUTABLE} ${Boost_THREAD_LIBRARY}) |
|||
target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) |
|||
else () |
|||
target_link_libraries(${EXECUTABLE} boost_system) |
|||
target_link_libraries(${EXECUTABLE} boost_thread) |
|||
find_package(Threads REQUIRED) |
|||
target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) |
|||
endif () |
|||
|
|||
install( TARGETS ${EXECUTABLE} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) |
|||
install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) |
|||
|
@ -0,0 +1,37 @@ |
|||
/*
|
|||
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 exception hierarchy. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <libdevcore/Exceptions.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
struct ParserError: virtual Exception {}; |
|||
struct TypeError: virtual Exception {}; |
|||
struct DeclarationError: virtual Exception {}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,198 @@ |
|||
/*
|
|||
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 |
|||
* Parser part that determines the declarations corresponding to names and the types of expressions. |
|||
*/ |
|||
|
|||
#include <libsolidity/NameAndTypeResolver.h> |
|||
|
|||
#include <libsolidity/AST.h> |
|||
#include <libsolidity/Exceptions.h> |
|||
#include <boost/assert.hpp> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
|
|||
void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) |
|||
{ |
|||
reset(); |
|||
DeclarationRegistrationHelper registrar(m_scopes, _contract); |
|||
m_currentScope = &m_scopes[&_contract]; |
|||
//@todo structs
|
|||
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables()) |
|||
ReferencesResolver resolver(*variable, *this, nullptr); |
|||
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions()) |
|||
{ |
|||
m_currentScope = &m_scopes[function.get()]; |
|||
ReferencesResolver referencesResolver(*function, *this, |
|||
function->getReturnParameterList().get()); |
|||
} |
|||
// First, the parameter types of all functions need to be resolved before we can check
|
|||
// the types, since it is possible to call functions that are only defined later
|
|||
// in the source.
|
|||
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions()) |
|||
{ |
|||
m_currentScope = &m_scopes[function.get()]; |
|||
function->getBody().checkTypeRequirements(); |
|||
} |
|||
} |
|||
|
|||
void NameAndTypeResolver::reset() |
|||
{ |
|||
m_scopes.clear(); |
|||
m_currentScope = nullptr; |
|||
} |
|||
|
|||
Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) |
|||
{ |
|||
return m_currentScope->resolveName(_name, _recursive); |
|||
} |
|||
|
|||
|
|||
DeclarationRegistrationHelper::DeclarationRegistrationHelper(std::map<ASTNode*, Scope>& _scopes, |
|||
ASTNode& _astRoot): |
|||
m_scopes(_scopes), m_currentScope(&m_scopes[nullptr]) |
|||
{ |
|||
_astRoot.accept(*this); |
|||
} |
|||
|
|||
bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract) |
|||
{ |
|||
registerDeclaration(_contract, true); |
|||
return true; |
|||
} |
|||
|
|||
void DeclarationRegistrationHelper::endVisit(ContractDefinition&) |
|||
{ |
|||
closeCurrentScope(); |
|||
} |
|||
|
|||
bool DeclarationRegistrationHelper::visit(StructDefinition& _struct) |
|||
{ |
|||
registerDeclaration(_struct, true); |
|||
return true; |
|||
} |
|||
|
|||
void DeclarationRegistrationHelper::endVisit(StructDefinition&) |
|||
{ |
|||
closeCurrentScope(); |
|||
} |
|||
|
|||
bool DeclarationRegistrationHelper::visit(FunctionDefinition& _function) |
|||
{ |
|||
registerDeclaration(_function, true); |
|||
return true; |
|||
} |
|||
|
|||
void DeclarationRegistrationHelper::endVisit(FunctionDefinition&) |
|||
{ |
|||
closeCurrentScope(); |
|||
} |
|||
|
|||
bool DeclarationRegistrationHelper::visit(VariableDeclaration& _declaration) |
|||
{ |
|||
registerDeclaration(_declaration, false); |
|||
return true; |
|||
} |
|||
|
|||
void DeclarationRegistrationHelper::endVisit(VariableDeclaration&) |
|||
{ |
|||
} |
|||
|
|||
void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _node) |
|||
{ |
|||
std::map<ASTNode*, Scope>::iterator iter; |
|||
bool newlyAdded; |
|||
std::tie(iter, newlyAdded) = m_scopes.emplace(&_node, Scope(m_currentScope)); |
|||
BOOST_ASSERT(newlyAdded); |
|||
m_currentScope = &iter->second; |
|||
} |
|||
|
|||
void DeclarationRegistrationHelper::closeCurrentScope() |
|||
{ |
|||
BOOST_ASSERT(m_currentScope != nullptr); |
|||
m_currentScope = m_currentScope->getOuterScope(); |
|||
} |
|||
|
|||
void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaration, bool _opensScope) |
|||
{ |
|||
BOOST_ASSERT(m_currentScope != nullptr); |
|||
if (!m_currentScope->registerDeclaration(_declaration)) |
|||
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Identifier already declared.")); |
|||
if (_opensScope) |
|||
enterNewSubScope(_declaration); |
|||
} |
|||
|
|||
ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, |
|||
ParameterList* _returnParameters): |
|||
m_resolver(_resolver), m_returnParameters(_returnParameters) |
|||
{ |
|||
_root.accept(*this); |
|||
} |
|||
|
|||
void ReferencesResolver::endVisit(VariableDeclaration& _variable) |
|||
{ |
|||
// endVisit because the internal type needs resolving if it is a user defined type
|
|||
// or mapping
|
|||
if (_variable.getTypeName() != nullptr) |
|||
_variable.setType(_variable.getTypeName()->toType()); |
|||
// otherwise we have a "var"-declaration whose type is resolved by the first assignment
|
|||
} |
|||
|
|||
bool ReferencesResolver::visit(Return& _return) |
|||
{ |
|||
BOOST_ASSERT(m_returnParameters != nullptr); |
|||
_return.setFunctionReturnParameters(*m_returnParameters); |
|||
return true; |
|||
} |
|||
|
|||
bool ReferencesResolver::visit(Mapping&) |
|||
{ |
|||
// @todo
|
|||
return true; |
|||
} |
|||
|
|||
bool ReferencesResolver::visit(UserDefinedTypeName& _typeName) |
|||
{ |
|||
Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); |
|||
if (declaration == nullptr) |
|||
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier.")); |
|||
StructDefinition* referencedStruct = dynamic_cast<StructDefinition*>(declaration); |
|||
//@todo later, contracts are also valid types
|
|||
if (referencedStruct == nullptr) |
|||
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Identifier does not name a type name.")); |
|||
_typeName.setReferencedStruct(*referencedStruct); |
|||
return false; |
|||
} |
|||
|
|||
bool ReferencesResolver::visit(Identifier& _identifier) |
|||
{ |
|||
Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); |
|||
if (declaration == nullptr) |
|||
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Undeclared identifier.")); |
|||
_identifier.setReferencedDeclaration(*declaration); |
|||
return false; |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
@ -0,0 +1,100 @@ |
|||
/*
|
|||
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 |
|||
* Parser part that determines the declarations corresponding to names and the types of expressions. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <map> |
|||
#include <boost/noncopyable.hpp> |
|||
|
|||
#include <libsolidity/Scope.h> |
|||
#include <libsolidity/ASTVisitor.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
//! Resolves name references, resolves all types and checks that all operations are valid for the
|
|||
//! inferred types. An exception is throw on the first error.
|
|||
class NameAndTypeResolver: private boost::noncopyable |
|||
{ |
|||
public: |
|||
NameAndTypeResolver() {} |
|||
|
|||
void resolveNamesAndTypes(ContractDefinition& _contract); |
|||
Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); |
|||
|
|||
private: |
|||
void reset(); |
|||
|
|||
//! Maps nodes declaring a scope to scopes, i.e. ContractDefinition, FunctionDeclaration and
|
|||
//! StructDefinition (@todo not yet implemented), where nullptr denotes the global scope.
|
|||
std::map<ASTNode*, Scope> m_scopes; |
|||
|
|||
Scope* m_currentScope; |
|||
}; |
|||
|
|||
//! Traverses the given AST upon construction and fills _scopes with all declarations inside the
|
|||
//! AST.
|
|||
class DeclarationRegistrationHelper: private ASTVisitor |
|||
{ |
|||
public: |
|||
DeclarationRegistrationHelper(std::map<ASTNode*, Scope>& _scopes, ASTNode& _astRoot); |
|||
|
|||
private: |
|||
bool visit(ContractDefinition& _contract); |
|||
void endVisit(ContractDefinition& _contract); |
|||
bool visit(StructDefinition& _struct); |
|||
void endVisit(StructDefinition& _struct); |
|||
bool visit(FunctionDefinition& _function); |
|||
void endVisit(FunctionDefinition& _function); |
|||
bool visit(VariableDeclaration& _declaration); |
|||
void endVisit(VariableDeclaration& _declaration); |
|||
|
|||
void enterNewSubScope(ASTNode& _node); |
|||
void closeCurrentScope(); |
|||
void registerDeclaration(Declaration& _declaration, bool _opensScope); |
|||
|
|||
std::map<ASTNode*, Scope>& m_scopes; |
|||
Scope* m_currentScope; |
|||
}; |
|||
|
|||
//! Resolves references to declarations (of variables and types) and also establishes the link
|
|||
//! between a return statement and the return parameter list.
|
|||
class ReferencesResolver: private ASTVisitor |
|||
{ |
|||
public: |
|||
ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, ParameterList* _returnParameters); |
|||
|
|||
private: |
|||
virtual void endVisit(VariableDeclaration& _variable) override; |
|||
virtual bool visit(Identifier& _identifier) override; |
|||
virtual bool visit(UserDefinedTypeName& _typeName) override; |
|||
virtual bool visit(Mapping&) override; |
|||
virtual bool visit(Return& _return) override; |
|||
|
|||
NameAndTypeResolver& m_resolver; |
|||
ParameterList* m_returnParameters; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,553 @@ |
|||
/*
|
|||
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 <libdevcore/Log.h> |
|||
#include <libsolidity/BaseTypes.h> |
|||
#include <libsolidity/Parser.h> |
|||
#include <libsolidity/Scanner.h> |
|||
#include <libsolidity/Exceptions.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
ASTPointer<ContractDefinition> Parser::parse(std::shared_ptr<Scanner> const& _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(Parser const& _parser): m_parser(_parser), m_location(_parser.getPosition(), -1) {} |
|||
|
|||
void markEndPosition() { m_location.end = m_parser.getEndPosition(); } |
|||
void setLocationEmpty() { m_location.end = m_location.start; } |
|||
/// Set the end position to the one of the given node.
|
|||
void setEndPositionFromNode(ASTPointer<ASTNode> const& _node) { m_location.end = _node->getLocation().end; } |
|||
|
|||
template <class NodeType, typename... Args> |
|||
ASTPointer<NodeType> createNode(Args&& ... _args) |
|||
{ |
|||
if (m_location.end < 0) |
|||
markEndPosition(); |
|||
return std::make_shared<NodeType>(m_location, std::forward<Args>(_args)...); |
|||
} |
|||
|
|||
private: |
|||
Parser const& m_parser; |
|||
Location m_location; |
|||
}; |
|||
|
|||
int Parser::getPosition() const |
|||
{ |
|||
return m_scanner->getCurrentLocation().start; |
|||
} |
|||
|
|||
int Parser::getEndPosition() const |
|||
{ |
|||
return m_scanner->getCurrentLocation().end; |
|||
} |
|||
|
|||
ASTPointer<ContractDefinition> Parser::parseContractDefinition() |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
expectToken(Token::CONTRACT); |
|||
ASTPointer<ASTString> name = expectIdentifierToken(); |
|||
expectToken(Token::LBRACE); |
|||
std::vector<ASTPointer<StructDefinition>> structs; |
|||
std::vector<ASTPointer<VariableDeclaration>> stateVariables; |
|||
std::vector<ASTPointer<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()); |
|||
else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || |
|||
Token::isElementaryTypeName(currentToken)) |
|||
{ |
|||
bool const allowVar = false; |
|||
stateVariables.push_back(parseVariableDeclaration(allowVar)); |
|||
expectToken(Token::SEMICOLON); |
|||
} |
|||
else |
|||
throwExpectationError("Function, variable or struct declaration expected."); |
|||
} |
|||
nodeFactory.markEndPosition(); |
|||
expectToken(Token::RBRACE); |
|||
expectToken(Token::EOS); |
|||
return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions); |
|||
} |
|||
|
|||
ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic) |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
expectToken(Token::FUNCTION); |
|||
ASTPointer<ASTString> name(expectIdentifierToken()); |
|||
ASTPointer<ParameterList> parameters(parseParameterList()); |
|||
bool isDeclaredConst = false; |
|||
if (m_scanner->getCurrentToken() == Token::CONST) |
|||
{ |
|||
isDeclaredConst = true; |
|||
m_scanner->next(); |
|||
} |
|||
ASTPointer<ParameterList> returnParameters; |
|||
if (m_scanner->getCurrentToken() == Token::RETURNS) |
|||
{ |
|||
bool const permitEmptyParameterList = false; |
|||
m_scanner->next(); |
|||
returnParameters = parseParameterList(permitEmptyParameterList); |
|||
} |
|||
else |
|||
{ |
|||
// create an empty parameter list at a zero-length location
|
|||
ASTNodeFactory nodeFactory(*this); |
|||
nodeFactory.setLocationEmpty(); |
|||
returnParameters = nodeFactory.createNode<ParameterList>(std::vector<ASTPointer<VariableDeclaration>>()); |
|||
} |
|||
ASTPointer<Block> block = parseBlock(); |
|||
nodeFactory.setEndPositionFromNode(block); |
|||
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, parameters, |
|||
isDeclaredConst, returnParameters, block); |
|||
} |
|||
|
|||
ASTPointer<StructDefinition> Parser::parseStructDefinition() |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
expectToken(Token::STRUCT); |
|||
ASTPointer<ASTString> name = expectIdentifierToken(); |
|||
std::vector<ASTPointer<VariableDeclaration>> members; |
|||
expectToken(Token::LBRACE); |
|||
while (m_scanner->getCurrentToken() != Token::RBRACE) |
|||
{ |
|||
bool const allowVar = false; |
|||
members.push_back(parseVariableDeclaration(allowVar)); |
|||
expectToken(Token::SEMICOLON); |
|||
} |
|||
nodeFactory.markEndPosition(); |
|||
expectToken(Token::RBRACE); |
|||
return nodeFactory.createNode<StructDefinition>(name, members); |
|||
} |
|||
|
|||
ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar) |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
ASTPointer<TypeName> type = parseTypeName(_allowVar); |
|||
nodeFactory.markEndPosition(); |
|||
return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken()); |
|||
} |
|||
|
|||
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) |
|||
{ |
|||
ASTPointer<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) |
|||
{ |
|||
if (!_allowVar) |
|||
throwExpectationError("Expected explicit type name."); |
|||
m_scanner->next(); |
|||
} |
|||
else if (token == Token::MAPPING) |
|||
{ |
|||
type = parseMapping(); |
|||
} |
|||
else if (token == Token::IDENTIFIER) |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
nodeFactory.markEndPosition(); |
|||
type = nodeFactory.createNode<UserDefinedTypeName>(expectIdentifierToken()); |
|||
} |
|||
else |
|||
throwExpectationError("Expected type name"); |
|||
return type; |
|||
} |
|||
|
|||
ASTPointer<Mapping> Parser::parseMapping() |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
expectToken(Token::MAPPING); |
|||
expectToken(Token::LPAREN); |
|||
if (!Token::isElementaryTypeName(m_scanner->getCurrentToken())) |
|||
throwExpectationError("Expected elementary type name for mapping key type"); |
|||
ASTPointer<ElementaryTypeName> keyType; |
|||
keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->getCurrentToken()); |
|||
m_scanner->next(); |
|||
expectToken(Token::ARROW); |
|||
bool const allowVar = false; |
|||
ASTPointer<TypeName> valueType = parseTypeName(allowVar); |
|||
nodeFactory.markEndPosition(); |
|||
expectToken(Token::RPAREN); |
|||
return nodeFactory.createNode<Mapping>(keyType, valueType); |
|||
} |
|||
|
|||
ASTPointer<ParameterList> Parser::parseParameterList(bool _allowEmpty) |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
std::vector<ASTPointer<VariableDeclaration>> parameters; |
|||
expectToken(Token::LPAREN); |
|||
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) |
|||
{ |
|||
bool const allowVar = false; |
|||
parameters.push_back(parseVariableDeclaration(allowVar)); |
|||
while (m_scanner->getCurrentToken() != Token::RPAREN) |
|||
{ |
|||
expectToken(Token::COMMA); |
|||
parameters.push_back(parseVariableDeclaration(allowVar)); |
|||
} |
|||
} |
|||
nodeFactory.markEndPosition(); |
|||
m_scanner->next(); |
|||
return nodeFactory.createNode<ParameterList>(parameters); |
|||
} |
|||
|
|||
ASTPointer<Block> Parser::parseBlock() |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
expectToken(Token::LBRACE); |
|||
std::vector<ASTPointer<Statement>> statements; |
|||
while (m_scanner->getCurrentToken() != Token::RBRACE) |
|||
statements.push_back(parseStatement()); |
|||
nodeFactory.markEndPosition(); |
|||
expectToken(Token::RBRACE); |
|||
return nodeFactory.createNode<Block>(statements); |
|||
} |
|||
|
|||
ASTPointer<Statement> Parser::parseStatement() |
|||
{ |
|||
ASTPointer<Statement> statement; |
|||
switch (m_scanner->getCurrentToken()) |
|||
{ |
|||
case Token::IF: |
|||
return parseIfStatement(); |
|||
case Token::WHILE: |
|||
return parseWhileStatement(); |
|||
case Token::LBRACE: |
|||
return parseBlock(); |
|||
// starting from here, all statements must be terminated by a semicolon
|
|||
case Token::CONTINUE: |
|||
statement = ASTNodeFactory(*this).createNode<Continue>(); |
|||
break; |
|||
case Token::BREAK: |
|||
statement = ASTNodeFactory(*this).createNode<Break>(); |
|||
break; |
|||
case Token::RETURN: |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
ASTPointer<Expression> expression; |
|||
if (m_scanner->next() != Token::SEMICOLON) |
|||
{ |
|||
expression = parseExpression(); |
|||
nodeFactory.setEndPositionFromNode(expression); |
|||
} |
|||
statement = nodeFactory.createNode<Return>(expression); |
|||
} |
|||
break; |
|||
default: |
|||
// distinguish between variable definition (and potentially assignment) and expressions
|
|||
// (which include assignments to other expressions and pre-declared variables)
|
|||
// We have a variable definition if we ge a keyword that specifies a type name, or
|
|||
// in the case of a user-defined type, we have two identifiers following each other.
|
|||
if (m_scanner->getCurrentToken() == Token::MAPPING || |
|||
m_scanner->getCurrentToken() == Token::VAR || |
|||
Token::isElementaryTypeName(m_scanner->getCurrentToken()) || |
|||
(m_scanner->getCurrentToken() == Token::IDENTIFIER && |
|||
m_scanner->peekNextToken() == Token::IDENTIFIER)) |
|||
statement = parseVariableDefinition(); |
|||
else // "ordinary" expression
|
|||
statement = parseExpression(); |
|||
} |
|||
expectToken(Token::SEMICOLON); |
|||
return statement; |
|||
} |
|||
|
|||
ASTPointer<IfStatement> Parser::parseIfStatement() |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
expectToken(Token::IF); |
|||
expectToken(Token::LPAREN); |
|||
ASTPointer<Expression> condition = parseExpression(); |
|||
expectToken(Token::RPAREN); |
|||
ASTPointer<Statement> trueBody = parseStatement(); |
|||
ASTPointer<Statement> falseBody; |
|||
if (m_scanner->getCurrentToken() == Token::ELSE) |
|||
{ |
|||
m_scanner->next(); |
|||
falseBody = parseStatement(); |
|||
nodeFactory.setEndPositionFromNode(falseBody); |
|||
} |
|||
else |
|||
nodeFactory.setEndPositionFromNode(trueBody); |
|||
return nodeFactory.createNode<IfStatement>(condition, trueBody, falseBody); |
|||
} |
|||
|
|||
ASTPointer<WhileStatement> Parser::parseWhileStatement() |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
expectToken(Token::WHILE); |
|||
expectToken(Token::LPAREN); |
|||
ASTPointer<Expression> condition = parseExpression(); |
|||
expectToken(Token::RPAREN); |
|||
ASTPointer<Statement> body = parseStatement(); |
|||
nodeFactory.setEndPositionFromNode(body); |
|||
return nodeFactory.createNode<WhileStatement>(condition, body); |
|||
} |
|||
|
|||
ASTPointer<VariableDefinition> Parser::parseVariableDefinition() |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
bool const allowVar = true; |
|||
ASTPointer<VariableDeclaration> variable = parseVariableDeclaration(allowVar); |
|||
ASTPointer<Expression> value; |
|||
if (m_scanner->getCurrentToken() == Token::ASSIGN) |
|||
{ |
|||
m_scanner->next(); |
|||
value = parseExpression(); |
|||
nodeFactory.setEndPositionFromNode(value); |
|||
} |
|||
else |
|||
nodeFactory.setEndPositionFromNode(variable); |
|||
return nodeFactory.createNode<VariableDefinition>(variable, value); |
|||
} |
|||
|
|||
ASTPointer<Expression> Parser::parseExpression() |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
ASTPointer<Expression> expression = parseBinaryExpression(); |
|||
if (!Token::isAssignmentOp(m_scanner->getCurrentToken())) |
|||
return expression; |
|||
Token::Value assignmentOperator = expectAssignmentOperator(); |
|||
ASTPointer<Expression> rightHandSide = parseExpression(); |
|||
nodeFactory.setEndPositionFromNode(rightHandSide); |
|||
return nodeFactory.createNode<Assignment>(expression, assignmentOperator, rightHandSide); |
|||
} |
|||
|
|||
ASTPointer<Expression> Parser::parseBinaryExpression(int _minPrecedence) |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
ASTPointer<Expression> expression = parseUnaryExpression(); |
|||
int precedence = Token::precedence(m_scanner->getCurrentToken()); |
|||
for (; precedence >= _minPrecedence; --precedence) |
|||
{ |
|||
while (Token::precedence(m_scanner->getCurrentToken()) == precedence) |
|||
{ |
|||
Token::Value op = m_scanner->getCurrentToken(); |
|||
m_scanner->next(); |
|||
ASTPointer<Expression> right = parseBinaryExpression(precedence + 1); |
|||
nodeFactory.setEndPositionFromNode(right); |
|||
expression = nodeFactory.createNode<BinaryOperation>(expression, op, right); |
|||
} |
|||
} |
|||
return expression; |
|||
} |
|||
|
|||
ASTPointer<Expression> Parser::parseUnaryExpression() |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
Token::Value token = m_scanner->getCurrentToken(); |
|||
if (Token::isUnaryOp(token) || Token::isCountOp(token)) |
|||
{ |
|||
// prefix expression
|
|||
m_scanner->next(); |
|||
ASTPointer<Expression> subExpression = parseUnaryExpression(); |
|||
nodeFactory.setEndPositionFromNode(subExpression); |
|||
return nodeFactory.createNode<UnaryOperation>(token, subExpression, true); |
|||
} |
|||
else |
|||
{ |
|||
// potential postfix expression
|
|||
ASTPointer<Expression> subExpression = parseLeftHandSideExpression(); |
|||
token = m_scanner->getCurrentToken(); |
|||
if (!Token::isCountOp(token)) |
|||
return subExpression; |
|||
nodeFactory.markEndPosition(); |
|||
m_scanner->next(); |
|||
return nodeFactory.createNode<UnaryOperation>(token, subExpression, false); |
|||
} |
|||
} |
|||
|
|||
ASTPointer<Expression> Parser::parseLeftHandSideExpression() |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
ASTPointer<Expression> expression = parsePrimaryExpression(); |
|||
while (true) |
|||
{ |
|||
switch (m_scanner->getCurrentToken()) |
|||
{ |
|||
case Token::LBRACK: |
|||
{ |
|||
m_scanner->next(); |
|||
ASTPointer<Expression> index = parseExpression(); |
|||
nodeFactory.markEndPosition(); |
|||
expectToken(Token::RBRACK); |
|||
expression = nodeFactory.createNode<IndexAccess>(expression, index); |
|||
} |
|||
break; |
|||
case Token::PERIOD: |
|||
{ |
|||
m_scanner->next(); |
|||
nodeFactory.markEndPosition(); |
|||
expression = nodeFactory.createNode<MemberAccess>(expression, expectIdentifierToken()); |
|||
} |
|||
break; |
|||
case Token::LPAREN: |
|||
{ |
|||
m_scanner->next(); |
|||
std::vector<ASTPointer<Expression>> arguments = parseFunctionCallArguments(); |
|||
nodeFactory.markEndPosition(); |
|||
expectToken(Token::RPAREN); |
|||
expression = nodeFactory.createNode<FunctionCall>(expression, arguments); |
|||
} |
|||
break; |
|||
default: |
|||
return expression; |
|||
} |
|||
} |
|||
} |
|||
|
|||
ASTPointer<Expression> Parser::parsePrimaryExpression() |
|||
{ |
|||
ASTNodeFactory nodeFactory(*this); |
|||
Token::Value token = m_scanner->getCurrentToken(); |
|||
ASTPointer<Expression> expression; |
|||
switch (token) |
|||
{ |
|||
case Token::TRUE_LITERAL: |
|||
case Token::FALSE_LITERAL: |
|||
expression = nodeFactory.createNode<Literal>(token, ASTPointer<ASTString>()); |
|||
m_scanner->next(); |
|||
break; |
|||
case Token::NUMBER: |
|||
case Token::STRING_LITERAL: |
|||
nodeFactory.markEndPosition(); |
|||
expression = nodeFactory.createNode<Literal>(token, getLiteralAndAdvance()); |
|||
break; |
|||
case Token::IDENTIFIER: |
|||
nodeFactory.markEndPosition(); |
|||
expression = nodeFactory.createNode<Identifier>(getLiteralAndAdvance()); |
|||
break; |
|||
case Token::LPAREN: |
|||
{ |
|||
m_scanner->next(); |
|||
ASTPointer<Expression> expression = parseExpression(); |
|||
expectToken(Token::RPAREN); |
|||
return expression; |
|||
} |
|||
default: |
|||
if (Token::isElementaryTypeName(token)) |
|||
{ |
|||
// used for casts
|
|||
expression = nodeFactory.createNode<ElementaryTypeNameExpression>(token); |
|||
m_scanner->next(); |
|||
} |
|||
else |
|||
{ |
|||
throwExpectationError("Expected primary expression."); |
|||
return ASTPointer<Expression>(); // this is not reached
|
|||
} |
|||
break; |
|||
} |
|||
return expression; |
|||
} |
|||
|
|||
std::vector<ASTPointer<Expression>> Parser::parseFunctionCallArguments() |
|||
{ |
|||
std::vector<ASTPointer<Expression>> arguments; |
|||
if (m_scanner->getCurrentToken() != Token::RPAREN) |
|||
{ |
|||
arguments.push_back(parseExpression()); |
|||
while (m_scanner->getCurrentToken() != Token::RPAREN) |
|||
{ |
|||
expectToken(Token::COMMA); |
|||
arguments.push_back(parseExpression()); |
|||
} |
|||
} |
|||
return arguments; |
|||
} |
|||
|
|||
void Parser::expectToken(Token::Value _value) |
|||
{ |
|||
if (m_scanner->getCurrentToken() != _value) |
|||
throwExpectationError(std::string("Expected token ") + std::string(Token::getName(_value))); |
|||
m_scanner->next(); |
|||
} |
|||
|
|||
Token::Value Parser::expectAssignmentOperator() |
|||
{ |
|||
Token::Value op = m_scanner->getCurrentToken(); |
|||
if (!Token::isAssignmentOp(op)) |
|||
throwExpectationError(std::string("Expected assignment operator")); |
|||
m_scanner->next(); |
|||
return op; |
|||
} |
|||
|
|||
ASTPointer<ASTString> Parser::expectIdentifierToken() |
|||
{ |
|||
if (m_scanner->getCurrentToken() != Token::IDENTIFIER) |
|||
throwExpectationError("Expected identifier"); |
|||
return getLiteralAndAdvance(); |
|||
} |
|||
|
|||
ASTPointer<ASTString> Parser::getLiteralAndAdvance() |
|||
{ |
|||
ASTPointer<ASTString> identifier = std::make_shared<ASTString>(m_scanner->getCurrentLiteral()); |
|||
m_scanner->next(); |
|||
return identifier; |
|||
} |
|||
|
|||
void Parser::throwExpectationError(std::string const& _description) |
|||
{ |
|||
//@todo put some of this stuff into ParserError
|
|||
int line, column; |
|||
std::tie(line, column) = m_scanner->translatePositionToLineColumn(getPosition()); |
|||
std::stringstream buf; |
|||
buf << "Solidity parser error: " << _description |
|||
<< " at line " << (line + 1) |
|||
<< ", column " << (column + 1) << "\n" |
|||
<< m_scanner->getLineAtPosition(getPosition()) << "\n" |
|||
<< std::string(column, ' ') << "^"; |
|||
BOOST_THROW_EXCEPTION(ParserError() << errinfo_comment(buf.str())); |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
@ -0,0 +1,83 @@ |
|||
/*
|
|||
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: |
|||
ASTPointer<ContractDefinition> parse(std::shared_ptr<Scanner> const& _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
|
|||
/// @{
|
|||
ASTPointer<ContractDefinition> parseContractDefinition(); |
|||
ASTPointer<FunctionDefinition> parseFunctionDefinition(bool _isPublic); |
|||
ASTPointer<StructDefinition> parseStructDefinition(); |
|||
ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar); |
|||
ASTPointer<TypeName> parseTypeName(bool _allowVar); |
|||
ASTPointer<Mapping> parseMapping(); |
|||
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true); |
|||
ASTPointer<Block> parseBlock(); |
|||
ASTPointer<Statement> parseStatement(); |
|||
ASTPointer<IfStatement> parseIfStatement(); |
|||
ASTPointer<WhileStatement> parseWhileStatement(); |
|||
ASTPointer<VariableDefinition> parseVariableDefinition(); |
|||
ASTPointer<Expression> parseExpression(); |
|||
ASTPointer<Expression> parseBinaryExpression(int _minPrecedence = 4); |
|||
ASTPointer<Expression> parseUnaryExpression(); |
|||
ASTPointer<Expression> parseLeftHandSideExpression(); |
|||
ASTPointer<Expression> parsePrimaryExpression(); |
|||
std::vector<ASTPointer<Expression>> parseFunctionCallArguments(); |
|||
/// @}
|
|||
|
|||
/// Helper functions
|
|||
/// @{
|
|||
/// If current token value is not _value, throw exception otherwise advance token.
|
|||
void expectToken(Token::Value _value); |
|||
Token::Value expectAssignmentOperator(); |
|||
ASTPointer<ASTString> expectIdentifierToken(); |
|||
ASTPointer<ASTString> getLiteralAndAdvance(); |
|||
void throwExpectationError(std::string const& _description); |
|||
/// @}
|
|||
|
|||
std::shared_ptr<Scanner> m_scanner; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,702 @@ |
|||
/*
|
|||
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/>.
|
|||
|
|||
This file is derived from the file "scanner.cc", which was part of the |
|||
V8 project. The original copyright header follows: |
|||
|
|||
Copyright 2006-2012, the V8 project authors. All rights reserved. |
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions are |
|||
met: |
|||
|
|||
* Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
* Redistributions in binary form must reproduce the above |
|||
copyright notice, this list of conditions and the following |
|||
disclaimer in the documentation and/or other materials provided |
|||
with the distribution. |
|||
* Neither the name of Google Inc. nor the names of its |
|||
contributors may be used to endorse or promote products derived |
|||
from this software without specific prior written permission. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
*/ |
|||
/**
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2014 |
|||
* Solidity scanner. |
|||
*/ |
|||
|
|||
#include <algorithm> |
|||
#include <tuple> |
|||
|
|||
#include <libsolidity/Scanner.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
namespace |
|||
{ |
|||
bool IsDecimalDigit(char c) |
|||
{ |
|||
return '0' <= c && c <= '9'; |
|||
} |
|||
bool IsHexDigit(char c) |
|||
{ |
|||
return IsDecimalDigit(c) |
|||
|| ('a' <= c && c <= 'f') |
|||
|| ('A' <= c && c <= 'F'); |
|||
} |
|||
bool IsLineTerminator(char c) |
|||
{ |
|||
return c == '\n'; |
|||
} |
|||
bool IsWhiteSpace(char c) |
|||
{ |
|||
return c == ' ' || c == '\n' || c == '\t'; |
|||
} |
|||
bool IsIdentifierStart(char c) |
|||
{ |
|||
return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); |
|||
} |
|||
bool IsIdentifierPart(char c) |
|||
{ |
|||
return IsIdentifierStart(c) || IsDecimalDigit(c); |
|||
} |
|||
|
|||
int HexValue(char c) |
|||
{ |
|||
if (c >= '0' && c <= '9') |
|||
return c - '0'; |
|||
else if (c >= 'a' && c <= 'f') |
|||
return c - 'a' + 10; |
|||
else if (c >= 'A' && c <= 'F') |
|||
return c - 'A' + 10; |
|||
else return -1; |
|||
} |
|||
} // end anonymous namespace
|
|||
|
|||
Scanner::Scanner(CharStream const& _source) |
|||
{ |
|||
reset(_source); |
|||
} |
|||
|
|||
void Scanner::reset(CharStream const& _source) |
|||
{ |
|||
m_source = _source; |
|||
m_char = m_source.get(); |
|||
skipWhitespace(); |
|||
scanToken(); |
|||
next(); |
|||
} |
|||
|
|||
|
|||
bool Scanner::scanHexNumber(char& o_scannedNumber, int _expectedLength) |
|||
{ |
|||
BOOST_ASSERT(_expectedLength <= 4); // prevent overflow
|
|||
char x = 0; |
|||
for (int i = 0; i < _expectedLength; i++) |
|||
{ |
|||
int d = HexValue(m_char); |
|||
if (d < 0) |
|||
{ |
|||
rollback(i); |
|||
return false; |
|||
} |
|||
x = x * 16 + d; |
|||
advance(); |
|||
} |
|||
o_scannedNumber = x; |
|||
return true; |
|||
} |
|||
|
|||
|
|||
// Ensure that tokens can be stored in a byte.
|
|||
BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); |
|||
|
|||
Token::Value Scanner::next() |
|||
{ |
|||
m_current_token = m_next_token; |
|||
scanToken(); |
|||
return m_current_token.token; |
|||
} |
|||
|
|||
Token::Value Scanner::selectToken(char _next, Token::Value _then, Token::Value _else) |
|||
{ |
|||
advance(); |
|||
if (m_char == _next) |
|||
return selectToken(_then); |
|||
else |
|||
return _else; |
|||
} |
|||
|
|||
|
|||
bool Scanner::skipWhitespace() |
|||
{ |
|||
int const start_position = getSourcePos(); |
|||
while (IsWhiteSpace(m_char)) |
|||
advance(); |
|||
// Return whether or not we skipped any characters.
|
|||
return getSourcePos() != start_position; |
|||
} |
|||
|
|||
|
|||
Token::Value Scanner::skipSingleLineComment() |
|||
{ |
|||
// The line terminator at the end of the line is not considered
|
|||
// to be part of the single-line comment; it is recognized
|
|||
// separately by the lexical grammar and becomes part of the
|
|||
// stream of input elements for the syntactic grammar
|
|||
while (advance() && !IsLineTerminator(m_char)) { }; |
|||
return Token::WHITESPACE; |
|||
} |
|||
|
|||
Token::Value Scanner::skipMultiLineComment() |
|||
{ |
|||
BOOST_ASSERT(m_char == '*'); |
|||
advance(); |
|||
while (!isSourcePastEndOfInput()) |
|||
{ |
|||
char ch = m_char; |
|||
advance(); |
|||
|
|||
// If we have reached the end of the multi-line comment, we
|
|||
// consume the '/' and insert a whitespace. This way all
|
|||
// multi-line comments are treated as whitespace.
|
|||
if (ch == '*' && m_char == '/') |
|||
{ |
|||
m_char = ' '; |
|||
return Token::WHITESPACE; |
|||
} |
|||
} |
|||
// Unterminated multi-line comment.
|
|||
return Token::ILLEGAL; |
|||
} |
|||
|
|||
void Scanner::scanToken() |
|||
{ |
|||
m_next_token.literal.clear(); |
|||
Token::Value token; |
|||
do |
|||
{ |
|||
// Remember the position of the next token
|
|||
m_next_token.location.start = getSourcePos(); |
|||
switch (m_char) |
|||
{ |
|||
case '\n': // fall-through
|
|||
case ' ': |
|||
case '\t': |
|||
token = selectToken(Token::WHITESPACE); |
|||
break; |
|||
case '"': |
|||
case '\'': |
|||
token = scanString(); |
|||
break; |
|||
case '<': |
|||
// < <= << <<=
|
|||
advance(); |
|||
if (m_char == '=') |
|||
token = selectToken(Token::LTE); |
|||
else if (m_char == '<') |
|||
token = selectToken('=', Token::ASSIGN_SHL, Token::SHL); |
|||
else |
|||
token = Token::LT; |
|||
break; |
|||
case '>': |
|||
// > >= >> >>= >>> >>>=
|
|||
advance(); |
|||
if (m_char == '=') |
|||
token = selectToken(Token::GTE); |
|||
else if (m_char == '>') |
|||
{ |
|||
// >> >>= >>> >>>=
|
|||
advance(); |
|||
if (m_char == '=') |
|||
token = selectToken(Token::ASSIGN_SAR); |
|||
else if (m_char == '>') |
|||
token = selectToken('=', Token::ASSIGN_SHR, Token::SHR); |
|||
else |
|||
token = Token::SAR; |
|||
} |
|||
else |
|||
token = Token::GT; |
|||
break; |
|||
case '=': |
|||
// = == =>
|
|||
advance(); |
|||
if (m_char == '=') |
|||
token = selectToken(Token::EQ); |
|||
else if (m_char == '>') |
|||
token = selectToken(Token::ARROW); |
|||
else |
|||
token = Token::ASSIGN; |
|||
break; |
|||
case '!': |
|||
// ! !=
|
|||
advance(); |
|||
if (m_char == '=') |
|||
token = selectToken(Token::NE); |
|||
else |
|||
token = Token::NOT; |
|||
break; |
|||
case '+': |
|||
// + ++ +=
|
|||
advance(); |
|||
if (m_char == '+') |
|||
token = selectToken(Token::INC); |
|||
else if (m_char == '=') |
|||
token = selectToken(Token::ASSIGN_ADD); |
|||
else |
|||
token = Token::ADD; |
|||
break; |
|||
case '-': |
|||
// - -- -=
|
|||
advance(); |
|||
if (m_char == '-') |
|||
{ |
|||
advance(); |
|||
token = Token::DEC; |
|||
} |
|||
else if (m_char == '=') |
|||
token = selectToken(Token::ASSIGN_SUB); |
|||
else |
|||
token = Token::SUB; |
|||
break; |
|||
case '*': |
|||
// * *=
|
|||
token = selectToken('=', Token::ASSIGN_MUL, Token::MUL); |
|||
break; |
|||
case '%': |
|||
// % %=
|
|||
token = selectToken('=', Token::ASSIGN_MOD, Token::MOD); |
|||
break; |
|||
case '/': |
|||
// / // /* /=
|
|||
advance(); |
|||
if (m_char == '/') |
|||
token = skipSingleLineComment(); |
|||
else if (m_char == '*') |
|||
token = skipMultiLineComment(); |
|||
else if (m_char == '=') |
|||
token = selectToken(Token::ASSIGN_DIV); |
|||
else |
|||
token = Token::DIV; |
|||
break; |
|||
case '&': |
|||
// & && &=
|
|||
advance(); |
|||
if (m_char == '&') |
|||
token = selectToken(Token::AND); |
|||
else if (m_char == '=') |
|||
token = selectToken(Token::ASSIGN_BIT_AND); |
|||
else |
|||
token = Token::BIT_AND; |
|||
break; |
|||
case '|': |
|||
// | || |=
|
|||
advance(); |
|||
if (m_char == '|') |
|||
token = selectToken(Token::OR); |
|||
else if (m_char == '=') |
|||
token = selectToken(Token::ASSIGN_BIT_OR); |
|||
else |
|||
token = Token::BIT_OR; |
|||
break; |
|||
case '^': |
|||
// ^ ^=
|
|||
token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); |
|||
break; |
|||
case '.': |
|||
// . Number
|
|||
advance(); |
|||
if (IsDecimalDigit(m_char)) |
|||
token = scanNumber(true); |
|||
else |
|||
token = Token::PERIOD; |
|||
break; |
|||
case ':': |
|||
token = selectToken(Token::COLON); |
|||
break; |
|||
case ';': |
|||
token = selectToken(Token::SEMICOLON); |
|||
break; |
|||
case ',': |
|||
token = selectToken(Token::COMMA); |
|||
break; |
|||
case '(': |
|||
token = selectToken(Token::LPAREN); |
|||
break; |
|||
case ')': |
|||
token = selectToken(Token::RPAREN); |
|||
break; |
|||
case '[': |
|||
token = selectToken(Token::LBRACK); |
|||
break; |
|||
case ']': |
|||
token = selectToken(Token::RBRACK); |
|||
break; |
|||
case '{': |
|||
token = selectToken(Token::LBRACE); |
|||
break; |
|||
case '}': |
|||
token = selectToken(Token::RBRACE); |
|||
break; |
|||
case '?': |
|||
token = selectToken(Token::CONDITIONAL); |
|||
break; |
|||
case '~': |
|||
token = selectToken(Token::BIT_NOT); |
|||
break; |
|||
default: |
|||
if (IsIdentifierStart(m_char)) |
|||
token = scanIdentifierOrKeyword(); |
|||
else if (IsDecimalDigit(m_char)) |
|||
token = scanNumber(false); |
|||
else if (skipWhitespace()) |
|||
token = Token::WHITESPACE; |
|||
else if (isSourcePastEndOfInput()) |
|||
token = Token::EOS; |
|||
else |
|||
token = selectToken(Token::ILLEGAL); |
|||
break; |
|||
} |
|||
// Continue scanning for tokens as long as we're just skipping
|
|||
// whitespace.
|
|||
} |
|||
while (token == Token::WHITESPACE); |
|||
m_next_token.location.end = getSourcePos(); |
|||
m_next_token.token = token; |
|||
} |
|||
|
|||
bool Scanner::scanEscape() |
|||
{ |
|||
char c = m_char; |
|||
advance(); |
|||
// Skip escaped newlines.
|
|||
if (IsLineTerminator(c)) |
|||
return true; |
|||
switch (c) |
|||
{ |
|||
case '\'': // fall through
|
|||
case '"': // fall through
|
|||
case '\\': |
|||
break; |
|||
case 'b': |
|||
c = '\b'; |
|||
break; |
|||
case 'f': |
|||
c = '\f'; |
|||
break; |
|||
case 'n': |
|||
c = '\n'; |
|||
break; |
|||
case 'r': |
|||
c = '\r'; |
|||
break; |
|||
case 't': |
|||
c = '\t'; |
|||
break; |
|||
case 'u': |
|||
if (!scanHexNumber(c, 4)) |
|||
return false; |
|||
break; |
|||
case 'v': |
|||
c = '\v'; |
|||
break; |
|||
case 'x': |
|||
if (!scanHexNumber(c, 2)) |
|||
return false; |
|||
break; |
|||
} |
|||
|
|||
addLiteralChar(c); |
|||
return true; |
|||
} |
|||
|
|||
Token::Value Scanner::scanString() |
|||
{ |
|||
char const quote = m_char; |
|||
advance(); // consume quote
|
|||
LiteralScope literal(this); |
|||
while (m_char != quote && !isSourcePastEndOfInput() && !IsLineTerminator(m_char)) |
|||
{ |
|||
char c = m_char; |
|||
advance(); |
|||
if (c == '\\') |
|||
{ |
|||
if (isSourcePastEndOfInput() || !scanEscape()) |
|||
return Token::ILLEGAL; |
|||
} |
|||
else |
|||
addLiteralChar(c); |
|||
} |
|||
if (m_char != quote) return Token::ILLEGAL; |
|||
literal.Complete(); |
|||
advance(); // consume quote
|
|||
return Token::STRING_LITERAL; |
|||
} |
|||
|
|||
|
|||
void Scanner::scanDecimalDigits() |
|||
{ |
|||
while (IsDecimalDigit(m_char)) |
|||
addLiteralCharAndAdvance(); |
|||
} |
|||
|
|||
|
|||
Token::Value Scanner::scanNumber(bool _periodSeen) |
|||
{ |
|||
BOOST_ASSERT(IsDecimalDigit(m_char)); // the first digit of the number or the fraction
|
|||
enum { DECIMAL, HEX, OCTAL, IMPLICIT_OCTAL, BINARY } kind = DECIMAL; |
|||
LiteralScope literal(this); |
|||
if (_periodSeen) |
|||
{ |
|||
// we have already seen a decimal point of the float
|
|||
addLiteralChar('.'); |
|||
scanDecimalDigits(); // we know we have at least one digit
|
|||
} |
|||
else |
|||
{ |
|||
// if the first character is '0' we must check for octals and hex
|
|||
if (m_char == '0') |
|||
{ |
|||
addLiteralCharAndAdvance(); |
|||
// either 0, 0exxx, 0Exxx, 0.xxx, a hex number, a binary number or
|
|||
// an octal number.
|
|||
if (m_char == 'x' || m_char == 'X') |
|||
{ |
|||
// hex number
|
|||
kind = HEX; |
|||
addLiteralCharAndAdvance(); |
|||
if (!IsHexDigit(m_char)) |
|||
return Token::ILLEGAL; // we must have at least one hex digit after 'x'/'X'
|
|||
while (IsHexDigit(m_char)) |
|||
addLiteralCharAndAdvance(); |
|||
} |
|||
} |
|||
// Parse decimal digits and allow trailing fractional part.
|
|||
if (kind == DECIMAL) |
|||
{ |
|||
scanDecimalDigits(); // optional
|
|||
if (m_char == '.') |
|||
{ |
|||
addLiteralCharAndAdvance(); |
|||
scanDecimalDigits(); // optional
|
|||
} |
|||
} |
|||
} |
|||
// scan exponent, if any
|
|||
if (m_char == 'e' || m_char == 'E') |
|||
{ |
|||
BOOST_ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number
|
|||
if (kind != DECIMAL) return Token::ILLEGAL; |
|||
// scan exponent
|
|||
addLiteralCharAndAdvance(); |
|||
if (m_char == '+' || m_char == '-') |
|||
addLiteralCharAndAdvance(); |
|||
if (!IsDecimalDigit(m_char)) |
|||
return Token::ILLEGAL; // we must have at least one decimal digit after 'e'/'E'
|
|||
scanDecimalDigits(); |
|||
} |
|||
// The source character immediately following a numeric literal must
|
|||
// not be an identifier start or a decimal digit; see ECMA-262
|
|||
// section 7.8.3, page 17 (note that we read only one decimal digit
|
|||
// if the value is 0).
|
|||
if (IsDecimalDigit(m_char) || IsIdentifierStart(m_char)) |
|||
return Token::ILLEGAL; |
|||
literal.Complete(); |
|||
return Token::NUMBER; |
|||
} |
|||
|
|||
|
|||
// ----------------------------------------------------------------------------
|
|||
// Keyword Matcher
|
|||
|
|||
#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ |
|||
KEYWORD_GROUP('a') \ |
|||
KEYWORD("address", Token::ADDRESS) \ |
|||
KEYWORD_GROUP('b') \ |
|||
KEYWORD("break", Token::BREAK) \ |
|||
KEYWORD("bool", Token::BOOL) \ |
|||
KEYWORD_GROUP('c') \ |
|||
KEYWORD("case", Token::CASE) \ |
|||
KEYWORD("const", Token::CONST) \ |
|||
KEYWORD("continue", Token::CONTINUE) \ |
|||
KEYWORD("contract", Token::CONTRACT) \ |
|||
KEYWORD_GROUP('d') \ |
|||
KEYWORD("default", Token::DEFAULT) \ |
|||
KEYWORD("delete", Token::DELETE) \ |
|||
KEYWORD("do", Token::DO) \ |
|||
KEYWORD_GROUP('e') \ |
|||
KEYWORD("else", Token::ELSE) \ |
|||
KEYWORD("extends", Token::EXTENDS) \ |
|||
KEYWORD_GROUP('f') \ |
|||
KEYWORD("false", Token::FALSE_LITERAL) \ |
|||
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("in", Token::IN) \ |
|||
KEYWORD("int", Token::INT) \ |
|||
KEYWORD("int32", Token::INT32) \ |
|||
KEYWORD("int64", Token::INT64) \ |
|||
KEYWORD("int128", Token::INT128) \ |
|||
KEYWORD("int256", Token::INT256) \ |
|||
KEYWORD_GROUP('l') \ |
|||
KEYWORD_GROUP('m') \ |
|||
KEYWORD("mapping", Token::MAPPING) \ |
|||
KEYWORD_GROUP('n') \ |
|||
KEYWORD("new", Token::NEW) \ |
|||
KEYWORD("null", Token::NULL_LITERAL) \ |
|||
KEYWORD_GROUP('p') \ |
|||
KEYWORD("private", Token::PRIVATE) \ |
|||
KEYWORD("public", Token::PUBLIC) \ |
|||
KEYWORD_GROUP('r') \ |
|||
KEYWORD("real", Token::REAL) \ |
|||
KEYWORD("return", Token::RETURN) \ |
|||
KEYWORD("returns", Token::RETURNS) \ |
|||
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("true", Token::TRUE_LITERAL) \ |
|||
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_GROUP('w') \ |
|||
KEYWORD("while", Token::WHILE) \ |
|||
|
|||
|
|||
static Token::Value KeywordOrIdentifierToken(std::string const& input) |
|||
{ |
|||
BOOST_ASSERT(!input.empty()); |
|||
int const kMinLength = 2; |
|||
int const kMaxLength = 10; |
|||
if (input.size() < kMinLength || input.size() > kMaxLength) |
|||
return Token::IDENTIFIER; |
|||
switch (input[0]) |
|||
{ |
|||
default: |
|||
#define KEYWORD_GROUP_CASE(ch) \ |
|||
break; \ |
|||
case ch: |
|||
#define KEYWORD(keyword, token) \ |
|||
{ \ |
|||
/* 'keyword' is a char array, so sizeof(keyword) is */ \ |
|||
/* strlen(keyword) plus 1 for the NUL char. */ \ |
|||
int const keyword_length = sizeof(keyword) - 1; \ |
|||
BOOST_STATIC_ASSERT(keyword_length >= kMinLength); \ |
|||
BOOST_STATIC_ASSERT(keyword_length <= kMaxLength); \ |
|||
if (input == keyword) \ |
|||
return token; \ |
|||
} |
|||
KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) |
|||
} |
|||
return Token::IDENTIFIER; |
|||
} |
|||
|
|||
Token::Value Scanner::scanIdentifierOrKeyword() |
|||
{ |
|||
BOOST_ASSERT(IsIdentifierStart(m_char)); |
|||
LiteralScope literal(this); |
|||
addLiteralCharAndAdvance(); |
|||
// Scan the rest of the identifier characters.
|
|||
while (IsIdentifierPart(m_char)) |
|||
addLiteralCharAndAdvance(); |
|||
literal.Complete(); |
|||
return KeywordOrIdentifierToken(m_next_token.literal); |
|||
} |
|||
|
|||
char CharStream::advanceAndGet() |
|||
{ |
|||
if (isPastEndOfInput()) |
|||
return 0; |
|||
++m_pos; |
|||
if (isPastEndOfInput()) |
|||
return 0; |
|||
return get(); |
|||
} |
|||
|
|||
char CharStream::rollback(size_t _amount) |
|||
{ |
|||
BOOST_ASSERT(m_pos >= _amount); |
|||
m_pos -= _amount; |
|||
return get(); |
|||
} |
|||
|
|||
std::string CharStream::getLineAtPosition(int _position) const |
|||
{ |
|||
// if _position points to \n, it returns the line before the \n
|
|||
using size_type = std::string::size_type; |
|||
size_type searchStart = std::min<size_type>(m_source.size(), _position); |
|||
if (searchStart > 0) |
|||
searchStart--; |
|||
size_type lineStart = m_source.rfind('\n', searchStart); |
|||
if (lineStart == std::string::npos) |
|||
lineStart = 0; |
|||
else |
|||
lineStart++; |
|||
return m_source.substr(lineStart, |
|||
std::min(m_source.find('\n', lineStart), |
|||
m_source.size()) - lineStart); |
|||
} |
|||
|
|||
std::tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const |
|||
{ |
|||
using size_type = std::string::size_type; |
|||
size_type searchPosition = std::min<size_type>(m_source.size(), _position); |
|||
int lineNumber = std::count(m_source.begin(), m_source.begin() + searchPosition, '\n'); |
|||
size_type lineStart; |
|||
if (searchPosition == 0) |
|||
lineStart = 0; |
|||
else |
|||
{ |
|||
lineStart = m_source.rfind('\n', searchPosition - 1); |
|||
lineStart = lineStart == std::string::npos ? 0 : lineStart + 1; |
|||
} |
|||
return std::tuple<int, int>(lineNumber, searchPosition - lineStart); |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
@ -0,0 +1,204 @@ |
|||
/*
|
|||
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/>.
|
|||
|
|||
This file is derived from the file "scanner.h", which was part of the |
|||
V8 project. The original copyright header follows: |
|||
|
|||
Copyright 2006-2012, the V8 project authors. All rights reserved. |
|||
Redistribution and use in source and binary forms, with or without |
|||
modification, are permitted provided that the following conditions are |
|||
met: |
|||
|
|||
* Redistributions of source code must retain the above copyright |
|||
notice, this list of conditions and the following disclaimer. |
|||
* Redistributions in binary form must reproduce the above |
|||
copyright notice, this list of conditions and the following |
|||
disclaimer in the documentation and/or other materials provided |
|||
with the distribution. |
|||
* Neither the name of Google Inc. nor the names of its |
|||
contributors may be used to endorse or promote products derived |
|||
from this software without specific prior written permission. |
|||
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|||
*/ |
|||
/**
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2014 |
|||
* Solidity scanner. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <boost/assert.hpp> |
|||
|
|||
#include <libdevcore/Common.h> |
|||
#include <libdevcore/Log.h> |
|||
#include <libdevcore/CommonData.h> |
|||
#include <libsolidity/BaseTypes.h> |
|||
#include <libsolidity/Token.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
|
|||
class AstRawString; |
|||
class AstValueFactory; |
|||
class ParserRecorder; |
|||
|
|||
class CharStream |
|||
{ |
|||
public: |
|||
CharStream(): m_pos(0) {} |
|||
explicit CharStream(std::string const& _source): m_source(_source), m_pos(0) {} |
|||
int getPos() const { return m_pos; } |
|||
bool isPastEndOfInput() const { return m_pos >= m_source.size(); } |
|||
char get() const { return m_source[m_pos]; } |
|||
char advanceAndGet(); |
|||
char rollback(size_t _amount); |
|||
|
|||
/// Functions that help pretty-printing parse errors
|
|||
/// Do only use in error cases, they are quite expensive.
|
|||
/// @{
|
|||
std::string getLineAtPosition(int _position) const; |
|||
std::tuple<int, int> translatePositionToLineColumn(int _position) const; |
|||
/// @}
|
|||
|
|||
private: |
|||
std::string m_source; |
|||
size_t m_pos; |
|||
}; |
|||
|
|||
|
|||
class Scanner |
|||
{ |
|||
public: |
|||
// Scoped helper for literal recording. Automatically drops the literal
|
|||
// if aborting the scanning before it's complete.
|
|||
class LiteralScope |
|||
{ |
|||
public: |
|||
explicit LiteralScope(Scanner* self): scanner_(self), complete_(false) { scanner_->startNewLiteral(); } |
|||
~LiteralScope() { if (!complete_) scanner_->dropLiteral(); } |
|||
void Complete() { complete_ = true; } |
|||
|
|||
private: |
|||
Scanner* scanner_; |
|||
bool complete_; |
|||
}; |
|||
|
|||
explicit Scanner(CharStream const& _source); |
|||
|
|||
/// Resets the scanner as if newly constructed with _input as input.
|
|||
void reset(CharStream const& _source); |
|||
|
|||
/// Returns the next token and advances input.
|
|||
Token::Value next(); |
|||
|
|||
/// Information about the current token
|
|||
/// @{
|
|||
|
|||
/// Returns the current token
|
|||
Token::Value getCurrentToken() { return m_current_token.token; } |
|||
Location getCurrentLocation() const { return m_current_token.location; } |
|||
const std::string& getCurrentLiteral() const { return m_current_token.literal; } |
|||
/// @}
|
|||
|
|||
/// Information about the next token
|
|||
/// @{
|
|||
/// Returns the next token without advancing input.
|
|||
Token::Value peekNextToken() const { return m_next_token.token; } |
|||
Location peekLocation() const { return m_next_token.location; } |
|||
const std::string& peekLiteral() const { return m_next_token.literal; } |
|||
/// @}
|
|||
|
|||
/// Functions that help pretty-printing parse errors.
|
|||
/// Do only use in error cases, they are quite expensive.
|
|||
/// @{
|
|||
std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); } |
|||
std::tuple<int, int> translatePositionToLineColumn(int _position) const { return m_source.translatePositionToLineColumn(_position); } |
|||
/// @}
|
|||
|
|||
private: |
|||
// Used for the current and look-ahead token.
|
|||
struct TokenDesc |
|||
{ |
|||
Token::Value token; |
|||
Location location; |
|||
std::string literal; |
|||
}; |
|||
|
|||
/// Literal buffer support
|
|||
/// @{
|
|||
inline void startNewLiteral() { m_next_token.literal.clear(); } |
|||
inline void addLiteralChar(char c) { m_next_token.literal.push_back(c); } |
|||
inline void dropLiteral() { m_next_token.literal.clear(); } |
|||
inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); } |
|||
/// @}
|
|||
|
|||
bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } |
|||
void rollback(int _amount) { m_char = m_source.rollback(_amount); } |
|||
|
|||
inline Token::Value selectToken(Token::Value _tok) { advance(); return _tok; } |
|||
/// If the next character is _next, advance and return _then, otherwise return _else.
|
|||
inline Token::Value selectToken(char _next, Token::Value _then, Token::Value _else); |
|||
|
|||
bool scanHexNumber(char& o_scannedNumber, int _expectedLength); |
|||
|
|||
// Scans a single JavaScript token.
|
|||
void scanToken(); |
|||
|
|||
bool skipWhitespace(); |
|||
Token::Value skipSingleLineComment(); |
|||
Token::Value skipMultiLineComment(); |
|||
|
|||
void scanDecimalDigits(); |
|||
Token::Value scanNumber(bool _periodSeen); |
|||
Token::Value scanIdentifierOrKeyword(); |
|||
|
|||
Token::Value scanString(); |
|||
|
|||
/// Scans an escape-sequence which is part of a string and adds the
|
|||
/// decoded character to the current literal. Returns true if a pattern
|
|||
/// is scanned.
|
|||
bool scanEscape(); |
|||
|
|||
/// Return the current source position.
|
|||
int getSourcePos() { return m_source.getPos(); } |
|||
bool isSourcePastEndOfInput() { return m_source.isPastEndOfInput(); } |
|||
|
|||
TokenDesc m_current_token; // desc for current token (as returned by Next())
|
|||
TokenDesc m_next_token; // desc for next token (one token look-ahead)
|
|||
|
|||
CharStream m_source; |
|||
|
|||
/// one character look-ahead, equals 0 at end of input
|
|||
char m_char; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,50 @@ |
|||
/*
|
|||
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 |
|||
* Scope - object that holds declaration of names. |
|||
*/ |
|||
|
|||
#include <libsolidity/Scope.h> |
|||
#include <libsolidity/AST.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
bool Scope::registerDeclaration(Declaration& _declaration) |
|||
{ |
|||
if (m_declarations.find(_declaration.getName()) != m_declarations.end()) |
|||
return false; |
|||
m_declarations[_declaration.getName()] = &_declaration; |
|||
return true; |
|||
} |
|||
|
|||
Declaration* Scope::resolveName(ASTString const& _name, bool _recursive) const |
|||
{ |
|||
auto result = m_declarations.find(_name); |
|||
if (result != m_declarations.end()) |
|||
return result->second; |
|||
if (_recursive && m_outerScope != nullptr) |
|||
return m_outerScope->resolveName(_name, true); |
|||
return nullptr; |
|||
} |
|||
|
|||
} |
|||
} |
@ -0,0 +1,51 @@ |
|||
/*
|
|||
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 |
|||
* Scope - object that holds declaration of names. |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <map> |
|||
#include <boost/noncopyable.hpp> |
|||
|
|||
#include <libsolidity/ASTForward.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
class Scope |
|||
{ |
|||
public: |
|||
explicit Scope(Scope* _outerScope = nullptr): m_outerScope(_outerScope) {} |
|||
/// Registers the declaration in the scope unless its name is already declared. Returns true iff
|
|||
/// it was not yet declared.
|
|||
bool registerDeclaration(Declaration& _declaration); |
|||
Declaration* resolveName(ASTString const& _name, bool _recursive = false) const; |
|||
Scope* getOuterScope() const { return m_outerScope; } |
|||
|
|||
private: |
|||
Scope* m_outerScope; |
|||
std::map<ASTString, Declaration*> m_declarations; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,84 @@ |
|||
// Copyright 2006-2012, the V8 project authors. All rights reserved.
|
|||
// Redistribution and use in source and binary forms, with or without
|
|||
// modification, are permitted provided that the following conditions are
|
|||
// met:
|
|||
//
|
|||
// * Redistributions of source code must retain the above copyright
|
|||
// notice, this list of conditions and the following disclaimer.
|
|||
// * Redistributions in binary form must reproduce the above
|
|||
// copyright notice, this list of conditions and the following
|
|||
// disclaimer in the documentation and/or other materials provided
|
|||
// with the distribution.
|
|||
// * Neither the name of Google Inc. nor the names of its
|
|||
// contributors may be used to endorse or promote products derived
|
|||
// from this software without specific prior written permission.
|
|||
//
|
|||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
//
|
|||
// Modifications as part of cpp-ethereum under the following license:
|
|||
//
|
|||
// 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/>.
|
|||
|
|||
#include <libsolidity/Token.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
#define T(name, string, precedence) #name, |
|||
char const* const Token::m_name[NUM_TOKENS] = |
|||
{ |
|||
TOKEN_LIST(T, T) |
|||
}; |
|||
#undef T |
|||
|
|||
|
|||
#define T(name, string, precedence) string, |
|||
char const* const Token::m_string[NUM_TOKENS] = |
|||
{ |
|||
TOKEN_LIST(T, T) |
|||
}; |
|||
#undef T |
|||
|
|||
|
|||
#define T(name, string, precedence) precedence, |
|||
int8_t const Token::m_precedence[NUM_TOKENS] = |
|||
{ |
|||
TOKEN_LIST(T, T) |
|||
}; |
|||
#undef T |
|||
|
|||
|
|||
#define KT(a, b, c) 'T', |
|||
#define KK(a, b, c) 'K', |
|||
char const Token::m_tokenType[] = |
|||
{ |
|||
TOKEN_LIST(KT, KK) |
|||
}; |
|||
#undef KT |
|||
#undef KK |
|||
|
|||
} |
|||
} |
@ -0,0 +1,334 @@ |
|||
// Copyright 2006-2012, the V8 project authors. All rights reserved.
|
|||
// Redistribution and use in source and binary forms, with or without
|
|||
// modification, are permitted provided that the following conditions are
|
|||
// met:
|
|||
//
|
|||
// * Redistributions of source code must retain the above copyright
|
|||
// notice, this list of conditions and the following disclaimer.
|
|||
// * Redistributions in binary form must reproduce the above
|
|||
// copyright notice, this list of conditions and the following
|
|||
// disclaimer in the documentation and/or other materials provided
|
|||
// with the distribution.
|
|||
// * Neither the name of Google Inc. nor the names of its
|
|||
// contributors may be used to endorse or promote products derived
|
|||
// from this software without specific prior written permission.
|
|||
//
|
|||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||
//
|
|||
// Modifications as part of cpp-ethereum under the following license:
|
|||
//
|
|||
// 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/>.
|
|||
|
|||
#pragma once |
|||
|
|||
#include <boost/assert.hpp> |
|||
|
|||
#include <libdevcore/Common.h> |
|||
#include <libdevcore/Log.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
// TOKEN_LIST takes a list of 3 macros M, all of which satisfy the
|
|||
// same signature M(name, string, precedence), where name is the
|
|||
// symbolic token name, string is the corresponding syntactic symbol
|
|||
// (or NULL, for literals), and precedence is the precedence (or 0).
|
|||
// The parameters are invoked for token categories as follows:
|
|||
//
|
|||
// T: Non-keyword tokens
|
|||
// K: Keyword tokens
|
|||
|
|||
// IGNORE_TOKEN is a convenience macro that can be supplied as
|
|||
// an argument (at any position) for a TOKEN_LIST call. It does
|
|||
// nothing with tokens belonging to the respective category.
|
|||
|
|||
#define IGNORE_TOKEN(name, string, precedence) |
|||
|
|||
#define TOKEN_LIST(T, K) \ |
|||
/* End of source indicator. */ \ |
|||
T(EOS, "EOS", 0) \ |
|||
\ |
|||
/* Punctuators (ECMA-262, section 7.7, page 15). */ \ |
|||
T(LPAREN, "(", 0) \ |
|||
T(RPAREN, ")", 0) \ |
|||
T(LBRACK, "[", 0) \ |
|||
T(RBRACK, "]", 0) \ |
|||
T(LBRACE, "{", 0) \ |
|||
T(RBRACE, "}", 0) \ |
|||
T(COLON, ":", 0) \ |
|||
T(SEMICOLON, ";", 0) \ |
|||
T(PERIOD, ".", 0) \ |
|||
T(CONDITIONAL, "?", 3) \ |
|||
T(INC, "++", 0) \ |
|||
T(DEC, "--", 0) \ |
|||
T(ARROW, "=>", 0) \ |
|||
\ |
|||
/* Assignment operators. */ \ |
|||
/* IsAssignmentOp() relies on this block of enum values being */ \ |
|||
/* contiguous and sorted in the same order!*/ \ |
|||
T(ASSIGN, "=", 2) \ |
|||
/* The following have to be in exactly the same order as the simple binary operators*/ \ |
|||
T(ASSIGN_BIT_OR, "|=", 2) \ |
|||
T(ASSIGN_BIT_XOR, "^=", 2) \ |
|||
T(ASSIGN_BIT_AND, "&=", 2) \ |
|||
T(ASSIGN_SHL, "<<=", 2) \ |
|||
T(ASSIGN_SAR, ">>=", 2) \ |
|||
T(ASSIGN_SHR, ">>>=", 2) \ |
|||
T(ASSIGN_ADD, "+=", 2) \ |
|||
T(ASSIGN_SUB, "-=", 2) \ |
|||
T(ASSIGN_MUL, "*=", 2) \ |
|||
T(ASSIGN_DIV, "/=", 2) \ |
|||
T(ASSIGN_MOD, "%=", 2) \ |
|||
\ |
|||
/* Binary operators sorted by precedence. */ \ |
|||
/* IsBinaryOp() relies on this block of enum values */ \ |
|||
/* being contiguous and sorted in the same order! */ \ |
|||
T(COMMA, ",", 1) \ |
|||
T(OR, "||", 4) \ |
|||
T(AND, "&&", 5) \ |
|||
T(BIT_OR, "|", 6) \ |
|||
T(BIT_XOR, "^", 7) \ |
|||
T(BIT_AND, "&", 8) \ |
|||
T(SHL, "<<", 11) \ |
|||
T(SAR, ">>", 11) \ |
|||
T(SHR, ">>>", 11) \ |
|||
T(ADD, "+", 12) \ |
|||
T(SUB, "-", 12) \ |
|||
T(MUL, "*", 13) \ |
|||
T(DIV, "/", 13) \ |
|||
T(MOD, "%", 13) \ |
|||
\ |
|||
/* Compare operators sorted by precedence. */ \ |
|||
/* IsCompareOp() relies on this block of enum values */ \ |
|||
/* being contiguous and sorted in the same order! */ \ |
|||
T(EQ, "==", 9) \ |
|||
T(NE, "!=", 9) \ |
|||
T(LT, "<", 10) \ |
|||
T(GT, ">", 10) \ |
|||
T(LTE, "<=", 10) \ |
|||
T(GTE, ">=", 10) \ |
|||
K(IN, "in", 10) \ |
|||
\ |
|||
/* Unary operators. */ \ |
|||
/* IsUnaryOp() relies on this block of enum values */ \ |
|||
/* being contiguous and sorted in the same order! */ \ |
|||
T(NOT, "!", 0) \ |
|||
T(BIT_NOT, "~", 0) \ |
|||
K(DELETE, "delete", 0) \ |
|||
\ |
|||
/* Keywords */ \ |
|||
K(BREAK, "break", 0) \ |
|||
K(CASE, "case", 0) \ |
|||
K(CONST, "const", 0) \ |
|||
K(CONTINUE, "continue", 0) \ |
|||
K(CONTRACT, "contract", 0) \ |
|||
K(DEFAULT, "default", 0) \ |
|||
K(DO, "do", 0) \ |
|||
K(ELSE, "else", 0) \ |
|||
K(EXTENDS, "extends", 0) \ |
|||
K(FOR, "for", 0) \ |
|||
K(FUNCTION, "function", 0) \ |
|||
K(IF, "if", 0) \ |
|||
K(IMPORT, "import", 0) \ |
|||
K(MAPPING, "mapping", 0) \ |
|||
K(NEW, "new", 0) \ |
|||
K(PUBLIC, "public", 0) \ |
|||
K(PRIVATE, "private", 0) \ |
|||
K(RETURN, "return", 0) \ |
|||
K(RETURNS, "returns", 0) \ |
|||
K(STRUCT, "struct", 0) \ |
|||
K(SWITCH, "switch", 0) \ |
|||
K(THIS, "this", 0) \ |
|||
K(VAR, "var", 0) \ |
|||
K(WHILE, "while", 0) \ |
|||
\ |
|||
\ |
|||
/* type keywords, keep them in this order, keep int as first keyword
|
|||
* the implementation in Types.cpp has to be synced to this here |
|||
* 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 */ \ |
|||
K(NULL_LITERAL, "null", 0) \ |
|||
K(TRUE_LITERAL, "true", 0) \ |
|||
K(FALSE_LITERAL, "false", 0) \ |
|||
T(NUMBER, NULL, 0) \ |
|||
T(STRING_LITERAL, NULL, 0) \ |
|||
\ |
|||
/* Identifiers (not keywords or future reserved words). */ \ |
|||
T(IDENTIFIER, NULL, 0) \ |
|||
\ |
|||
/* Illegal token - not able to scan. */ \ |
|||
T(ILLEGAL, "ILLEGAL", 0) \ |
|||
\ |
|||
/* Scanner-internal use only. */ \ |
|||
T(WHITESPACE, NULL, 0) |
|||
|
|||
|
|||
class Token |
|||
{ |
|||
public: |
|||
// All token values.
|
|||
#define T(name, string, precedence) name, |
|||
enum Value |
|||
{ |
|||
TOKEN_LIST(T, T) |
|||
NUM_TOKENS |
|||
}; |
|||
#undef T |
|||
|
|||
// Returns a string corresponding to the C++ token name
|
|||
// (e.g. "LT" for the token LT).
|
|||
static char const* getName(Value tok) |
|||
{ |
|||
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned
|
|||
return m_name[tok]; |
|||
} |
|||
|
|||
// Predicates
|
|||
static bool isKeyword(Value tok) { return m_tokenType[tok] == 'K'; } |
|||
static bool isIdentifier(Value tok) { return tok == IDENTIFIER; } |
|||
static bool isElementaryTypeName(Value tok) { return INT <= tok && tok < TYPES_END; } |
|||
static bool isAssignmentOp(Value tok) { return ASSIGN <= tok && tok <= ASSIGN_MOD; } |
|||
static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; } |
|||
static bool isTruncatingBinaryOp(Value op) { return BIT_OR <= op && op <= SHR; } |
|||
static bool isCompareOp(Value op) { return EQ <= op && op <= IN; } |
|||
static bool isOrderedRelationalCompareOp(Value op) |
|||
{ |
|||
return op == LT || op == LTE || op == GT || op == GTE; |
|||
} |
|||
static bool isEqualityOp(Value op) { return op == EQ; } |
|||
static bool isInequalityOp(Value op) { return op == NE; } |
|||
static bool isArithmeticCompareOp(Value op) |
|||
{ |
|||
return isOrderedRelationalCompareOp(op) || |
|||
isEqualityOp(op) || isInequalityOp(op); |
|||
} |
|||
|
|||
static Value negateCompareOp(Value op) |
|||
{ |
|||
BOOST_ASSERT(isArithmeticCompareOp(op)); |
|||
switch (op) |
|||
{ |
|||
case EQ: |
|||
return NE; |
|||
case NE: |
|||
return EQ; |
|||
case LT: |
|||
return GTE; |
|||
case GT: |
|||
return LTE; |
|||
case LTE: |
|||
return GT; |
|||
case GTE: |
|||
return LT; |
|||
default: |
|||
BOOST_ASSERT(false); // should not get here
|
|||
return op; |
|||
} |
|||
} |
|||
|
|||
static Value reverseCompareOp(Value op) |
|||
{ |
|||
BOOST_ASSERT(isArithmeticCompareOp(op)); |
|||
switch (op) |
|||
{ |
|||
case EQ: |
|||
return EQ; |
|||
case NE: |
|||
return NE; |
|||
case LT: |
|||
return GT; |
|||
case GT: |
|||
return LT; |
|||
case LTE: |
|||
return GTE; |
|||
case GTE: |
|||
return LTE; |
|||
default: |
|||
BOOST_ASSERT(false); // should not get here
|
|||
return op; |
|||
} |
|||
} |
|||
|
|||
static Value AssignmentToBinaryOp(Value op) |
|||
{ |
|||
BOOST_ASSERT(isAssignmentOp(op) && op != ASSIGN); |
|||
return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR)); |
|||
} |
|||
|
|||
static bool isBitOp(Value op) { return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; } |
|||
static bool isUnaryOp(Value op) { return (NOT <= op && op <= DELETE) || op == ADD || op == SUB; } |
|||
static bool isCountOp(Value op) { return op == INC || op == DEC; } |
|||
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); } |
|||
|
|||
// Returns a string corresponding to the JS token string
|
|||
// (.e., "<" for the token LT) or NULL if the token doesn't
|
|||
// have a (unique) string (e.g. an IDENTIFIER).
|
|||
static char const* toString(Value tok) |
|||
{ |
|||
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned.
|
|||
return m_string[tok]; |
|||
} |
|||
|
|||
// Returns the precedence > 0 for binary and compare
|
|||
// operators; returns 0 otherwise.
|
|||
static int precedence(Value tok) |
|||
{ |
|||
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned.
|
|||
return m_precedence[tok]; |
|||
} |
|||
|
|||
private: |
|||
static char const* const m_name[NUM_TOKENS]; |
|||
static char const* const m_string[NUM_TOKENS]; |
|||
static int8_t const m_precedence[NUM_TOKENS]; |
|||
static char const m_tokenType[NUM_TOKENS]; |
|||
}; |
|||
|
|||
} |
|||
} |
@ -0,0 +1,164 @@ |
|||
/*
|
|||
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 data types |
|||
*/ |
|||
|
|||
#include <libsolidity/Types.h> |
|||
#include <libsolidity/AST.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
std::shared_ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken) |
|||
{ |
|||
if (Token::INT <= _typeToken && _typeToken <= Token::HASH256) |
|||
{ |
|||
int offset = _typeToken - Token::INT; |
|||
int bits = offset % 5; |
|||
if (bits == 0) |
|||
bits = 256; |
|||
else |
|||
bits = (1 << (bits - 1)) * 32; |
|||
int modifier = offset / 5; |
|||
return std::make_shared<IntegerType>(bits, |
|||
modifier == 0 ? IntegerType::Modifier::SIGNED : |
|||
modifier == 1 ? IntegerType::Modifier::UNSIGNED : |
|||
IntegerType::Modifier::HASH); |
|||
} |
|||
else if (_typeToken == Token::ADDRESS) |
|||
return std::make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS); |
|||
else if (_typeToken == Token::BOOL) |
|||
return std::make_shared<BoolType>(); |
|||
else |
|||
BOOST_ASSERT(false); // @todo add other tyes
|
|||
} |
|||
|
|||
std::shared_ptr<Type> Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName) |
|||
{ |
|||
return std::make_shared<StructType>(*_typeName.getReferencedStruct()); |
|||
} |
|||
|
|||
std::shared_ptr<Type> Type::fromMapping(Mapping const&) |
|||
{ |
|||
BOOST_ASSERT(false); //@todo not yet implemented
|
|||
return std::shared_ptr<Type>(); |
|||
} |
|||
|
|||
std::shared_ptr<Type> Type::forLiteral(Literal const& _literal) |
|||
{ |
|||
switch (_literal.getToken()) |
|||
{ |
|||
case Token::TRUE_LITERAL: |
|||
case Token::FALSE_LITERAL: |
|||
return std::make_shared<BoolType>(); |
|||
case Token::NUMBER: |
|||
return IntegerType::smallestTypeForLiteral(_literal.getValue()); |
|||
case Token::STRING_LITERAL: |
|||
return std::shared_ptr<Type>(); // @todo
|
|||
default: |
|||
return std::shared_ptr<Type>(); |
|||
} |
|||
} |
|||
|
|||
std::shared_ptr<IntegerType> IntegerType::smallestTypeForLiteral(std::string const&) |
|||
{ |
|||
//@todo
|
|||
return std::make_shared<IntegerType>(256, Modifier::UNSIGNED); |
|||
} |
|||
|
|||
IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier): |
|||
m_bits(_bits), m_modifier(_modifier) |
|||
{ |
|||
BOOST_ASSERT(_bits > 0 && _bits <= 256 && _bits % 8 == 0); |
|||
if (isAddress()) |
|||
_bits = 160; |
|||
} |
|||
|
|||
bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const |
|||
{ |
|||
if (_convertTo.getCategory() != Category::INTEGER) |
|||
return false; |
|||
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); |
|||
if (convertTo.m_bits < m_bits) |
|||
return false; |
|||
if (isAddress()) |
|||
return convertTo.isAddress(); |
|||
else if (isHash()) |
|||
return convertTo.isHash(); |
|||
else if (isSigned()) |
|||
return convertTo.isSigned(); |
|||
else |
|||
return !convertTo.isSigned() || convertTo.m_bits > m_bits; |
|||
} |
|||
|
|||
bool IntegerType::isExplicitlyConvertibleTo(Type const& _convertTo) const |
|||
{ |
|||
return _convertTo.getCategory() == Category::INTEGER; |
|||
} |
|||
|
|||
bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const |
|||
{ |
|||
if (isAddress()) |
|||
return Token::isCompareOp(_operator); |
|||
else if (isHash()) |
|||
return Token::isCompareOp(_operator) || Token::isBitOp(_operator); |
|||
else |
|||
return true; |
|||
} |
|||
|
|||
bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const |
|||
{ |
|||
return _operator == Token::DELETE || (!isAddress() && _operator == Token::BIT_NOT); |
|||
} |
|||
|
|||
bool BoolType::isExplicitlyConvertibleTo(Type const& _convertTo) const |
|||
{ |
|||
// conversion to integer is fine, but not to address
|
|||
// this is an example of explicit conversions being not transitive (though implicit should be)
|
|||
if (_convertTo.getCategory() == Category::INTEGER) |
|||
{ |
|||
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); |
|||
if (!convertTo.isAddress()) |
|||
return true; |
|||
} |
|||
return isImplicitlyConvertibleTo(_convertTo); |
|||
} |
|||
|
|||
bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const |
|||
{ |
|||
if (_convertTo.getCategory() != Category::CONTRACT) |
|||
return false; |
|||
ContractType const& convertTo = dynamic_cast<ContractType const&>(_convertTo); |
|||
return &m_contract == &convertTo.m_contract; |
|||
} |
|||
|
|||
bool StructType::isImplicitlyConvertibleTo(Type const& _convertTo) const |
|||
{ |
|||
if (_convertTo.getCategory() != Category::STRUCT) |
|||
return false; |
|||
StructType const& convertTo = dynamic_cast<StructType const&>(_convertTo); |
|||
return &m_struct == &convertTo.m_struct; |
|||
} |
|||
|
|||
|
|||
} |
|||
} |
@ -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 data types |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <memory> |
|||
#include <string> |
|||
#include <boost/noncopyable.hpp> |
|||
#include <boost/assert.hpp> |
|||
#include <libsolidity/ASTForward.h> |
|||
#include <libsolidity/Token.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
// @todo realMxN, string<N>, mapping
|
|||
|
|||
class Type: private boost::noncopyable |
|||
{ |
|||
public: |
|||
enum class Category |
|||
{ |
|||
INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE |
|||
}; |
|||
|
|||
//! factory functions that convert an AST TypeName to a Type.
|
|||
static std::shared_ptr<Type> fromElementaryTypeName(Token::Value _typeToken); |
|||
static std::shared_ptr<Type> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); |
|||
static std::shared_ptr<Type> fromMapping(Mapping const& _typeName); |
|||
|
|||
static std::shared_ptr<Type> forLiteral(Literal const& _literal); |
|||
|
|||
virtual Category getCategory() const = 0; |
|||
virtual bool isImplicitlyConvertibleTo(Type const&) const { return false; } |
|||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const |
|||
{ |
|||
return isImplicitlyConvertibleTo(_convertTo); |
|||
} |
|||
virtual bool acceptsBinaryOperator(Token::Value) const { return false; } |
|||
virtual bool acceptsUnaryOperator(Token::Value) const { return false; } |
|||
}; |
|||
|
|||
class IntegerType: public Type |
|||
{ |
|||
public: |
|||
enum class Modifier |
|||
{ |
|||
UNSIGNED, SIGNED, HASH, ADDRESS |
|||
}; |
|||
virtual Category getCategory() const { return Category::INTEGER; } |
|||
|
|||
static std::shared_ptr<IntegerType> smallestTypeForLiteral(std::string const& _literal); |
|||
|
|||
explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED); |
|||
|
|||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; |
|||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; |
|||
virtual bool acceptsBinaryOperator(Token::Value _operator) const override; |
|||
virtual bool acceptsUnaryOperator(Token::Value _operator) const override; |
|||
|
|||
int getNumBits() const { return m_bits; } |
|||
bool isHash() const { return m_modifier == Modifier::HASH || m_modifier == Modifier::ADDRESS; } |
|||
bool isAddress() const { return m_modifier == Modifier::ADDRESS; } |
|||
int isSigned() const { return m_modifier == Modifier::SIGNED; } |
|||
|
|||
private: |
|||
int m_bits; |
|||
Modifier m_modifier; |
|||
}; |
|||
|
|||
class BoolType: public Type |
|||
{ |
|||
public: |
|||
virtual Category getCategory() const { return Category::BOOL; } |
|||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override |
|||
{ |
|||
return _convertTo.getCategory() == Category::BOOL; |
|||
} |
|||
virtual bool isExplicitlyConvertibleTo(Type const& _convertTo) const override; |
|||
virtual bool acceptsBinaryOperator(Token::Value _operator) const override |
|||
{ |
|||
return _operator == Token::AND || _operator == Token::OR; |
|||
} |
|||
virtual bool acceptsUnaryOperator(Token::Value _operator) const override |
|||
{ |
|||
return _operator == Token::NOT || _operator == Token::DELETE; |
|||
} |
|||
}; |
|||
|
|||
class ContractType: public Type |
|||
{ |
|||
public: |
|||
virtual Category getCategory() const { return Category::CONTRACT; } |
|||
ContractType(ContractDefinition const& _contract): m_contract(_contract) {} |
|||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const; |
|||
|
|||
private: |
|||
ContractDefinition const& m_contract; |
|||
}; |
|||
|
|||
class StructType: public Type |
|||
{ |
|||
public: |
|||
virtual Category getCategory() const { return Category::STRUCT; } |
|||
StructType(StructDefinition const& _struct): m_struct(_struct) {} |
|||
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const; |
|||
virtual bool acceptsUnaryOperator(Token::Value _operator) const override |
|||
{ |
|||
return _operator == Token::DELETE; |
|||
} |
|||
|
|||
private: |
|||
StructDefinition const& m_struct; |
|||
}; |
|||
|
|||
class FunctionType: public Type |
|||
{ |
|||
public: |
|||
virtual Category getCategory() const { return Category::FUNCTION; } |
|||
FunctionType(FunctionDefinition const& _function): m_function(_function) {} |
|||
|
|||
FunctionDefinition const& getFunction() const { return m_function; } |
|||
|
|||
private: |
|||
FunctionDefinition const& m_function; |
|||
}; |
|||
|
|||
class MappingType: public Type |
|||
{ |
|||
public: |
|||
virtual Category getCategory() const { return Category::MAPPING; } |
|||
MappingType() {} |
|||
private: |
|||
//@todo
|
|||
}; |
|||
|
|||
//@todo should be changed into "empty anonymous struct"
|
|||
class VoidType: public Type |
|||
{ |
|||
public: |
|||
virtual Category getCategory() const { return Category::VOID; } |
|||
VoidType() {} |
|||
}; |
|||
|
|||
class TypeType: public Type |
|||
{ |
|||
public: |
|||
virtual Category getCategory() const { return Category::TYPE; } |
|||
TypeType(std::shared_ptr<Type const> const& _actualType): m_actualType(_actualType) {} |
|||
|
|||
std::shared_ptr<Type const> const& getActualType() const { return m_actualType; } |
|||
|
|||
private: |
|||
std::shared_ptr<Type const> m_actualType; |
|||
}; |
|||
|
|||
|
|||
} |
|||
} |
@ -0,0 +1,35 @@ |
|||
ContractDefinition = 'contract' Identifier '{' ContractPart* '}' |
|||
ContractPart = VariableDeclaration ';' | StructDefinition | |
|||
FunctionDefinition | 'public:' | 'private:' |
|||
|
|||
StructDefinition = 'struct' Identifier '{' |
|||
( VariableDeclaration (';' VariableDeclaration)* )? '} |
|||
|
|||
FunctionDefinition = 'function' Identifier ParameterList 'const'? |
|||
( 'returns' ParameterList )? Block |
|||
ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')' |
|||
// semantic restriction: mappings and structs (recursively) containing mappings |
|||
// are not allowed in argument lists |
|||
VariableDeclaration = TypeName Identifier |
|||
TypeName = ElementaryTypeName | Identifier | Mapping |
|||
Mapping = 'mapping' '(' ElementaryTypeName '=>' TypeName ')' |
|||
|
|||
Block = '{' Statement* '}' |
|||
Statement = IfStatement | WhileStatement | Block | |
|||
( Continue | Break | Return | VariableDefinition | Expression ) ';' |
|||
|
|||
IfStatement = 'if' '(' Expression ')' Statement ( 'else' Statement )? |
|||
WhileStatement = 'while' '(' Expression ')' Statement |
|||
Continue = 'continue' ';' |
|||
Break = 'break' ';' |
|||
Return = 'return' Expression? ';' |
|||
VariableDefinition = VariableDeclaration ( = Expression )? ';' |
|||
|
|||
Expression = Assignment | UnaryOperation | BinaryOperation | FunctionCall | IndexAccess | |
|||
MemberAccess | PrimaryExpression |
|||
// The expression syntax is actually much more complicated |
|||
Assignment = Expression (AssignmentOp Expression) |
|||
FunctionCall = Expression '(' ( Expression ( ',' Expression )* ) ')' |
|||
MemberAccess = Expression '.' Identifier |
|||
IndexAccess = Expression '[' Expresison ']' |
|||
PrimaryExpression = Identifier | NumberLiteral | StringLiteral | ElementaryTypeName | '(' Expression ')' |
@ -0,0 +1,31 @@ |
|||
cmake_policy(SET CMP0015 NEW) |
|||
|
|||
aux_source_directory(. SRC_LIST) |
|||
|
|||
include_directories(..) |
|||
|
|||
set(EXECUTABLE solc) |
|||
|
|||
add_executable(${EXECUTABLE} ${SRC_LIST}) |
|||
|
|||
target_link_libraries(${EXECUTABLE} solidity) |
|||
target_link_libraries(${EXECUTABLE} devcore) |
|||
|
|||
if ("${TARGET_PLATFORM}" STREQUAL "w64") |
|||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") |
|||
target_link_libraries(${EXECUTABLE} gcc) |
|||
target_link_libraries(${EXECUTABLE} gdi32) |
|||
target_link_libraries(${EXECUTABLE} ws2_32) |
|||
target_link_libraries(${EXECUTABLE} mswsock) |
|||
target_link_libraries(${EXECUTABLE} shlwapi) |
|||
target_link_libraries(${EXECUTABLE} iphlpapi) |
|||
target_link_libraries(${EXECUTABLE} boost_thread_win32-mt-s) |
|||
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS) |
|||
elseif (UNIX) |
|||
else () |
|||
find_package(Threads REQUIRED) |
|||
target_link_libraries(${EXECUTABLE} ${CMAKE_THREAD_LIBS_INIT}) |
|||
endif () |
|||
|
|||
install( TARGETS ${EXECUTABLE} DESTINATION bin ) |
|||
|
@ -0,0 +1,84 @@ |
|||
|
|||
#include <string> |
|||
#include <iostream> |
|||
|
|||
#include <libdevcore/Common.h> |
|||
#include <libdevcore/CommonData.h> |
|||
#include <libdevcore/CommonIO.h> |
|||
#include <libsolidity/Scanner.h> |
|||
#include <libsolidity/Parser.h> |
|||
#include <libsolidity/ASTPrinter.h> |
|||
#include <libsolidity/NameAndTypeResolver.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
|
|||
ASTPointer<ContractDefinition> parseAST(std::string const& _source) |
|||
{ |
|||
ASTPointer<Scanner> scanner = std::make_shared<Scanner>(CharStream(_source)); |
|||
Parser parser; |
|||
return parser.parse(scanner); |
|||
} |
|||
|
|||
} |
|||
} // end namespaces
|
|||
|
|||
void help() |
|||
{ |
|||
std::cout |
|||
<< "Usage solc [OPTIONS] <file>" << std::endl |
|||
<< "Options:" << std::endl |
|||
<< " -h,--help Show this help message and exit." << std::endl |
|||
<< " -V,--version Show the version and exit." << std::endl; |
|||
exit(0); |
|||
} |
|||
|
|||
void version() |
|||
{ |
|||
std::cout |
|||
<< "solc, the solidity complier commandline interface " << dev::Version << std::endl |
|||
<< " by Christian <c@ethdev.com>, (c) 2014." << std::endl |
|||
<< "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << std::endl; |
|||
exit(0); |
|||
} |
|||
|
|||
int main(int argc, char** argv) |
|||
{ |
|||
std::string infile; |
|||
for (int i = 1; i < argc; ++i) |
|||
{ |
|||
std::string arg = argv[i]; |
|||
if (arg == "-h" || arg == "--help") |
|||
help(); |
|||
else if (arg == "-V" || arg == "--version") |
|||
version(); |
|||
else |
|||
infile = argv[i]; |
|||
} |
|||
std::string src; |
|||
if (infile.empty()) |
|||
{ |
|||
std::string s; |
|||
while (!std::cin.eof()) |
|||
{ |
|||
getline(std::cin, s); |
|||
src.append(s); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
src = dev::asString(dev::contents(infile)); |
|||
} |
|||
std::cout << "Parsing..." << std::endl; |
|||
// @todo catch exception
|
|||
dev::solidity::ASTPointer<dev::solidity::ContractDefinition> ast = dev::solidity::parseAST(src); |
|||
std::cout << "Syntax tree for the contract:" << std::endl; |
|||
dev::solidity::ASTPrinter printer(ast, src); |
|||
printer.print(std::cout); |
|||
std::cout << "Resolving identifiers..." << std::endl; |
|||
dev::solidity::NameAndTypeResolver resolver; |
|||
resolver.resolveNamesAndTypes(*ast.get()); |
|||
return 0; |
|||
} |
@ -0,0 +1,178 @@ |
|||
/*
|
|||
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 name and type resolution of the solidity parser. |
|||
*/ |
|||
|
|||
#include <string> |
|||
|
|||
#include <libdevcore/Log.h> |
|||
#include <libsolidity/Scanner.h> |
|||
#include <libsolidity/Parser.h> |
|||
#include <libsolidity/NameAndTypeResolver.h> |
|||
#include <libsolidity/Exceptions.h> |
|||
#include <boost/test/unit_test.hpp> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
namespace test |
|||
{ |
|||
|
|||
namespace |
|||
{ |
|||
void parseTextAndResolveNames(const std::string& _source) |
|||
{ |
|||
Parser parser; |
|||
ASTPointer<ContractDefinition> contract = parser.parse( |
|||
std::make_shared<Scanner>(CharStream(_source))); |
|||
NameAndTypeResolver resolver; |
|||
resolver.resolveNamesAndTypes(*contract); |
|||
} |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_SUITE(SolidityNameAndTypeResolution) |
|||
|
|||
BOOST_AUTO_TEST_CASE(smoke_test) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" uint256 stateVariable1;\n" |
|||
" function fun(uint256 arg1) { var x; uint256 y; }" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(double_stateVariable_declaration) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" uint256 variable;\n" |
|||
" uint128 variable;\n" |
|||
"}\n"; |
|||
BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(double_function_declaration) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function fun() { var x; }\n" |
|||
" function fun() { var x; }\n" |
|||
"}\n"; |
|||
BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(double_variable_declaration) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function f() { uint256 x; if (true) { uint256 x; } }\n" |
|||
"}\n"; |
|||
BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(name_shadowing) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" uint256 variable;\n" |
|||
" function f() { uint32 variable ; }" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(name_references) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" uint256 variable;\n" |
|||
" function f(uint256 arg) returns (uint out) { f(variable); test; out; }" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(undeclared_name) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" uint256 variable;\n" |
|||
" function f(uint256 arg) { f(notfound); }" |
|||
"}\n"; |
|||
BOOST_CHECK_THROW(parseTextAndResolveNames(text), DeclarationError); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(reference_to_later_declaration) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function g() { f(); }" |
|||
" function f() { }" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(type_inference_smoke_test) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function f(uint256 arg1, uint32 arg2) returns (bool ret) { var x = arg1 + arg2 == 8; ret = x; }" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(type_checking_return) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function f() returns (bool r) { return 1 >= 2; }" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(type_checking_return_wrong_number) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function f() returns (bool r1, bool r2) { return 1 >= 2; }" |
|||
"}\n"; |
|||
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(type_checking_return_wrong_type) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function f() returns (uint256 r) { return 1 >= 2; }" |
|||
"}\n"; |
|||
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(type_checking_function_call) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function f() returns (bool r) { return g(12, true) == 3; }\n" |
|||
" function g(uint256 a, bool b) returns (uint256 r) { }\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function f() returns (int256 r) { var x = int256(uint32(2)); return x; }" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_SUITE_END() |
|||
|
|||
} |
|||
} |
|||
} // end namespaces
|
|||
|
@ -0,0 +1,221 @@ |
|||
/*
|
|||
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 <libdevcore/Log.h> |
|||
#include <libsolidity/Scanner.h> |
|||
#include <libsolidity/Parser.h> |
|||
#include <libsolidity/Exceptions.h> |
|||
#include <boost/test/unit_test.hpp> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
namespace test |
|||
{ |
|||
|
|||
namespace |
|||
{ |
|||
ASTPointer<ASTNode> parseText(const std::string& _source) |
|||
{ |
|||
Parser parser; |
|||
return parser.parse(std::make_shared<Scanner>(CharStream(_source))); |
|||
} |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_SUITE(SolidityParser) |
|||
|
|||
BOOST_AUTO_TEST_CASE(smoke_test) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" uint256 stateVariable1;\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(missing_variable_name_in_declaration) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" uint256 ;\n" |
|||
"}\n"; |
|||
BOOST_CHECK_THROW(parseText(text), ParserError); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(empty_function) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" uint256 stateVar;\n" |
|||
" function functionName(hash160 arg1, address addr) const\n" |
|||
" returns (int id)\n" |
|||
" { }\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(no_function_params) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" uint256 stateVar;\n" |
|||
" function functionName() {}\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(single_function_param) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" uint256 stateVar;\n" |
|||
" function functionName(hash hashin) returns (hash hashout) {}\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(struct_definition) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" uint256 stateVar;\n" |
|||
" struct MyStructName {\n" |
|||
" address addr;\n" |
|||
" uint256 count;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(mapping) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" mapping(address => string) names;\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(mapping_in_struct) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" struct test_struct {\n" |
|||
" address addr;\n" |
|||
" uint256 count;\n" |
|||
" mapping(hash => test_struct) self_reference;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(mapping_to_mapping_in_struct) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" struct test_struct {\n" |
|||
" address addr;\n" |
|||
" mapping (uint64 => mapping (hash => uint)) complex_mapping;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(variable_definition) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function fun(uint256 a) {\n" |
|||
" var b;\n" |
|||
" uint256 c;\n" |
|||
" mapping(address=>hash) d;\n" |
|||
" customtype varname;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(variable_definition_with_initialization) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function fun(uint256 a) {\n" |
|||
" var b = 2;\n" |
|||
" uint256 c = 0x87;\n" |
|||
" mapping(address=>hash) d;\n" |
|||
" string name = \"Solidity\";" |
|||
" customtype varname;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(operator_expression) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function fun(uint256 a) {\n" |
|||
" uint256 x = (1 + 4) || false && (1 - 12) + -9;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(complex_expression) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function fun(uint256 a) {\n" |
|||
" uint256 x = (1 + 4).member(++67)[a/=9] || true;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(while_loop) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function fun(uint256 a) {\n" |
|||
" uint256 x = (1 + 4).member(++67) || true;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(if_statement) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function fun(uint256 a) {\n" |
|||
" if (a >= 8) return 2; else { var b = 7; }\n" |
|||
" }\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(else_if_statement) |
|||
{ |
|||
char const* text = "contract test {\n" |
|||
" function fun(uint256 a) returns (address b) {\n" |
|||
" if (a < 0) b = 0x67; else if (a == 0) b = 0x12; else b = 0x78;\n" |
|||
" }\n" |
|||
"}\n"; |
|||
BOOST_CHECK_NO_THROW(parseText(text)); |
|||
} |
|||
|
|||
|
|||
|
|||
BOOST_AUTO_TEST_SUITE_END() |
|||
|
|||
} |
|||
} |
|||
} // end namespaces
|
|||
|
@ -0,0 +1,143 @@ |
|||
/*
|
|||
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 scanner. |
|||
*/ |
|||
|
|||
#include <libsolidity/Scanner.h> |
|||
#include <boost/test/unit_test.hpp> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace solidity |
|||
{ |
|||
namespace test |
|||
{ |
|||
|
|||
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::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_LITERAL); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "string1"); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::COMMA); |
|||
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"); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(string_escapes) |
|||
{ |
|||
Scanner scanner(CharStream(" { \"a\\x61\"")); |
|||
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::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::IDENTIFIER); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), ""); |
|||
// TODO recovery from illegal tokens should be improved
|
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::ILLEGAL); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(hex_numbers) |
|||
{ |
|||
Scanner scanner(CharStream("var x = 0x765432536763762734623472346;")); |
|||
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); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLiteral(), "0x765432536763762734623472346"); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(locations) |
|||
{ |
|||
Scanner scanner(CharStream("function_identifier has ; -0x743/*comment*/\n ident //comment")); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentToken(), Token::IDENTIFIER); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 0); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 19); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 20); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 23); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::SEMICOLON); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 24); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 25); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::SUB); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 26); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 27); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::NUMBER); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 27); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 32); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLocation().start, 45); |
|||
BOOST_CHECK_EQUAL(scanner.getCurrentLocation().end, 50); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::EOS); |
|||
} |
|||
|
|||
BOOST_AUTO_TEST_CASE(ambiguities) |
|||
{ |
|||
// test scanning of some operators which need look-ahead
|
|||
Scanner scanner(CharStream("<=""<""+ +=a++ =>""<<")); |
|||
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); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::IDENTIFIER); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::INC); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::ARROW); |
|||
BOOST_CHECK_EQUAL(scanner.next(), Token::SHL); |
|||
} |
|||
|
|||
|
|||
BOOST_AUTO_TEST_SUITE_END() |
|||
|
|||
} |
|||
} |
|||
} // end namespaces
|
Loading…
Reference in new issue