mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
User-defined operators: AST
This commit is contained in:
parent
fa0d3a44a7
commit
9a36438441
@ -63,6 +63,7 @@ set(sources
|
|||||||
ast/CallGraph.cpp
|
ast/CallGraph.cpp
|
||||||
ast/CallGraph.h
|
ast/CallGraph.h
|
||||||
ast/ExperimentalFeatures.h
|
ast/ExperimentalFeatures.h
|
||||||
|
ast/UserDefinableOperators.h
|
||||||
ast/Types.cpp
|
ast/Types.cpp
|
||||||
ast/Types.h
|
ast/Types.h
|
||||||
ast/TypeProvider.cpp
|
ast/TypeProvider.cpp
|
||||||
|
@ -30,7 +30,9 @@
|
|||||||
#include <libsolutil/FunctionSelector.h>
|
#include <libsolutil/FunctionSelector.h>
|
||||||
#include <libsolutil/Keccak256.h>
|
#include <libsolutil/Keccak256.h>
|
||||||
|
|
||||||
|
#include <range/v3/range/conversion.hpp>
|
||||||
#include <range/v3/view/tail.hpp>
|
#include <range/v3/view/tail.hpp>
|
||||||
|
#include <range/v3/view/zip.hpp>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
@ -367,6 +369,11 @@ TypeDeclarationAnnotation& UserDefinedValueTypeDefinition::annotation() const
|
|||||||
return initAnnotation<TypeDeclarationAnnotation>();
|
return initAnnotation<TypeDeclarationAnnotation>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<ASTPointer<IdentifierPath>, std::optional<Token>>> UsingForDirective::functionsAndOperators() const
|
||||||
|
{
|
||||||
|
return ranges::zip_view(m_functionsOrLibrary, m_operators) | ranges::to<vector>;
|
||||||
|
}
|
||||||
|
|
||||||
Type const* StructDefinition::type() const
|
Type const* StructDefinition::type() const
|
||||||
{
|
{
|
||||||
solAssert(annotation().recursive.has_value(), "Requested struct type before DeclarationTypeChecker.");
|
solAssert(annotation().recursive.has_value(), "Requested struct type before DeclarationTypeChecker.");
|
||||||
@ -895,6 +902,37 @@ MemberAccessAnnotation& MemberAccess::annotation() const
|
|||||||
return initAnnotation<MemberAccessAnnotation>();
|
return initAnnotation<MemberAccessAnnotation>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OperationAnnotation& UnaryOperation::annotation() const
|
||||||
|
{
|
||||||
|
return initAnnotation<OperationAnnotation>();
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionType const* UnaryOperation::userDefinedFunctionType() const
|
||||||
|
{
|
||||||
|
if (*annotation().userDefinedFunction == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
FunctionDefinition const* userDefinedFunction = *annotation().userDefinedFunction;
|
||||||
|
return dynamic_cast<FunctionType const*>(
|
||||||
|
userDefinedFunction->libraryFunction() ?
|
||||||
|
userDefinedFunction->typeViaContractName() :
|
||||||
|
userDefinedFunction->type()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionType const* BinaryOperation::userDefinedFunctionType() const
|
||||||
|
{
|
||||||
|
if (*annotation().userDefinedFunction == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
FunctionDefinition const* userDefinedFunction = *annotation().userDefinedFunction;
|
||||||
|
return dynamic_cast<FunctionType const*>(
|
||||||
|
userDefinedFunction->libraryFunction() ?
|
||||||
|
userDefinedFunction->typeViaContractName() :
|
||||||
|
userDefinedFunction->type()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
BinaryOperationAnnotation& BinaryOperation::annotation() const
|
BinaryOperationAnnotation& BinaryOperation::annotation() const
|
||||||
{
|
{
|
||||||
return initAnnotation<BinaryOperationAnnotation>();
|
return initAnnotation<BinaryOperationAnnotation>();
|
||||||
|
@ -663,16 +663,19 @@ public:
|
|||||||
int64_t _id,
|
int64_t _id,
|
||||||
SourceLocation const& _location,
|
SourceLocation const& _location,
|
||||||
std::vector<ASTPointer<IdentifierPath>> _functionsOrLibrary,
|
std::vector<ASTPointer<IdentifierPath>> _functionsOrLibrary,
|
||||||
|
std::vector<std::optional<Token>> _operators,
|
||||||
bool _usesBraces,
|
bool _usesBraces,
|
||||||
ASTPointer<TypeName> _typeName,
|
ASTPointer<TypeName> _typeName,
|
||||||
bool _global
|
bool _global
|
||||||
):
|
):
|
||||||
ASTNode(_id, _location),
|
ASTNode(_id, _location),
|
||||||
m_functionsOrLibrary(std::move(_functionsOrLibrary)),
|
m_functionsOrLibrary(std::move(_functionsOrLibrary)),
|
||||||
|
m_operators(std::move(_operators)),
|
||||||
m_usesBraces(_usesBraces),
|
m_usesBraces(_usesBraces),
|
||||||
m_typeName(std::move(_typeName)),
|
m_typeName(std::move(_typeName)),
|
||||||
m_global{_global}
|
m_global{_global}
|
||||||
{
|
{
|
||||||
|
solAssert(m_functionsOrLibrary.size() == m_operators.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void accept(ASTVisitor& _visitor) override;
|
void accept(ASTVisitor& _visitor) override;
|
||||||
@ -683,12 +686,18 @@ public:
|
|||||||
|
|
||||||
/// @returns a list of functions or the single library.
|
/// @returns a list of functions or the single library.
|
||||||
std::vector<ASTPointer<IdentifierPath>> const& functionsOrLibrary() const { return m_functionsOrLibrary; }
|
std::vector<ASTPointer<IdentifierPath>> const& functionsOrLibrary() const { return m_functionsOrLibrary; }
|
||||||
|
std::vector<std::pair<ASTPointer<IdentifierPath>, std::optional<Token>>> functionsAndOperators() const;
|
||||||
bool usesBraces() const { return m_usesBraces; }
|
bool usesBraces() const { return m_usesBraces; }
|
||||||
bool global() const { return m_global; }
|
bool global() const { return m_global; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Either the single library or a list of functions.
|
/// Either the single library or a list of functions.
|
||||||
std::vector<ASTPointer<IdentifierPath>> m_functionsOrLibrary;
|
std::vector<ASTPointer<IdentifierPath>> m_functionsOrLibrary;
|
||||||
|
/// Operators, the functions from @a m_functionsOrLibrary implement.
|
||||||
|
/// A token if the corresponding element in m_functionsOrLibrary
|
||||||
|
/// defines an operator, nullptr otherwise.
|
||||||
|
/// Note that this vector size must be equal to m_functionsOrLibrary size.
|
||||||
|
std::vector<std::optional<Token>> m_operators;
|
||||||
bool m_usesBraces;
|
bool m_usesBraces;
|
||||||
ASTPointer<TypeName> m_typeName;
|
ASTPointer<TypeName> m_typeName;
|
||||||
bool m_global = false;
|
bool m_global = false;
|
||||||
@ -2073,6 +2082,10 @@ public:
|
|||||||
bool isPrefixOperation() const { return m_isPrefix; }
|
bool isPrefixOperation() const { return m_isPrefix; }
|
||||||
Expression const& subExpression() const { return *m_subExpression; }
|
Expression const& subExpression() const { return *m_subExpression; }
|
||||||
|
|
||||||
|
FunctionType const* userDefinedFunctionType() const;
|
||||||
|
|
||||||
|
OperationAnnotation& annotation() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Token m_operator;
|
Token m_operator;
|
||||||
ASTPointer<Expression> m_subExpression;
|
ASTPointer<Expression> m_subExpression;
|
||||||
@ -2104,6 +2117,8 @@ public:
|
|||||||
Expression const& rightExpression() const { return *m_right; }
|
Expression const& rightExpression() const { return *m_right; }
|
||||||
Token getOperator() const { return m_operator; }
|
Token getOperator() const { return m_operator; }
|
||||||
|
|
||||||
|
FunctionType const* userDefinedFunctionType() const;
|
||||||
|
|
||||||
BinaryOperationAnnotation& annotation() const override;
|
BinaryOperationAnnotation& annotation() const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -312,7 +312,12 @@ struct MemberAccessAnnotation: ExpressionAnnotation
|
|||||||
util::SetOnce<VirtualLookup> requiredLookup;
|
util::SetOnce<VirtualLookup> requiredLookup;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BinaryOperationAnnotation: ExpressionAnnotation
|
struct OperationAnnotation: ExpressionAnnotation
|
||||||
|
{
|
||||||
|
util::SetOnce<FunctionDefinition const*> userDefinedFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BinaryOperationAnnotation: OperationAnnotation
|
||||||
{
|
{
|
||||||
/// The common type that is used for the operation, not necessarily the result type (which
|
/// The common type that is used for the operation, not necessarily the result type (which
|
||||||
/// e.g. for comparisons is bool).
|
/// e.g. for comparisons is bool).
|
||||||
|
@ -330,19 +330,31 @@ bool ASTJsonExporter::visit(UsingForDirective const& _node)
|
|||||||
vector<pair<string, Json::Value>> attributes = {
|
vector<pair<string, Json::Value>> attributes = {
|
||||||
make_pair("typeName", _node.typeName() ? toJson(*_node.typeName()) : Json::nullValue)
|
make_pair("typeName", _node.typeName() ? toJson(*_node.typeName()) : Json::nullValue)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_node.usesBraces())
|
if (_node.usesBraces())
|
||||||
{
|
{
|
||||||
Json::Value functionList;
|
Json::Value functionList;
|
||||||
for (auto const& function: _node.functionsOrLibrary())
|
for (auto&& [function, op]: _node.functionsAndOperators())
|
||||||
{
|
{
|
||||||
Json::Value functionNode;
|
Json::Value functionNode;
|
||||||
functionNode["function"] = toJson(*function);
|
if (!op.has_value())
|
||||||
|
functionNode["function"] = toJson(*function);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
functionNode["definition"] = toJson(*function);
|
||||||
|
functionNode["operator"] = string(TokenTraits::toString(*op));
|
||||||
|
}
|
||||||
functionList.append(std::move(functionNode));
|
functionList.append(std::move(functionNode));
|
||||||
}
|
}
|
||||||
attributes.emplace_back("functionList", std::move(functionList));
|
attributes.emplace_back("functionList", std::move(functionList));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
attributes.emplace_back("libraryName", toJson(*_node.functionsOrLibrary().front()));
|
{
|
||||||
|
auto const& functionAndOperators = _node.functionsAndOperators();
|
||||||
|
solAssert(_node.functionsAndOperators().size() == 1);
|
||||||
|
solAssert(!functionAndOperators.front().second.has_value());
|
||||||
|
attributes.emplace_back("libraryName", toJson(*(functionAndOperators.front().first)));
|
||||||
|
}
|
||||||
attributes.emplace_back("global", _node.global());
|
attributes.emplace_back("global", _node.global());
|
||||||
|
|
||||||
setJsonNode(_node, "UsingForDirective", std::move(attributes));
|
setJsonNode(_node, "UsingForDirective", std::move(attributes));
|
||||||
@ -830,6 +842,9 @@ bool ASTJsonExporter::visit(UnaryOperation const& _node)
|
|||||||
make_pair("operator", TokenTraits::toString(_node.getOperator())),
|
make_pair("operator", TokenTraits::toString(_node.getOperator())),
|
||||||
make_pair("subExpression", toJson(_node.subExpression()))
|
make_pair("subExpression", toJson(_node.subExpression()))
|
||||||
};
|
};
|
||||||
|
// NOTE: This annotation is guaranteed to be set but only if we didn't stop at the parsing stage.
|
||||||
|
if (_node.annotation().userDefinedFunction.set() && *_node.annotation().userDefinedFunction != nullptr)
|
||||||
|
attributes.emplace_back("function", nodeId(**_node.annotation().userDefinedFunction));
|
||||||
appendExpressionAttributes(attributes, _node.annotation());
|
appendExpressionAttributes(attributes, _node.annotation());
|
||||||
setJsonNode(_node, "UnaryOperation", std::move(attributes));
|
setJsonNode(_node, "UnaryOperation", std::move(attributes));
|
||||||
return false;
|
return false;
|
||||||
@ -843,6 +858,9 @@ bool ASTJsonExporter::visit(BinaryOperation const& _node)
|
|||||||
make_pair("rightExpression", toJson(_node.rightExpression())),
|
make_pair("rightExpression", toJson(_node.rightExpression())),
|
||||||
make_pair("commonType", typePointerToJson(_node.annotation().commonType)),
|
make_pair("commonType", typePointerToJson(_node.annotation().commonType)),
|
||||||
};
|
};
|
||||||
|
// NOTE: This annotation is guaranteed to be set but only if we didn't stop at the parsing stage.
|
||||||
|
if (_node.annotation().userDefinedFunction.set() && *_node.annotation().userDefinedFunction != nullptr)
|
||||||
|
attributes.emplace_back("function", nodeId(**_node.annotation().userDefinedFunction));
|
||||||
appendExpressionAttributes(attributes, _node.annotation());
|
appendExpressionAttributes(attributes, _node.annotation());
|
||||||
setJsonNode(_node, "BinaryOperation", std::move(attributes));
|
setJsonNode(_node, "BinaryOperation", std::move(attributes));
|
||||||
return false;
|
return false;
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libsolidity/ast/ASTJsonImporter.h>
|
#include <libsolidity/ast/ASTJsonImporter.h>
|
||||||
|
#include <libsolidity/ast/UserDefinableOperators.h>
|
||||||
|
|
||||||
#include <libyul/AsmJsonImporter.h>
|
#include <libyul/AsmJsonImporter.h>
|
||||||
#include <libyul/AST.h>
|
#include <libyul/AST.h>
|
||||||
@ -397,15 +398,42 @@ ASTPointer<InheritanceSpecifier> ASTJsonImporter::createInheritanceSpecifier(Jso
|
|||||||
ASTPointer<UsingForDirective> ASTJsonImporter::createUsingForDirective(Json::Value const& _node)
|
ASTPointer<UsingForDirective> ASTJsonImporter::createUsingForDirective(Json::Value const& _node)
|
||||||
{
|
{
|
||||||
vector<ASTPointer<IdentifierPath>> functions;
|
vector<ASTPointer<IdentifierPath>> functions;
|
||||||
|
vector<optional<Token>> operators;
|
||||||
if (_node.isMember("libraryName"))
|
if (_node.isMember("libraryName"))
|
||||||
|
{
|
||||||
|
astAssert(!_node["libraryName"].isArray());
|
||||||
|
astAssert(!_node["libraryName"]["operator"]);
|
||||||
functions.emplace_back(createIdentifierPath(_node["libraryName"]));
|
functions.emplace_back(createIdentifierPath(_node["libraryName"]));
|
||||||
|
operators.emplace_back(nullopt);
|
||||||
|
}
|
||||||
else if (_node.isMember("functionList"))
|
else if (_node.isMember("functionList"))
|
||||||
for (Json::Value const& function: _node["functionList"])
|
for (Json::Value const& function: _node["functionList"])
|
||||||
functions.emplace_back(createIdentifierPath(function["function"]));
|
{
|
||||||
|
if (function.isMember("function"))
|
||||||
|
{
|
||||||
|
astAssert(!function.isMember("operator"));
|
||||||
|
astAssert(!function.isMember("definition"));
|
||||||
|
|
||||||
|
functions.emplace_back(createIdentifierPath(function["function"]));
|
||||||
|
operators.emplace_back(nullopt);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
astAssert(function.isMember("operator"));
|
||||||
|
astAssert(function.isMember("definition"));
|
||||||
|
|
||||||
|
Token const operatorName = scanSingleToken(function["operator"]);
|
||||||
|
astAssert(util::contains(frontend::userDefinableOperators, operatorName));
|
||||||
|
|
||||||
|
functions.emplace_back(createIdentifierPath(function["definition"]));
|
||||||
|
operators.emplace_back(operatorName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return createASTNode<UsingForDirective>(
|
return createASTNode<UsingForDirective>(
|
||||||
_node,
|
_node,
|
||||||
std::move(functions),
|
std::move(functions),
|
||||||
|
std::move(operators),
|
||||||
!_node.isMember("libraryName"),
|
!_node.isMember("libraryName"),
|
||||||
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"]),
|
_node["typeName"].isNull() ? nullptr : convertJsonToASTNode<TypeName>(_node["typeName"]),
|
||||||
memberAsBool(_node, "global")
|
memberAsBool(_node, "global")
|
||||||
|
31
libsolidity/ast/UserDefinableOperators.h
Normal file
31
libsolidity/ast/UserDefinableOperators.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <liblangutil/Token.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace solidity::frontend
|
||||||
|
{
|
||||||
|
|
||||||
|
std::vector<langutil::Token> const userDefinableOperators = {
|
||||||
|
// Bitwise
|
||||||
|
langutil::Token::BitOr,
|
||||||
|
langutil::Token::BitAnd,
|
||||||
|
langutil::Token::BitXor,
|
||||||
|
langutil::Token::BitNot,
|
||||||
|
// Arithmetic
|
||||||
|
langutil::Token::Add,
|
||||||
|
langutil::Token::Sub,
|
||||||
|
langutil::Token::Mul,
|
||||||
|
langutil::Token::Div,
|
||||||
|
langutil::Token::Mod,
|
||||||
|
// Comparison
|
||||||
|
langutil::Token::Equal,
|
||||||
|
langutil::Token::NotEqual,
|
||||||
|
langutil::Token::LessThan,
|
||||||
|
langutil::Token::GreaterThan,
|
||||||
|
langutil::Token::LessThanOrEqual,
|
||||||
|
langutil::Token::GreaterThanOrEqual,
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user