Browse Source

Merge branch 'develop' into mk_jsonrpc

cl-refactor
Marek Kotewicz 10 years ago
parent
commit
0e87d5cb5b
  1. 2
      CMakeLists.txt
  2. 13
      exp/main.cpp
  3. 483
      libsolidity/AST.cpp
  4. 533
      libsolidity/AST.h
  5. 78
      libsolidity/ASTForward.h
  6. 424
      libsolidity/ASTPrinter.cpp
  7. 116
      libsolidity/ASTPrinter.h
  8. 104
      libsolidity/ASTVisitor.h
  9. 45
      libsolidity/BaseTypes.h
  10. 49
      libsolidity/CMakeLists.txt
  11. 37
      libsolidity/Exceptions.h
  12. 198
      libsolidity/NameAndTypeResolver.cpp
  13. 100
      libsolidity/NameAndTypeResolver.h
  14. 553
      libsolidity/Parser.cpp
  15. 83
      libsolidity/Parser.h
  16. 702
      libsolidity/Scanner.cpp
  17. 204
      libsolidity/Scanner.h
  18. 50
      libsolidity/Scope.cpp
  19. 51
      libsolidity/Scope.h
  20. 84
      libsolidity/Token.cpp
  21. 334
      libsolidity/Token.h
  22. 164
      libsolidity/Types.cpp
  23. 180
      libsolidity/Types.h
  24. 35
      libsolidity/grammar.txt
  25. 24
      libwhisper/Common.cpp
  26. 62
      libwhisper/Common.h
  27. 30
      libwhisper/Interface.h
  28. 10
      libwhisper/Message.cpp
  29. 2
      libwhisper/Message.h
  30. 4
      libwhisper/WhisperHost.cpp
  31. 3
      libwhisper/WhisperHost.h
  32. 31
      solc/CMakeLists.txt
  33. 84
      solc/main.cpp
  34. 1
      test/CMakeLists.txt
  35. 178
      test/solidityNameAndTypeResolution.cpp
  36. 221
      test/solidityParser.cpp
  37. 143
      test/solidityScanner.cpp

2
CMakeLists.txt

@ -109,6 +109,7 @@ add_subdirectory(libdevcore)
add_subdirectory(libevmface)
add_subdirectory(liblll)
add_subdirectory(libserpent)
add_subdirectory(libsolidity)
if(NOT APPLE)
if(PYTHON_LS)
add_subdirectory(libpyserpent)
@ -116,6 +117,7 @@ if(NOT APPLE)
endif()
add_subdirectory(lllc)
add_subdirectory(solc)
add_subdirectory(sc)
add_subdirectory(libethrpc)
if (NOT LANGUAGES)

13
exp/main.cpp

@ -102,17 +102,20 @@ int main(int argc, char** argv)
if (!remoteHost.empty())
ph.connect(remoteHost, remotePort);
/// Only interested in the packet if the lowest bit is 1
auto w = wh->installWatch(TopicFilter(TopicMasks({{Topic("0000000000000000000000000000000000000000000000000000000000000001"), Topic("0000000000000000000000000000000000000000000000000000000000000001")}})));
/// Only interested in odd packets
auto w = wh->installWatch(BuildTopicMask()("odd"));
KeyPair us = KeyPair::create();
for (int i = 0; ; ++i)
{
wh->sendRaw(RLPStream().append(i * i).out(), Topic(u256(i)));
wh->post(us.sec(), RLPStream().append(i * i).out(), BuildTopic(i)(i % 2 ? "odd" : "even"));
for (auto i: wh->checkWatch(w))
{
auto p = wh->envelope(i).open().payload();
cnote << "New message:" << RLP(p).toInt<unsigned>();
Message msg = wh->envelope(i).open();
cnote << "New message from:" << msg.from().abridged() << RLP(msg.payload()).toInt<unsigned>();
}
this_thread::sleep_for(chrono::seconds(1));
}
return 0;
}

