Browse Source

Corrected coding style.

cl-refactor
Christian 11 years ago
parent
commit
68a85f4f80
  1. 117
      libsolidity/AST.cpp
  2. 15
      libsolidity/AST.h
  3. 9
      libsolidity/ASTForward.h
  4. 24
      libsolidity/ASTPrinter.cpp
  5. 12
      libsolidity/ASTPrinter.h
  6. 12
      libsolidity/ASTVisitor.h
  7. 12
      libsolidity/BaseTypes.h
  8. 9
      libsolidity/Exceptions.h
  9. 22
      libsolidity/NameAndTypeResolver.cpp
  10. 9
      libsolidity/NameAndTypeResolver.h
  11. 188
      libsolidity/Parser.cpp
  12. 9
      libsolidity/Parser.h
  13. 327
      libsolidity/Scanner.cpp
  14. 96
      libsolidity/Scanner.h
  15. 11
      libsolidity/Scope.cpp
  16. 9
      libsolidity/Scope.h
  17. 21
      libsolidity/Token.cpp
  18. 170
      libsolidity/Token.h
  19. 40
      libsolidity/Types.cpp
  20. 36
      libsolidity/Types.h
  21. 16
      solc/main.cpp
  22. 22
      test/solidityNameAndTypeResolution.cpp
  23. 22
      test/solidityParser.cpp
  24. 13
      test/solidityScanner.cpp

117
libsolidity/AST.cpp

@ -26,12 +26,15 @@
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
void ContractDefinition::accept(ASTVisitor& _visitor) void ContractDefinition::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
listAccept(m_definedStructs, _visitor); listAccept(m_definedStructs, _visitor);
listAccept(m_stateVariables, _visitor); listAccept(m_stateVariables, _visitor);
listAccept(m_definedFunctions, _visitor); listAccept(m_definedFunctions, _visitor);
@ -41,7 +44,8 @@ void ContractDefinition::accept(ASTVisitor& _visitor)
void StructDefinition::accept(ASTVisitor& _visitor) void StructDefinition::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
listAccept(m_members, _visitor); listAccept(m_members, _visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
@ -49,7 +53,8 @@ void StructDefinition::accept(ASTVisitor& _visitor)
void ParameterList::accept(ASTVisitor& _visitor) void ParameterList::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
listAccept(m_parameters, _visitor); listAccept(m_parameters, _visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
@ -57,7 +62,8 @@ void ParameterList::accept(ASTVisitor& _visitor)
void FunctionDefinition::accept(ASTVisitor& _visitor) void FunctionDefinition::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
m_parameters->accept(_visitor); m_parameters->accept(_visitor);
if (m_returnParameters) if (m_returnParameters)
m_returnParameters->accept(_visitor); m_returnParameters->accept(_visitor);
@ -68,7 +74,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor)
void VariableDeclaration::accept(ASTVisitor& _visitor) void VariableDeclaration::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
if (m_typeName) if (m_typeName)
m_typeName->accept(_visitor); m_typeName->accept(_visitor);
} }
@ -95,7 +102,8 @@ void UserDefinedTypeName::accept(ASTVisitor& _visitor)
void Mapping::accept(ASTVisitor& _visitor) void Mapping::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
m_keyType->accept(_visitor); m_keyType->accept(_visitor);
m_valueType->accept(_visitor); m_valueType->accept(_visitor);
} }
@ -110,7 +118,8 @@ void Statement::accept(ASTVisitor& _visitor)
void Block::accept(ASTVisitor& _visitor) void Block::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
listAccept(m_statements, _visitor); listAccept(m_statements, _visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
@ -118,7 +127,8 @@ void Block::accept(ASTVisitor& _visitor)
void IfStatement::accept(ASTVisitor& _visitor) void IfStatement::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
m_condition->accept(_visitor); m_condition->accept(_visitor);
m_trueBody->accept(_visitor); m_trueBody->accept(_visitor);
if (m_falseBody) if (m_falseBody)
@ -135,7 +145,8 @@ void BreakableStatement::accept(ASTVisitor& _visitor)
void WhileStatement::accept(ASTVisitor& _visitor) void WhileStatement::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
m_condition->accept(_visitor); m_condition->accept(_visitor);
m_body->accept(_visitor); m_body->accept(_visitor);
} }
@ -156,7 +167,8 @@ void Break::accept(ASTVisitor& _visitor)
void Return::accept(ASTVisitor& _visitor) void Return::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
if (m_expression) if (m_expression)
m_expression->accept(_visitor); m_expression->accept(_visitor);
} }
@ -165,7 +177,8 @@ void Return::accept(ASTVisitor& _visitor)
void VariableDefinition::accept(ASTVisitor& _visitor) void VariableDefinition::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
m_variable->accept(_visitor); m_variable->accept(_visitor);
if (m_value) if (m_value)
m_value->accept(_visitor); m_value->accept(_visitor);
@ -175,7 +188,8 @@ void VariableDefinition::accept(ASTVisitor& _visitor)
void Assignment::accept(ASTVisitor& _visitor) void Assignment::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
m_leftHandSide->accept(_visitor); m_leftHandSide->accept(_visitor);
m_rightHandSide->accept(_visitor); m_rightHandSide->accept(_visitor);
} }
@ -184,7 +198,8 @@ void Assignment::accept(ASTVisitor& _visitor)
void UnaryOperation::accept(ASTVisitor& _visitor) void UnaryOperation::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
m_subExpression->accept(_visitor); m_subExpression->accept(_visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
@ -192,7 +207,8 @@ void UnaryOperation::accept(ASTVisitor& _visitor)
void BinaryOperation::accept(ASTVisitor& _visitor) void BinaryOperation::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
m_left->accept(_visitor); m_left->accept(_visitor);
m_right->accept(_visitor); m_right->accept(_visitor);
} }
@ -201,7 +217,8 @@ void BinaryOperation::accept(ASTVisitor& _visitor)
void FunctionCall::accept(ASTVisitor& _visitor) void FunctionCall::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
m_expression->accept(_visitor); m_expression->accept(_visitor);
listAccept(m_arguments, _visitor); listAccept(m_arguments, _visitor);
} }
@ -210,7 +227,8 @@ void FunctionCall::accept(ASTVisitor& _visitor)
void MemberAccess::accept(ASTVisitor& _visitor) void MemberAccess::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
m_expression->accept(_visitor); m_expression->accept(_visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
@ -218,7 +236,8 @@ void MemberAccess::accept(ASTVisitor& _visitor)
void IndexAccess::accept(ASTVisitor& _visitor) void IndexAccess::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this))
{
m_base->accept(_visitor); m_base->accept(_visitor);
m_index->accept(_visitor); m_index->accept(_visitor);
} }
@ -253,7 +272,7 @@ void Statement::expectType(Expression& _expression, const Type& _expectedType)
ptr<Type> Block::checkTypeRequirements() ptr<Type> Block::checkTypeRequirements()
{ {
for (ptr<Statement> const& statement : m_statements) for (ptr<Statement> const & statement : m_statements)
statement->checkTypeRequirements(); statement->checkTypeRequirements();
return ptr<Type>(); return ptr<Type>();
} }
@ -292,7 +311,6 @@ ptr<Type> Return::checkTypeRequirements()
"declaration.")); "declaration."));
// this could later be changed such that the paramaters type is an anonymous struct type, // this could later be changed such that the paramaters type is an anonymous struct type,
// but for now, we only allow one return parameter // but for now, we only allow one return parameter
expectType(*m_expression, *m_returnParameters->getParameters().front()->getType()); expectType(*m_expression, *m_returnParameters->getParameters().front()->getType());
return ptr<Type>(); return ptr<Type>();
} }
@ -303,10 +321,14 @@ ptr<Type> VariableDefinition::checkTypeRequirements()
// setsthe type. // setsthe type.
// Note that assignments before the first declaration are legal because of the special scoping // Note that assignments before the first declaration are legal because of the special scoping
// rules inherited from JavaScript. // rules inherited from JavaScript.
if (m_value) { if (m_value)
if (m_variable->getType()) { {
if (m_variable->getType())
{
expectType(*m_value, *m_variable->getType()); expectType(*m_value, *m_variable->getType());
} else { }
else
{
// no type declared and no previous assignment, infer the type // no type declared and no previous assignment, infer the type
m_variable->setType(m_value->checkTypeRequirements()); m_variable->setType(m_value->checkTypeRequirements());
} }
@ -320,7 +342,8 @@ ptr<Type> Assignment::checkTypeRequirements()
// add a feature to the type system to check that // add a feature to the type system to check that
expectType(*m_rightHandSide, *m_leftHandSide->checkTypeRequirements()); expectType(*m_rightHandSide, *m_leftHandSide->checkTypeRequirements());
m_type = m_leftHandSide->getType(); m_type = m_leftHandSide->getType();
if (m_assigmentOperator != Token::ASSIGN) { if (m_assigmentOperator != Token::ASSIGN)
{
// complex assignment // complex assignment
if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator))) if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator)))
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type.")); BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type."));
@ -341,18 +364,19 @@ ptr<Type> BinaryOperation::checkTypeRequirements()
{ {
m_right->checkTypeRequirements(); m_right->checkTypeRequirements();
m_left->checkTypeRequirements(); m_left->checkTypeRequirements();
if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType())) if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType()))
m_commonType = m_left->getType(); m_commonType = m_left->getType();
else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType())) else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType()))
m_commonType = m_right->getType(); m_commonType = m_right->getType();
else else
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("No common type found in binary operation.")); BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("No common type found in binary operation."));
if (Token::isCompareOp(m_operator))
if (Token::IsCompareOp(m_operator)) { {
m_type = std::make_shared<BoolType>(); m_type = std::make_shared<BoolType>();
} else { }
BOOST_ASSERT(Token::IsBinaryOp(m_operator)); else
{
BOOST_ASSERT(Token::isBinaryOp(m_operator));
m_type = m_commonType; m_type = m_commonType;
if (!m_commonType->acceptsBinaryOperator(m_operator)) if (!m_commonType->acceptsBinaryOperator(m_operator))
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type.")); BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Operator not compatible with type."));
@ -363,12 +387,12 @@ ptr<Type> BinaryOperation::checkTypeRequirements()
ptr<Type> FunctionCall::checkTypeRequirements() ptr<Type> FunctionCall::checkTypeRequirements()
{ {
m_expression->checkTypeRequirements(); m_expression->checkTypeRequirements();
for (ptr<Expression> const& argument : m_arguments) for (ptr<Expression> const & argument : m_arguments)
argument->checkTypeRequirements(); argument->checkTypeRequirements();
ptr<Type> expressionType = m_expression->getType(); ptr<Type> expressionType = m_expression->getType();
Type::Category const category = expressionType->getCategory(); Type::Category const category = expressionType->getCategory();
if (category == Type::Category::TYPE) { if (category == Type::Category::TYPE)
{
TypeType* type = dynamic_cast<TypeType*>(expressionType.get()); TypeType* type = dynamic_cast<TypeType*>(expressionType.get());
BOOST_ASSERT(type != nullptr); BOOST_ASSERT(type != nullptr);
//@todo for structs, we have to check the number of arguments to be equal to the //@todo for structs, we have to check the number of arguments to be equal to the
@ -380,7 +404,9 @@ ptr<Type> FunctionCall::checkTypeRequirements()
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Explicit type conversion not " BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Explicit type conversion not "
"allowed.")); "allowed."));
m_type = type->getActualType(); m_type = type->getActualType();
} else if (category == Type::Category::FUNCTION) { }
else if (category == Type::Category::FUNCTION)
{
//@todo would be nice to create a struct type from the arguments //@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 // and then ask if that is implicitly convertible to the struct represented by the
// function parameters // function parameters
@ -391,19 +417,21 @@ ptr<Type> FunctionCall::checkTypeRequirements()
if (parameters.size() != m_arguments.size()) if (parameters.size() != m_arguments.size())
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Wrong argument count for " BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Wrong argument count for "
"function call.")); "function call."));
for (size_t i = 0; i < m_arguments.size(); ++i) { for (size_t i = 0; i < m_arguments.size(); ++i)
{
if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType())) if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType()))
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Invalid type for argument in " BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Invalid type for argument in "
"function call.")); "function call."));
} }
// @todo actually the return type should be an anonymous struct, // @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 // but we change it to the type of the first return value until we have structs
if (fun.getReturnParameterList()->getParameters().empty()) if (fun.getReturnParameterList()->getParameters().empty())
m_type = std::make_shared<VoidType>(); m_type = std::make_shared<VoidType>();
else else
m_type = fun.getReturnParameterList()->getParameters().front()->getType(); m_type = fun.getReturnParameterList()->getParameters().front()->getType();
} else { }
else
{
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Type does not support invocation.")); BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Type does not support invocation."));
} }
return m_type; return m_type;
@ -435,7 +463,8 @@ ptr<Type> Identifier::checkTypeRequirements()
// var y = x; // var y = x;
// the type of x is not yet determined. // the type of x is not yet determined.
VariableDeclaration* variable = dynamic_cast<VariableDeclaration*>(m_referencedDeclaration); VariableDeclaration* variable = dynamic_cast<VariableDeclaration*>(m_referencedDeclaration);
if (variable != nullptr) { if (variable != nullptr)
{
if (variable->getType().get() == nullptr) if (variable->getType().get() == nullptr)
BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Variable referenced before type " BOOST_THROW_EXCEPTION(TypeError() << errinfo_comment("Variable referenced before type "
"could be determined.")); "could be determined."));
@ -444,13 +473,15 @@ ptr<Type> Identifier::checkTypeRequirements()
} }
//@todo can we unify these with TypeName::toType()? //@todo can we unify these with TypeName::toType()?
StructDefinition* structDef = dynamic_cast<StructDefinition*>(m_referencedDeclaration); StructDefinition* structDef = dynamic_cast<StructDefinition*>(m_referencedDeclaration);
if (structDef != nullptr) { if (structDef != nullptr)
{
// note that we do not have a struct type here // note that we do not have a struct type here
m_type = std::make_shared<TypeType>(std::make_shared<StructType>(*structDef)); m_type = std::make_shared<TypeType>(std::make_shared<StructType>(*structDef));
return m_type; return m_type;
} }
FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(m_referencedDeclaration); FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(m_referencedDeclaration);
if (functionDef != nullptr) { if (functionDef != nullptr)
{
// a function reference is not a TypeType, because calling a TypeType converts to the type. // 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 // Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
// conversion. // conversion.
@ -458,7 +489,8 @@ ptr<Type> Identifier::checkTypeRequirements()
return m_type; return m_type;
} }
ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration); ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration);
if (contractDef != nullptr) { if (contractDef != nullptr)
{
m_type = std::make_shared<TypeType>(std::make_shared<ContractType>(*contractDef)); m_type = std::make_shared<TypeType>(std::make_shared<ContractType>(*contractDef));
return m_type; return m_type;
} }
@ -478,4 +510,5 @@ ptr<Type> Literal::checkTypeRequirements()
return m_type; return m_type;
} }
} } }
}

