mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Corrected coding style.
This commit is contained in:
parent
4ece1ba1d3
commit
8a506b505f
113
AST.cpp
113
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);
|
||||||
}
|
}
|
||||||
@ -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."));
|
||||||
@ -365,10 +389,10 @@ 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
AST.h
15
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:
|
|||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
@ -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';
|
||||||
}
|
}
|
||||||
|
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
10
ASTPrinter.h
10
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
|
||||||
{
|
{
|
||||||
@ -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
ASTVisitor.h
12
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
BaseTypes.h
12
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
@ -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 {};
|
||||||
|
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
@ -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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
186
Parser.cpp
186
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)
|
||||||
@ -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
Parser.h
9
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
315
Scanner.cpp
315
Scanner.cpp
@ -45,30 +45,42 @@
|
|||||||
|
|
||||||
#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;
|
||||||
@ -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 == '=') {
|
|
||||||
token = selectToken(Token::ASSIGN_SUB);
|
|
||||||
} else {
|
|
||||||
token = Token::SUB;
|
|
||||||
}
|
}
|
||||||
|
else if (m_char == '=')
|
||||||
|
token = selectToken(Token::ASSIGN_SUB);
|
||||||
|
else
|
||||||
|
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,10 +617,10 @@ 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; \
|
||||||
@ -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
Scanner.h
96
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
@ -45,4 +47,5 @@ Declaration*Scope::resolveName(ASTString const& _name, bool _recursive) const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
9
Scope.h
9
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
Token.cpp
21
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
|
||||||
|
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
168
Token.h
168
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
|
||||||
@ -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) ||
|
||||||
|
isEqualityOp(op) || isInequalityOp(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsInequalityOp(Value op) {
|
static Value negateCompareOp(Value op)
|
||||||
return op == NE || op == NE_STRICT;
|
{
|
||||||
}
|
BOOST_ASSERT(isArithmeticCompareOp(op));
|
||||||
|
switch (op)
|
||||||
static bool IsArithmeticCompareOp(Value op) {
|
{
|
||||||
return IsOrderedRelationalCompareOp(op) ||
|
case EQ:
|
||||||
IsEqualityOp(op) || IsInequalityOp(op);
|
return NE;
|
||||||
}
|
case NE:
|
||||||
|
return EQ;
|
||||||
static Value NegateCompareOp(Value op) {
|
case EQ_STRICT:
|
||||||
BOOST_ASSERT(IsArithmeticCompareOp(op));
|
return NE_STRICT;
|
||||||
switch (op) {
|
case NE_STRICT:
|
||||||
case EQ: return NE;
|
return EQ_STRICT;
|
||||||
case NE: return EQ;
|
case LT:
|
||||||
case EQ_STRICT: return NE_STRICT;
|
return GTE;
|
||||||
case NE_STRICT: return EQ_STRICT;
|
case GT:
|
||||||
case LT: return GTE;
|
return LTE;
|
||||||
case GT: return LTE;
|
case LTE:
|
||||||
case LTE: return GT;
|
return GT;
|
||||||
case GTE: return LT;
|
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];
|
||||||
};
|
};
|
||||||
|
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
42
Types.cpp
42
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) {
|
|
||||||
return std::make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS);
|
|
||||||
} else if (_typeToken == Token::BOOL) {
|
|
||||||
return std::make_shared<BoolType>();
|
|
||||||
} else {
|
|
||||||
BOOST_ASSERT(false);
|
|
||||||
// @todo add other tyes
|
|
||||||
}
|
}
|
||||||
|
else if (_typeToken == Token::ADDRESS)
|
||||||
|
return std::make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS);
|
||||||
|
else if (_typeToken == Token::BOOL)
|
||||||
|
return std::make_shared<BoolType>();
|
||||||
|
else
|
||||||
|
BOOST_ASSERT(false); // @todo add other tyes
|
||||||
}
|
}
|
||||||
|
|
||||||
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,14 +117,13 @@ 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
Types.h
36
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:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user