zeek/auxil/spicy/hilti/toolchain/include/ast/operator-registry.h
Patrick Kelley 8fd444092b initial
2025-05-07 15:35:15 -04:00

139 lines
5.8 KiB
C++

// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
#pragma once
#include <list>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <hilti/ast/operator.h>
#define HILTI_OPERATOR(ns, cls) \
Result<hilti::expression::ResolvedOperator*> instantiate(hilti::Builder* builder, Expressions operands, Meta meta) \
const final { \
return {ns::operator_::cls::create(builder->context(), this, result(builder, operands, meta), \
std::move(operands), meta)}; \
} \
\
std::string name() const final { return hilti::util::replace(#cls, "_::", "::"); } \
std::string _typename() const final { return hilti::util::typename_(*this); }
#define HILTI_OPERATOR_IMPLEMENTATION(cls) \
namespace { \
static hilti::operator_::Register<cls> _operator_##cls; \
}
namespace hilti::operator_ {
/** Singleton registering available operators. */
class Registry {
public:
/**
* Returns all available built-on operators of kind function call matching
* a given function name.
*/
const auto& byBuiltinFunctionID(const ID& id) { return _operators_by_builtin_function[id]; }
/**
* Returns all available operators of a given kind.
*/
const auto& byKind(Kind kind) { return _operators_by_kind[kind]; }
/**
* Returns all available operators of kind `member call` matching a given
* method ID.
*/
const auto& byMethodID(const ID& id) { return _operators_by_method[id]; }
/** Returns all available operators of a given operator name. */
const auto& byName(const std::string_view& name) { return _operators_by_name[std::string(name)]; }
/**
* Returns any function call operators defining a static name matching a
* given unresolved operator.
*
* @param op unresolved operator to match against
* @returns tuple where the 1st element is a boolean indicating if the
* caller should proceed checking the candidates returned as the 2dn
* element; if not, a match has been found but is valid for calling, and
* hence the caller should abort resolution
*/
std::pair<bool, std::optional<std::vector<const Operator*>>> functionCallCandidates(
const expression::UnresolvedOperator* op);
/** Returns all available operators. */
const auto& operators() const { return _operators; }
/**
* Registers an operator with the registry. It will not immediately become
* available but remain pending until initialized later.
*/
void register_(std::unique_ptr<Operator> op);
/**
* Attempts to initialize all pending operators. Initialization will
* succeed for all operators for which argument types can be fully resolved
* at this time; these will then be available through the registry going
* forward. Any operators that cannot be initialized yet will remain
* pending and won't be available for lookup for the time being.
*
* @param builder builder to use for operator initialization
*/
void initPending(Builder* builder);
/** Returns true if any registered operators remain uninitialized. */
bool havePending() const { return ! _pending.empty(); }
/** Removes all registered operators, releasing their memory. */
void clear();
/**
* Aborts with an internal error if any registered built-in operators
* remain uninitialized. If this happens after an AST has otherwise be
* fully resolved, something's wrong those operator definitions (like an
* unknown type).
*/
void debugEnforceBuiltInsAreResolved(Builder* builder) const;
/** Returns a singleton instance of the current class. */
static auto& singleton() {
static Registry instance;
return instance;
}
private:
std::list<std::unique_ptr<Operator>> _pending; // all registered, but not yet initialized operators
std::vector<std::unique_ptr<Operator>> _operators; // all initialized operators
std::map<std::string, const Operator*> _operators_by_name; // initialized operators by name
std::map<Kind, std::vector<const Operator*>> _operators_by_kind; // initialized operators by kind
std::map<ID, std::vector<const Operator*>>
_operators_by_builtin_function; // initialized operators by builtin call operators; empty ID collect all without
// a static name
std::map<ID, std::vector<const Operator*>> _operators_by_method; // initialized operators by method
};
/**
* Retrieves an operator by name. Raises an internal error if there's no
* operator available under that name.
*/
inline auto get(std::string_view name) {
if ( auto op = Registry::singleton().byName(name) )
return op;
else
logger().internalError(util::fmt("unknown operator '%s'", name));
}
/** Helper class to register an operator on instantiation. */
template<typename T>
class Register {
public:
Register() { Registry::singleton().register_(std::make_unique<T>()); }
};
inline auto& registry() { return Registry::singleton(); }
} // namespace hilti::operator_