Browse Source

Corrected indentation.

cl-refactor
Christian 10 years ago
parent
commit
3fd9358c3e
  1. 22
      libsolidity/AST.cpp
  2. 228
      libsolidity/AST.h
  3. 10
      libsolidity/BaseTypes.h
  4. 382
      libsolidity/Parser.cpp
  5. 73
      libsolidity/Parser.h
  6. 894
      libsolidity/Scanner.cpp
  7. 354
      libsolidity/Scanner.h
  8. 8
      libsolidity/Token.cpp
  9. 594
      libsolidity/Token.h

22
libsolidity/AST.cpp

@ -1,18 +1,18 @@
/*
This file is part of cpp-ethereum.
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>

228
libsolidity/AST.h

@ -1,18 +1,18 @@
/*
This file is part of cpp-ethereum.
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
@ -50,47 +50,47 @@ class Expression;
class ASTNode
{
public:
explicit ASTNode(Location const& _location)
: m_location(_location)
{}
explicit ASTNode(Location const& _location)
: m_location(_location)
{}
Location getLocation() const { return m_location; }
Location getLocation() const { return m_location; }
private:
Location m_location;
Location m_location;
};
class ContractDefinition : public ASTNode
{
public:
ContractDefinition(Location const& _location,
std::string const& _name,
vecptr<StructDefinition> const& _definedStructs,
vecptr<VariableDeclaration> const& _stateVariables,
vecptr<FunctionDefinition> const& _definedFunctions)
: ASTNode(_location), m_name(_name),
m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions)
{}
ContractDefinition(Location const& _location,
std::string const& _name,
vecptr<StructDefinition> const& _definedStructs,
vecptr<VariableDeclaration> const& _stateVariables,
vecptr<FunctionDefinition> const& _definedFunctions)
: ASTNode(_location), m_name(_name),
m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions)
{}
private:
std::string m_name;
vecptr<StructDefinition> m_definedStructs;
vecptr<VariableDeclaration> m_stateVariables;
vecptr<FunctionDefinition> m_definedFunctions;
std::string m_name;
vecptr<StructDefinition> m_definedStructs;
vecptr<VariableDeclaration> m_stateVariables;
vecptr<FunctionDefinition> m_definedFunctions;
};
class StructDefinition : public ASTNode
{
public:
StructDefinition(Location const& _location,
std::string const& _name,
vecptr<VariableDeclaration> const& _members)
: ASTNode(_location), m_name(_name), m_members(_members)
{}
StructDefinition(Location const& _location,
std::string const& _name,
vecptr<VariableDeclaration> const& _members)
: ASTNode(_location), m_name(_name), m_members(_members)
{}
private:
std::string m_name;
vecptr<VariableDeclaration> m_members;
std::string m_name;
vecptr<VariableDeclaration> m_members;
};
/// Used as function parameter list and return list
@ -99,45 +99,45 @@ private:
class ParameterList : public ASTNode
{
public:
ParameterList(Location const& _location, vecptr<VariableDeclaration> const& _parameters)
: ASTNode(_location), m_parameters(_parameters)
{}
ParameterList(Location const& _location, vecptr<VariableDeclaration> const& _parameters)
: ASTNode(_location), m_parameters(_parameters)
{}
private:
vecptr<VariableDeclaration> m_parameters;
vecptr<VariableDeclaration> m_parameters;
};
class FunctionDefinition : public ASTNode
{
public:
FunctionDefinition(Location const& _location, std::string const& _name, bool _isPublic,
ptr<ParameterList> const& _parameters,
bool _isDeclaredConst,
ptr<ParameterList> const& _returnParameters,
ptr<Block> const& _body)
: ASTNode(_location), m_name(_name), m_isPublic(_isPublic), m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters),
m_body(_body)
{}
FunctionDefinition(Location const& _location, std::string const& _name, bool _isPublic,
ptr<ParameterList> const& _parameters,
bool _isDeclaredConst,
ptr<ParameterList> const& _returnParameters,
ptr<Block> const& _body)
: ASTNode(_location), m_name(_name), m_isPublic(_isPublic), m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters),
m_body(_body)
{}
private:
std::string m_name;
bool m_isPublic;
ptr<ParameterList> m_parameters;
bool m_isDeclaredConst;
ptr<ParameterList> m_returnParameters;
ptr<Block> m_body;
std::string m_name;
bool m_isPublic;
ptr<ParameterList> m_parameters;
bool m_isDeclaredConst;
ptr<ParameterList> m_returnParameters;
ptr<Block> m_body;
};
class VariableDeclaration : public ASTNode
{
public:
VariableDeclaration(Location const& _location,
ptr<TypeName> const& _type,
std::string const& _name)
: ASTNode(_location), m_type(_type), m_name(_name)
{}
VariableDeclaration(Location const& _location,
ptr<TypeName> const& _type,
std::string const& _name)
: ASTNode(_location), m_type(_type), m_name(_name)
{}
private:
ptr<TypeName> m_type; ///< can be empty ("var")
std::string m_name;
ptr<TypeName> m_type; ///< can be empty ("var")
std::string m_name;
};
/// types
@ -146,42 +146,42 @@ private:
class TypeName : public ASTNode
{
public:
explicit TypeName(Location const& _location)
: ASTNode(_location)
{}
explicit TypeName(Location const& _location)
: ASTNode(_location)
{}
};
/// any pre-defined type that is not a mapping
class ElementaryTypeName : public TypeName
{
public:
explicit ElementaryTypeName(Location const& _location, Token::Value _type)
: TypeName(_location), m_type(_type)
{}
explicit ElementaryTypeName(Location const& _location, Token::Value _type)
: TypeName(_location), m_type(_type)
{}
private:
Token::Value m_type;
Token::Value m_type;
};
class UserDefinedTypeName : public TypeName
{
public:
UserDefinedTypeName(Location const& _location, std::string const& _name)
: TypeName(_location), m_name(_name)
{}
UserDefinedTypeName(Location const& _location, std::string const& _name)
: TypeName(_location), m_name(_name)
{}
private:
std::string m_name;
std::string m_name;
};
class Mapping : public TypeName
{
public:
Mapping(Location const& _location, ptr<ElementaryTypeName> const& _keyType,
ptr<TypeName> const& _valueType)
: TypeName(_location), m_keyType(_keyType), m_valueType(_valueType)
{}
Mapping(Location const& _location, ptr<ElementaryTypeName> const& _keyType,
ptr<TypeName> const& _valueType)
: TypeName(_location), m_keyType(_keyType), m_valueType(_valueType)
{}
private:
ptr<ElementaryTypeName> m_keyType;
ptr<TypeName> m_valueType;
ptr<ElementaryTypeName> m_keyType;
ptr<TypeName> m_valueType;
};
/// @}
@ -192,28 +192,28 @@ private:
class Statement : public ASTNode
{
public:
explicit Statement(Location const& _location)
: ASTNode(_location)
{}
explicit Statement(Location const& _location)
: ASTNode(_location)
{}
};
class Block : public Statement
{
public:
explicit Block(Location const& _location)
: Statement(_location)
{}
explicit Block(Location const& _location, vecptr<Statement> const& _statements)
: Statement(_location), m_statements(_statements)
{}
private:
vecptr<Statement> m_statements;
vecptr<Statement> m_statements;
};
class IfStatement : public Statement
{
private:
ptr<Expression> m_condition;
ptr<Statement> m_trueBody;
ptr<Statement> m_falseBody;
ptr<Expression> m_condition;
ptr<Statement> m_trueBody;
ptr<Statement> m_falseBody;
};
class BreakableStatement : public Statement
@ -224,8 +224,8 @@ class BreakableStatement : public Statement
class WhileStatement : public BreakableStatement
{
private:
ptr<Expression> m_condition;
ptr<Statement> m_body;
ptr<Expression> m_condition;
ptr<Statement> m_body;
};
class Continue : public Statement
@ -241,15 +241,15 @@ class Break : public Statement
class Return : public Statement
{
private:
ptr<Expression> m_expression;
ptr<Expression> m_expression;
};
class VariableAssignment : public Statement
{
private:
ptr<VariableDeclaration> m_variable;
Token::Value m_assigmentOperator;
ptr<Expression> m_rightHandSide; ///< can be missing
ptr<VariableDeclaration> m_variable;
Token::Value m_assigmentOperator;
ptr<Expression> m_rightHandSide; ///< can be missing
};
class Expression : public Statement
@ -265,47 +265,47 @@ private:
class Assignment : public Expression
{
private:
ptr<Expression> m_leftHandSide;
Token::Value m_assigmentOperator;
ptr<Expression> m_rightHandSide;
ptr<Expression> m_leftHandSide;
Token::Value m_assigmentOperator;
ptr<Expression> m_rightHandSide;
};
class UnaryOperation : public Expression
{
private:
Token::Value m_operator;
ptr<Expression> m_subExpression;
bool isPrefix;
Token::Value m_operator;
ptr<Expression> m_subExpression;
bool isPrefix;
};
class BinaryOperation : public Expression
{
private:
ptr<Expression> m_left;
ptr<Expression> m_right;
Token::Value m_operator;
ptr<Expression> m_left;
ptr<Expression> m_right;
Token::Value m_operator;
};
/// Can be ordinary function call, type cast or struct construction.
class FunctionCall : public Expression
{
private:
// if m_functionName is the name of a type, store the token directly
std::string m_functionName; // "in place" calls of return values are not possible for now
vecptr<Expression> m_arguments;
// if m_functionName is the name of a type, store the token directly
std::string m_functionName; // "in place" calls of return values are not possible for now
vecptr<Expression> m_arguments;
};
class MemberAccess : public Expression
{
private:
ptr<Expression> m_expression;
std::string m_memberName;
ptr<Expression> m_expression;
std::string m_memberName;
};
class IndexAccess : public Expression
{
ptr<Expression> m_base;
ptr<Expression> m_index;
ptr<Expression> m_base;
ptr<Expression> m_index;
};
class PrimaryExpression : public Expression
@ -315,13 +315,13 @@ class PrimaryExpression : public Expression
class Identifier : public PrimaryExpression
{
private:
std::string m_name;
std::string m_name;
};
class Literal : public PrimaryExpression
{
private:
std::string m_value;
std::string m_value;
};
/// @}

10
libsolidity/BaseTypes.h

@ -7,13 +7,13 @@ namespace solidity {
/// Representation of an interval of source positions.
/// The interval includes start and excludes end.
struct Location {
Location(int _start, int _end) : start(_start), end(_end) { }
Location() : start(-1), end(-1) { }
Location(int _start, int _end) : start(_start), end(_end) { }
Location() : start(-1), end(-1) { }
bool IsValid() const { return start >= 0 && end >= start; }
bool IsValid() const { return start >= 0 && end >= start; }
int start;
int end;
int start;
int end;
};
} }

382
libsolidity/Parser.cpp

@ -1,18 +1,18 @@
/*
This file is part of cpp-ethereum.
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
@ -30,9 +30,9 @@ namespace solidity {
ptr<ASTNode> Parser::parse(std::shared_ptr<Scanner> const& _scanner)
{
m_scanner = _scanner;
m_scanner = _scanner;
return parseContractDefinition();
return parseContractDefinition();
}
@ -41,236 +41,254 @@ ptr<ASTNode> Parser::parse(std::shared_ptr<Scanner> const& _scanner)
class Parser::ASTNodeFactory
{
public:
ASTNodeFactory(const Parser& _parser)
: m_parser(_parser), m_location(_parser.getPosition(), -1)
{}
void markEndPosition() { m_location.end = m_parser.getEndPosition(); }
/// Set the end position to the one of the given node.
void setEndPositionFromNode(const ptr<ASTNode>& _node)
{
m_location.end = _node->getLocation().end;
}
/// @todo: check that this actually uses perfect forwarding
template <class NodeType, typename... Args>
ptr<NodeType> createNode(Args&&... _args)
{
if (m_location.end < 0) markEndPosition();
return std::make_shared<NodeType>(m_location, std::forward<Args>(_args)...);
}
ASTNodeFactory(const Parser& _parser)
: m_parser(_parser), m_location(_parser.getPosition(), -1)
{}
void markEndPosition() { m_location.end = m_parser.getEndPosition(); }
/// Set the end position to the one of the given node.
void setEndPositionFromNode(const ptr<ASTNode>& _node)
{
m_location.end = _node->getLocation().end;
}
/// @todo: check that this actually uses perfect forwarding
template <class NodeType, typename... Args>
ptr<NodeType> createNode(Args&&... _args)
{
if (m_location.end < 0) markEndPosition();
return std::make_shared<NodeType>(m_location, std::forward<Args>(_args)...);
}
private:
const Parser& m_parser;
Location m_location;
const Parser& m_parser;
Location m_location;
};
int Parser::getPosition() const
{
return m_scanner->getCurrentLocation().start;
return m_scanner->getCurrentLocation().start;
}
int Parser::getEndPosition() const
{
return m_scanner->getCurrentLocation().end;
return m_scanner->getCurrentLocation().end;
}
ptr<ContractDefinition> Parser::parseContractDefinition()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::CONTRACT);
std::string name = expectIdentifier();
expectToken(Token::LBRACE);
vecptr<StructDefinition> structs;
vecptr<VariableDeclaration> stateVariables;
vecptr<FunctionDefinition> functions;
bool visibilityIsPublic = true;
while (true) {
Token::Value currentToken = m_scanner->getCurrentToken();
if (currentToken == Token::RBRACE) {
break;
} else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE) {
visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC);
m_scanner->next();
expectToken(Token::COLON);
} else if (currentToken == Token::FUNCTION) {
functions.push_back(parseFunctionDefinition(visibilityIsPublic));
} else if (currentToken == Token::STRUCT) {
structs.push_back(parseStructDefinition());
} else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING ||
Token::IsElementaryTypeName(currentToken)) {
stateVariables.push_back(parseVariableDeclaration());
expectToken(Token::SEMICOLON);
} else {
throwExpectationError("Function, variable or struct declaration expected.");
}
}
nodeFactory.markEndPosition();
m_scanner->next();
expectToken(Token::EOS);
return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::CONTRACT);
std::string name = expectIdentifier();
expectToken(Token::LBRACE);
vecptr<StructDefinition> structs;
vecptr<VariableDeclaration> stateVariables;
vecptr<FunctionDefinition> functions;
bool visibilityIsPublic = true;
while (true) {
Token::Value currentToken = m_scanner->getCurrentToken();
if (currentToken == Token::RBRACE) {
break;
} else if (currentToken == Token::PUBLIC || currentToken == Token::PRIVATE) {
visibilityIsPublic = (m_scanner->getCurrentToken() == Token::PUBLIC);
m_scanner->next();
expectToken(Token::COLON);
} else if (currentToken == Token::FUNCTION) {
functions.push_back(parseFunctionDefinition(visibilityIsPublic));
} else if (currentToken == Token::STRUCT) {
structs.push_back(parseStructDefinition());
} else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING ||
Token::IsElementaryTypeName(currentToken)) {
stateVariables.push_back(parseVariableDeclaration());
expectToken(Token::SEMICOLON);
} else {
throwExpectationError("Function, variable or struct declaration expected.");
}
}
nodeFactory.markEndPosition();
m_scanner->next();
expectToken(Token::EOS);
return nodeFactory.createNode<ContractDefinition>(name, structs, stateVariables, functions);
}
ptr<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::FUNCTION);
std::string name(expectIdentifier());
ptr<ParameterList> parameters(parseParameterList());
bool isDeclaredConst = false;
if (m_scanner->getCurrentToken() == Token::CONST) {
isDeclaredConst = true;
m_scanner->next();
}
ptr<ParameterList> returnParameters;
if (m_scanner->getCurrentToken() == Token::RETURNS) {
m_scanner->next();
returnParameters = parseParameterList();
}
ptr<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, parameters,
isDeclaredConst, returnParameters, block);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::FUNCTION);
std::string name(expectIdentifier());
ptr<ParameterList> parameters(parseParameterList());
bool isDeclaredConst = false;
if (m_scanner->getCurrentToken() == Token::CONST) {
isDeclaredConst = true;
m_scanner->next();
}
ptr<ParameterList> returnParameters;
if (m_scanner->getCurrentToken() == Token::RETURNS) {
m_scanner->next();
returnParameters = parseParameterList();
}
ptr<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block);
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, parameters,
isDeclaredConst, returnParameters, block);
}
ptr<StructDefinition> Parser::parseStructDefinition()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::STRUCT);
std::string name = expectIdentifier();
vecptr<VariableDeclaration> members;
expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE) {
members.push_back(parseVariableDeclaration());
expectToken(Token::SEMICOLON);
}
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
return nodeFactory.createNode<StructDefinition>(name, members);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::STRUCT);
std::string name = expectIdentifier();
vecptr<VariableDeclaration> members;
expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE) {
members.push_back(parseVariableDeclaration());
expectToken(Token::SEMICOLON);
}
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
return nodeFactory.createNode<StructDefinition>(name, members);
}
ptr<VariableDeclaration> Parser::parseVariableDeclaration()
{
ASTNodeFactory nodeFactory(*this);
ASTNodeFactory nodeFactory(*this);
ptr<TypeName> type = parseTypeName();
nodeFactory.markEndPosition();
std::string name = expectIdentifier();
return nodeFactory.createNode<VariableDeclaration>(type, name);
ptr<TypeName> type = parseTypeName();
nodeFactory.markEndPosition();
std::string name = expectIdentifier();
return nodeFactory.createNode<VariableDeclaration>(type, name);
}
ptr<TypeName> Parser::parseTypeName()
{
ptr<TypeName> type;
Token::Value token = m_scanner->getCurrentToken();
if (Token::IsElementaryTypeName(token)) {
type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token);
m_scanner->next();
} else if (token == Token::VAR) {
type = ASTNodeFactory(*this).createNode<TypeName>();
m_scanner->next();
} else if (token == Token::MAPPING) {
type = parseMapping();
} else if (token == Token::IDENTIFIER) {
type = ASTNodeFactory(*this).createNode<UserDefinedTypeName>(m_scanner->getCurrentLiteral());
m_scanner->next();
} else {
throwExpectationError("Expected type name");
}
return type;
ptr<TypeName> type;
Token::Value token = m_scanner->getCurrentToken();
if (Token::IsElementaryTypeName(token)) {
type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token);
m_scanner->next();
} else if (token == Token::VAR) {
type = ASTNodeFactory(*this).createNode<TypeName>();
m_scanner->next();
} else if (token == Token::MAPPING) {
type = parseMapping();
} else if (token == Token::IDENTIFIER) {
type = ASTNodeFactory(*this).createNode<UserDefinedTypeName>(m_scanner->getCurrentLiteral());
m_scanner->next();
} else {
throwExpectationError("Expected type name");
}
return type;
}
ptr<Mapping> Parser::parseMapping()
{
ASTNodeFactory nodeFactory(*this);
ASTNodeFactory nodeFactory(*this);
expectToken(Token::MAPPING);
expectToken(Token::LPAREN);
expectToken(Token::MAPPING);
expectToken(Token::LPAREN);
if (!Token::IsElementaryTypeName(m_scanner->getCurrentToken()))
throwExpectationError("Expected elementary type name for mapping key type");
ptr<ElementaryTypeName> keyType;
keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->getCurrentToken());
m_scanner->next();
if (!Token::IsElementaryTypeName(m_scanner->getCurrentToken()))
throwExpectationError("Expected elementary type name for mapping key type");
ptr<ElementaryTypeName> keyType;
keyType = ASTNodeFactory(*this).createNode<ElementaryTypeName>(m_scanner->getCurrentToken());
m_scanner->next();
expectToken(Token::ARROW);
ptr<TypeName> valueType = parseTypeName();
nodeFactory.markEndPosition();
expectToken(Token::RPAREN);
expectToken(Token::ARROW);
ptr<TypeName> valueType = parseTypeName();
nodeFactory.markEndPosition();
expectToken(Token::RPAREN);
return nodeFactory.createNode<Mapping>(keyType, valueType);
return nodeFactory.createNode<Mapping>(keyType, valueType);
}
ptr<ParameterList> Parser::parseParameterList()
{
ASTNodeFactory nodeFactory(*this);
vecptr<VariableDeclaration> parameters;
expectToken(Token::LPAREN);
if (m_scanner->getCurrentToken() != Token::RPAREN) {
parameters.push_back(parseVariableDeclaration());
while (m_scanner->getCurrentToken() != Token::RPAREN) {
expectToken(Token::COMMA);
parameters.push_back(parseVariableDeclaration());
}
}
nodeFactory.markEndPosition();
m_scanner->next();
return nodeFactory.createNode<ParameterList>(parameters);
ASTNodeFactory nodeFactory(*this);
vecptr<VariableDeclaration> parameters;
expectToken(Token::LPAREN);
if (m_scanner->getCurrentToken() != Token::RPAREN) {
parameters.push_back(parseVariableDeclaration());
while (m_scanner->getCurrentToken() != Token::RPAREN) {
expectToken(Token::COMMA);
parameters.push_back(parseVariableDeclaration());
}
}
nodeFactory.markEndPosition();
m_scanner->next();
return nodeFactory.createNode<ParameterList>(parameters);
}
ptr<Block> Parser::parseBlock()
{
ASTNodeFactory nodeFactory(*this);
expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE) {
m_scanner->next();
// @todo
}
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
return nodeFactory.createNode<Block>();
ASTNodeFactory nodeFactory(*this);
expectToken(Token::LBRACE);
vecptr<Statement> statements;
while (m_scanner->getCurrentToken() != Token::RBRACE) {
m_scanner->next();
statements.push_back(parseStatement());
}
nodeFactory.markEndPosition();
expectToken(Token::RBRACE);
return nodeFactory.createNode<Block>(statements);
}
ptr<Statement> Parser::parseStatement()
{
switch (m_scanner->getCurrentToken()) {
case Token::IF:
return parseIfStatement();
case Token::WHILE:
return parseWhileStatement();
case Token::LBRACE:
return parseBlock();
// starting from here, all statements must be terminated by a semicolon
case Token::CONTINUE: // all following
return
}
}
void Parser::expectToken(Token::Value _value)
{
if (m_scanner->getCurrentToken() != _value)
throwExpectationError(std::string("Expected token ") + std::string(Token::Name(_value)));
m_scanner->next();
if (m_scanner->getCurrentToken() != _value)
throwExpectationError(std::string("Expected token ") + std::string(Token::Name(_value)));
m_scanner->next();
}
std::string Parser::expectIdentifier()
{
if (m_scanner->getCurrentToken() != Token::IDENTIFIER)
throwExpectationError("Expected identifier");
if (m_scanner->getCurrentToken() != Token::IDENTIFIER)
throwExpectationError("Expected identifier");
std::string literal = m_scanner->getCurrentLiteral();
m_scanner->next();
return literal;
std::string literal = m_scanner->getCurrentLiteral();
m_scanner->next();
return literal;
}
void Parser::throwExpectationError(const std::string& _description)
{
int line, column;
std::tie(line, column) = m_scanner->translatePositionToLineColumn(getPosition());
cwarn << "Solidity parser error: " << _description
<< "at line " << (line + 1)
<< ", column " << (column + 1);
cwarn << m_scanner->getLineAtPosition(getPosition());
cwarn << std::string(column, ' ') << "^";
/// @todo make a proper exception hierarchy
throw std::exception();
int line, column;
std::tie(line, column) = m_scanner->translatePositionToLineColumn(getPosition());
cwarn << "Solidity parser error: " << _description
<< "at line " << (line + 1)
<< ", column " << (column + 1);
cwarn << m_scanner->getLineAtPosition(getPosition());
cwarn << std::string(column, ' ') << "^";
/// @todo make a proper exception hierarchy
throw std::exception();
}

73
libsolidity/Parser.h

@ -1,18 +1,18 @@
/*
This file is part of cpp-ethereum.
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Christian <c@ethdev.com>
@ -32,37 +32,38 @@ class Scanner;
class Parser
{
public:
ptr<ASTNode> parse(std::shared_ptr<Scanner> const& _scanner);
ptr<ASTNode> parse(std::shared_ptr<Scanner> const& _scanner);
private:
class ASTNodeFactory;
class ASTNodeFactory;
/// Start position of the current token
int getPosition() const;
/// End position of the current token
int getEndPosition() const;
/// Start position of the current token
int getPosition() const;
/// End position of the current token
int getEndPosition() const;
/// Parsing functions for the AST nodes
/// @{
ptr<ContractDefinition> parseContractDefinition();
ptr<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
ptr<StructDefinition> parseStructDefinition();
ptr<VariableDeclaration> parseVariableDeclaration();
ptr<TypeName> parseTypeName();
ptr<Mapping> parseMapping();
ptr<ParameterList> parseParameterList();
ptr<Block> parseBlock();
/// @}
/// Parsing functions for the AST nodes
/// @{
ptr<ContractDefinition> parseContractDefinition();
ptr<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
ptr<StructDefinition> parseStructDefinition();
ptr<VariableDeclaration> parseVariableDeclaration();
ptr<TypeName> parseTypeName();
ptr<Mapping> parseMapping();
ptr<ParameterList> parseParameterList();
ptr<Block> parseBlock();
ptr<Statement> parseStatement();
/// @}
/// Helper functions
/// @{
/// If current token value is not _value, throw exception otherwise advance token.
void expectToken(Token::Value _value);
std::string expectIdentifier();
void throwExpectationError(const std::string& _description);
/// @}
/// Helper functions
/// @{
/// If current token value is not _value, throw exception otherwise advance token.
void expectToken(Token::Value _value);
std::string expectIdentifier();
void throwExpectationError(const std::string& _description);
/// @}
std::shared_ptr<Scanner> m_scanner;
std::shared_ptr<Scanner> m_scanner;
};
} }

894
libsolidity/Scanner.cpp

@ -49,66 +49,66 @@ namespace dev {
namespace solidity {
namespace {
bool IsDecimalDigit(char c) {
return '0' <= c && c <= '9';
}
bool IsHexDigit(char c) {
return IsDecimalDigit(c)
|| ('a' <= c && c <= 'f')
|| ('A' <= c && c <= 'F');
}
bool IsLineTerminator(char c) { return c == '\n'; }
bool IsWhiteSpace(char c) {
return c == ' ' || c == '\n' || c == '\t';
}
bool IsIdentifierStart(char c) {
return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
}
bool IsIdentifierPart(char c) {
return IsIdentifierStart(c) || IsDecimalDigit(c);
}
int HexValue(char c) {
if (c >= '0' && c <= '9') return c - '0';
else if (c >= 'a' && c <= 'f') return c - 'a' + 10;
else if (c >= 'A' && c <= 'F') return c - 'A' + 10;
else return -1;
}
bool IsDecimalDigit(char c) {
return '0' <= c && c <= '9';
}
bool IsHexDigit(char c) {
return IsDecimalDigit(c)
|| ('a' <= c && c <= 'f')
|| ('A' <= c && c <= 'F');
}
bool IsLineTerminator(char c) { return c == '\n'; }
bool IsWhiteSpace(char c) {
return c == ' ' || c == '\n' || c == '\t';
}
bool IsIdentifierStart(char c) {
return c == '_' || c == '$' || ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z');
}
bool IsIdentifierPart(char c) {
return IsIdentifierStart(c) || IsDecimalDigit(c);
}
int HexValue(char c) {
if (c >= '0' && c <= '9') return c - '0';
else if (c >= 'a' && c <= 'f') return c - 'a' + 10;
else if (c >= 'A' && c <= 'F') return c - 'A' + 10;
else return -1;
}
}
Scanner::Scanner(const CharStream& _source)
{
reset(_source);
reset(_source);
}
void Scanner::reset(const CharStream& _source)
{
m_source = _source;
m_source = _source;
m_char = m_source.get();
skipWhitespace();
scanToken();
next();
m_char = m_source.get();
skipWhitespace();
scanToken();
next();
}
bool Scanner::scanHexNumber(char& scanned_number, int expected_length)
{
BOOST_ASSERT(expected_length <= 4); // prevent overflow
char x = 0;
for (int i = 0; i < expected_length; i++) {
int d = HexValue(m_char);
if (d < 0) {
rollback(i);
return false;
}
x = x * 16 + d;
advance();
}
scanned_number = x;
return true;
BOOST_ASSERT(expected_length <= 4); // prevent overflow
char x = 0;
for (int i = 0; i < expected_length; i++) {
int d = HexValue(m_char);
if (d < 0) {
rollback(i);
return false;
}
x = x * 16 + d;
advance();
}
scanned_number = x;
return true;
}
@ -117,29 +117,29 @@ BOOST_STATIC_ASSERT(Token::NUM_TOKENS <= 0x100);
Token::Value Scanner::next()
{
m_current_token = m_next_token;
m_hasLineTerminatorBeforeNext = false;
m_hasMultilineCommentBeforeNext = false;
scanToken();
return m_current_token.token;
m_current_token = m_next_token;
m_hasLineTerminatorBeforeNext = false;
m_hasMultilineCommentBeforeNext = false;
scanToken();
return m_current_token.token;
}
bool Scanner::skipWhitespace()
{
const int start_position = getSourcePos();
while (true) {
if (IsLineTerminator(m_char)) {
m_hasLineTerminatorBeforeNext = true;
} else if (!IsWhiteSpace(m_char)) {
break;
}
advance();
}
// Return whether or not we skipped any characters.
return getSourcePos() != start_position;
const int start_position = getSourcePos();
while (true) {
if (IsLineTerminator(m_char)) {
m_hasLineTerminatorBeforeNext = true;
} else if (!IsWhiteSpace(m_char)) {
break;
}
advance();
}
// Return whether or not we skipped any characters.
return getSourcePos() != start_position;
}
@ -156,28 +156,28 @@ Token::Value Scanner::skipSingleLineComment()
Token::Value Scanner::skipMultiLineComment()
{
BOOST_ASSERT(m_char == '*');
advance();
while (!isSourcePastEndOfInput()) {
char ch = m_char;
advance();
if (IsLineTerminator(ch)) {
// Following ECMA-262, section 7.4, a comment containing
// a newline will make the comment count as a line-terminator.
m_hasMultilineCommentBeforeNext = true;
}
// If we have reached the end of the multi-line comment, we
// consume the '/' and insert a whitespace. This way all
// multi-line comments are treated as whitespace.
if (ch == '*' && m_char == '/') {
m_char = ' ';
return Token::WHITESPACE;
}
}
// Unterminated multi-line comment.
return Token::ILLEGAL;
BOOST_ASSERT(m_char == '*');
advance();
while (!isSourcePastEndOfInput()) {
char ch = m_char;
advance();
if (IsLineTerminator(ch)) {
// Following ECMA-262, section 7.4, a comment containing
// a newline will make the comment count as a line-terminator.
m_hasMultilineCommentBeforeNext = true;
}
// If we have reached the end of the multi-line comment, we
// consume the '/' and insert a whitespace. This way all
// multi-line comments are treated as whitespace.
if (ch == '*' && m_char == '/') {
m_char = ' ';
return Token::WHITESPACE;
}
}
// Unterminated multi-line comment.
return Token::ILLEGAL;
}
void Scanner::scanToken()
@ -185,224 +185,224 @@ void Scanner::scanToken()
m_next_token.literal.clear();
Token::Value token;
do {
// Remember the position of the next token
m_next_token.location.start = getSourcePos();
switch (m_char) {
case '\n':
m_hasLineTerminatorBeforeNext = true; // fall-through
case ' ':
case '\t':
token = selectToken(Token::WHITESPACE);
break;
case '"': case '\'':
token = scanString();
break;
case '<':
// < <= << <<=
advance();
if (m_char == '=') {
token = selectToken(Token::LTE);
} else if (m_char == '<') {
token = selectToken('=', Token::ASSIGN_SHL, Token::SHL);
} else {
token = Token::LT;
}
break;
case '>':
// > >= >> >>= >>> >>>=
advance();
if (m_char == '=') {
token = selectToken(Token::GTE);
} else if (m_char == '>') {
// >> >>= >>> >>>=
advance();
if (m_char == '=') {
token = selectToken(Token::ASSIGN_SAR);
} else if (m_char == '>') {
token = selectToken('=', Token::ASSIGN_SHR, Token::SHR);
} else {
token = Token::SAR;
}
} else {
token = Token::GT;
}
break;
case '=':
// = == =>
advance();
if (m_char == '=') {
token = selectToken(Token::EQ);
} else if (m_char == '>') {
token = selectToken(Token::ARROW);
} else {
token = Token::ASSIGN;
}
break;
case '!':
// ! != !==
advance();
if (m_char == '=') {
token = selectToken(Token::NE);
} else {
token = Token::NOT;
}
break;
case '+':
// + ++ +=
advance();
if (m_char == '+') {
token = selectToken(Token::INC);
} else if (m_char == '=') {
token = selectToken(Token::ASSIGN_ADD);
} else {
token = Token::ADD;
}
break;
case '-':
// - -- -=
advance();
if (m_char == '-') {
advance();
token = Token::DEC;
} else if (m_char == '=') {
token = selectToken(Token::ASSIGN_SUB);
} else {
token = Token::SUB;
}
break;
case '*':
// * *=
token = selectToken('=', Token::ASSIGN_MUL, Token::MUL);
break;
case '%':
// % %=
token = selectToken('=', Token::ASSIGN_MOD, Token::MOD);
break;
case '/':
// / // /* /=
advance();
if (m_char == '/') {
token = skipSingleLineComment();
} else if (m_char == '*') {
token = skipMultiLineComment();
} else if (m_char == '=') {
token = selectToken(Token::ASSIGN_DIV);
} else {
token = Token::DIV;
}
break;
case '&':
// & && &=
advance();
if (m_char == '&') {
token = selectToken(Token::AND);
} else if (m_char == '=') {
token = selectToken(Token::ASSIGN_BIT_AND);
} else {
token = Token::BIT_AND;
}
break;
case '|':
// | || |=
advance();
if (m_char == '|') {
token = selectToken(Token::OR);
} else if (m_char == '=') {
token = selectToken(Token::ASSIGN_BIT_OR);
} else {
token = Token::BIT_OR;
}
break;
case '^':
// ^ ^=
token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR);
break;
case '.':
// . Number
advance();
if (IsDecimalDigit(m_char)) {
token = scanNumber(true);
} else {
token = Token::PERIOD;
}
break;
case ':':
token = selectToken(Token::COLON);
break;
case ';':
token = selectToken(Token::SEMICOLON);
break;
case ',':
token = selectToken(Token::COMMA);
break;
case '(':
token = selectToken(Token::LPAREN);
break;
case ')':
token = selectToken(Token::RPAREN);
break;
case '[':
token = selectToken(Token::LBRACK);
break;
case ']':
token = selectToken(Token::RBRACK);
break;
case '{':
token = selectToken(Token::LBRACE);
break;
case '}':
token = selectToken(Token::RBRACE);
break;
case '?':
token = selectToken(Token::CONDITIONAL);
break;
case '~':
token = selectToken(Token::BIT_NOT);
break;
default:
if (IsIdentifierStart(m_char)) {
token = scanIdentifierOrKeyword();
} else if (IsDecimalDigit(m_char)) {
token = scanNumber(false);
} else if (skipWhitespace()) {
token = Token::WHITESPACE;
} else if (isSourcePastEndOfInput()) {
token = Token::EOS;
} else {
token = selectToken(Token::ILLEGAL);
}
break;
}
// Continue scanning for tokens as long as we're just skipping
// whitespace.
// Remember the position of the next token
m_next_token.location.start = getSourcePos();
switch (m_char) {
case '\n':
m_hasLineTerminatorBeforeNext = true; // fall-through
case ' ':
case '\t':
token = selectToken(Token::WHITESPACE);
break;
case '"': case '\'':
token = scanString();
break;
case '<':
// < <= << <<=
advance();
if (m_char == '=') {
token = selectToken(Token::LTE);
} else if (m_char == '<') {
token = selectToken('=', Token::ASSIGN_SHL, Token::SHL);
} else {
token = Token::LT;
}
break;
case '>':
// > >= >> >>= >>> >>>=
advance();
if (m_char == '=') {
token = selectToken(Token::GTE);
} else if (m_char == '>') {
// >> >>= >>> >>>=
advance();
if (m_char == '=') {
token = selectToken(Token::ASSIGN_SAR);
} else if (m_char == '>') {
token = selectToken('=', Token::ASSIGN_SHR, Token::SHR);
} else {
token = Token::SAR;
}
} else {
token = Token::GT;
}
break;
case '=':
// = == =>
advance();
if (m_char == '=') {
token = selectToken(Token::EQ);
} else if (m_char == '>') {
token = selectToken(Token::ARROW);
} else {
token = Token::ASSIGN;
}
break;
case '!':
// ! != !==
advance();
if (m_char == '=') {
token = selectToken(Token::NE);
} else {
token = Token::NOT;
}
break;
case '+':
// + ++ +=
advance();
if (m_char == '+') {
token = selectToken(Token::INC);
} else if (m_char == '=') {
token = selectToken(Token::ASSIGN_ADD);
} else {
token = Token::ADD;
}
break;
case '-':
// - -- -=
advance();
if (m_char == '-') {
advance();
token = Token::DEC;
} else if (m_char == '=') {
token = selectToken(Token::ASSIGN_SUB);
} else {
token = Token::SUB;
}
break;
case '*':
// * *=
token = selectToken('=', Token::ASSIGN_MUL, Token::MUL);
break;
case '%':
// % %=
token = selectToken('=', Token::ASSIGN_MOD, Token::MOD);
break;
case '/':
// / // /* /=
advance();
if (m_char == '/') {
token = skipSingleLineComment();
} else if (m_char == '*') {
token = skipMultiLineComment();
} else if (m_char == '=') {
token = selectToken(Token::ASSIGN_DIV);
} else {
token = Token::DIV;
}
break;
case '&':
// & && &=
advance();
if (m_char == '&') {
token = selectToken(Token::AND);
} else if (m_char == '=') {
token = selectToken(Token::ASSIGN_BIT_AND);
} else {
token = Token::BIT_AND;
}
break;
case '|':
// | || |=
advance();
if (m_char == '|') {
token = selectToken(Token::OR);
} else if (m_char == '=') {
token = selectToken(Token::ASSIGN_BIT_OR);
} else {
token = Token::BIT_OR;
}
break;
case '^':
// ^ ^=
token = selectToken('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR);
break;
case '.':
// . Number
advance();
if (IsDecimalDigit(m_char)) {
token = scanNumber(true);
} else {
token = Token::PERIOD;
}
break;
case ':':
token = selectToken(Token::COLON);
break;
case ';':
token = selectToken(Token::SEMICOLON);
break;
case ',':
token = selectToken(Token::COMMA);
break;
case '(':
token = selectToken(Token::LPAREN);
break;
case ')':
token = selectToken(Token::RPAREN);
break;
case '[':
token = selectToken(Token::LBRACK);
break;
case ']':
token = selectToken(Token::RBRACK);
break;
case '{':
token = selectToken(Token::LBRACE);
break;
case '}':
token = selectToken(Token::RBRACE);
break;
case '?':
token = selectToken(Token::CONDITIONAL);
break;
case '~':
token = selectToken(Token::BIT_NOT);
break;
default:
if (IsIdentifierStart(m_char)) {
token = scanIdentifierOrKeyword();
} else if (IsDecimalDigit(m_char)) {
token = scanNumber(false);
} else if (skipWhitespace()) {
token = Token::WHITESPACE;
} else if (isSourcePastEndOfInput()) {
token = Token::EOS;
} else {
token = selectToken(Token::ILLEGAL);
}
break;
}
// Continue scanning for tokens as long as we're just skipping
// whitespace.
} while (token == Token::WHITESPACE);
m_next_token.location.end = getSourcePos();
@ -411,67 +411,67 @@ void Scanner::scanToken()
bool Scanner::scanEscape()
{
char c = m_char;
advance();
// Skip escaped newlines.
if (IsLineTerminator(c))
return true;
switch (c) {
case '\'': // fall through
case '"' : // fall through
case '\\': break;
case 'b' : c = '\b'; break;
case 'f' : c = '\f'; break;
case 'n' : c = '\n'; break;
case 'r' : c = '\r'; break;
case 't' : c = '\t'; break;
case 'u' : {
if (!scanHexNumber(c, 4)) return false;
break;
}
case 'v' : c = '\v'; break;
case 'x' : {
if (!scanHexNumber(c, 2)) return false;
break;
}
}
// According to ECMA-262, section 7.8.4, characters not covered by the
// above cases should be illegal, but they are commonly handled as
// non-escaped characters by JS VMs.
addLiteralChar(c);
return true;
char c = m_char;
advance();
// Skip escaped newlines.
if (IsLineTerminator(c))
return true;
switch (c) {
case '\'': // fall through
case '"' : // fall through
case '\\': break;
case 'b' : c = '\b'; break;
case 'f' : c = '\f'; break;
case 'n' : c = '\n'; break;
case 'r' : c = '\r'; break;
case 't' : c = '\t'; break;
case 'u' : {
if (!scanHexNumber(c, 4)) return false;
break;
}
case 'v' : c = '\v'; break;
case 'x' : {
if (!scanHexNumber(c, 2)) return false;
break;
}
}
// According to ECMA-262, section 7.8.4, characters not covered by the
// above cases should be illegal, but they are commonly handled as
// non-escaped characters by JS VMs.
addLiteralChar(c);
return true;
}
Token::Value Scanner::scanString()
{
const char quote = m_char;
advance(); // consume quote
LiteralScope literal(this);
while (m_char != quote && !isSourcePastEndOfInput() && !IsLineTerminator(m_char)) {
char c = m_char;
advance();
if (c == '\\') {
if (isSourcePastEndOfInput() || !scanEscape()) return Token::ILLEGAL;
} else {
addLiteralChar(c);
}
}
if (m_char != quote) return Token::ILLEGAL;
literal.Complete();
advance(); // consume quote
return Token::STRING_LITERAL;
const char quote = m_char;
advance(); // consume quote
LiteralScope literal(this);
while (m_char != quote && !isSourcePastEndOfInput() && !IsLineTerminator(m_char)) {
char c = m_char;
advance();
if (c == '\\') {
if (isSourcePastEndOfInput() || !scanEscape()) return Token::ILLEGAL;
} else {
addLiteralChar(c);
}
}
if (m_char != quote) return Token::ILLEGAL;
literal.Complete();
advance(); // consume quote
return Token::STRING_LITERAL;
}
void Scanner::scanDecimalDigits()
{
while (IsDecimalDigit(m_char))
addLiteralCharAndAdvance();
while (IsDecimalDigit(m_char))
addLiteralCharAndAdvance();
}
@ -483,53 +483,53 @@ Token::Value Scanner::scanNumber(bool _periodSeen)
LiteralScope literal(this);
if (_periodSeen) {
// we have already seen a decimal point of the float
addLiteralChar('.');
scanDecimalDigits(); // we know we have at least one digit
// we have already seen a decimal point of the float
addLiteralChar('.');
scanDecimalDigits(); // we know we have at least one digit
} else {
// if the first character is '0' we must check for octals and hex
if (m_char == '0') {
addLiteralCharAndAdvance();
// either 0, 0exxx, 0Exxx, 0.xxx, a hex number, a binary number or
// an octal number.
if (m_char == 'x' || m_char == 'X') {
// hex number
kind = HEX;
addLiteralCharAndAdvance();
if (!IsHexDigit(m_char)) {
// we must have at least one hex digit after 'x'/'X'
return Token::ILLEGAL;
}
while (IsHexDigit(m_char)) {
addLiteralCharAndAdvance();
}
}
}
// Parse decimal digits and allow trailing fractional part.
if (kind == DECIMAL) {
scanDecimalDigits(); // optional
if (m_char == '.') {
addLiteralCharAndAdvance();
scanDecimalDigits(); // optional
}
}
// if the first character is '0' we must check for octals and hex
if (m_char == '0') {
addLiteralCharAndAdvance();
// either 0, 0exxx, 0Exxx, 0.xxx, a hex number, a binary number or
// an octal number.
if (m_char == 'x' || m_char == 'X') {
// hex number
kind = HEX;
addLiteralCharAndAdvance();
if (!IsHexDigit(m_char)) {
// we must have at least one hex digit after 'x'/'X'
return Token::ILLEGAL;
}
while (IsHexDigit(m_char)) {
addLiteralCharAndAdvance();
}
}
}
// Parse decimal digits and allow trailing fractional part.
if (kind == DECIMAL) {
scanDecimalDigits(); // optional
if (m_char == '.') {
addLiteralCharAndAdvance();
scanDecimalDigits(); // optional
}
}
}
// scan exponent, if any
if (m_char == 'e' || m_char == 'E') {
BOOST_ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number
if (kind != DECIMAL) return Token::ILLEGAL;
// scan exponent
addLiteralCharAndAdvance();
if (m_char == '+' || m_char == '-')
addLiteralCharAndAdvance();
if (!IsDecimalDigit(m_char)) {
// we must have at least one decimal digit after 'e'/'E'
return Token::ILLEGAL;
}
scanDecimalDigits();
BOOST_ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number
if (kind != DECIMAL) return Token::ILLEGAL;
// scan exponent
addLiteralCharAndAdvance();
if (m_char == '+' || m_char == '-')
addLiteralCharAndAdvance();
if (!IsDecimalDigit(m_char)) {
// we must have at least one decimal digit after 'e'/'E'
return Token::ILLEGAL;
}
scanDecimalDigits();
}
// The source character immediately following a numeric literal must
@ -537,7 +537,7 @@ Token::Value Scanner::scanNumber(bool _periodSeen)
// section 7.8.3, page 17 (note that we read only one decimal digit
// if the value is 0).
if (IsDecimalDigit(m_char) || IsIdentifierStart(m_char))
return Token::ILLEGAL;
return Token::ILLEGAL;
literal.Complete();
@ -637,76 +637,76 @@ static Token::Value KeywordOrIdentifierToken(const std::string& input)
const int kMinLength = 2;
const int kMaxLength = 10;
if (input.size() < kMinLength || input.size() > kMaxLength) {
return Token::IDENTIFIER;
return Token::IDENTIFIER;
}
switch (input[0]) {
default:
default:
#define KEYWORD_GROUP_CASE(ch) \
break; \
case ch:
break; \
case ch:
#define KEYWORD(keyword, token) \
{ \
/* 'keyword' is a char array, so sizeof(keyword) is */ \
/* strlen(keyword) plus 1 for the NUL char. */ \
const int keyword_length = sizeof(keyword) - 1; \
BOOST_STATIC_ASSERT(keyword_length >= kMinLength); \
BOOST_STATIC_ASSERT(keyword_length <= kMaxLength); \
if (input == keyword) { \
return token; \
} \
}
KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD)
{ \
/* 'keyword' is a char array, so sizeof(keyword) is */ \
/* strlen(keyword) plus 1 for the NUL char. */ \
const int keyword_length = sizeof(keyword) - 1; \
BOOST_STATIC_ASSERT(keyword_length >= kMinLength); \
BOOST_STATIC_ASSERT(keyword_length <= kMaxLength); \
if (input == keyword) { \
return token; \
} \
}
KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD)
}
return Token::IDENTIFIER;
}
Token::Value Scanner::scanIdentifierOrKeyword()
{
BOOST_ASSERT(IsIdentifierStart(m_char));
LiteralScope literal(this);
BOOST_ASSERT(IsIdentifierStart(m_char));
LiteralScope literal(this);
addLiteralCharAndAdvance();
addLiteralCharAndAdvance();
// Scan the rest of the identifier characters.
while (IsIdentifierPart(m_char))
addLiteralCharAndAdvance();
// Scan the rest of the identifier characters.
while (IsIdentifierPart(m_char))
addLiteralCharAndAdvance();
literal.Complete();
literal.Complete();
return KeywordOrIdentifierToken(m_next_token.literal);
return KeywordOrIdentifierToken(m_next_token.literal);
}
std::string CharStream::getLineAtPosition(int _position) const
{
// if _position points to \n, it returns the line before the \n
using size_type = std::string::size_type;
size_type searchStart = std::min<size_type>(m_source.size(), _position);
if (searchStart > 0) searchStart--;
size_type lineStart = m_source.rfind('\n', searchStart);
if (lineStart == std::string::npos)
lineStart = 0;
else
lineStart++;
return m_source.substr(lineStart,
std::min(m_source.find('\n', lineStart),
m_source.size()) - lineStart);
// if _position points to \n, it returns the line before the \n
using size_type = std::string::size_type;
size_type searchStart = std::min<size_type>(m_source.size(), _position);
if (searchStart > 0) searchStart--;
size_type lineStart = m_source.rfind('\n', searchStart);
if (lineStart == std::string::npos)
lineStart = 0;
else
lineStart++;
return m_source.substr(lineStart,
std::min(m_source.find('\n', lineStart),
m_source.size()) - lineStart);
}
std::tuple<int, int> CharStream::translatePositionToLineColumn(int _position) const
{
using size_type = std::string::size_type;
size_type searchPosition = std::min<size_type>(m_source.size(), _position);
int lineNumber = std::count(m_source.begin(), m_source.begin() + searchPosition, '\n');
size_type lineStart;
if (searchPosition == 0) {
lineStart = 0;
} else {
lineStart = m_source.rfind('\n', searchPosition - 1);
lineStart = lineStart == std::string::npos ? 0 : lineStart + 1;
}
return std::tuple<int, int>(lineNumber, searchPosition - lineStart);
using size_type = std::string::size_type;
size_type searchPosition = std::min<size_type>(m_source.size(), _position);
int lineNumber = std::count(m_source.begin(), m_source.begin() + searchPosition, '\n');
size_type lineStart;
if (searchPosition == 0) {
lineStart = 0;
} else {
lineStart = m_source.rfind('\n', searchPosition - 1);
lineStart = lineStart == std::string::npos ? 0 : lineStart + 1;
}
return std::tuple<int, int>(lineNumber, searchPosition - lineStart);
}