483
libsolidity/AST.cpp

@ -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);
}
}
}

533
libsolidity/AST.h

@ -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;
};
/// @}
}
}

78
libsolidity/ASTForward.h

@ -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;
}
}

424
libsolidity/ASTPrinter.cpp

@ -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';
}
}
}

116
libsolidity/ASTPrinter.h

@ -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;
};
}
}

104
libsolidity/ASTVisitor.h

@ -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&) { }
};
}
}

45
libsolidity/BaseTypes.h

@ -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;
};
}
}

49
libsolidity/CMakeLists.txt

@ -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} )

37
libsolidity/Exceptions.h

@ -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 {};
}
}

198
libsolidity/NameAndTypeResolver.cpp

@ -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;
}
}
}

100
libsolidity/NameAndTypeResolver.h

@ -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;
};
}
}

553
libsolidity/Parser.cpp

@ -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()));
}
}
}

83
libsolidity/Parser.h

@ -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;
};
}
}

702
libsolidity/Scanner.cpp

@ -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);
}
}
}

204
libsolidity/Scanner.h

@ -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;
};
}
}

50
libsolidity/Scope.cpp

@ -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;
}
}
}

51
libsolidity/Scope.h

@ -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;
};
}
}

84
libsolidity/Token.cpp

@ -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
}
}

334
libsolidity/Token.h

@ -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];
};
}
}

164
libsolidity/Types.cpp

@ -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;
}
}
}

180
libsolidity/Types.h

@ -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;
};
}
}

35
libsolidity/grammar.txt

@ -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 ')'

24
libwhisper/Common.cpp

@ -21,8 +21,32 @@
#include "Common.h"
#include <libdevcrypto/SHA3.h>
using namespace std;
using namespace dev;
using namespace dev::p2p;
using namespace dev::shh;
BuildTopic& BuildTopic::shiftBytes(bytes const& _b)
{
m_parts.push_back(dev::sha3(_b));
return *this;
}
h256 TopicFilter::sha3() const
{
RLPStream s;
fillStream(s);
return dev::sha3(s.out());
}
TopicMask BuildTopicMask::toTopicMask() const
{
TopicMask ret;
for (auto i = 0; i < 32; ++i)
{
ret.first[i] = m_parts[i * m_parts.size() / 32][i];
ret.second[i] = m_parts[i * m_parts.size() / 32] ? 255 : 0;
}
return ret;
}

62
libwhisper/Common.h

@ -25,7 +25,7 @@
#include <chrono>
#include <libdevcore/Common.h>
#include <libdevcore/Log.h>
#include <libethcore/CommonEth.h>
#include <libdevcore/RLP.h>
#include <libp2p/Capability.h>
namespace dev
@ -48,6 +48,8 @@ class WhisperHost;
class WhisperPeer;
class Whisper;
class Envelope;
enum WhisperPacket
{
StatusPacket = 0,
@ -59,5 +61,63 @@ enum WhisperPacket
using Topic = h256;
class BuildTopic
{
public:
template <class T> BuildTopic(T const& _t) { shift(_t); }
template <class T> BuildTopic& shift(T const& _r) { return shiftBytes(RLPStream().append(_r).out()); }
template <class T> BuildTopic& operator()(T const& _t) { return shift(_t); }
BuildTopic& shiftRaw(h256 const& _part) { m_parts.push_back(_part); return *this; }
operator Topic() const { return toTopic(); }
Topic toTopic() const { Topic ret; for (auto i = 0; i < 32; ++i) ret[i] = m_parts[i * m_parts.size() / 32][i]; return ret; }
protected:
BuildTopic() {}
BuildTopic& shiftBytes(bytes const& _b);
h256s m_parts;
};
using TopicMask = std::pair<Topic, Topic>;
using TopicMasks = std::vector<TopicMask>;
class TopicFilter
{
public:
TopicFilter() {}
TopicFilter(TopicMask const& _m): m_topicMasks(1, _m) {}
TopicFilter(TopicMasks const& _m): m_topicMasks(_m) {}
TopicFilter(RLP const& _r): m_topicMasks((TopicMasks)_r) {}
void fillStream(RLPStream& _s) const { _s << m_topicMasks; }
h256 sha3() const;
bool matches(Envelope const& _m) const;
private:
TopicMasks m_topicMasks;
};
class BuildTopicMask: BuildTopic
{
public:
BuildTopicMask() { shift(); }
template <class T> BuildTopicMask(T const& _t) { shift(_t); }
template <class T> BuildTopicMask& shift(T const& _r) { BuildTopic::shift(_r); return *this; }
BuildTopicMask& shiftRaw(h256 const& _h) { BuildTopic::shiftRaw(_h); return *this; }
BuildTopic& shift() { m_parts.push_back(h256()); return *this; }
BuildTopicMask& operator()() { shift(); return *this; }
template <class T> BuildTopicMask& operator()(T const& _t) { shift(_t); return *this; }
operator TopicMask() const { return toTopicMask(); }
TopicMask toTopicMask() const;
};
}
}