15
libsolidity/AST.h

@ -33,8 +33,10 @@
#include <libsolidity/Token.h> #include <libsolidity/Token.h>
#include <libsolidity/Types.h> #include <libsolidity/Types.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
class ASTVisitor; class ASTVisitor;
@ -49,8 +51,10 @@ public:
virtual void accept(ASTVisitor& _visitor) = 0; virtual void accept(ASTVisitor& _visitor) = 0;
template <class T> template <class T>
static void listAccept(vecptr<T>& _list, ASTVisitor& _visitor) { static void listAccept(vecptr<T>& _list, ASTVisitor& _visitor)
for (ptr<T>& element : _list) element->accept(_visitor); {
for (ptr<T>& element : _list)
element->accept(_visitor);
} }
Location const& getLocation() const { return m_location; } Location const& getLocation() const { return m_location; }
@ -521,4 +525,5 @@ private:
/// @} /// @}
} } }
}

9
libsolidity/ASTForward.h

@ -28,8 +28,10 @@
// Forward-declare all AST node types // Forward-declare all AST node types
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
class ASTNode; class ASTNode;
class Declaration; class Declaration;
@ -74,4 +76,5 @@ using vecptr = std::vector<ptr<T>>;
using ASTString = std::string; using ASTString = std::string;
} } }
}

24
libsolidity/ASTPrinter.cpp

@ -23,8 +23,10 @@
#include <libsolidity/ASTPrinter.h> #include <libsolidity/ASTPrinter.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
ASTPrinter::ASTPrinter(ptr<ASTNode> _ast, const std::string& _source) ASTPrinter::ASTPrinter(ptr<ASTNode> _ast, const std::string& _source)
: m_indentation(0), m_source(_source), m_ast(_ast) : m_indentation(0), m_source(_source), m_ast(_ast)
@ -85,7 +87,7 @@ bool ASTPrinter::visit(TypeName& _node)
bool ASTPrinter::visit(ElementaryTypeName& _node) bool ASTPrinter::visit(ElementaryTypeName& _node)
{ {
writeLine(std::string("ElementaryTypeName ") + Token::String(_node.getType())); writeLine(std::string("ElementaryTypeName ") + Token::toString(_node.getType()));
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
@ -176,7 +178,7 @@ bool ASTPrinter::visit(Expression& _node)
bool ASTPrinter::visit(Assignment& _node) bool ASTPrinter::visit(Assignment& _node)
{ {
writeLine(std::string("Assignment using operator ") + Token::String(_node.getAssignmentOperator())); writeLine(std::string("Assignment using operator ") + Token::toString(_node.getAssignmentOperator()));
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
@ -184,14 +186,14 @@ bool ASTPrinter::visit(Assignment& _node)
bool ASTPrinter::visit(UnaryOperation& _node) bool ASTPrinter::visit(UnaryOperation& _node)
{ {
writeLine(std::string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") + writeLine(std::string("UnaryOperation (") + (_node.isPrefixOperation() ? "prefix" : "postfix") +
") " + Token::String(_node.getOperator())); ") " + Token::toString(_node.getOperator()));
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(BinaryOperation& _node) bool ASTPrinter::visit(BinaryOperation& _node)
{ {
writeLine(std::string("BinaryOperation using operator ") + Token::String(_node.getOperator())); writeLine(std::string("BinaryOperation using operator ") + Token::toString(_node.getOperator()));
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
@ -233,14 +235,14 @@ bool ASTPrinter::visit(Identifier& _node)
bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) bool ASTPrinter::visit(ElementaryTypeNameExpression& _node)
{ {
writeLine(std::string("ElementaryTypeNameExpression ") + Token::String(_node.getTypeToken())); writeLine(std::string("ElementaryTypeNameExpression ") + Token::toString(_node.getTypeToken()));
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }
bool ASTPrinter::visit(Literal& _node) bool ASTPrinter::visit(Literal& _node)
{ {
const char* tokenString = Token::String(_node.getToken()); const char* tokenString = Token::toString(_node.getToken());
if (tokenString == nullptr) if (tokenString == nullptr)
tokenString = "----"; tokenString = "----";
writeLine(std::string("Literal, token: ") + tokenString + " value: " + _node.getValue()); writeLine(std::string("Literal, token: ") + tokenString + " value: " + _node.getValue());
@ -402,7 +404,8 @@ void ASTPrinter::endVisit(Literal&)
void ASTPrinter::printSourcePart(ASTNode const& _node) void ASTPrinter::printSourcePart(ASTNode const& _node)
{ {
if (!m_source.empty()) { if (!m_source.empty())
{
Location const& location(_node.getLocation()); Location const& location(_node.getLocation());
*m_ostream << getIndentation() << " Source: |" *m_ostream << getIndentation() << " Source: |"
<< m_source.substr(location.start, location.end - location.start) << "|\n"; << m_source.substr(location.start, location.end - location.start) << "|\n";
@ -419,4 +422,5 @@ void ASTPrinter::writeLine(const std::string& _line)
*m_ostream << getIndentation() << _line << '\n'; *m_ostream << getIndentation() << _line << '\n';
} }
} } }
}

12
libsolidity/ASTPrinter.h

@ -25,8 +25,10 @@
#include <ostream> #include <ostream>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
class ASTPrinter : public ASTVisitor class ASTPrinter : public ASTVisitor
{ {
@ -67,7 +69,7 @@ public:
bool visit(ElementaryTypeNameExpression& _node) override; bool visit(ElementaryTypeNameExpression& _node) override;
bool visit(Literal& _node) override; bool visit(Literal& _node) override;
void endVisit(ASTNode & _node) override; void endVisit(ASTNode& _node) override;
void endVisit(ContractDefinition&) override; void endVisit(ContractDefinition&) override;
void endVisit(StructDefinition&) override; void endVisit(StructDefinition&) override;
void endVisit(ParameterList&) override; void endVisit(ParameterList&) override;
@ -103,10 +105,12 @@ private:
std::string getIndentation() const; std::string getIndentation() const;
void writeLine(std::string const& _line); void writeLine(std::string const& _line);
bool goDeeper() { m_indentation++; return true; } bool goDeeper() { m_indentation++; return true; }
int m_indentation; int m_indentation;
std::string m_source; std::string m_source;
ptr<ASTNode> m_ast; ptr<ASTNode> m_ast;
std::ostream* m_ostream; std::ostream* m_ostream;
}; };
} } }
}

12
libsolidity/ASTVisitor.h

@ -25,10 +25,13 @@
#include <libsolidity/ASTForward.h> #include <libsolidity/ASTForward.h>
#include <string> #include <string>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
class ASTVisitor { class ASTVisitor
{
public: public:
/// These functions are called after a call to ASTNode::accept, /// These functions are called after a call to ASTNode::accept,
/// first visit, then (if visit returns true) recursively for all /// first visit, then (if visit returns true) recursively for all
@ -97,4 +100,5 @@ public:
virtual void endVisit(Literal&) { } virtual void endVisit(Literal&) { }
}; };
} } }
}

12
libsolidity/BaseTypes.h

@ -23,12 +23,15 @@
#pragma once #pragma once
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
/// Representation of an interval of source positions. /// Representation of an interval of source positions.
/// The interval includes start and excludes end. /// The interval includes start and excludes end.
struct Location { struct Location
{
Location(int _start, int _end) : start(_start), end(_end) { } Location(int _start, int _end) : start(_start), end(_end) { }
Location() : start(-1), end(-1) { } Location() : start(-1), end(-1) { }
@ -38,4 +41,5 @@ struct Location {
int end; int end;
}; };
} } }
}

9
libsolidity/Exceptions.h

@ -24,11 +24,14 @@
#include <libdevcore/Exceptions.h> #include <libdevcore/Exceptions.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
struct ParserError : virtual Exception {}; struct ParserError : virtual Exception {};
struct TypeError : virtual Exception {}; struct TypeError : virtual Exception {};
struct DeclarationError : virtual Exception {}; struct DeclarationError : virtual Exception {};
} } }
}

22
libsolidity/NameAndTypeResolver.cpp

