
class Operator;

/**
* Interface for HILTI operators.
*
* Operators aren't AST nodes themselves. Instead, they generally *define* an
* operator that's available in HILTI. An operator can then instantiated with
* concrete operands to create a corresponding AST expression node.
*/
class Concept : public hilti::util::type_erasure::ConceptBase {
public:
    Concept() = default;
    Concept(const Concept&) = default;
    Concept(Concept&&) = default;
    virtual ~Concept() = default;
    /** Returns the operator's kind. */
    virtual hilti::operator_::Kind kind() const = 0;
    /**
* Returns a documentation string associated with the operator. This
* goes into automatically generated HILTI reference documentation.
*/
    virtual std::string doc() const = 0;
    /**
* Returns the internal namespace this operator is defined in. This is
* used for grouping operators in the auto-generated documentation.
*/
    virtual std::string docNamespace() const = 0;
    /**
* Validates correctness of an instantiated version of this operator.
* This will be called as part of HILTI's AST validation process and
* should ensure that the operator's operands are valid.
*
* @param o instantiated operator expression
* @param p position of `o` in the AST being validated; can be used to derive further context about the usage
* @return false if the instantiation doesn't reflect a correct usage of the operator
*/
    virtual void validate(const expression::ResolvedOperator& o, operator_::position_t p) const = 0;
    /**
* Returns the HILTI type that the operator yields when evaluated with a
* given set of operands.
*
* @param ops operands to use for deriving the result type; note that
* when auto-generating documentation, this vector will always be empty.
* Operators that require expressions to calculate their return type
* dynamically must catch getting an empty vector and return a
* type::DocOnly() instance in that case that provides a textual
* description for documentation.
*
* @return type when evaluated
*
*/
    virtual Type result(const hilti::node::Range<Expression>& ops) const = 0;
    /** True if operator's result can be assigned to. */
    virtual bool isLhs() const = 0;
    /** Returns the operators resolver priority relative to others of the same kind. */
    virtual hilti::operator_::Priority priority() const = 0;
    /**
* Describes the operands that the operator accepts, such as their types
* and default values.
*
* @return
*/
    virtual const std::vector<hilti::operator_::Operand>& operands() const = 0;
    /**
* Creates an expression representing this operator instantied with a
* given set of operands. The returned expression will typically be a
* `expression::ResolvedOperator`.
*
* @param ops operands to instantiate the operator with
* @param m meta information to associated with the instantiated expression
* @return new expression to integrate into AST
*/
    virtual Expression instantiate(const std::vector<Expression>& operands, const Meta& m) const = 0;
    Concept& operator=(const Concept&) = default;
    Concept& operator=(Concept&&) = default;
    virtual Operator _clone() const = 0;
    virtual hilti::IntrusivePtr<Concept> _clone_ptr() const = 0;
private:
    
};

template<typename T>
using ModelBase = hilti::util::type_erasure::ModelBase<T, Concept>;

template<class T>
class Model : public ModelBase<T> {
public:
    using ModelBase<T>::ModelBase;
    using ModelBase<T>::data;

    hilti::operator_::Kind kind() const final;
    std::string doc() const final;
    std::string docNamespace() const final;
    void validate(const expression::ResolvedOperator& o, operator_::position_t p) const final;
    Type result(const hilti::node::Range<Expression>& ops) const final;
    bool isLhs() const final;
    hilti::operator_::Priority priority() const final;
    const std::vector<hilti::operator_::Operand>& operands() const final;
    Expression instantiate(const std::vector<Expression>& operands, const Meta& m) const final;
    Operator _clone() const final;
    hilti::IntrusivePtr<Concept> _clone_ptr() const final;
};

using ErasedBase = hilti::util::type_erasure::ErasedBase<trait::isOperator, Concept, Model>;

class Operator : public ErasedBase {
public:
    using ErasedBase::ErasedBase;

    Operator() noexcept = default;
    Operator(Operator&&) noexcept = default;
    Operator& operator=(Operator&&) = default;
    ~Operator() override = default;
    Operator(const Operator& other) = default;
    Operator& operator=(const Operator& other) = default;

    hilti::operator_::Kind kind() const;
    std::string doc() const;
    std::string docNamespace() const;
    void validate(const expression::ResolvedOperator& o, operator_::position_t p) const;
    Type result(const hilti::node::Range<Expression>& ops) const;
    bool isLhs() const;
    hilti::operator_::Priority priority() const;
    const std::vector<hilti::operator_::Operand>& operands() const;
    Expression instantiate(const std::vector<Expression>& operands, const Meta& m) const;
    Operator _clone() const;

    hilti::IntrusivePtr<Concept> _clone_ptr() const;

protected:
    
};

template<typename T>
hilti::operator_::Kind Model<T>::kind() const { return data().kind(); }

template<typename T>
std::string Model<T>::doc() const { return data().doc(); }

template<typename T>
std::string Model<T>::docNamespace() const { return data().docNamespace(); }

template<typename T>
void Model<T>::validate(const expression::ResolvedOperator& o, operator_::position_t p) const { return data().validate(o, p); }

template<typename T>
Type Model<T>::result(const hilti::node::Range<Expression>& ops) const { return data().result(ops); }

template<typename T>
bool Model<T>::isLhs() const { return data().isLhs(); }

template<typename T>
hilti::operator_::Priority Model<T>::priority() const { return data().priority(); }

template<typename T>
const std::vector<hilti::operator_::Operand>& Model<T>::operands() const { return data().operands(); }

template<typename T>
Expression Model<T>::instantiate(const std::vector<Expression>& operands, const Meta& m) const { return data().instantiate(operands, m); }

template<typename T>
Operator Model<T>::_clone() const { return T(data()); }

template<typename T>
hilti::IntrusivePtr<Concept> Model<T>::_clone_ptr() const {
    if constexpr ( std::is_base_of<hilti::util::type_erasure::trait::Singleton, T>::value )
        return nullptr;
    else
        return hilti::make_intrusive<Model>(T(data()));
}

inline hilti::operator_::Kind Operator::kind() const { return data()->kind(); }

inline std::string Operator::doc() const { return data()->doc(); }

inline std::string Operator::docNamespace() const { return data()->docNamespace(); }

inline void Operator::validate(const expression::ResolvedOperator& o, operator_::position_t p) const { return data()->validate(o, std::move(p)); }

inline Type Operator::result(const hilti::node::Range<Expression>& ops) const { return data()->result(ops); }

inline bool Operator::isLhs() const { return data()->isLhs(); }

inline hilti::operator_::Priority Operator::priority() const { return data()->priority(); }

inline const std::vector<hilti::operator_::Operand>& Operator::operands() const { return data()->operands(); }

inline Expression Operator::instantiate(const std::vector<Expression>& operands, const Meta& m) const { return data()->instantiate(operands, m); }

inline Operator Operator::_clone() const { return data() ? data()->_clone() : Operator(); }

inline hilti::IntrusivePtr<Concept> Operator::_clone_ptr() const {
    if ( data() )
        return data()->_clone_ptr();
    else
        return nullptr;
}