30
libwhisper/Interface.h

@ -43,25 +43,6 @@ namespace shh
Topic mask;
};*/
using TopicMask = std::pair<Topic, Topic>;
using TopicMasks = std::vector<TopicMask>;
class TopicFilter
{
public:
TopicFilter() {}
TopicFilter(TopicMasks const& _m): m_topicMasks(_m) {}
TopicFilter(RLP const& _r): m_topicMasks((TopicMasks)_r) {}
void fillStream(RLPStream& _s) const { _s << m_topicMasks; }
h256 sha3() const { RLPStream s; fillStream(s); return dev::sha3(s.out()); }
bool matches(Envelope const& _m) const;
private:
TopicMasks m_topicMasks;
};
struct InstalledFilter
{
InstalledFilter(TopicFilter const& _f): filter(_f) {}
@ -86,18 +67,19 @@ public:
virtual void inject(Envelope const& _m, WhisperPeer* _from = nullptr) = 0;
unsigned installWatch(TopicMask const& _mask) { return installWatch(TopicFilter(_mask)); }
virtual unsigned installWatch(TopicFilter const& _filter) = 0;
virtual unsigned installWatch(h256 _filterId) = 0;
virtual unsigned installWatchOnId(h256 _filterId) = 0;
virtual void uninstallWatch(unsigned _watchId) = 0;
virtual h256s peekWatch(unsigned _watchId) const = 0;
virtual h256s checkWatch(unsigned _watchId) = 0;
virtual Envelope envelope(h256 _m) const = 0;
void sendRaw(bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_topic, _ttl, _workToProve)); }
void sendRaw(Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_to, _topic, _ttl, _workToProve)); }
void sendRaw(Secret _from, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _topic, _ttl, _workToProve)); }
void sendRaw(Secret _from, Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _to, _topic, _ttl, _workToProve)); }
void post(bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_topic, _ttl, _workToProve)); }
void post(Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_to, _topic, _ttl, _workToProve)); }
void post(Secret _from, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _topic, _ttl, _workToProve)); }
void post(Secret _from, Public _to, bytes const& _payload, Topic _topic, unsigned _ttl = 50, unsigned _workToProve = 50) { inject(Message(_payload).seal(_from, _to, _topic, _ttl, _workToProve)); }
};
struct WatshhChannel: public dev::LogChannel { static const char* name() { return "shh"; } static const int verbosity = 1; };

10
libwhisper/Message.cpp