@ -26,8 +26,10 @@
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#include <boost/assert.hpp> #include <boost/assert.hpp>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
NameAndTypeResolver::NameAndTypeResolver() NameAndTypeResolver::NameAndTypeResolver()
@ -38,15 +40,12 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
{ {
reset(); reset();
DeclarationRegistrationHelper registrar(m_scopes, _contract); DeclarationRegistrationHelper registrar(m_scopes, _contract);
m_currentScope = &m_scopes[&_contract]; m_currentScope = &m_scopes[&_contract];
//@todo structs //@todo structs
for (ptr<VariableDeclaration> const & variable : _contract.getStateVariables())
for (ptr<VariableDeclaration> const& variable : _contract.getStateVariables())
ReferencesResolver resolver(*variable, *this, nullptr); ReferencesResolver resolver(*variable, *this, nullptr);
for (ptr<FunctionDefinition> const & function : _contract.getDefinedFunctions())
for (ptr<FunctionDefinition> const& function : _contract.getDefinedFunctions()) { {
m_currentScope = &m_scopes[function.get()]; m_currentScope = &m_scopes[function.get()];
ReferencesResolver referencesResolver(*function, *this, ReferencesResolver referencesResolver(*function, *this,
function->getReturnParameterList().get()); function->getReturnParameterList().get());
@ -54,7 +53,8 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
// First, all function parameter types need to be resolved before we can check // First, all function parameter types need to be resolved before we can check
// the types, since it is possible to call functions that are only defined later // the types, since it is possible to call functions that are only defined later
// in the source. // in the source.
for (ptr<FunctionDefinition> const& function : _contract.getDefinedFunctions()) { for (ptr<FunctionDefinition> const & function : _contract.getDefinedFunctions())
{
m_currentScope = &m_scopes[function.get()]; m_currentScope = &m_scopes[function.get()];
function->getBody().checkTypeRequirements(); function->getBody().checkTypeRequirements();
} }
@ -141,7 +141,6 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
BOOST_ASSERT(m_currentScope != nullptr); BOOST_ASSERT(m_currentScope != nullptr);
if (!m_currentScope->registerDeclaration(_declaration)) if (!m_currentScope->registerDeclaration(_declaration))
BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Identifier already declared.")); BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_comment("Identifier already declared."));
if (_opensScope) if (_opensScope)
enterNewSubScope(_declaration); enterNewSubScope(_declaration);
} }
@ -198,4 +197,5 @@ bool ReferencesResolver::visit(Identifier& _identifier)
} }
} } }
}

9
libsolidity/NameAndTypeResolver.h

@ -29,8 +29,10 @@
#include <libsolidity/Scope.h> #include <libsolidity/Scope.h>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
class NameAndTypeResolver : private boost::noncopyable class NameAndTypeResolver : private boost::noncopyable
@ -92,4 +94,5 @@ private:
ParameterList* m_returnParameters; ParameterList* m_returnParameters;
}; };
} } }
}

188
libsolidity/Parser.cpp