354
libsolidity/Scanner.h

@ -60,37 +60,37 @@ class ParserRecorder;
class CharStream {
public:
CharStream()
: m_pos(0)
{}
explicit CharStream(const std::string& _source)
: m_source(_source), m_pos(0)
{}
int getPos() const { return m_pos; }
bool isPastEndOfInput() const { return m_pos >= m_source.size(); }
char get() const { return m_source[m_pos]; }
char advanceAndGet() {
if (isPastEndOfInput()) return 0;
++m_pos;
if (isPastEndOfInput()) return 0;
return get();
}
char rollback(size_t _amount) {
BOOST_ASSERT(m_pos >= _amount);
m_pos -= _amount;
return get();
}
/// Functions that help pretty-printing parse errors
/// Do only use in error cases, they are quite expensive.
/// @{
std::string getLineAtPosition(int _position) const;
std::tuple<int, int> translatePositionToLineColumn(int _position) const;
/// @}
CharStream()
: m_pos(0)
{}
explicit CharStream(const std::string& _source)
: m_source(_source), m_pos(0)
{}
int getPos() const { return m_pos; }
bool isPastEndOfInput() const { return m_pos >= m_source.size(); }
char get() const { return m_source[m_pos]; }
char advanceAndGet() {
if (isPastEndOfInput()) return 0;
++m_pos;
if (isPastEndOfInput()) return 0;
return get();
}
char rollback(size_t _amount) {
BOOST_ASSERT(m_pos >= _amount);
m_pos -= _amount;
return get();
}
/// Functions that help pretty-printing parse errors
/// Do only use in error cases, they are quite expensive.
/// @{
std::string getLineAtPosition(int _position) const;
std::tuple<int, int> translatePositionToLineColumn(int _position) const;
/// @}
private:
std::string m_source;
size_t m_pos;
std::string m_source;
size_t m_pos;
};
// ----------------------------------------------------------------------------
@ -98,155 +98,155 @@ private:
class Scanner {
public:
// Scoped helper for literal recording. Automatically drops the literal
// if aborting the scanning before it's complete.
class LiteralScope {
public:
explicit LiteralScope(Scanner* self)
: scanner_(self), complete_(false) {
scanner_->startNewLiteral();
}
~LiteralScope() {
if (!complete_) scanner_->dropLiteral();
}
void Complete() {
complete_ = true;
}
private:
Scanner* scanner_;
bool complete_;
};
explicit Scanner(const CharStream& _source);
// Resets the scanner as if newly constructed with _input as input.
void reset(const CharStream& _source);
// Returns the next token and advances input.
Token::Value next();
// Returns the current token again.
Token::Value getCurrentToken() { return m_current_token.token; }
// Returns the location information for the current token
// (the token last returned by Next()).
Location getCurrentLocation() const { return m_current_token.location; }
const std::string& getCurrentLiteral() const { return m_current_token.literal; }
// Similar functions for the upcoming token.
// One token look-ahead (past the token returned by Next()).
Token::Value peek() const { return m_next_token.token; }
Location peekLocation() const { return m_next_token.location; }
const std::string& peekLiteral() const { return m_next_token.literal; }
/// Functions that help pretty-printing parse errors.
/// Do only use in error cases, they are quite expensive.
/// @{
std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); }
std::tuple<int, int> translatePositionToLineColumn(int _position) const
{
return m_source.translatePositionToLineColumn(_position);
}
/// @}
// Returns true if there was a line terminator before the peek'ed token,
// possibly inside a multi-line comment.
bool hasAnyLineTerminatorBeforeNext() const {
return m_hasLineTerminatorBeforeNext ||
m_hasMultilineCommentBeforeNext;
}
// Scoped helper for literal recording. Automatically drops the literal
// if aborting the scanning before it's complete.
class LiteralScope {
public:
explicit LiteralScope(Scanner* self)
: scanner_(self), complete_(false) {
scanner_->startNewLiteral();
}
~LiteralScope() {
if (!complete_) scanner_->dropLiteral();
}
void Complete() {
complete_ = true;
}
private:
Scanner* scanner_;
bool complete_;
};
explicit Scanner(const CharStream& _source);
// Resets the scanner as if newly constructed with _input as input.
void reset(const CharStream& _source);
// Returns the next token and advances input.
Token::Value next();
// Returns the current token again.
Token::Value getCurrentToken() { return m_current_token.token; }
// Returns the location information for the current token
// (the token last returned by Next()).
Location getCurrentLocation() const { return m_current_token.location; }
const std::string& getCurrentLiteral() const { return m_current_token.literal; }
// Similar functions for the upcoming token.
// One token look-ahead (past the token returned by Next()).
Token::Value peek() const { return m_next_token.token; }
Location peekLocation() const { return m_next_token.location; }
const std::string& peekLiteral() const { return m_next_token.literal; }
/// Functions that help pretty-printing parse errors.
/// Do only use in error cases, they are quite expensive.
/// @{
std::string getLineAtPosition(int _position) const { return m_source.getLineAtPosition(_position); }
std::tuple<int, int> translatePositionToLineColumn(int _position) const
{
return m_source.translatePositionToLineColumn(_position);
}
/// @}
// Returns true if there was a line terminator before the peek'ed token,
// possibly inside a multi-line comment.
bool hasAnyLineTerminatorBeforeNext() const {
return m_hasLineTerminatorBeforeNext ||
m_hasMultilineCommentBeforeNext;
}
private:
// Used for the current and look-ahead token.
struct TokenDesc {
Token::Value token;
Location location;
std::string literal;
};
// Literal buffer support
inline void startNewLiteral() {
m_next_token.literal.clear();
}
inline void addLiteralChar(char c) {
m_next_token.literal.push_back(c);
}
inline void dropLiteral() {
m_next_token.literal.clear();
}
inline void addLiteralCharAndAdvance() {
addLiteralChar(m_char);
advance();
}
// Low-level scanning support.
bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); }
void rollback(int amount) {
m_char = m_source.rollback(amount);
}
inline Token::Value selectToken(Token::Value tok) {
advance();
return tok;
}
inline Token::Value selectToken(char next, Token::Value then, Token::Value else_) {
advance();
if (m_char == next) {
advance();
return then;
} else {
return else_;
}
}
bool scanHexNumber(char& scanned_number, int expected_length);
// Scans a single JavaScript token.
void scanToken();
bool skipWhitespace();
Token::Value skipSingleLineComment();
Token::Value skipMultiLineComment();
void scanDecimalDigits();
Token::Value scanNumber(bool _periodSeen);
Token::Value scanIdentifierOrKeyword();
Token::Value scanString();
// Scans an escape-sequence which is part of a string and adds the
// decoded character to the current literal. Returns true if a pattern
// is scanned.
bool scanEscape();
// Return the current source position.
int getSourcePos() {
return m_source.getPos();
}
bool isSourcePastEndOfInput() {
return m_source.isPastEndOfInput();
}
TokenDesc m_current_token; // desc for current token (as returned by Next())
TokenDesc m_next_token; // desc for next token (one token look-ahead)
CharStream m_source;
// one character look-ahead, equals 0 at end of input
char m_char;
// Whether there is a line terminator whitespace character after
// the current token, and before the next. Does not count newlines
// inside multiline comments.
bool m_hasLineTerminatorBeforeNext;
// Whether there is a multi-line comment that contains a
// line-terminator after the current token, and before the next.
bool m_hasMultilineCommentBeforeNext;
// Used for the current and look-ahead token.
struct TokenDesc {
Token::Value token;
Location location;
std::string literal;
};
// Literal buffer support
inline void startNewLiteral() {
m_next_token.literal.clear();
}
inline void addLiteralChar(char c) {
m_next_token.literal.push_back(c);
}
inline void dropLiteral() {
m_next_token.literal.clear();
}
inline void addLiteralCharAndAdvance() {
addLiteralChar(m_char);
advance();
}
// Low-level scanning support.
bool advance() { m_char = m_source.advanceAndGet(); return !m_source.isPastEndOfInput(); }
void rollback(int amount) {
m_char = m_source.rollback(amount);
}
inline Token::Value selectToken(Token::Value tok) {
advance();
return tok;
}
inline Token::Value selectToken(char next, Token::Value then, Token::Value else_) {
advance();
if (m_char == next) {
advance();
return then;
} else {
return else_;
}
}
bool scanHexNumber(char& scanned_number, int expected_length);
// Scans a single JavaScript token.
void scanToken();
bool skipWhitespace();
Token::Value skipSingleLineComment();
Token::Value skipMultiLineComment();
void scanDecimalDigits();
Token::Value scanNumber(bool _periodSeen);
Token::Value scanIdentifierOrKeyword();
Token::Value scanString();
// Scans an escape-sequence which is part of a string and adds the
// decoded character to the current literal. Returns true if a pattern
// is scanned.
bool scanEscape();
// Return the current source position.
int getSourcePos() {
return m_source.getPos();
}
bool isSourcePastEndOfInput() {
return m_source.isPastEndOfInput();
}
TokenDesc m_current_token; // desc for current token (as returned by Next())
TokenDesc m_next_token; // desc for next token (one token look-ahead)
CharStream m_source;
// one character look-ahead, equals 0 at end of input
char m_char;
// Whether there is a line terminator whitespace character after
// the current token, and before the next. Does not count newlines
// inside multiline comments.
bool m_hasLineTerminatorBeforeNext;
// Whether there is a multi-line comment that contains a
// line-terminator after the current token, and before the next.
bool m_hasMultilineCommentBeforeNext;
};
} }