@ -32,7 +32,8 @@ Message::Message(Envelope const& _e, Secret const& _s)
{
bytes b;
if (_s)
decrypt(_s, &(_e.data()), b);
if (!decrypt(_s, &(_e.data()), b))
return;
populate(_s ? b : _e.data());
m_to = KeyPair(_s).pub();
}
@ -49,9 +50,10 @@ void Message::populate(bytes const& _data)
byte flags = _data[0];
if (!!(flags & ContainsSignature) && _data.size() > sizeof(Signature) + 1) // has a signature
{
bytesConstRef payload = bytesConstRef(&_data).cropped(sizeof(Signature) + 1);
bytesConstRef payload = bytesConstRef(&_data).cropped(1, _data.size() - sizeof(Signature) - 1);
h256 h = sha3(payload);
m_from = recover(*(Signature const*)&(_data[1]), h);
Signature const& sig = *(Signature const*)&(_data[1 + payload.size()]);
m_from = recover(sig, h);
m_payload = payload.toBytes();
}
else
@ -71,6 +73,8 @@ Envelope Message::seal(Secret _from, Topic const& _topic, unsigned _ttl, unsigne
input.resize(1 + m_payload.size() + sizeof(Signature));
input[0] |= ContainsSignature;
*(Signature*)&(input[1 + m_payload.size()]) = sign(_from, sha3(m_payload));
// If this fails, the something is wrong with the sign-recover round-trip.
assert(recover(*(Signature*)&(input[1 + m_payload.size()]), sha3(m_payload)) == KeyPair(_from).pub());
}
if (m_to)

2
libwhisper/Message.h

@ -84,7 +84,7 @@ private:
enum /*Message Flags*/
{
ContainsSignature = 0
ContainsSignature = 1
};
/// An (unencrypted) message, constructed from the combination of an Envelope, and, potentially,

4
libwhisper/WhisperHost.cpp

@ -87,7 +87,7 @@ void WhisperHost::noteChanged(h256 _messageHash, h256 _filter)
}
}
unsigned WhisperHost::installWatch(h256 _h)
unsigned WhisperHost::installWatchOnId(h256 _h)
{
auto ret = m_watches.size() ? m_watches.rbegin()->first + 1 : 0;
m_watches[ret] = ClientWatch(_h);
@ -104,7 +104,7 @@ unsigned WhisperHost::installWatch(shh::TopicFilter const& _f)
if (!m_filters.count(h))
m_filters.insert(make_pair(h, _f));
return installWatch(h);
return installWatchOnId(h);
}
void WhisperHost::uninstallWatch(unsigned _i)

3
libwhisper/WhisperHost.h

@ -50,8 +50,9 @@ public:
virtual void inject(Envelope const& _e, WhisperPeer* _from = nullptr);
using Interface::installWatch;
virtual unsigned installWatch(TopicFilter const& _filter);
virtual unsigned installWatch(h256 _filterId);
virtual unsigned installWatchOnId(h256 _filterId);
virtual void uninstallWatch(unsigned _watchId);
virtual h256s peekWatch(unsigned _watchId) const { dev::Guard l(m_filterLock); try { return m_watches.at(_watchId).changes; } catch (...) { return h256s(); } }
virtual h256s checkWatch(unsigned _watchId) { dev::Guard l(m_filterLock); h256s ret; try { ret = m_watches.at(_watchId).changes; m_watches.at(_watchId).changes.clear(); } catch (...) {} return ret; }

31
solc/CMakeLists.txt

@ -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 )

84
solc/main.cpp

@ -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;
}

1
test/CMakeLists.txt

@ -15,6 +15,7 @@ target_link_libraries(testeth ethereum)
target_link_libraries(testeth ethcore)
target_link_libraries(testeth secp256k1)
target_link_libraries(testeth gmp)
target_link_libraries(testeth solidity)
target_link_libraries(testeth ${CRYPTOPP_LS})
target_link_libraries(testeth webthree)
target_link_libraries(testeth ethrpc)

178
test/solidityNameAndTypeResolution.cpp

@ -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

221
test/solidityParser.cpp

@ -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

143
test/solidityScanner.cpp

@ -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…
Cancel
Save