@ -26,13 +26,14 @@
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
ptr<ContractDefinition> Parser::parse(std::shared_ptr<Scanner> const& _scanner) ptr<ContractDefinition> Parser::parse(std::shared_ptr<Scanner> const& _scanner)
{ {
m_scanner = _scanner; m_scanner = _scanner;
return parseContractDefinition(); return parseContractDefinition();
} }
@ -46,9 +47,15 @@ public:
: m_parser(_parser), m_location(_parser.getPosition(), -1) : m_parser(_parser), m_location(_parser.getPosition(), -1)
{} {}
void markEndPosition() { m_location.end = m_parser.getEndPosition(); } void markEndPosition()
{
m_location.end = m_parser.getEndPosition();
}
void setLocationEmpty() { m_location.end = m_location.start; } void setLocationEmpty()
{
m_location.end = m_location.start;
}
/// Set the end position to the one of the given node. /// Set the end position to the one of the given node.
void setEndPositionFromNode(const ptr<ASTNode>& _node) void setEndPositionFromNode(const ptr<ASTNode>& _node)
@ -58,7 +65,7 @@ public:
/// @todo: check that this actually uses perfect forwarding /// @todo: check that this actually uses perfect forwarding
template <class NodeType, typename... Args> template <class NodeType, typename... Args>
ptr<NodeType> createNode(Args&&... _args) ptr<NodeType> createNode(Args&& ... _args)
{ {
if (m_location.end < 0) if (m_location.end < 0)
markEndPosition(); markEndPosition();
@ -84,62 +91,73 @@ int Parser::getEndPosition() const
ptr<ContractDefinition> Parser::parseContractDefinition() ptr<ContractDefinition> Parser::parseContractDefinition()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::CONTRACT); expectToken(Token::CONTRACT);
ptr<ASTString> name = expectIdentifierToken(); ptr<ASTString> name = expectIdentifierToken();
expectToken(Token::LBRACE); expectToken(Token::LBRACE);
vecptr<StructDefinition> structs; vecptr<StructDefinition> structs;
vecptr<VariableDeclaration> stateVariables; vecptr<VariableDeclaration> stateVariables;
vecptr<FunctionDefinition> functions; vecptr<FunctionDefinition> functions;
bool visibilityIsPublic = true; bool visibilityIsPublic = true;
while (true) { while (true)
{
Token::Value currentToken = m_scanner->getCurrentToken(); Token::Value currentToken = m_scanner->getCurrentToken();
if (currentToken == Token::RBRACE) { if (currentToken == Token::RBRACE)
{
break; break;
} else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE) { }
else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE)
{
visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC); visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC);
m_scanner->next(); m_scanner->next();
expectToken(Token::COLON); expectToken(Token::COLON);
} else if (currentToken == Token::FUNCTION) { }
else if (currentToken == Token::FUNCTION)
{
functions.push_back(parseFunctionDefinition(visibilityIsPublic)); functions.push_back(parseFunctionDefinition(visibilityIsPublic));
} else if (currentToken == Token::STRUCT) { }
else if (currentToken == Token::STRUCT)
{
structs.push_back(parseStructDefinition()); structs.push_back(parseStructDefinition());
} else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || }
Token::IsElementaryTypeName(currentToken)) { else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING ||
Token::isElementaryTypeName(currentToken))
{
bool const allowVar = false; bool const allowVar = false;
stateVariables.push_back(parseVariableDeclaration(allowVar)); stateVariables.push_back(parseVariableDeclaration(allowVar));
expectToken(Token::SEMICOLON); expectToken(Token::SEMICOLON);
} else { }
else
{
throwExpectationError("Function, variable or struct declaration expected."); throwExpectationError("Function, variable or struct declaration expected.");
} }
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBRACE); expectToken(Token::RBRACE);
expectToken(Token::EOS); expectToken(Token::EOS);
return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions); return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions);
} }
ptr<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic) ptr<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::FUNCTION); expectToken(Token::FUNCTION);
ptr<ASTString> name(expectIdentifierToken()); ptr<ASTString> name(expectIdentifierToken());
ptr<ParameterList> parameters(parseParameterList()); ptr<ParameterList> parameters(parseParameterList());
bool isDeclaredConst = false; bool isDeclaredConst = false;
if (m_scanner->getCurrentToken() == Token::CONST) { if (m_scanner->getCurrentToken() == Token::CONST)
{
isDeclaredConst = true; isDeclaredConst = true;
m_scanner->next(); m_scanner->next();
} }
ptr<ParameterList> returnParameters; ptr<ParameterList> returnParameters;
if (m_scanner->getCurrentToken() == Token::RETURNS) { if (m_scanner->getCurrentToken() == Token::RETURNS)
{
const bool permitEmptyParameterList = false; const bool permitEmptyParameterList = false;
m_scanner->next(); m_scanner->next();
returnParameters = parseParameterList(permitEmptyParameterList); returnParameters = parseParameterList(permitEmptyParameterList);
} else { }
else
{
// create an empty parameter list at a zero-length location // create an empty parameter list at a zero-length location
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
nodeFactory.setLocationEmpty(); nodeFactory.setLocationEmpty();
@ -154,26 +172,24 @@ ptr<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
ptr<StructDefinition> Parser::parseStructDefinition() ptr<StructDefinition> Parser::parseStructDefinition()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::STRUCT); expectToken(Token::STRUCT);
ptr<ASTString> name = expectIdentifierToken(); ptr<ASTString> name = expectIdentifierToken();
vecptr<VariableDeclaration> members; vecptr<VariableDeclaration> members;
expectToken(Token::LBRACE); expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE) { while (m_scanner->getCurrentToken() != Token::RBRACE)
{
bool const allowVar = false; bool const allowVar = false;
members.push_back(parseVariableDeclaration(allowVar)); members.push_back(parseVariableDeclaration(allowVar));
expectToken(Token::SEMICOLON); expectToken(Token::SEMICOLON);
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RBRACE); expectToken(Token::RBRACE);
return nodeFactory.createNode<StructDefinition>(name, members); return nodeFactory.createNode<StructDefinition>(name, members);
} }
ptr<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar) ptr<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ptr<TypeName> type = parseTypeName(_allowVar); ptr<TypeName> type = parseTypeName(_allowVar);
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken()); return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken());
@ -183,58 +199,63 @@ ptr<TypeName> Parser::parseTypeName(bool _allowVar)
{ {
ptr<TypeName> type; ptr<TypeName> type;
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
if (Token::IsElementaryTypeName(token)) { if (Token::isElementaryTypeName(token))
{
type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token); type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token);
m_scanner->next(); m_scanner->next();
} else if (token == Token::VAR) { }
else if (token == Token::VAR)
{
if (!_allowVar) if (!_allowVar)
throwExpectationError("Expected explicit type name."); throwExpectationError("Expected explicit type name.");
m_scanner->next(); m_scanner->next();
} else if (token == Token::MAPPING) { }
else if (token == Token::MAPPING)
{
type = parseMapping(); type = parseMapping();
} else if (token == Token::IDENTIFIER) { }
else if (token == Token::IDENTIFIER)
{
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
type = nodeFactory.createNode<UserDefinedTypeName>(expectIdentifierToken()); type = nodeFactory.createNode<UserDefinedTypeName>(expectIdentifierToken());
} else { }
else
{
throwExpectationError("Expected type name"); throwExpectationError("Expected type name");
} }
return type; return type;
} }
ptr<Mapping> Parser::parseMapping() ptr<Mapping> Parser::parseMapping()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::MAPPING); expectToken(Token::MAPPING);
expectToken(Token::LPAREN); expectToken(Token::LPAREN);
if (!Token::isElementaryTypeName(m_scanner->getCurrentToken()))
if (!Token::IsElementaryTypeName(m_scanner->getCurrentToken()))
throwExpectationError("Expected elementary type name for mapping key type"); throwExpectationError("Expected elementary type name for mapping key type");
ptr<ElementaryTypeName> keyType; ptr<ElementaryTypeName> keyType;
keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->getCurrentToken()); keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->getCurrentToken());
m_scanner->next(); m_scanner->next();
expectToken(Token::ARROW); expectToken(Token::ARROW);
bool const allowVar = false; bool const allowVar = false;
ptr<TypeName> valueType = parseTypeName(allowVar); ptr<TypeName> valueType = parseTypeName(allowVar);
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RPAREN); expectToken(Token::RPAREN);
return nodeFactory.createNode<Mapping>(keyType, valueType); return nodeFactory.createNode<Mapping>(keyType, valueType);
} }
ptr<ParameterList> Parser::parseParameterList(bool _allowEmpty) ptr<ParameterList> Parser::parseParameterList(bool _allowEmpty)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
vecptr<VariableDeclaration> parameters; vecptr<VariableDeclaration> parameters;
expectToken(Token::LPAREN); expectToken(Token::LPAREN);
if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN)
{
bool const allowVar = false; bool const allowVar = false;
parameters.push_back(parseVariableDeclaration(allowVar)); parameters.push_back(parseVariableDeclaration(allowVar));
while (m_scanner->getCurrentToken() != Token::RPAREN) { while (m_scanner->getCurrentToken() != Token::RPAREN)
{
expectToken(Token::COMMA); expectToken(Token::COMMA);
parameters.push_back(parseVariableDeclaration(allowVar)); parameters.push_back(parseVariableDeclaration(allowVar));
} }
@ -249,7 +270,8 @@ ptr<Block> Parser::parseBlock()
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
expectToken(Token::LBRACE); expectToken(Token::LBRACE);
vecptr<Statement> statements; vecptr<Statement> statements;
while (m_scanner->getCurrentToken() != Token::RBRACE) { while (m_scanner->getCurrentToken() != Token::RBRACE)
{
statements.push_back(parseStatement()); statements.push_back(parseStatement());
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
@ -260,15 +282,14 @@ ptr<Block> Parser::parseBlock()
ptr<Statement> Parser::parseStatement() ptr<Statement> Parser::parseStatement()
{ {
ptr<Statement> statement; ptr<Statement> statement;
switch (m_scanner->getCurrentToken())
switch (m_scanner->getCurrentToken()) { {
case Token::IF: case Token::IF:
return parseIfStatement(); return parseIfStatement();
case Token::WHILE: case Token::WHILE:
return parseWhileStatement(); return parseWhileStatement();
case Token::LBRACE: case Token::LBRACE:
return parseBlock(); return parseBlock();
// starting from here, all statements must be terminated by a semicolon // starting from here, all statements must be terminated by a semicolon
case Token::CONTINUE: case Token::CONTINUE:
statement = ASTNodeFactory(*this).createNode<Continue>(); statement = ASTNodeFactory(*this).createNode<Continue>();
@ -280,7 +301,8 @@ ptr<Statement> Parser::parseStatement()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ptr<Expression> expression; ptr<Expression> expression;
if (m_scanner->next() != Token::SEMICOLON) { if (m_scanner->next() != Token::SEMICOLON)
{
expression = parseExpression(); expression = parseExpression();
nodeFactory.setEndPositionFromNode(expression); nodeFactory.setEndPositionFromNode(expression);
} }
@ -294,11 +316,14 @@ ptr<Statement> Parser::parseStatement()
// in the case of a user-defined type, we have two identifiers following each other. // in the case of a user-defined type, we have two identifiers following each other.
if (m_scanner->getCurrentToken() == Token::MAPPING || if (m_scanner->getCurrentToken() == Token::MAPPING ||
m_scanner->getCurrentToken() == Token::VAR || m_scanner->getCurrentToken() == Token::VAR ||
Token::IsElementaryTypeName(m_scanner->getCurrentToken()) || Token::isElementaryTypeName(m_scanner->getCurrentToken()) ||
(m_scanner->getCurrentToken() == Token::IDENTIFIER && (m_scanner->getCurrentToken() == Token::IDENTIFIER &&
m_scanner->peek() == Token::IDENTIFIER)) { m_scanner->peek() == Token::IDENTIFIER))
{
statement = parseVariableDefinition(); statement = parseVariableDefinition();
} else { }
else
{
// "ordinary" expression // "ordinary" expression
statement = parseExpression(); statement = parseExpression();
} }
@ -316,11 +341,14 @@ ptr<IfStatement> Parser::parseIfStatement()
expectToken(Token::RPAREN); expectToken(Token::RPAREN);
ptr<Statement> trueBody = parseStatement(); ptr<Statement> trueBody = parseStatement();
ptr<Statement> falseBody; ptr<Statement> falseBody;
if (m_scanner->getCurrentToken() == Token::ELSE) { if (m_scanner->getCurrentToken() == Token::ELSE)
{
m_scanner->next(); m_scanner->next();
falseBody = parseStatement(); falseBody = parseStatement();
nodeFactory.setEndPositionFromNode(falseBody); nodeFactory.setEndPositionFromNode(falseBody);
} else { }
else
{
nodeFactory.setEndPositionFromNode(trueBody); nodeFactory.setEndPositionFromNode(trueBody);
} }
return nodeFactory.createNode<IfStatement>(condition, trueBody, falseBody); return nodeFactory.createNode<IfStatement>(condition, trueBody, falseBody);
@ -344,11 +372,14 @@ ptr<VariableDefinition> Parser::parseVariableDefinition()
bool const allowVar = true; bool const allowVar = true;
ptr<VariableDeclaration> variable = parseVariableDeclaration(allowVar); ptr<VariableDeclaration> variable = parseVariableDeclaration(allowVar);
ptr<Expression> value; ptr<Expression> value;
if (m_scanner->getCurrentToken() == Token::ASSIGN) { if (m_scanner->getCurrentToken() == Token::ASSIGN)
{
m_scanner->next(); m_scanner->next();
value = parseExpression(); value = parseExpression();
nodeFactory.setEndPositionFromNode(value); nodeFactory.setEndPositionFromNode(value);
} else { }
else
{
nodeFactory.setEndPositionFromNode(variable); nodeFactory.setEndPositionFromNode(variable);
} }
return nodeFactory.createNode<VariableDefinition>(variable, value); return nodeFactory.createNode<VariableDefinition>(variable, value);
@ -358,9 +389,8 @@ ptr<Expression> Parser::parseExpression()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ptr<Expression> expression = parseBinaryExpression(); ptr<Expression> expression = parseBinaryExpression();
if (!Token::IsAssignmentOp(m_scanner->getCurrentToken())) if (!Token::isAssignmentOp(m_scanner->getCurrentToken()))
return expression; return expression;
Token::Value assignmentOperator = expectAssignmentOperator(); Token::Value assignmentOperator = expectAssignmentOperator();
ptr<Expression> rightHandSide = parseExpression(); ptr<Expression> rightHandSide = parseExpression();
nodeFactory.setEndPositionFromNode(rightHandSide); nodeFactory.setEndPositionFromNode(rightHandSide);
@ -371,9 +401,11 @@ ptr<Expression> Parser::parseBinaryExpression(int _minPrecedence)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ptr<Expression> expression = parseUnaryExpression(); ptr<Expression> expression = parseUnaryExpression();
int precedence = Token::Precedence(m_scanner->getCurrentToken()); int precedence = Token::precedence(m_scanner->getCurrentToken());
for (; precedence >= _minPrecedence; --precedence) { for (; precedence >= _minPrecedence; --precedence)
while (Token::Precedence(m_scanner->getCurrentToken()) == precedence) { {
while (Token::precedence(m_scanner->getCurrentToken()) == precedence)
{
Token::Value op = m_scanner->getCurrentToken(); Token::Value op = m_scanner->getCurrentToken();
m_scanner->next(); m_scanner->next();
ptr<Expression> right = parseBinaryExpression(precedence + 1); ptr<Expression> right = parseBinaryExpression(precedence + 1);
@ -388,17 +420,20 @@ ptr<Expression> Parser::parseUnaryExpression()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
if (Token::IsUnaryOp(token) || Token::IsCountOp(token)) { if (Token::isUnaryOp(token) || Token::isCountOp(token))
{
// prefix expression // prefix expression
m_scanner->next(); m_scanner->next();
ptr<Expression> subExpression = parseUnaryExpression(); ptr<Expression> subExpression = parseUnaryExpression();
nodeFactory.setEndPositionFromNode(subExpression); nodeFactory.setEndPositionFromNode(subExpression);
return nodeFactory.createNode<UnaryOperation>(token, subExpression, true); return nodeFactory.createNode<UnaryOperation>(token, subExpression, true);
} else { }
else
{
// potential postfix expression // potential postfix expression
ptr<Expression> subExpression = parseLeftHandSideExpression(); ptr<Expression> subExpression = parseLeftHandSideExpression();
token = m_scanner->getCurrentToken(); token = m_scanner->getCurrentToken();
if (!Token::IsCountOp(token)) if (!Token::isCountOp(token))
return subExpression; return subExpression;
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
m_scanner->next(); m_scanner->next();
@ -410,9 +445,10 @@ ptr<Expression> Parser::parseLeftHandSideExpression()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ptr<Expression> expression = parsePrimaryExpression(); ptr<Expression> expression = parsePrimaryExpression();
while (true)
while (true) { {
switch (m_scanner->getCurrentToken()) { switch (m_scanner->getCurrentToken())
{
case Token::LBRACK: case Token::LBRACK:
{ {
m_scanner->next(); m_scanner->next();
@ -449,8 +485,8 @@ ptr<Expression> Parser::parsePrimaryExpression()
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
ptr<Expression> expression; ptr<Expression> expression;
switch (token)
switch (token) { {
case Token::TRUE_LITERAL: case Token::TRUE_LITERAL:
case Token::FALSE_LITERAL: case Token::FALSE_LITERAL:
expression = nodeFactory.createNode<Literal>(token, ptr<ASTString>()); expression = nodeFactory.createNode<Literal>(token, ptr<ASTString>());
@ -473,11 +509,14 @@ ptr<Expression> Parser::parsePrimaryExpression()
return expression; return expression;
} }
default: default:
if (Token::IsElementaryTypeName(token)) { if (Token::isElementaryTypeName(token))
{
// used for casts // used for casts
expression = nodeFactory.createNode<ElementaryTypeNameExpression>(token); expression = nodeFactory.createNode<ElementaryTypeNameExpression>(token);
m_scanner->next(); m_scanner->next();
} else { }
else
{
throwExpectationError("Expected primary expression."); throwExpectationError("Expected primary expression.");
return ptr<Expression>(); // this is not reached return ptr<Expression>(); // this is not reached
} }
@ -488,9 +527,11 @@ ptr<Expression> Parser::parsePrimaryExpression()
vecptr<Expression> Parser::parseFunctionCallArguments() vecptr<Expression> Parser::parseFunctionCallArguments()
{ {
vecptr<Expression> arguments; vecptr<Expression> arguments;
if (m_scanner->getCurrentToken() != Token::RPAREN) { if (m_scanner->getCurrentToken() != Token::RPAREN)
{
arguments.push_back(parseExpression()); arguments.push_back(parseExpression());
while (m_scanner->getCurrentToken() != Token::RPAREN) { while (m_scanner->getCurrentToken() != Token::RPAREN)
{
expectToken(Token::COMMA); expectToken(Token::COMMA);
arguments.push_back(parseExpression()); arguments.push_back(parseExpression());
} }
@ -501,14 +542,14 @@ vecptr<Expression> Parser::parseFunctionCallArguments()
void Parser::expectToken(Token::Value _value) void Parser::expectToken(Token::Value _value)
{ {
if (m_scanner->getCurrentToken() != _value) if (m_scanner->getCurrentToken() != _value)
throwExpectationError(std::string("Expected token ") + std::string(Token::Name(_value))); throwExpectationError(std::string("Expected token ") + std::string(Token::getName(_value)));
m_scanner->next(); m_scanner->next();
} }
Token::Value Parser::expectAssignmentOperator() Token::Value Parser::expectAssignmentOperator()
{ {
Token::Value op = m_scanner->getCurrentToken(); Token::Value op = m_scanner->getCurrentToken();
if (!Token::IsAssignmentOp(op)) if (!Token::isAssignmentOp(op))
throwExpectationError(std::string("Expected assignment operator")); throwExpectationError(std::string("Expected assignment operator"));
m_scanner->next(); m_scanner->next();
return op; return op;
@ -518,7 +559,6 @@ ptr<ASTString> Parser::expectIdentifierToken()
{ {
if (m_scanner->getCurrentToken() != Token::IDENTIFIER) if (m_scanner->getCurrentToken() != Token::IDENTIFIER)
throwExpectationError("Expected identifier"); throwExpectationError("Expected identifier");
return getLiteralAndAdvance(); return getLiteralAndAdvance();
} }
@ -540,9 +580,9 @@ void Parser::throwExpectationError(const std::string& _description)
<< ", column " << (column + 1) << "\n" << ", column " << (column + 1) << "\n"
<< m_scanner->getLineAtPosition(getPosition()) << "\n" << m_scanner->getLineAtPosition(getPosition()) << "\n"
<< std::string(column, ' ') << "^"; << std::string(column, ' ') << "^";
BOOST_THROW_EXCEPTION(ParserError() << errinfo_comment(buf.str())); BOOST_THROW_EXCEPTION(ParserError() << errinfo_comment(buf.str()));
} }
} } }
}

9
libsolidity/Parser.h

@ -24,8 +24,10 @@
#include "libsolidity/AST.h" #include "libsolidity/AST.h"
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
class Scanner; class Scanner;
@ -77,4 +79,5 @@ private:
std::shared_ptr<Scanner> m_scanner; std::shared_ptr<Scanner> m_scanner;
}; };
} } }
}

327
libsolidity/Scanner.cpp

@ -45,35 +45,47 @@
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
namespace { namespace
bool IsDecimalDigit(char c) { {
bool IsDecimalDigit(char c)
{
return '0' <= c && c <= '9'; return '0' <= c && c <= '9';
} }
bool IsHexDigit(char c) { bool IsHexDigit(char c)
{
return IsDecimalDigit(c) return IsDecimalDigit(c)
|| ('a' <= c && c <= 'f') || ('a' <= c && c <= 'f')
|| ('A' <= c && c <= 'F'); || ('A' <= c && c <= 'F');
} }
bool IsLineTerminator(char c) { return c == '\n'; } bool IsLineTerminator(char c)
bool IsWhiteSpace(char c) { {
return c == '\n';
}
bool IsWhiteSpace(char c)
{
return c == ' ' || c == '\n' || c == '\t'; return c == ' ' || c == '\n' || c == '\t';
} }
bool IsIdentifierStart(char c) { bool IsIdentifierStart(char c)
{
return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z'); return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
} }
bool IsIdentifierPart(char c) { bool IsIdentifierPart(char c)
{
return IsIdentifierStart(c) || IsDecimalDigit(c); return IsIdentifierStart(c) || IsDecimalDigit(c);
} }
int HexValue(char c) { int HexValue(char c)
{
if (c >= '0' && c <= '9') return c - '0'; 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 if (c >= 'A' && c <= 'F') return c - 'A' + 10; else if (c >= 'A' && c <= 'F') return c - 'A' + 10;
else return -1; else return -1;
} }
} }
Scanner::Scanner(const CharStream& _source) Scanner::Scanner(const CharStream& _source)
@ -84,7 +96,6 @@ Scanner::Scanner(const CharStream& _source)
void Scanner::reset(const CharStream& _source) void Scanner::reset(const CharStream& _source)
{ {
m_source = _source; m_source = _source;
m_char = m_source.get(); m_char = m_source.get();
skipWhitespace(); skipWhitespace();
scanToken(); scanToken();
@ -95,18 +106,18 @@ void Scanner::reset(const CharStream& _source)
bool Scanner::scanHexNumber(char& scanned_number, int expected_length) bool Scanner::scanHexNumber(char& scanned_number, int expected_length)
{ {
BOOST_ASSERT(expected_length <= 4); // prevent overflow BOOST_ASSERT(expected_length <= 4); // prevent overflow
char x = 0; char x = 0;
for (int i = 0; i < expected_length; i++) { for (int i = 0; i < expected_length; i++)
{
int d = HexValue(m_char); int d = HexValue(m_char);
if (d < 0) { if (d < 0)
{
rollback(i); rollback(i);
return false; return false;
} }
x = x * 16 + d; x = x * 16 + d;
advance(); advance();
} }
scanned_number = x; scanned_number = x;
return true; return true;
} }
@ -128,16 +139,14 @@ Token::Value Scanner::next()
bool Scanner::skipWhitespace() bool Scanner::skipWhitespace()
{ {
const int start_position = getSourcePos(); const int start_position = getSourcePos();
while (true)
while (true) { {
if (IsLineTerminator(m_char)) { if (IsLineTerminator(m_char))
m_hasLineTerminatorBeforeNext = true; m_hasLineTerminatorBeforeNext = true;
} else if (!IsWhiteSpace(m_char)) { else if (!IsWhiteSpace(m_char))
break; break;
}
advance(); advance();
} }
// Return whether or not we skipped any characters. // Return whether or not we skipped any characters.
return getSourcePos() != start_position; return getSourcePos() != start_position;
} }
@ -150,7 +159,6 @@ Token::Value Scanner::skipSingleLineComment()
// separately by the lexical grammar and becomes part of the // separately by the lexical grammar and becomes part of the
// stream of input elements for the syntactic grammar // stream of input elements for the syntactic grammar
while (advance() && !IsLineTerminator(m_char)) { }; while (advance() && !IsLineTerminator(m_char)) { };
return Token::WHITESPACE; return Token::WHITESPACE;
} }
@ -158,11 +166,12 @@ Token::Value Scanner::skipMultiLineComment()
{ {
BOOST_ASSERT(m_char == '*'); BOOST_ASSERT(m_char == '*');
advance(); advance();
while (!isSourcePastEndOfInput())
while (!isSourcePastEndOfInput()) { {
char ch = m_char; char ch = m_char;
advance(); advance();
if (IsLineTerminator(ch)) { if (IsLineTerminator(ch))
{
// Following ECMA-262, section 7.4, a comment containing // Following ECMA-262, section 7.4, a comment containing
// a newline will make the comment count as a line-terminator. // a newline will make the comment count as a line-terminator.
m_hasMultilineCommentBeforeNext = true; m_hasMultilineCommentBeforeNext = true;
@ -170,12 +179,12 @@ Token::Value Scanner::skipMultiLineComment()
// If we have reached the end of the multi-line comment, we // If we have reached the end of the multi-line comment, we
// consume the '/' and insert a whitespace. This way all // consume the '/' and insert a whitespace. This way all
// multi-line comments are treated as whitespace. // multi-line comments are treated as whitespace.
if (ch == '*' && m_char == '/') { if (ch == '*' && m_char == '/')
{
m_char = ' '; m_char = ' ';
return Token::WHITESPACE; return Token::WHITESPACE;
} }
} }
// Unterminated multi-line comment. // Unterminated multi-line comment.
return Token::ILLEGAL; return Token::ILLEGAL;
} }
@ -184,227 +193,194 @@ void Scanner::scanToken()
{ {
m_next_token.literal.clear(); m_next_token.literal.clear();
Token::Value token; Token::Value token;
do { do
{
// Remember the position of the next token // Remember the position of the next token
m_next_token.location.start = getSourcePos(); m_next_token.location.start = getSourcePos();
switch (m_char)
switch (m_char) { {
case '\n': case '\n':
m_hasLineTerminatorBeforeNext = true; // fall-through m_hasLineTerminatorBeforeNext = true; // fall-through
case ' ': case ' ':
case '\t': case '\t':
token = selectToken(Token::WHITESPACE); token = selectToken(Token::WHITESPACE);
break; break;
case '"':
case '"': case '\'': case '\'':
token = scanString(); token = scanString();
break; break;
case '<': case '<':
// < <= << <<= // < <= << <<=
advance(); advance();
if (m_char == '=') { if (m_char == '=')
token = selectToken(Token::LTE); token = selectToken(Token::LTE);
} else if (m_char == '<') { else if (m_char == '<')
token = selectToken('=', Token::ASSIGN_SHL, Token::SHL); token = selectToken('=', Token::ASSIGN_SHL, Token::SHL);
} else { else
token = Token::LT; token = Token::LT;
}
break; break;
case '>': case '>':
// > >= >> >>= >>> >>>= // > >= >> >>= >>> >>>=
advance(); advance();
if (m_char == '=') { if (m_char == '=')
token = selectToken(Token::GTE); token = selectToken(Token::GTE);
} else if (m_char == '>') { else if (m_char == '>')
{
// >> >>= >>> >>>= // >> >>= >>> >>>=
advance(); advance();
if (m_char == '=') { if (m_char == '=')
token = selectToken(Token::ASSIGN_SAR); token = selectToken(Token::ASSIGN_SAR);
} else if (m_char == '>') { else if (m_char == '>')
token = selectToken('=', Token::ASSIGN_SHR, Token::SHR); token = selectToken('=', Token::ASSIGN_SHR, Token::SHR);
} else { else
token = Token::SAR; token = Token::SAR;
} }
} else { else
token = Token::GT; token = Token::GT;
}
break; break;
case '=': case '=':
// = == => // = == =>
advance(); advance();
if (m_char == '=') { if (m_char == '=')
token = selectToken(Token::EQ); token = selectToken(Token::EQ);
} else if (m_char == '>') { else if (m_char == '>')
token = selectToken(Token::ARROW); token = selectToken(Token::ARROW);
} else { else
token = Token::ASSIGN; token = Token::ASSIGN;
}
break; break;
case '!': case '!':
// ! != !== // ! !=
advance(); advance();
if (m_char == '=') { if (m_char == '=')
token = selectToken(Token::NE); token = selectToken(Token::NE);
} else { else
token = Token::NOT; token = Token::NOT;
}
break; break;
case '+': case '+':
// + ++ += // + ++ +=
advance(); advance();
if (m_char == '+') { if (m_char == '+')
token = selectToken(Token::INC); token = selectToken(Token::INC);
} else if (m_char == '=') { else if (m_char == '=')
token = selectToken(Token::ASSIGN_ADD); token = selectToken(Token::ASSIGN_ADD);
} else { else
token = Token::ADD; token = Token::ADD;
}
break; break;
case '-': case '-':
// - -- -= // - -- -=
advance(); advance();
if (m_char == '-') { if (m_char == '-')
{
advance(); advance();
token = Token::DEC; token = Token::DEC;
} else if (m_char == '=') { }
else if (m_char == '=')
token = selectToken(Token::ASSIGN_SUB); token = selectToken(Token::ASSIGN_SUB);
} else { else
token = Token::SUB; token = Token::SUB;
}
break; break;
case '*': case '*':
// * *= // * *=
token = selectToken('=', Token::ASSIGN_MUL, Token::MUL); token = selectToken('=', Token::ASSIGN_MUL, Token::MUL);
break; break;
case '%': case '%':
// % %= // % %=
token = selectToken('=', Token::ASSIGN_MOD, Token::MOD); token = selectToken('=', Token::ASSIGN_MOD, Token::MOD);
break; break;
case '/': case '/':
// / // /* /= // / // /* /=
advance(); advance();
if (m_char == '/') { if (m_char == '/')
token = skipSingleLineComment(); token = skipSingleLineComment();
} else if (m_char == '*') { else if (m_char == '*')
token = skipMultiLineComment(); token = skipMultiLineComment();
} else if (m_char == '=') { else if (m_char == '=')
token = selectToken(Token::ASSIGN_DIV); token = selectToken(Token::ASSIGN_DIV);
} else { else
token = Token::DIV; token = Token::DIV;
}
break; break;
case '&': case '&':
// & && &= // & && &=
advance(); advance();
if (m_char == '&') { if (m_char == '&')
token = selectToken(Token::AND); token = selectToken(Token::AND);
} else if (m_char == '=') { else if (m_char == '=')
token = selectToken(Token::ASSIGN_BIT_AND); token = selectToken(Token::ASSIGN_BIT_AND);
} else { else
token = Token::BIT_AND; token = Token::BIT_AND;
}
break; break;
case '|': case '|':
// | || |= // | || |=
advance(); advance();
if (m_char == '|') { if (m_char == '|')
token = selectToken(Token::OR); token = selectToken(Token::OR);
} else if (m_char == '=') { else if (m_char == '=')
token = selectToken(Token::ASSIGN_BIT_OR); token = selectToken(Token::ASSIGN_BIT_OR);
} else { else
token = Token::BIT_OR; token = Token::BIT_OR;
}
break; break;
case '^': case '^':
// ^ ^= // ^ ^=
token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR);
break; break;
case '.': case '.':
// . Number // . Number
advance(); advance();
if (IsDecimalDigit(m_char)) { if (IsDecimalDigit(m_char))
token = scanNumber(true); token = scanNumber(true);
} else { else
token = Token::PERIOD; token = Token::PERIOD;
}
break; break;
case ':': case ':':
token = selectToken(Token::COLON); token = selectToken(Token::COLON);
break; break;
case ';': case ';':
token = selectToken(Token::SEMICOLON); token = selectToken(Token::SEMICOLON);
break; break;
case ',': case ',':
token = selectToken(Token::COMMA); token = selectToken(Token::COMMA);
break; break;
case '(': case '(':
token = selectToken(Token::LPAREN); token = selectToken(Token::LPAREN);
break; break;
case ')': case ')':
token = selectToken(Token::RPAREN); token = selectToken(Token::RPAREN);
break; break;
case '[': case '[':
token = selectToken(Token::LBRACK); token = selectToken(Token::LBRACK);
break; break;
case ']': case ']':
token = selectToken(Token::RBRACK); token = selectToken(Token::RBRACK);
break; break;
case '{': case '{':
token = selectToken(Token::LBRACE); token = selectToken(Token::LBRACE);
break; break;
case '}': case '}':
token = selectToken(Token::RBRACE); token = selectToken(Token::RBRACE);
break; break;
case '?': case '?':
token = selectToken(Token::CONDITIONAL); token = selectToken(Token::CONDITIONAL);
break; break;
case '~': case '~':
token = selectToken(Token::BIT_NOT); token = selectToken(Token::BIT_NOT);
break; break;
default: default:
if (IsIdentifierStart(m_char)) { if (IsIdentifierStart(m_char))
token = scanIdentifierOrKeyword(); token = scanIdentifierOrKeyword();
} else if (IsDecimalDigit(m_char)) { else if (IsDecimalDigit(m_char))
token = scanNumber(false); token = scanNumber(false);
} else if (skipWhitespace()) { else if (skipWhitespace())
token = Token::WHITESPACE; token = Token::WHITESPACE;
} else if (isSourcePastEndOfInput()) { else if (isSourcePastEndOfInput())
token = Token::EOS; token = Token::EOS;
} else { else
token = selectToken(Token::ILLEGAL); token = selectToken(Token::ILLEGAL);
}
break; break;
} }
// Continue scanning for tokens as long as we're just skipping // Continue scanning for tokens as long as we're just skipping
// whitespace. // whitespace.
} while (token == Token::WHITESPACE); }
while (token == Token::WHITESPACE);
m_next_token.location.end = getSourcePos(); m_next_token.location.end = getSourcePos();
m_next_token.token = token; m_next_token.token = token;
} }
@ -413,31 +389,40 @@ bool Scanner::scanEscape()
{ {
char c = m_char; char c = m_char;
advance(); advance();
// Skip escaped newlines. // Skip escaped newlines.
if (IsLineTerminator(c)) if (IsLineTerminator(c))
return true; return true;
switch (c)
switch (c) { {
case '\'': // fall through case '\'': // fall through
case '"' : // fall through case '"' : // fall through
case '\\': break; case '\\':
case 'b' : c = '\b'; break; break;
case 'f' : c = '\f'; break; case 'b' :
case 'n' : c = '\n'; break; c = '\b';
case 'r' : c = '\r'; break; break;
case 't' : c = '\t'; break; case 'f' :
case 'u' : { 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; if (!scanHexNumber(c, 4)) return false;
break; break;
} case 'v' :
case 'v' : c = '\v'; break; c = '\v';
case 'x' : { break;
case 'x' :
if (!scanHexNumber(c, 2)) return false; if (!scanHexNumber(c, 2)) return false;
break; break;
} }
}
// According to ECMA-262, section 7.8.4, characters not covered by the // According to ECMA-262, section 7.8.4, characters not covered by the
// above cases should be illegal, but they are commonly handled as // above cases should be illegal, but they are commonly handled as
// non-escaped characters by JS VMs. // non-escaped characters by JS VMs.
@ -449,20 +434,21 @@ Token::Value Scanner::scanString()
{ {
const char quote = m_char; const char quote = m_char;
advance(); // consume quote advance(); // consume quote
LiteralScope literal(this); LiteralScope literal(this);
while (m_char != quote && !isSourcePastEndOfInput() && !IsLineTerminator(m_char)) { while (m_char != quote && !isSourcePastEndOfInput() && !IsLineTerminator(m_char))
{
char c = m_char; char c = m_char;
advance(); advance();
if (c == '\\') { if (c == '\\')
if (isSourcePastEndOfInput() || !scanEscape()) return Token::ILLEGAL; {
} else { if (isSourcePastEndOfInput() || !scanEscape())
addLiteralChar(c); return Token::ILLEGAL;
} }
else
addLiteralChar(c);
} }
if (m_char != quote) return Token::ILLEGAL; if (m_char != quote) return Token::ILLEGAL;
literal.Complete(); literal.Complete();
advance(); // consume quote advance(); // consume quote
return Token::STRING_LITERAL; return Token::STRING_LITERAL;
} }
@ -478,69 +464,64 @@ void Scanner::scanDecimalDigits()
Token::Value Scanner::scanNumber(bool _periodSeen) Token::Value Scanner::scanNumber(bool _periodSeen)
{ {
BOOST_ASSERT(IsDecimalDigit(m_char)); // the first digit of the number or the fraction BOOST_ASSERT(IsDecimalDigit(m_char)); // the first digit of the number or the fraction
enum { DECIMAL, HEX, OCTAL, IMPLICIT_OCTAL, BINARY } kind = DECIMAL; enum { DECIMAL, HEX, OCTAL, IMPLICIT_OCTAL, BINARY } kind = DECIMAL;
LiteralScope literal(this); LiteralScope literal(this);
if (_periodSeen) { if (_periodSeen)
{
// we have already seen a decimal point of the float // we have already seen a decimal point of the float
addLiteralChar('.'); addLiteralChar('.');
scanDecimalDigits(); // we know we have at least one digit scanDecimalDigits(); // we know we have at least one digit
} else { }
else
{
// if the first character is '0' we must check for octals and hex // if the first character is '0' we must check for octals and hex
if (m_char == '0') { if (m_char == '0')
{
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
// either 0, 0exxx, 0Exxx, 0.xxx, a hex number, a binary number or // either 0, 0exxx, 0Exxx, 0.xxx, a hex number, a binary number or
// an octal number. // an octal number.
if (m_char == 'x' || m_char == 'X') { if (m_char == 'x' || m_char == 'X')
{
// hex number // hex number
kind = HEX; kind = HEX;
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
if (!IsHexDigit(m_char)) { if (!IsHexDigit(m_char))
// we must have at least one hex digit after 'x'/'X' return Token::ILLEGAL; // we must have at least one hex digit after 'x'/'X'
return Token::ILLEGAL; while (IsHexDigit(m_char))
}
while (IsHexDigit(m_char)) {
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
} }
} }
}
// Parse decimal digits and allow trailing fractional part. // Parse decimal digits and allow trailing fractional part.
if (kind == DECIMAL) { if (kind == DECIMAL)
{
scanDecimalDigits(); // optional scanDecimalDigits(); // optional
if (m_char == '.') { if (m_char == '.')
{
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
scanDecimalDigits(); // optional scanDecimalDigits(); // optional
} }
} }
} }
// scan exponent, if any // scan exponent, if any
if (m_char == 'e' || m_char == 'E') { if (m_char == 'e' || m_char == 'E')
{
BOOST_ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number BOOST_ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number
if (kind != DECIMAL) return Token::ILLEGAL; if (kind != DECIMAL) return Token::ILLEGAL;
// scan exponent // scan exponent
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
if (m_char == '+' || m_char == '-') if (m_char == '+' || m_char == '-')
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
if (!IsDecimalDigit(m_char)) { if (!IsDecimalDigit(m_char))
// we must have at least one decimal digit after 'e'/'E' return Token::ILLEGAL; // we must have at least one decimal digit after 'e'/'E'
return Token::ILLEGAL;
}
scanDecimalDigits(); scanDecimalDigits();
} }
// The source character immediately following a numeric literal must // The source character immediately following a numeric literal must
// not be an identifier start or a decimal digit; see ECMA-262 // 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 // section 7.8.3, page 17 (note that we read only one decimal digit
// if the value is 0). // if the value is 0).
if (IsDecimalDigit(m_char) || IsIdentifierStart(m_char)) if (IsDecimalDigit(m_char) || IsIdentifierStart(m_char))
return Token::ILLEGAL; return Token::ILLEGAL;
literal.Complete(); literal.Complete();
return Token::NUMBER; return Token::NUMBER;
} }
@ -636,14 +617,14 @@ static Token::Value KeywordOrIdentifierToken(const std::string& input)
BOOST_ASSERT(!input.empty()); BOOST_ASSERT(!input.empty());
const int kMinLength = 2; const int kMinLength = 2;
const int kMaxLength = 10; const int kMaxLength = 10;
if (input.size() < kMinLength || input.size() > kMaxLength) { if (input.size() < kMinLength || input.size() > kMaxLength)
return Token::IDENTIFIER; return Token::IDENTIFIER;
} switch (input[0])
switch (input[0]) { {
default: default:
#define KEYWORD_GROUP_CASE(ch) \ #define KEYWORD_GROUP_CASE(ch) \
break; \ break; \
case ch: case ch:
#define KEYWORD(keyword, token) \ #define KEYWORD(keyword, token) \
{ \ { \
/* 'keyword' is a char array, so sizeof(keyword) is */ \ /* 'keyword' is a char array, so sizeof(keyword) is */ \
@ -664,15 +645,11 @@ Token::Value Scanner::scanIdentifierOrKeyword()
{ {
BOOST_ASSERT(IsIdentifierStart(m_char)); BOOST_ASSERT(IsIdentifierStart(m_char));
LiteralScope literal(this); LiteralScope literal(this);
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
// Scan the rest of the identifier characters. // Scan the rest of the identifier characters.
while (IsIdentifierPart(m_char)) while (IsIdentifierPart(m_char))
addLiteralCharAndAdvance(); addLiteralCharAndAdvance();
literal.Complete(); literal.Complete();
return KeywordOrIdentifierToken(m_next_token.literal); return KeywordOrIdentifierToken(m_next_token.literal);
} }
@ -697,17 +674,17 @@ std::tuple<int, int> CharStream::translatePositionToLineColumn(int _position) co
using size_type = std::string::size_type; using size_type = std::string::size_type;
size_type searchPosition = std::min<size_type>(m_source.size(), _position); size_type searchPosition = std::min<size_type>(m_source.size(), _position);
int lineNumber = std::count(m_source.begin(), m_source.begin() + searchPosition, '\n'); int lineNumber = std::count(m_source.begin(), m_source.begin() + searchPosition, '\n');
size_type lineStart; size_type lineStart;
if (searchPosition == 0) { if (searchPosition == 0)
lineStart = 0; lineStart = 0;
} else { else
{
lineStart = m_source.rfind('\n', searchPosition - 1); lineStart = m_source.rfind('\n', searchPosition - 1);
lineStart = lineStart == std::string::npos ? 0 : lineStart + 1; lineStart = lineStart == std::string::npos ? 0 : lineStart + 1;
} }
return std::tuple<int, int>(lineNumber, searchPosition - lineStart); return std::tuple<int, int>(lineNumber, searchPosition - lineStart);
} }
} } }
}

96
libsolidity/Scanner.h

@ -50,33 +50,36 @@
#include <libsolidity/BaseTypes.h> #include <libsolidity/BaseTypes.h>
#include <libsolidity/Token.h> #include <libsolidity/Token.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
class AstRawString; class AstRawString;
class AstValueFactory; class AstValueFactory;
class ParserRecorder; class ParserRecorder;
class CharStream { class CharStream
{
public: public:
CharStream() CharStream()
: m_pos(0) : m_pos(0)
{} {}
explicit CharStream(const std::string& _source) explicit CharStream(const std::string& _source) : m_source(_source), m_pos(0) {}
: m_source(_source), m_pos(0)
{}
int getPos() const { return m_pos; } int getPos() const { return m_pos; }
bool isPastEndOfInput() const { return m_pos >= m_source.size(); } bool isPastEndOfInput() const { return m_pos >= m_source.size(); }
char get() const { return m_source[m_pos]; } char get() const { return m_source[m_pos]; }
char advanceAndGet() { char advanceAndGet()
{
if (isPastEndOfInput()) return 0; if (isPastEndOfInput()) return 0;
++m_pos; ++m_pos;
if (isPastEndOfInput()) return 0; if (isPastEndOfInput()) return 0;
return get(); return get();
} }
char rollback(size_t _amount) { char rollback(size_t _amount)
{
BOOST_ASSERT(m_pos >= _amount); BOOST_ASSERT(m_pos >= _amount);
m_pos -= _amount; m_pos -= _amount;
return get(); return get();
@ -96,22 +99,17 @@ private:
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// JavaScript Scanner. // JavaScript Scanner.
class Scanner { class Scanner
{
public: public:
// Scoped helper for literal recording. Automatically drops the literal // Scoped helper for literal recording. Automatically drops the literal
// if aborting the scanning before it's complete. // if aborting the scanning before it's complete.
class LiteralScope { class LiteralScope
{
public: public:
explicit LiteralScope(Scanner* self) explicit LiteralScope(Scanner* self) : scanner_(self), complete_(false) { scanner_->startNewLiteral(); }
: scanner_(self), complete_(false) { ~LiteralScope() { if (!complete_) scanner_->dropLiteral(); }
scanner_->startNewLiteral(); void Complete() { complete_ = true; }
}
~LiteralScope() {
if (!complete_) scanner_->dropLiteral();
}
void Complete() {
complete_ = true;
}
private: private:
Scanner* scanner_; Scanner* scanner_;
@ -143,7 +141,10 @@ public:
/// Functions that help pretty-printing parse errors. /// Functions that help pretty-printing parse errors.
/// Do only use in error cases, they are quite expensive. /// Do only use in error cases, they are quite expensive.
/// @{ /// @{
std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); } std::string getLineAtPosition(int _position) const
{
return m_source.getLineAtPosition(_position);
}
std::tuple<int, int> translatePositionToLineColumn(int _position) const std::tuple<int, int> translatePositionToLineColumn(int _position) const
{ {
return m_source.translatePositionToLineColumn(_position); return m_source.translatePositionToLineColumn(_position);
@ -152,56 +153,46 @@ public:
// Returns true if there was a line terminator before the peek'ed token, // Returns true if there was a line terminator before the peek'ed token,
// possibly inside a multi-line comment. // possibly inside a multi-line comment.
bool hasAnyLineTerminatorBeforeNext() const { bool hasAnyLineTerminatorBeforeNext() const
{
return m_hasLineTerminatorBeforeNext || return m_hasLineTerminatorBeforeNext ||
m_hasMultilineCommentBeforeNext; m_hasMultilineCommentBeforeNext;
} }
private: private:
// Used for the current and look-ahead token. // Used for the current and look-ahead token.
struct TokenDesc { struct TokenDesc
{
Token::Value token; Token::Value token;
Location location; Location location;
std::string literal; std::string literal;
}; };
// Literal buffer support // Literal buffer support
inline void startNewLiteral() { inline void startNewLiteral() { m_next_token.literal.clear(); }
m_next_token.literal.clear();
}
inline void addLiteralChar(char c) { inline void addLiteralChar(char c) { m_next_token.literal.push_back(c); }
m_next_token.literal.push_back(c);
}
inline void dropLiteral() { inline void dropLiteral() { m_next_token.literal.clear(); }
m_next_token.literal.clear();
}
inline void addLiteralCharAndAdvance() { inline void addLiteralCharAndAdvance() { addLiteralChar(m_char); advance(); }
addLiteralChar(m_char);
advance();
}
// Low-level scanning support. // Low-level scanning support.
bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); } bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); }
void rollback(int amount) { void rollback(int amount) { m_char = m_source.rollback(amount); }
m_char = m_source.rollback(amount);
}
inline Token::Value selectToken(Token::Value tok) { inline Token::Value selectToken(Token::Value tok) { advance(); return tok; }
advance();
return tok;
}
inline Token::Value selectToken(char next, Token::Value then, Token::Value else_) { inline Token::Value selectToken(char next, Token::Value then, Token::Value else_)
{
advance(); advance();
if (m_char == next) { if (m_char == next)
{
advance(); advance();
return then; return then;
} else {
return else_;
} }
else
return else_;
} }
bool scanHexNumber(char& scanned_number, int expected_length); bool scanHexNumber(char& scanned_number, int expected_length);
@ -225,12 +216,8 @@ private:
bool scanEscape(); bool scanEscape();
// Return the current source position. // Return the current source position.
int getSourcePos() { int getSourcePos() { return m_source.getPos(); }
return m_source.getPos(); bool isSourcePastEndOfInput() { return m_source.isPastEndOfInput(); }
}
bool isSourcePastEndOfInput() {
return m_source.isPastEndOfInput();
}
TokenDesc m_current_token; // desc for current token (as returned by Next()) TokenDesc m_current_token; // desc for current token (as returned by Next())
TokenDesc m_next_token; // desc for next token (one token look-ahead) TokenDesc m_next_token; // desc for next token (one token look-ahead)
@ -249,4 +236,5 @@ private:
bool m_hasMultilineCommentBeforeNext; bool m_hasMultilineCommentBeforeNext;
}; };
} } }
}

11
libsolidity/Scope.cpp

@ -23,8 +23,10 @@
#include <libsolidity/Scope.h> #include <libsolidity/Scope.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
bool Scope::registerDeclaration(Declaration& _declaration) bool Scope::registerDeclaration(Declaration& _declaration)
@ -35,7 +37,7 @@ bool Scope::registerDeclaration(Declaration& _declaration)
return true; return true;
} }
Declaration*Scope::resolveName(ASTString const& _name, bool _recursive) const Declaration* Scope::resolveName(ASTString const& _name, bool _recursive) const
{ {
auto result = m_declarations.find(_name); auto result = m_declarations.find(_name);
if (result != m_declarations.end()) if (result != m_declarations.end())
@ -45,4 +47,5 @@ Declaration*Scope::resolveName(ASTString const& _name, bool _recursive) const
return nullptr; return nullptr;
} }
} } }
}

9
libsolidity/Scope.h

@ -28,8 +28,10 @@
#include <libsolidity/ASTForward.h> #include <libsolidity/ASTForward.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
class Scope class Scope
{ {
@ -46,4 +48,5 @@ private:
std::map<ASTString, Declaration*> m_declarations; std::map<ASTString, Declaration*> m_declarations;
}; };
} } }
}

21
libsolidity/Token.cpp

@ -42,25 +42,30 @@
#include <libsolidity/Token.h> #include <libsolidity/Token.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
#define T(name, string, precedence) #name, #define T(name, string, precedence) #name,
const char* const Token::m_name[NUM_TOKENS] = { const char* const Token::m_name[NUM_TOKENS] =
{
TOKEN_LIST(T, T) TOKEN_LIST(T, T)
}; };
#undef T #undef T
#define T(name, string, precedence) string, #define T(name, string, precedence) string,
const char* const Token::m_string[NUM_TOKENS] = { const char* const Token::m_string[NUM_TOKENS] =
{
TOKEN_LIST(T, T) TOKEN_LIST(T, T)
}; };
#undef T #undef T
#define T(name, string, precedence) precedence, #define T(name, string, precedence) precedence,
const int8_t Token::m_precedence[NUM_TOKENS] = { const int8_t Token::m_precedence[NUM_TOKENS] =
{
TOKEN_LIST(T, T) TOKEN_LIST(T, T)
}; };
#undef T #undef T
@ -68,10 +73,12 @@ const int8_t Token::m_precedence[NUM_TOKENS] = {
#define KT(a, b, c) 'T', #define KT(a, b, c) 'T',
#define KK(a, b, c) 'K', #define KK(a, b, c) 'K',
const char Token::m_tokenType[] = { const char Token::m_tokenType[] =
{
TOKEN_LIST(KT, KK) TOKEN_LIST(KT, KK)
}; };
#undef KT #undef KT
#undef KK #undef KK
} } }
}

