// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details. #pragma once #include #include #include #include #include #include namespace hilti { namespace expression { class ResolvedOperator; } namespace operator_ { class Registry; /** Enumeration of all types of operators that HILTI supports. */ enum class Kind { Add, Begin, BitAnd, BitOr, BitXor, Call, Cast, CustomAssign, DecrPostfix, DecrPrefix, Delete, Deref, Difference, DifferenceAssign, Division, DivisionAssign, Equal, End, Greater, GreaterEqual, HasMember, In, IncrPostfix, IncrPrefix, Index, IndexAssign, Lower, LowerEqual, Member, MemberCall, Modulo, Multiple, MultipleAssign, Negate, New, Pack, Power, ShiftLeft, ShiftRight, SignNeg, SignPos, Size, Sum, SumAssign, TryMember, Unequal, Unknown, Unpack, Unset }; /** Returns true for operator types that HILTI considers commutative. */ inline auto isCommutative(Kind k) { switch ( k ) { case Kind::BitAnd: case Kind::BitOr: case Kind::BitXor: case Kind::Equal: case Kind::Unequal: case Kind::Multiple: case Kind::Sum: return true; case Kind::Add: case Kind::Begin: case Kind::Call: case Kind::Cast: case Kind::CustomAssign: case Kind::DecrPostfix: case Kind::DecrPrefix: case Kind::Delete: case Kind::Deref: case Kind::Difference: case Kind::DifferenceAssign: case Kind::Division: case Kind::DivisionAssign: case Kind::End: case Kind::Greater: case Kind::GreaterEqual: case Kind::HasMember: case Kind::In: case Kind::IncrPostfix: case Kind::IncrPrefix: case Kind::Index: case Kind::IndexAssign: case Kind::Lower: case Kind::LowerEqual: case Kind::Member: case Kind::MemberCall: case Kind::Modulo: case Kind::MultipleAssign: case Kind::Negate: case Kind::New: case Kind::Pack: case Kind::Power: case Kind::ShiftLeft: case Kind::ShiftRight: case Kind::SignNeg: case Kind::SignPos: case Kind::Size: case Kind::SumAssign: case Kind::TryMember: case Kind::Unknown: case Kind::Unpack: case Kind::Unset: return false; }; util::cannotBeReached(); } namespace detail { constexpr util::enum_::Value Kinds[] = {{Kind::Add, "add"}, {Kind::Begin, "begin"}, {Kind::BitAnd, "&"}, {Kind::BitOr, "|"}, {Kind::BitXor, "^"}, {Kind::Call, "call"}, {Kind::Cast, "cast"}, {Kind::CustomAssign, "="}, {Kind::DecrPostfix, "--"}, {Kind::DecrPrefix, "--"}, {Kind::Delete, "delete"}, {Kind::Deref, "*"}, {Kind::Division, "/"}, {Kind::DivisionAssign, "/="}, {Kind::Equal, "=="}, {Kind::End, "end"}, {Kind::Greater, ">"}, {Kind::GreaterEqual, ">="}, {Kind::HasMember, "?."}, {Kind::In, "in"}, {Kind::IncrPostfix, "++"}, {Kind::IncrPrefix, "++"}, {Kind::Index, "index"}, {Kind::IndexAssign, "index_assign"}, {Kind::Lower, "<"}, {Kind::LowerEqual, "<="}, {Kind::Member, "."}, {Kind::MemberCall, "method call"}, {Kind::Negate, "~"}, {Kind::New, "new"}, {Kind::Difference, "-"}, {Kind::DifferenceAssign, "-="}, {Kind::Modulo, "%"}, {Kind::Multiple, "*"}, {Kind::MultipleAssign, "*="}, {Kind::Sum, "+"}, {Kind::Pack, "unpack"}, {Kind::Unset, "unset"}, {Kind::SumAssign, "+="}, {Kind::Power, "**"}, {Kind::ShiftLeft, "<<"}, {Kind::ShiftRight, ">>"}, {Kind::SignNeg, "-"}, {Kind::SignPos, "+"}, {Kind::Size, "size"}, {Kind::TryMember, ".?"}, {Kind::Unequal, "!="}, {Kind::Unknown, ""}, {Kind::Unpack, "unpack"}, {Kind::Unset, "unset"}}; /** Render an operator with its operand expressions. */ extern std::string print(Kind kind, const Expressions& operands); /** Render an operator with its operand types. */ extern std::string printSignature(Kind kind, const Expressions& operands, const Meta& meta); } // namespace detail /** * Returns a descriptive string representation of an operator kind. This is * meant just for display purposes, and does not correspond directly to the * HILTI code representation (because they may differ based on context). */ constexpr auto to_string(Kind m) { return util::enum_::to_string(m, detail::Kinds); } /** Operator priority during resolving relative to others of the same kind. */ enum class Priority { Low, Normal }; using Operand = type::operand_list::Operand; using Operands = type::operand_list::Operands; /** Helper for defining operator signatures. */ struct Signature { /** Defines an operator argument. */ struct QType { parameter::Kind kind = parameter::Kind::Unknown; /**< defines passing-style */ UnqualifiedType* type = nullptr; /**< type of the argument */ std::string doc; /**< documentation string */ UnqualifiedType* external_type = nullptr; /**< alternative way to specify type through an existing one */ UnqualifiedType* getType() const { if ( external_type ) return external_type; else return type; } operator bool() const { return kind != parameter::Kind::Unknown && getType(); } }; struct QResult { Constness constness = Constness::Const; /**< constness of the result */ UnqualifiedType* type = nullptr; /**< type of the argument */ std::string doc; /**< documentation string */ operator bool() const { return type != nullptr; } }; /** Defines an operator parameter for constructor calls. */ struct QParam { std::string name; /**< ID of parameter */ QType type; /**< type of parameter */ Expression* default_ = nullptr; /**< optional default value if parameter is not given */ bool optional = false; /**< true if parameter is optional */ operator bool() const { return type; } }; operator_::Kind kind; operator_::Priority priority = operator_::Priority::Normal; QType self; QType op0; QType op1; QType op2; std::optional member; QParam param0; QParam param1; QParam param2; QParam param3; QParam param4; QResult result; /**< result of the method; if not set, `result()` will be called dynamically */ std::string result_doc; /**< documentation string for the result */ std::string ns; /**< namespace where to document this operator */ std::string doc; /**< documentation string describing the operator */ bool skip_doc = false; /**< if true, do not include this operator into any documentation */ }; namespace detail { /** Internal representation of an operator signature after it has been processed. */ struct ProcessedSignature { operator_::Kind kind = operator_::Kind::Unknown; node::RetainedPtr result; /**< result of the method; if null, `result()` will be called dynamically */ node::RetainedPtr operands; /**< null for operators to be instantiated only manually */ operator_::Priority priority; std::string doc; /**< documentation string */ std::string result_doc; /**< explicitly set documentation string for result type */ std::string namespace_; /**< namespace where to document this operator */ bool skip_doc; /**< if true, do not include this operator into any documentation */ }; } // namespace detail } // namespace operator_ /** * Class representing available HILTI operators. * * Operators aren't AST nodes themselves, but they define an operator that's * available for instantiation as an AST expression node. Given an operator, * one can instantiate a corresponding AST node by passing the concrete * operands to the `instantiate()` method. */ class Operator { public: template using Result = ::hilti::Result; /** * Constructor. * * @param meta meta data associated with the operator * @param builtin true if the operator is predefined statically by the compiler; false if it's generated from user * code (like functions and methods) */ Operator(Meta meta = Meta(), bool builtin = true) : _meta(std::move(meta)), _builtin(builtin) {} /** Destructor. */ virtual ~Operator() {} Operator(const Operator& other) = delete; Operator(Operator&& other) = delete; Operator& operator=(const Operator& other) = delete; Operator& operator=(Operator&& other) = delete; /** Returns true if `init()` has run and returned success. */ auto isInitialized() const { return _signature.has_value(); } /** * Returns true if operator's signature has operands defined. If that's not * the case, the operator can be instantiated only manually, not through * the resolver. **/ auto hasOperands() const { return _signature->operands; } /** Returns the operator's signature. */ const auto& signature() const { assert(_signature); return *_signature; } /** Returns the operator's kind. */ auto kind() const { return signature().kind; } /** * Returns if the operator is predefined statically by the compiler, rather * than created through user code (like functions or methods). */ auto isBuiltIn() const { return _builtin; } /** Returns the operator's operands. */ auto operands() const { return signature().operands->operands(); } /** Returns the operator's first operand. */ auto op0() const { return operands()[0]; } /** Returns the operator's second operand. */ auto op1() const { return operands()[1]; } /** Returns the operator's third operand. */ auto op2() const { return operands()[2]; } /** Returns the operator's meta information. */ const auto& meta() const { return _meta; } /** Returns the operator's documentation string. */ const auto& doc() const { return signature().doc; } /** * Returns the C++-level name of the operator's class. Should be used only * for debugging purposes. */ auto typename_() const { return _typename(); } /** * Returns the operator's result type, given specific operand expressions. * Must be implemented by operators if the signature does not define a * static result type. */ virtual QualifiedType* result(Builder* builder, const Expressions& operands, const Meta& meta) const; /** * Refines the operator's signature based on the given operands. This can * be used to change the signature to more specific types given concreate * operands. To not change anything, return an mepty optional */ virtual std::optional filter(Builder* builder, const Expressions& operands) const { return {}; } /** * Performs semantics validation of an instantiated operator. To record any * errors, add them to the given AST node. */ virtual void validate(expression::ResolvedOperator* n) const {}; /** Instantiates the operator as an AST node, given specific operand expressions. */ virtual Result instantiate(Builder* builder, Expressions operands, Meta meta) const = 0; /** * Returns a readable name describing the operator. Must be provided by * derived classes. */ virtual std::string name() const = 0; /** Prints the operator in a human-readable format. */ virtual std::string print() const; /** Dumps out the operator's operands in their AST node representation. */ virtual std::string dump() const; protected: friend class operator_::Registry; /** Initializes the operator. To be called only from the registry. */ bool init(Builder* builder, Node* scope_root = nullptr); /** * Returns the operator's signature. Must be overridden by derived * classes. */ virtual operator_::Signature signature(Builder* builder) const = 0; /** Backend for `typename_()`. Must be overridden by derived classes. */ virtual std::string _typename() const { return util::typename_(*this); } /** * Helper to create an signature operand matching a given type. * * @param kind kind of the operand specifying passing style * @param t type of the operand */ static operator_::Operand* operandForType(Builder* builder, parameter::Kind kind, UnqualifiedType* t, std::string doc = ""); /** * Helper to create an signature operand matching the type of a given expression. * * @param kind kind of the operand specifying passing style * @param e expression whose type to use */ static operator_::Operand* operandForExpression(Builder* builder, parameter::Kind kind, const Expressions& e, size_t i) { return operandForType(builder, kind, e[i]->type()->type(), ""); } private: Meta _meta; bool _builtin; std::optional _signature; }; /** * Base class for operators representing built-in method calls on a type. The * base class exists so that `print()` can be customized for these. */ class BuiltInMemberCall : public Operator { public: ~BuiltInMemberCall() override {} /** Customized version of `print()`. */ std::string print() const final; }; } // namespace hilti