Patrick Kelley 8fd444092b initial
2025-05-07 15:35:15 -04:00

433 lines
15 KiB
C++

// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
#pragma once
#include <string>
#include <utility>
#include <hilti/ast/forward.h>
#include <hilti/ast/node.h>
#include <hilti/ast/types/operand-list.h>
#include <hilti/base/logger.h>
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<Kind> 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, "<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<std::string> 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<QualifiedType>
result; /**< result of the method; if null, `result()` will be called dynamically */
node::RetainedPtr<type::OperandList> 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<typename T>
using Result = ::hilti::Result<T>;
/**
* 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<operator_::Operands> 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<expression::ResolvedOperator*> 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<operator_::detail::ProcessedSignature> _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