170
libsolidity/Token.h

@ -47,8 +47,10 @@
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
// TOKEN_LIST takes a list of 3 macros M, all of which satisfy the // TOKEN_LIST takes a list of 3 macros M, all of which satisfy the
// same signature M(name, string, precedence), where name is the // same signature M(name, string, precedence), where name is the
@ -227,7 +229,7 @@ namespace solidity {
K(IMPORT, "import", 0) \ K(IMPORT, "import", 0) \
K(LET, "let", 0) \ K(LET, "let", 0) \
K(STATIC, "static", 0) \ K(STATIC, "static", 0) \
/* K(YIELD, "yield", 0) */ \ /* K(YIELD, "yield", 0) */ \
K(SUPER, "super", 0) \ K(SUPER, "super", 0) \
\ \
/* Illegal token - not able to scan. */ \ /* Illegal token - not able to scan. */ \
@ -237,11 +239,13 @@ namespace solidity {
T(WHITESPACE, NULL, 0) T(WHITESPACE, NULL, 0)
class Token { class Token
{
public: public:
// All token values. // All token values.
#define T(name, string, precedence) name, #define T(name, string, precedence) name,
enum Value { enum Value
{
TOKEN_LIST(T, T) TOKEN_LIST(T, T)
NUM_TOKENS NUM_TOKENS
}; };
@ -249,123 +253,110 @@ public:
// Returns a string corresponding to the C++ token name // Returns a string corresponding to the C++ token name
// (e.g. "LT" for the token LT). // (e.g. "LT" for the token LT).
static const char* Name(Value tok) { static const char* getName(Value tok)
{
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned
return m_name[tok]; return m_name[tok];
} }
// Predicates // Predicates
static bool IsKeyword(Value tok) { static bool isKeyword(Value tok) { return m_tokenType[tok] == 'K'; }
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 INIT_VAR <= tok && tok <= ASSIGN_MOD; }
static bool IsIdentifier(Value tok) { static bool isBinaryOp(Value op) { return COMMA <= op && op <= MOD; }
return tok == IDENTIFIER; 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)
static bool IsElementaryTypeName(Value tok) { {
return INT <= tok && tok < TYPES_END;
}
static bool IsAssignmentOp(Value tok) {
return INIT_VAR <= 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; return op == LT || op == LTE || op == GT || op == GTE;
} }
static bool isEqualityOp(Value op) { return op == EQ || op == EQ_STRICT; }
static bool IsEqualityOp(Value op) { static bool isInequalityOp(Value op) { return op == NE || op == NE_STRICT; }
return op == EQ || op == EQ_STRICT; static bool isArithmeticCompareOp(Value op)
} {
return isOrderedRelationalCompareOp(op) ||
static bool IsInequalityOp(Value op) { isEqualityOp(op) || isInequalityOp(op);
return op == NE || op == NE_STRICT;
} }
static bool IsArithmeticCompareOp(Value op) { static Value negateCompareOp(Value op)
return IsOrderedRelationalCompareOp(op) || {
IsEqualityOp(op) || IsInequalityOp(op); BOOST_ASSERT(isArithmeticCompareOp(op));
} switch (op)
{
static Value NegateCompareOp(Value op) { case EQ:
BOOST_ASSERT(IsArithmeticCompareOp(op)); return NE;
switch (op) { case NE:
case EQ: return NE; return EQ;
case NE: return EQ; case EQ_STRICT:
case EQ_STRICT: return NE_STRICT; return NE_STRICT;
case NE_STRICT: return EQ_STRICT; case NE_STRICT:
case LT: return GTE; return EQ_STRICT;
case GT: return LTE; case LT:
case LTE: return GT; return GTE;
case GTE: return LT; case GT:
return LTE;
case LTE:
return GT;
case GTE:
return LT;
default: default:
BOOST_ASSERT(false); // should not get here BOOST_ASSERT(false); // should not get here
return op; return op;
} }
} }
static Value ReverseCompareOp(Value op) { static Value reverseCompareOp(Value op)
BOOST_ASSERT(IsArithmeticCompareOp(op)); {
switch (op) { BOOST_ASSERT(isArithmeticCompareOp(op));
case EQ: return EQ; switch (op)
case NE: return NE; {
case EQ_STRICT: return EQ_STRICT; case EQ:
case NE_STRICT: return NE_STRICT; return EQ;
case LT: return GT; case NE:
case GT: return LT; return NE;
case LTE: return GTE; case EQ_STRICT:
case GTE: return LTE; return EQ_STRICT;
case NE_STRICT:
return NE_STRICT;
case LT:
return GT;
case GT:
return LT;
case LTE:
return GTE;
case GTE:
return LTE;
default: default:
BOOST_ASSERT(false); // should not get here BOOST_ASSERT(false); // should not get here
return op; return op;
} }
} }
static Value AssignmentToBinaryOp(Value op) { static Value AssignmentToBinaryOp(Value op)
BOOST_ASSERT(IsAssignmentOp(op) && op != ASSIGN); {
BOOST_ASSERT(isAssignmentOp(op) && op != ASSIGN);
return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR)); return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR));
} }
static bool IsBitOp(Value op) { static bool isBitOp(Value op) { return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; }
return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; static bool isUnaryOp(Value op) { return (NOT <= op && op <= VOID) || op == ADD || op == SUB; }
} static bool isCountOp(Value op) { return op == INC || op == DEC; }
static bool isShiftOp(Value op) { return (SHL <= op) && (op <= SHR); }
static bool IsUnaryOp(Value op) {
return (NOT <= op && op <= VOID) || 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 // Returns a string corresponding to the JS token string
// (.e., "<" for the token LT) or NULL if the token doesn't // (.e., "<" for the token LT) or NULL if the token doesn't
// have a (unique) string (e.g. an IDENTIFIER). // have a (unique) string (e.g. an IDENTIFIER).
static const char* String(Value tok) { static const char* toString(Value tok)
{
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned. BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned.
return m_string[tok]; return m_string[tok];
} }
// Returns the precedence > 0 for binary and compare // Returns the precedence > 0 for binary and compare
// operators; returns 0 otherwise. // operators; returns 0 otherwise.
static int Precedence(Value tok) { static int precedence(Value tok)
{
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned. BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned.
return m_precedence[tok]; return m_precedence[tok];
} }
@ -377,4 +368,5 @@ private:
static const char m_tokenType[NUM_TOKENS]; static const char m_tokenType[NUM_TOKENS];
}; };
} } }
}

