mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Corrected indentation.
This commit is contained in:
parent
0a1ebe4f51
commit
c3faa433ef
22
AST.cpp
22
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
AST.h
228
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
BaseTypes.h
10
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;
|
||||
};
|
||||
|
||||
} }
|
||||
|
356
Parser.cpp
356
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)
|
||||
{}
|
||||
ASTNodeFactory(const Parser& _parser)
|
||||
: m_parser(_parser), m_location(_parser.getPosition(), -1)
|
||||
{}
|
||||
|
||||
void markEndPosition() { m_location.end = m_parser.getEndPosition(); }
|
||||
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;
|
||||
}
|
||||
/// 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)...);
|
||||
}
|
||||
/// @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);
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
|
||||
expectToken(Token::CONTRACT);
|
||||
std::string name = expectIdentifier();
|
||||
expectToken(Token::LBRACE);
|
||||
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();
|
||||
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);
|
||||
m_scanner->next();
|
||||
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)
|
||||
{
|
||||
ASTNodeFactory nodeFactory(*this);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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");
|
||||
}
|
||||
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;
|
||||
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);
|
||||
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);
|
||||
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, ' ') << "^";
|
||||
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();
|
||||
/// @todo make a proper exception hierarchy
|
||||
throw std::exception();
|
||||
}
|
||||
|
||||
|
||||
|
73
Parser.h
73
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;
|
||||
};
|
||||
|
||||
} }
|
||||
|
806
Scanner.cpp
806
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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
|
||||
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();
|
||||
}
|
||||
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;
|
||||
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();
|
||||
const int start_position = getSourcePos();
|
||||
|
||||
while (true) {
|
||||
if (IsLineTerminator(m_char)) {
|
||||
m_hasLineTerminatorBeforeNext = true;
|
||||
} else if (!IsWhiteSpace(m_char)) {
|
||||
break;
|
||||
}
|
||||
advance();
|
||||
}
|
||||
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;
|
||||
// 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();
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
// 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();
|
||||
// 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;
|
||||
switch (m_char) {
|
||||
case '\n':
|
||||
m_hasLineTerminatorBeforeNext = true; // fall-through
|
||||
case ' ':
|
||||
case '\t':
|
||||
token = selectToken(Token::WHITESPACE);
|
||||
break;
|
||||
|
||||
case '"': case '\'':
|
||||
token = scanString();
|
||||
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::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::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::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::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 == '+') {
|
||||
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 '-':
|
||||
// - -- -=
|
||||
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_MUL, Token::MUL);
|
||||
break;
|
||||
|
||||
case '%':
|
||||
// % %=
|
||||
token = selectToken('=', Token::ASSIGN_MOD, Token::MOD);
|
||||
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 = 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::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 '|':
|
||||
// | || |=
|
||||
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 '^':
|
||||
// ^ ^=
|
||||
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 '.':
|
||||
// . Number
|
||||
advance();
|
||||
if (IsDecimalDigit(m_char)) {
|
||||
token = scanNumber(true);
|
||||
} else {
|
||||
token = Token::PERIOD;
|
||||
}
|
||||
break;
|
||||
|
||||
case ':':
|
||||
token = selectToken(Token::COLON);
|
||||
break;
|
||||
case ':':
|
||||
token = selectToken(Token::COLON);
|
||||
break;
|
||||
|
||||
case ';':
|
||||
token = selectToken(Token::SEMICOLON);
|
||||
break;
|
||||
case ';':
|
||||
token = selectToken(Token::SEMICOLON);
|
||||
break;
|
||||
|
||||
case ',':
|
||||
token = selectToken(Token::COMMA);
|
||||
break;
|
||||
case ',':
|
||||
token = selectToken(Token::COMMA);
|
||||
break;
|
||||
|
||||
case '(':
|
||||
token = selectToken(Token::LPAREN);
|
||||
break;
|
||||
case '(':
|
||||
token = selectToken(Token::LPAREN);
|
||||
break;
|
||||
|
||||
case ')':
|
||||
token = selectToken(Token::RPAREN);
|
||||
break;
|
||||
case ')':
|
||||
token = selectToken(Token::RPAREN);
|
||||
break;
|
||||
|
||||
case '[':
|
||||
token = selectToken(Token::LBRACK);
|
||||
break;
|
||||
case '[':
|
||||
token = selectToken(Token::LBRACK);
|
||||
break;
|
||||
|
||||
case ']':
|
||||
token = selectToken(Token::RBRACK);
|
||||
break;
|
||||
case ']':
|
||||
token = selectToken(Token::RBRACK);
|
||||
break;
|
||||
|
||||
case '{':
|
||||
token = selectToken(Token::LBRACE);
|
||||
break;
|
||||
case '{':
|
||||
token = selectToken(Token::LBRACE);
|
||||
break;
|
||||
|
||||
case '}':
|
||||
token = selectToken(Token::RBRACE);
|
||||
break;
|
||||
case '}':
|
||||
token = selectToken(Token::RBRACE);
|
||||
break;
|
||||
|
||||
case '?':
|
||||
token = selectToken(Token::CONDITIONAL);
|
||||
break;
|
||||
case '?':
|
||||
token = selectToken(Token::CONDITIONAL);
|
||||
break;
|
||||
|
||||
case '~':
|
||||
token = selectToken(Token::BIT_NOT);
|
||||
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;
|
||||
}
|
||||
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.
|
||||
// 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();
|
||||
char c = m_char;
|
||||
advance();
|
||||
|
||||
// Skip escaped newlines.
|
||||
if (IsLineTerminator(c))
|
||||
return true;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
// 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
|
||||
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();
|
||||
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;
|
||||
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();
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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
|
||||
}
|
||||
}
|
||||
// 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');
|
||||
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;
|
||||
}
|
||||
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);
|
||||
return std::tuple<int, int>(lineNumber, searchPosition - lineStart);
|
||||
}
|
||||
|
||||
|
||||
|
296
Scanner.h
296
Scanner.h
@ -60,37 +60,37 @@ class ParserRecorder;
|
||||
|
||||
class CharStream {
|
||||
public:
|
||||
CharStream()
|
||||
: m_pos(0)
|
||||
{}
|
||||
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();
|
||||
}
|
||||
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;
|
||||
/// @}
|
||||
/// 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;
|
||||
}
|
||||
// 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_;
|
||||
};
|
||||
private:
|
||||
Scanner* scanner_;
|
||||
bool complete_;
|
||||
};
|
||||
|
||||
explicit Scanner(const CharStream& _source);
|
||||
explicit Scanner(const CharStream& _source);
|
||||
|
||||
// Resets the scanner as if newly constructed with _input as input.
|
||||
void reset(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; }
|
||||
// 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.
|
||||
// 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; }
|
||||
// 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; }
|
||||
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);
|
||||
}
|
||||
/// @}
|
||||
/// 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;
|
||||
}
|
||||
// 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;
|
||||
};
|
||||
// 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();
|
||||
}
|
||||
// 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 addLiteralChar(char c) {
|
||||
m_next_token.literal.push_back(c);
|
||||
}
|
||||
|
||||
inline void dropLiteral() {
|
||||
m_next_token.literal.clear();
|
||||
}
|
||||
inline void dropLiteral() {
|
||||
m_next_token.literal.clear();
|
||||
}
|
||||
|
||||
inline void addLiteralCharAndAdvance() {
|
||||
addLiteralChar(m_char);
|
||||
advance();
|
||||
}
|
||||
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);
|
||||
}
|
||||
// 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(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_;
|
||||
}
|
||||
}
|
||||
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);
|
||||
bool scanHexNumber(char& scanned_number, int expected_length);
|
||||
|
||||
// Scans a single JavaScript token.
|
||||
void scanToken();
|
||||
// Scans a single JavaScript token.
|
||||
void scanToken();
|
||||
|
||||
bool skipWhitespace();
|
||||
Token::Value skipSingleLineComment();
|
||||
Token::Value skipMultiLineComment();
|
||||
bool skipWhitespace();
|
||||
Token::Value skipSingleLineComment();
|
||||
Token::Value skipMultiLineComment();
|
||||
|
||||
void scanDecimalDigits();
|
||||
Token::Value scanNumber(bool _periodSeen);
|
||||
Token::Value scanIdentifierOrKeyword();
|
||||
void scanDecimalDigits();
|
||||
Token::Value scanNumber(bool _periodSeen);
|
||||
Token::Value scanIdentifierOrKeyword();
|
||||
|
||||
Token::Value scanString();
|
||||
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();
|
||||
// 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();
|
||||
}
|
||||
// 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)
|
||||
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;
|
||||
CharStream m_source;
|
||||
|
||||
// one character look-ahead, equals 0 at end of input
|
||||
char m_char;
|
||||
// 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;
|
||||
// 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;
|
||||
};
|
||||
|
||||
} }
|
||||
|
@ -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
|
||||
|
554
Token.h
554
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];
|
||||
}
|
||||
// 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';
|
||||
}
|
||||
// Predicates
|
||||
static bool IsKeyword(Value tok) {
|
||||
return m_tokenType[tok] == 'K';
|
||||
}
|
||||
|
||||
static bool IsIdentifier(Value tok) {
|
||||
return tok == IDENTIFIER;
|
||||
}
|
||||
static bool IsIdentifier(Value tok) {
|
||||
return tok == IDENTIFIER;
|
||||
}
|
||||
|
||||
static bool IsElementaryTypeName(Value tok) {
|
||||
return INT <= tok && tok < TYPES_END;
|
||||
}
|
||||
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 IsAssignmentOp(Value tok) {
|
||||
return INIT_VAR <= tok && tok <= ASSIGN_MOD;
|
||||
}
|
||||
|
||||
static bool IsBinaryOp(Value op) {
|
||||
return COMMA <= op && op <= MOD;
|
||||
}
|
||||
static bool IsBinaryOp(Value op) {
|
||||
return COMMA <= op && op <= MOD;
|
||||
}
|
||||
|
||||
static bool IsTruncatingBinaryOp(Value op) {
|
||||
return BIT_OR <= op && op <= ROR;
|
||||
}
|
||||
static bool IsTruncatingBinaryOp(Value op) {
|
||||
return BIT_OR <= op && op <= ROR;
|
||||
}
|
||||
|
||||
static bool IsCompareOp(Value op) {
|
||||
return EQ <= op && op <= IN;
|
||||
}
|
||||
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 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 IsEqualityOp(Value op) {
|
||||
return op == EQ || op == EQ_STRICT;
|
||||
}
|
||||
|
||||
static bool IsInequalityOp(Value op) {
|
||||
return op == NE || op == NE_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 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 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 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 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 IsUnaryOp(Value op) {
|
||||
return (NOT <= op && op <= VOID) || op == ADD || op == SUB;
|
||||
}
|
||||
|
||||
static bool IsCountOp(Value op) {
|
||||
return op == INC || op == DEC;
|
||||
}
|
||||
static bool IsCountOp(Value op) {
|
||||
return op == INC || op == DEC;
|
||||
}
|
||||
|
||||
static bool IsShiftOp(Value op) {
|
||||
return (SHL <= op) && (op <= SHR);
|
||||
}
|
||||
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 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];
|
||||
}
|
||||
// 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];
|
||||
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…
Reference in New Issue
Block a user