8
libsolidity/Token.cpp

@ -47,21 +47,21 @@ namespace solidity {
#define T(name, string, precedence) #name,
const char* const Token::m_name[NUM_TOKENS] = {
TOKEN_LIST(T, T)
TOKEN_LIST(T, T)
};
#undef T
#define T(name, string, precedence) string,
const char* const Token::m_string[NUM_TOKENS] = {
TOKEN_LIST(T, T)
TOKEN_LIST(T, T)
};
#undef T
#define T(name, string, precedence) precedence,
const int8_t Token::m_precedence[NUM_TOKENS] = {
TOKEN_LIST(T, T)
TOKEN_LIST(T, T)
};
#undef T
@ -69,7 +69,7 @@ const int8_t Token::m_precedence[NUM_TOKENS] = {
#define KT(a, b, c) 'T',
#define KK(a, b, c) 'K',
const char Token::m_tokenType[] = {
TOKEN_LIST(KT, KK)
TOKEN_LIST(KT, KK)
};
#undef KT
#undef KK

594
libsolidity/Token.h

@ -65,309 +65,309 @@ namespace solidity {
#define IGNORE_TOKEN(name, string, precedence)
#define TOKEN_LIST(T, K) \
/* End of source indicator. */ \
T(EOS, "EOS", 0) \
\
/* Punctuators (ECMA-262, section 7.7, page 15). */ \
T(LPAREN, "(", 0) \
T(RPAREN, ")", 0) \
T(LBRACK, "[", 0) \
T(RBRACK, "]", 0) \
T(LBRACE, "{", 0) \
T(RBRACE, "}", 0) \
T(COLON, ":", 0) \
T(SEMICOLON, ";", 0) \
T(PERIOD, ".", 0) \
T(CONDITIONAL, "?", 3) \
T(INC, "++", 0) \
T(DEC, "--", 0) \
T(ARROW, "=>", 0) \
\
/* Assignment operators. */ \
/* IsAssignmentOp() and Assignment::is_compound() relies on */ \
/* this block of enum values being contiguous and sorted in the */ \
/* same order! */ \
T(INIT_VAR, "=init_var", 2) /* AST-use only. */ \
T(INIT_LET, "=init_let", 2) /* AST-use only. */ \
T(INIT_CONST, "=init_const", 2) /* AST-use only. */ \
T(INIT_CONST_LEGACY, "=init_const_legacy", 2) /* AST-use only. */ \
T(ASSIGN, "=", 2) \
T(ASSIGN_BIT_OR, "|=", 2) \
T(ASSIGN_BIT_XOR, "^=", 2) \
T(ASSIGN_BIT_AND, "&=", 2) \
T(ASSIGN_SHL, "<<=", 2) \
T(ASSIGN_SAR, ">>=", 2) \
T(ASSIGN_SHR, ">>>=", 2) \
T(ASSIGN_ADD, "+=", 2) \
T(ASSIGN_SUB, "-=", 2) \
T(ASSIGN_MUL, "*=", 2) \
T(ASSIGN_DIV, "/=", 2) \
T(ASSIGN_MOD, "%=", 2) \
\
/* Binary operators sorted by precedence. */ \
/* IsBinaryOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
T(COMMA, ",", 1) \
T(OR, "||", 4) \
T(AND, "&&", 5) \
T(BIT_OR, "|", 6) \
T(BIT_XOR, "^", 7) \
T(BIT_AND, "&", 8) \
T(SHL, "<<", 11) \
T(SAR, ">>", 11) \
T(SHR, ">>>", 11) \
T(ROR, "rotate right", 11) /* only used by Crankshaft */ \
T(ADD, "+", 12) \
T(SUB, "-", 12) \
T(MUL, "*", 13) \
T(DIV, "/", 13) \
T(MOD, "%", 13) \
\
/* Compare operators sorted by precedence. */ \
/* IsCompareOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
T(EQ, "==", 9) \
T(NE, "!=", 9) \
T(EQ_STRICT, "===", 9) \
T(NE_STRICT, "!==", 9) \
T(LT, "<", 10) \
T(GT, ">", 10) \
T(LTE, "<=", 10) \
T(GTE, ">=", 10) \
K(INSTANCEOF, "instanceof", 10) \
K(IN, "in", 10) \
\
/* Unary operators. */ \
/* IsUnaryOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
T(NOT, "!", 0) \
T(BIT_NOT, "~", 0) \
K(DELETE, "delete", 0) \
K(TYPEOF, "typeof", 0) \
K(VOID, "void", 0) \
\
/* Keywords (ECMA-262, section 7.5.2, page 13). */ \
K(BREAK, "break", 0) \
K(CASE, "case", 0) \
K(CATCH, "catch", 0) \
K(CONTINUE, "continue", 0) \
K(CONTRACT, "contract", 0) \
K(DEBUGGER, "debugger", 0) \
K(DEFAULT, "default", 0) \
/* DELETE */ \
K(DO, "do", 0) \
K(ELSE, "else", 0) \
K(FINALLY, "finally", 0) \
K(FOR, "for", 0) \
K(FUNCTION, "function", 0) \
K(IF, "if", 0) \
/* IN */ \
/* INSTANCEOF */ \
K(MAPPING, "mapping", 0) \
K(NEW, "new", 0) \
K(PUBLIC, "public", 0) \
K(PRIVATE, "private", 0) \
K(RETURN, "return", 0) \
K(RETURNS, "returns", 0) \
K(STRUCT, "struct", 0) \
K(SWITCH, "switch", 0) \
K(THIS, "this", 0) \
K(THROW, "throw", 0) \
K(TRY, "try", 0) \
/* TYPEOF */ \
K(VAR, "var", 0) \
/* VOID */ \
K(WHILE, "while", 0) \
K(WITH, "with", 0) \
\
/* type keywords, keep them in this order, keep int as first keyword TODO more to be added */ \
K(INT, "int", 0) \
K(INT32, "int32", 0) \
K(INT64, "int64", 0) \
K(INT128, "int128", 0) \
K(INT256, "int256", 0) \
K(UINT, "uint", 0) \
K(UINT32, "uint32", 0) \
K(UINT64, "uint64", 0) \
K(UINT128, "uint128", 0) \
K(UINT256, "uint256", 0) \
K(HASH, "hash", 0) \
K(HASH32, "hash32", 0) \
K(HASH64, "hash64", 0) \
K(HASH128, "hash128", 0) \
K(HASH256, "hash256", 0) \
K(ADDRESS, "address", 0) \
K(BOOL, "bool", 0) \
K(STRING_TYPE, "string", 0) \
K(TEXT, "text", 0) \
K(REAL, "real", 0) \
K(UREAL, "ureal", 0) \
T(TYPES_END, NULL, 0) /* used as type enum end marker */ \
\
/* Literals (ECMA-262, section 7.8, page 16). */ \
K(NULL_LITERAL, "null", 0) \
K(TRUE_LITERAL, "true", 0) \
K(FALSE_LITERAL, "false", 0) \
T(NUMBER, NULL, 0) \
T(STRING_LITERAL, NULL, 0) \
\
/* Identifiers (not keywords or future reserved words). */ \
T(IDENTIFIER, NULL, 0) \
\
/* Future reserved words (ECMA-262, section 7.6.1.2). */ \
T(FUTURE_RESERVED_WORD, NULL, 0) \
T(FUTURE_STRICT_RESERVED_WORD, NULL, 0) \
K(CLASS, "class", 0) \
K(CONST, "const", 0) \
K(EXPORT, "export", 0) \
K(EXTENDS, "extends", 0) \
K(IMPORT, "import", 0) \
K(LET, "let", 0) \
K(STATIC, "static", 0) \
/* K(YIELD, "yield", 0) */ \
K(SUPER, "super", 0) \
\
/* Illegal token - not able to scan. */ \
T(ILLEGAL, "ILLEGAL", 0) \
\
/* Scanner-internal use only. */ \
T(WHITESPACE, NULL, 0)
#define TOKEN_LIST(T, K) \
/* End of source indicator. */ \
T(EOS, "EOS", 0) \
\
/* Punctuators (ECMA-262, section 7.7, page 15). */ \
T(LPAREN, "(", 0) \
T(RPAREN, ")", 0) \
T(LBRACK, "[", 0) \
T(RBRACK, "]", 0) \
T(LBRACE, "{", 0) \
T(RBRACE, "}", 0) \
T(COLON, ":", 0) \
T(SEMICOLON, ";", 0) \
T(PERIOD, ".", 0) \
T(CONDITIONAL, "?", 3) \
T(INC, "++", 0) \
T(DEC, "--", 0) \
T(ARROW, "=>", 0) \
\
/* Assignment operators. */ \
/* IsAssignmentOp() and Assignment::is_compound() relies on */ \
/* this block of enum values being contiguous and sorted in the */ \
/* same order! */ \
T(INIT_VAR, "=init_var", 2) /* AST-use only. */ \
T(INIT_LET, "=init_let", 2) /* AST-use only. */ \
T(INIT_CONST, "=init_const", 2) /* AST-use only. */ \
T(INIT_CONST_LEGACY, "=init_const_legacy", 2) /* AST-use only. */ \
T(ASSIGN, "=", 2) \
T(ASSIGN_BIT_OR, "|=", 2) \
T(ASSIGN_BIT_XOR, "^=", 2) \
T(ASSIGN_BIT_AND, "&=", 2) \
T(ASSIGN_SHL, "<<=", 2) \
T(ASSIGN_SAR, ">>=", 2) \
T(ASSIGN_SHR, ">>>=", 2) \
T(ASSIGN_ADD, "+=", 2) \
T(ASSIGN_SUB, "-=", 2) \
T(ASSIGN_MUL, "*=", 2) \
T(ASSIGN_DIV, "/=", 2) \
T(ASSIGN_MOD, "%=", 2) \
\
/* Binary operators sorted by precedence. */ \
/* IsBinaryOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
T(COMMA, ",", 1) \
T(OR, "||", 4) \
T(AND, "&&", 5) \
T(BIT_OR, "|", 6) \
T(BIT_XOR, "^", 7) \
T(BIT_AND, "&", 8) \
T(SHL, "<<", 11) \
T(SAR, ">>", 11) \
T(SHR, ">>>", 11) \
T(ROR, "rotate right", 11) /* only used by Crankshaft */ \
T(ADD, "+", 12) \
T(SUB, "-", 12) \
T(MUL, "*", 13) \
T(DIV, "/", 13) \
T(MOD, "%", 13) \
\
/* Compare operators sorted by precedence. */ \
/* IsCompareOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
T(EQ, "==", 9) \
T(NE, "!=", 9) \
T(EQ_STRICT, "===", 9) \
T(NE_STRICT, "!==", 9) \
T(LT, "<", 10) \
T(GT, ">", 10) \
T(LTE, "<=", 10) \
T(GTE, ">=", 10) \
K(INSTANCEOF, "instanceof", 10) \
K(IN, "in", 10) \
\
/* Unary operators. */ \
/* IsUnaryOp() relies on this block of enum values */ \
/* being contiguous and sorted in the same order! */ \
T(NOT, "!", 0) \
T(BIT_NOT, "~", 0) \
K(DELETE, "delete", 0) \
K(TYPEOF, "typeof", 0) \
K(VOID, "void", 0) \
\
/* Keywords (ECMA-262, section 7.5.2, page 13). */ \
K(BREAK, "break", 0) \
K(CASE, "case", 0) \
K(CATCH, "catch", 0) \
K(CONTINUE, "continue", 0) \
K(CONTRACT, "contract", 0) \
K(DEBUGGER, "debugger", 0) \
K(DEFAULT, "default", 0) \
/* DELETE */ \
K(DO, "do", 0) \
K(ELSE, "else", 0) \
K(FINALLY, "finally", 0) \
K(FOR, "for", 0) \
K(FUNCTION, "function", 0) \
K(IF, "if", 0) \
/* IN */ \
/* INSTANCEOF */ \
K(MAPPING, "mapping", 0) \
K(NEW, "new", 0) \
K(PUBLIC, "public", 0) \
K(PRIVATE, "private", 0) \
K(RETURN, "return", 0) \
K(RETURNS, "returns", 0) \
K(STRUCT, "struct", 0) \
K(SWITCH, "switch", 0) \
K(THIS, "this", 0) \
K(THROW, "throw", 0) \
K(TRY, "try", 0) \
/* TYPEOF */ \
K(VAR, "var", 0) \
/* VOID */ \
K(WHILE, "while", 0) \
K(WITH, "with", 0) \
\
/* type keywords, keep them in this order, keep int as first keyword TODO more to be added */ \
K(INT, "int", 0) \
K(INT32, "int32", 0) \
K(INT64, "int64", 0) \
K(INT128, "int128", 0) \
K(INT256, "int256", 0) \
K(UINT, "uint", 0) \
K(UINT32, "uint32", 0) \
K(UINT64, "uint64", 0) \
K(UINT128, "uint128", 0) \
K(UINT256, "uint256", 0) \
K(HASH, "hash", 0) \
K(HASH32, "hash32", 0) \
K(HASH64, "hash64", 0) \
K(HASH128, "hash128", 0) \
K(HASH256, "hash256", 0) \
K(ADDRESS, "address", 0) \
K(BOOL, "bool", 0) \
K(STRING_TYPE, "string", 0) \
K(TEXT, "text", 0) \
K(REAL, "real", 0) \
K(UREAL, "ureal", 0) \
T(TYPES_END, NULL, 0) /* used as type enum end marker */ \
\
/* Literals (ECMA-262, section 7.8, page 16). */ \
K(NULL_LITERAL, "null", 0) \
K(TRUE_LITERAL, "true", 0) \
K(FALSE_LITERAL, "false", 0) \
T(NUMBER, NULL, 0) \
T(STRING_LITERAL, NULL, 0) \
\
/* Identifiers (not keywords or future reserved words). */ \
T(IDENTIFIER, NULL, 0) \
\
/* Future reserved words (ECMA-262, section 7.6.1.2). */ \
T(FUTURE_RESERVED_WORD, NULL, 0) \
T(FUTURE_STRICT_RESERVED_WORD, NULL, 0) \
K(CLASS, "class", 0) \
K(CONST, "const", 0) \
K(EXPORT, "export", 0) \
K(EXTENDS, "extends", 0) \
K(IMPORT, "import", 0) \
K(LET, "let", 0) \
K(STATIC, "static", 0) \
/* K(YIELD, "yield", 0) */ \
K(SUPER, "super", 0) \
\
/* Illegal token - not able to scan. */ \
T(ILLEGAL, "ILLEGAL", 0) \
\
/* Scanner-internal use only. */ \
T(WHITESPACE, NULL, 0)
class Token {
public:
// All token values.
public:
// All token values.
#define T(name, string, precedence) name,
enum Value {
TOKEN_LIST(T, T)
NUM_TOKENS
};
enum Value {
TOKEN_LIST(T, T)
NUM_TOKENS
};
#undef T
// Returns a string corresponding to the C++ token name
// (e.g. "LT" for the token LT).
static const char* Name(Value tok) {
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned
return m_name[tok];
}
// Predicates
static bool IsKeyword(Value tok) {
return m_tokenType[tok] == 'K';
}
static bool IsIdentifier(Value tok) {
return tok == IDENTIFIER;
}
static bool IsElementaryTypeName(Value tok) {
return INT <= tok && tok < TYPES_END;
}
static bool IsAssignmentOp(Value tok) {
return 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 <= ROR;
}
static bool IsCompareOp(Value op) {
return EQ <= op && op <= IN;
}
static bool IsOrderedRelationalCompareOp(Value op) {
return op == LT || op == LTE || op == GT || op == GTE;
}
static bool IsEqualityOp(Value op) {
return op == EQ || op == EQ_STRICT;
}
static bool IsInequalityOp(Value op) {
return op == NE || op == NE_STRICT;
}
static bool IsArithmeticCompareOp(Value op) {
return IsOrderedRelationalCompareOp(op) ||
IsEqualityOp(op) || IsInequalityOp(op);
}
static Value NegateCompareOp(Value op) {
BOOST_ASSERT(IsArithmeticCompareOp(op));
switch (op) {
case EQ: return NE;
case NE: return EQ;
case EQ_STRICT: return NE_STRICT;
case NE_STRICT: return EQ_STRICT;
case LT: return GTE;
case GT: return LTE;
case LTE: return GT;
case GTE: return LT;
default:
BOOST_ASSERT(false); // should not get here
return op;
}
}
static Value ReverseCompareOp(Value op) {
BOOST_ASSERT(IsArithmeticCompareOp(op));
switch (op) {
case EQ: return EQ;
case NE: return NE;
case EQ_STRICT: 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:
BOOST_ASSERT(false); // should not get here
return op;
}
}
static bool IsBitOp(Value op) {
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);
}
// Returns a string corresponding to the JS token string
// (.e., "<" for the token LT) or NULL if the token doesn't
// have a (unique) string (e.g. an IDENTIFIER).
static const char* String(Value tok) {
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned.
return m_string[tok];
}
// Returns the precedence > 0 for binary and compare
// operators; returns 0 otherwise.
static int Precedence(Value tok) {
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned.
return m_precedence[tok];
}
private:
static const char* const m_name[NUM_TOKENS];
static const char* const m_string[NUM_TOKENS];
static const int8_t m_precedence[NUM_TOKENS];
static const char m_tokenType[NUM_TOKENS];
// Returns a string corresponding to the C++ token name
// (e.g. "LT" for the token LT).
static const char* Name(Value tok) {
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned
return m_name[tok];
}
// Predicates
static bool IsKeyword(Value tok) {
return m_tokenType[tok] == 'K';
}
static bool IsIdentifier(Value tok) {
return tok == IDENTIFIER;
}
static bool IsElementaryTypeName(Value tok) {
return INT <= tok && tok < TYPES_END;
}
static bool IsAssignmentOp(Value tok) {
return 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 <= ROR;
}
static bool IsCompareOp(Value op) {
return EQ <= op && op <= IN;
}
static bool IsOrderedRelationalCompareOp(Value op) {
return op == LT || op == LTE || op == GT || op == GTE;
}
static bool IsEqualityOp(Value op) {
return op == EQ || op == EQ_STRICT;
}
static bool IsInequalityOp(Value op) {
return op == NE || op == NE_STRICT;
}
static bool IsArithmeticCompareOp(Value op) {
return IsOrderedRelationalCompareOp(op) ||
IsEqualityOp(op) || IsInequalityOp(op);
}
static Value NegateCompareOp(Value op) {
BOOST_ASSERT(IsArithmeticCompareOp(op));
switch (op) {
case EQ: return NE;
case NE: return EQ;
case EQ_STRICT: return NE_STRICT;
case NE_STRICT: return EQ_STRICT;
case LT: return GTE;
case GT: return LTE;
case LTE: return GT;
case GTE: return LT;
default:
BOOST_ASSERT(false); // should not get here
return op;
}
}
static Value ReverseCompareOp(Value op) {
BOOST_ASSERT(IsArithmeticCompareOp(op));
switch (op) {
case EQ: return EQ;
case NE: return NE;
case EQ_STRICT: 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:
BOOST_ASSERT(false); // should not get here
return op;
}
}
static bool IsBitOp(Value op) {
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);
}
// Returns a string corresponding to the JS token string
// (.e., "<" for the token LT) or NULL if the token doesn't
// have a (unique) string (e.g. an IDENTIFIER).
static const char* String(Value tok) {
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned.
return m_string[tok];
}
// Returns the precedence > 0 for binary and compare
// operators; returns 0 otherwise.
static int Precedence(Value tok) {
BOOST_ASSERT(tok < NUM_TOKENS); // tok is unsigned.
return m_precedence[tok];
}
private:
static const char* const m_name[NUM_TOKENS];
static const char* const m_string[NUM_TOKENS];
static const int8_t m_precedence[NUM_TOKENS];
static const char m_tokenType[NUM_TOKENS];
};
} }

Loading…
Cancel
Save