40
libsolidity/Types.cpp

@ -23,12 +23,15 @@
#include <libsolidity/Types.h> #include <libsolidity/Types.h>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken) ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken)
{ {
if (Token::INT <= _typeToken && _typeToken <= Token::HASH256) { if (Token::INT <= _typeToken && _typeToken <= Token::HASH256)
{
int offset = _typeToken - Token::INT; int offset = _typeToken - Token::INT;
int bits = offset % 5; int bits = offset % 5;
if (bits == 0) if (bits == 0)
@ -40,14 +43,13 @@ ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken)
modifier == 0 ? IntegerType::Modifier::SIGNED : modifier == 0 ? IntegerType::Modifier::SIGNED :
modifier == 1 ? IntegerType::Modifier::UNSIGNED : modifier == 1 ? IntegerType::Modifier::UNSIGNED :
IntegerType::Modifier::HASH); IntegerType::Modifier::HASH);
} else if (_typeToken == Token::ADDRESS) { }
else if (_typeToken == Token::ADDRESS)
return std::make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS); return std::make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS);
} else if (_typeToken == Token::BOOL) { else if (_typeToken == Token::BOOL)
return std::make_shared<BoolType>(); return std::make_shared<BoolType>();
} else { else
BOOST_ASSERT(false); BOOST_ASSERT(false); // @todo add other tyes
// @todo add other tyes
}
} }
ptr<Type> Type::fromUserDefinedTypeName(const UserDefinedTypeName& _typeName) ptr<Type> Type::fromUserDefinedTypeName(const UserDefinedTypeName& _typeName)
@ -63,7 +65,8 @@ ptr<Type> Type::fromMapping(const Mapping&)
ptr<Type> Type::forLiteral(const Literal& _literal) ptr<Type> Type::forLiteral(const Literal& _literal)
{ {
switch (_literal.getToken()) { switch (_literal.getToken())
{
case Token::TRUE_LITERAL: case Token::TRUE_LITERAL:
case Token::FALSE_LITERAL: case Token::FALSE_LITERAL:
return std::make_shared<BoolType>(); return std::make_shared<BoolType>();
@ -114,13 +117,12 @@ bool IntegerType::isExplicitlyConvertibleTo(const Type& _convertTo) const
bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const
{ {
if (isAddress()) { if (isAddress())
return Token::IsCompareOp(_operator); return Token::isCompareOp(_operator);
} else if (isHash()) { else if (isHash())
return Token::IsCompareOp(_operator) || Token::IsBitOp(_operator); return Token::isCompareOp(_operator) || Token::isBitOp(_operator);
} else { else
return true; return true;
}
} }
bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const
@ -132,7 +134,8 @@ bool BoolType::isExplicitlyConvertibleTo(const Type& _convertTo) const
{ {
// conversion to integer is fine, but not to address // conversion to integer is fine, but not to address
// this is an example of explicit conversions being not transitive (though implicit should be) // this is an example of explicit conversions being not transitive (though implicit should be)
if (_convertTo.getCategory() == Category::INTEGER) { if (_convertTo.getCategory() == Category::INTEGER)
{
IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo);
if (!convertTo.isAddress()) if (!convertTo.isAddress())
return true; return true;
@ -157,4 +160,5 @@ bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const
} }
} } }
}

36
libsolidity/Types.h

@ -29,8 +29,10 @@
#include <libsolidity/Token.h> #include <libsolidity/Token.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
// AST forward declarations // AST forward declarations
class ContractDefinition; class ContractDefinition;
@ -49,7 +51,8 @@ using ptr = std::shared_ptr<T>;
class Type : private boost::noncopyable class Type : private boost::noncopyable
{ {
public: public:
enum class Category { enum class Category
{
INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE
}; };
@ -62,7 +65,10 @@ public:
virtual Category getCategory() const = 0; virtual Category getCategory() const = 0;
virtual bool isImplicitlyConvertibleTo(const Type&) const { return false; } virtual bool isImplicitlyConvertibleTo(const Type&) const { return false; }
virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const { return isImplicitlyConvertibleTo(_convertTo); } virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const
{
return isImplicitlyConvertibleTo(_convertTo);
}
virtual bool acceptsBinaryOperator(Token::Value) const { return false; } virtual bool acceptsBinaryOperator(Token::Value) const { return false; }
virtual bool acceptsUnaryOperator(Token::Value) const { return false; } virtual bool acceptsUnaryOperator(Token::Value) const { return false; }
}; };
@ -70,7 +76,8 @@ public:
class IntegerType : public Type class IntegerType : public Type
{ {
public: public:
enum class Modifier { enum class Modifier
{
UNSIGNED, SIGNED, HASH, ADDRESS UNSIGNED, SIGNED, HASH, ADDRESS
}; };
virtual Category getCategory() const { return Category::INTEGER; } virtual Category getCategory() const { return Category::INTEGER; }
@ -98,12 +105,18 @@ class BoolType : public Type
public: public:
virtual Category getCategory() const { return Category::BOOL; } virtual Category getCategory() const { return Category::BOOL; }
virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override
{ return _convertTo.getCategory() == Category::BOOL; } {
return _convertTo.getCategory() == Category::BOOL;
}
virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const override; virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const override;
virtual bool acceptsBinaryOperator(Token::Value _operator) const override virtual bool acceptsBinaryOperator(Token::Value _operator) const override
{ return _operator == Token::AND || _operator == Token::OR; } {
return _operator == Token::AND || _operator == Token::OR;
}
virtual bool acceptsUnaryOperator(Token::Value _operator) const override virtual bool acceptsUnaryOperator(Token::Value _operator) const override
{ return _operator == Token::NOT || _operator == Token::DELETE; } {
return _operator == Token::NOT || _operator == Token::DELETE;
}
}; };
class ContractType : public Type class ContractType : public Type
@ -123,7 +136,9 @@ public:
StructType(StructDefinition const& _struct) : m_struct(_struct) {} StructType(StructDefinition const& _struct) : m_struct(_struct) {}
virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const; virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const;
virtual bool acceptsUnaryOperator(Token::Value _operator) const override virtual bool acceptsUnaryOperator(Token::Value _operator) const override
{ return _operator == Token::DELETE; } {
return _operator == Token::DELETE;
}
private: private:
StructDefinition const& m_struct; StructDefinition const& m_struct;
}; };
@ -168,4 +183,5 @@ private:
}; };
} } }
}

16
solc/main.cpp

@ -10,8 +10,10 @@
#include <libsolidity/ASTPrinter.h> #include <libsolidity/ASTPrinter.h>
#include <libsolidity/NameAndTypeResolver.h> #include <libsolidity/NameAndTypeResolver.h>
namespace dev { namespace dev
namespace solidity { {
namespace solidity
{
ptr<ContractDefinition> parseAST(std::string const& _source) ptr<ContractDefinition> parseAST(std::string const& _source)
{ {
@ -20,7 +22,8 @@ ptr<ContractDefinition> parseAST(std::string const& _source)
return parser.parse(scanner); return parser.parse(scanner);
} }
} } // end namespaces }
} // end namespaces
void help() void help()
{ {
@ -44,7 +47,6 @@ void version()
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
std::string infile; std::string infile;
for (int i = 1; i < argc; ++i) for (int i = 1; i < argc; ++i)
{ {
std::string arg = argv[i]; std::string arg = argv[i];
@ -55,7 +57,6 @@ int main(int argc, char** argv)
else else
infile = argv[i]; infile = argv[i];
} }
std::string src; std::string src;
if (infile.empty()) if (infile.empty())
{ {
@ -65,10 +66,11 @@ int main(int argc, char** argv)
getline(std::cin, s); getline(std::cin, s);
src.append(s); src.append(s);
} }
} else { }
else
{
src = dev::asString(dev::contents(infile)); src = dev::asString(dev::contents(infile));
} }
std::cout << "Parsing..." << std::endl; std::cout << "Parsing..." << std::endl;
// @todo catch exception // @todo catch exception
dev::solidity::ptr<dev::solidity::ContractDefinition> ast = dev::solidity::parseAST(src); dev::solidity::ptr<dev::solidity::ContractDefinition> ast = dev::solidity::parseAST(src);

22
test/solidityNameAndTypeResolution.cpp

@ -29,19 +29,23 @@
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
namespace dev { namespace dev
namespace solidity { {
namespace test { namespace solidity
{
namespace test
{
namespace { namespace
void parseTextAndResolveNames(const std::string& _source) {
{ void parseTextAndResolveNames(const std::string& _source)
{
Parser parser; Parser parser;
ptr<ContractDefinition> contract = parser.parse( ptr<ContractDefinition> contract = parser.parse(
std::make_shared<Scanner>(CharStream(_source))); std::make_shared<Scanner>(CharStream(_source)));
NameAndTypeResolver resolver; NameAndTypeResolver resolver;
resolver.resolveNamesAndTypes(*contract); resolver.resolveNamesAndTypes(*contract);
} }
} }
BOOST_AUTO_TEST_SUITE(SolidityNameAndTypeResolution) BOOST_AUTO_TEST_SUITE(SolidityNameAndTypeResolution)
@ -168,5 +172,7 @@ BOOST_AUTO_TEST_CASE(type_inference_explicit_conversion)
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} } } // end namespaces }
}
} // end namespaces

22
test/solidityParser.cpp

@ -28,16 +28,20 @@
#include <libsolidity/Exceptions.h> #include <libsolidity/Exceptions.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
namespace dev { namespace dev
namespace solidity { {
namespace test { namespace solidity
{
namespace test
{
namespace { namespace
ptr<ASTNode> parseText(const std::string& _source) {
{ ptr<ASTNode> parseText(const std::string& _source)
{
Parser parser; Parser parser;
return parser.parse(std::make_shared<Scanner>(CharStream(_source))); return parser.parse(std::make_shared<Scanner>(CharStream(_source)));
} }
} }
BOOST_AUTO_TEST_SUITE(SolidityParser) BOOST_AUTO_TEST_SUITE(SolidityParser)
@ -211,5 +215,7 @@ BOOST_AUTO_TEST_CASE(else_if_statement)
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} } } // end namespaces }
}
} // end namespaces

13
test/solidityScanner.cpp

@ -23,9 +23,12 @@
#include <libsolidity/Scanner.h> #include <libsolidity/Scanner.h>
#include <boost/test/unit_test.hpp> #include <boost/test/unit_test.hpp>
namespace dev { namespace dev
namespace solidity { {
namespace test { namespace solidity
{
namespace test
{
BOOST_AUTO_TEST_SUITE(SolidityScanner) BOOST_AUTO_TEST_SUITE(SolidityScanner)
@ -135,4 +138,6 @@ BOOST_AUTO_TEST_CASE(ambiguities)
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()
} } } // end namespaces }
}
} // end namespaces

Loading…
Cancel
Save