
class Production_;

class Concept : public hilti::util::type_erasure::ConceptBase {
public:
    Concept() = default;
    Concept(const Concept&) = default;
    Concept(Concept&&) = default;
    virtual ~Concept() = default;
    /**
* Returns true if it's possible to derive the production to an Epsilon
* production. Note that it doesn't *always* need to do so, just one
* possible derivation is sufficient.
*/
    virtual bool nullable() const = 0;
    /**
* Returns true if running out of data while parsing this production
* should not be considered an error.
*/
    virtual bool eodOk() const = 0;
    /**
* Returns true if this production does not recursively contain other
* productions.
*/
    virtual bool atomic() const = 0;
    /** Returns any type associated with this production. */
    virtual std::optional<Type> type() const = 0;
    /**
* Returns a readable representation of the production, suitable to
* include in error message and debugging output. This should usually not
* be called directly; convert the production into a string instead,
* which will incorporate the output of this method, but augment it
* further.
*/
    virtual std::string render() const = 0;
    /**
* For literals, returns the expression associated with it.
*/
    virtual Expression expression() const = 0;
    /**
* Returns a ID for this literal that's guaranteed to be globally
* unique for the literal's value, including across grmmars.
*/
    virtual int64_t tokenID() const = 0;
    /**
* Returns a list of RHS alternatives for this production. Each RHS is
* itself a list of Production instances.
*/
    virtual std::vector<std::vector<Production>> rhss() const = 0;
    /**
* Returns the location associated with the production, or Location::None
* if none.  This is implemented for all productions in `ProductionBase`.
*
*/
    virtual const Location& location() const = 0;
    /** Returns the symbol associated with the production. This is
implemented for all productions in `ProductionBase`. */
    virtual const std::string& symbol() const = 0;
    /**
* Renames the production.  This is implemented for all productions in
* `ProductionBase`.
*/
    virtual void setSymbol(const std::string& s)  = 0;
    /**
* For terminals, returns the filter function associated with it, if any.
* This is implemented for all productions in `ProductionBase`.
*/
    virtual std::optional<Expression> filter() const = 0;
    /**
* For terminals, associates a filter function with it. The filter
* function will be called when a value has been parsed for the terminal.
* It must return a value to use instead of the parsed value. This is
* implemented for all productions in `ProductionBase`.
*/
    virtual void setFilter(const Expression& filter)  = 0;
    /**
* For terminals, returns the sink associated with it, if any. This is
* implemented for all productions in `ProductionBase`.
*/
    virtual std::optional<Expression> sink() const = 0;
    /**
* For terminals, associates a sink with it. Any parsed data will be
* forwarded to the sink. This is implemented for all productions in
* `ProductionBase`.
*/
    virtual void setSink(const Expression& sink)  = 0;
    /**
* Returns access to the production meta data. The meta data is filled as
* grammar and parser are being built. This is implemented for all
* productions in `ProductionBase`.
*/
    virtual const production::Meta& meta() const = 0;
    /**
* Sets the production meta data. The meta data is filled as
* grammar and parser are being built. This is implemented for all
* productions in `ProductionBase`.
*/
    virtual void setMeta(production::Meta m)  = 0;
    /**
* Returns the internal meta instance the production is using. For
* internal infrastructure use only.
*/
    virtual std::shared_ptr<production::Meta> _metaInstance() const = 0;
    /**
* Sets the internal meta instance the production is using. For internal
* infrastructure use only.
*/
    virtual void _setMetaInstance(std::shared_ptr<production::Meta> m)  = 0;
    virtual bool isLiteral() const = 0;
    virtual bool isNonTerminal() const = 0;
    virtual bool isTerminal() const = 0;
    Concept& operator=(const Concept&) = default;
    Concept& operator=(Concept&&) = default;
    virtual Production_ _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;

    bool nullable() const final;
    bool eodOk() const final;
    bool atomic() const final;
    std::optional<Type> type() const final;
    std::string render() const final;
    Expression expression() const final;
    int64_t tokenID() const final;
    std::vector<std::vector<Production>> rhss() const final;
    const Location& location() const final;
    const std::string& symbol() const final;
    void setSymbol(const std::string& s)  final;
    std::optional<Expression> filter() const final;
    void setFilter(const Expression& filter)  final;
    std::optional<Expression> sink() const final;
    void setSink(const Expression& sink)  final;
    const production::Meta& meta() const final;
    void setMeta(production::Meta m)  final;
    std::shared_ptr<production::Meta> _metaInstance() const final;
    void _setMetaInstance(std::shared_ptr<production::Meta> m)  final;
    bool isLiteral() const final;
    bool isNonTerminal() const final;
    bool isTerminal() const final;
    Production_ _clone() const final;
    hilti::IntrusivePtr<Concept> _clone_ptr() const final;
};

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

class Production_ : public ErasedBase {
public:
    using ErasedBase::ErasedBase;

    Production_() noexcept = default;
    Production_(Production_&&) noexcept = default;
    Production_& operator=(Production_&&) = default;
    ~Production_() override = default;
    Production_(const Production_& other) {
        if ( auto x = other._clone_ptr() )
            ErasedBase::operator=(x);
        else
            ErasedBase::operator=(other.data());
    }

    Production_& operator=(const Production_& other) {
        if ( this != &other ) {
            if ( auto x = other._clone_ptr() )
                ErasedBase::operator=(x);
            else
                ErasedBase::operator=(other.data());
        }

        return *this;
    }

    bool nullable() const;
    bool eodOk() const;
    bool atomic() const;
    std::optional<Type> type() const;
    std::string render() const;
    Expression expression() const;
    int64_t tokenID() const;
    std::vector<std::vector<Production>> rhss() const;
    const Location& location() const;
    const std::string& symbol() const;
    void setSymbol(const std::string& s) ;
    std::optional<Expression> filter() const;
    void setFilter(const Expression& filter) ;
    std::optional<Expression> sink() const;
    void setSink(const Expression& sink) ;
    const production::Meta& meta() const;
    void setMeta(production::Meta m) ;
    std::shared_ptr<production::Meta> _metaInstance() const;
    void _setMetaInstance(std::shared_ptr<production::Meta> m) ;
    bool isLiteral() const;
    bool isNonTerminal() const;
    bool isTerminal() const;
    Production_ _clone() const;

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

protected:
    
};

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

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

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

template<typename T>
std::optional<Type> Model<T>::type() const { return data().type(); }

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

template<typename T>
Expression Model<T>::expression() const { 
    if constexpr ( std::is_base_of<spicy::trait::isLiteral, T>::value )
        return data().expression();
    else
        throw std::bad_cast();
 }

template<typename T>
int64_t Model<T>::tokenID() const { 
    if constexpr ( std::is_base_of<spicy::trait::isLiteral, T>::value )
        return data().tokenID();
    else
        throw std::bad_cast();
 }

template<typename T>
std::vector<std::vector<Production>> Model<T>::rhss() const { 
    if constexpr ( std::is_base_of<spicy::trait::isNonTerminal, T>::value )
        return data().rhss();
    else
        throw std::bad_cast();
 }

template<typename T>
const Location& Model<T>::location() const { return data().location(); }

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

template<typename T>
void Model<T>::setSymbol(const std::string& s)  { return data().setSymbol(s); }

template<typename T>
std::optional<Expression> Model<T>::filter() const { 
    if constexpr ( std::is_base_of<spicy::trait::isTerminal, T>::value )
        return data().filter();
    else
        throw std::bad_cast();
 }

template<typename T>
void Model<T>::setFilter(const Expression& filter)  { 
    if constexpr ( std::is_base_of<spicy::trait::isTerminal, T>::value )
        return data().setFilter(filter);
    else
        throw std::bad_cast();
 }

template<typename T>
std::optional<Expression> Model<T>::sink() const { 
    if constexpr ( std::is_base_of<spicy::trait::isTerminal, T>::value )
        return data().sink();
    else
        throw std::bad_cast();
 }

template<typename T>
void Model<T>::setSink(const Expression& sink)  { 
    if constexpr ( std::is_base_of<spicy::trait::isTerminal, T>::value )
        return data().setSink(sink);
    else
        throw std::bad_cast();
 }

template<typename T>
const production::Meta& Model<T>::meta() const { return data().meta(); }

template<typename T>
void Model<T>::setMeta(production::Meta m)  { return data().setMeta(m); }

template<typename T>
std::shared_ptr<production::Meta> Model<T>::_metaInstance() const { return data()._metaInstance(); }

template<typename T>
void Model<T>::_setMetaInstance(std::shared_ptr<production::Meta> m)  { return data()._setMetaInstance(m); }

template<typename T>
bool Model<T>::isLiteral() const { return std::is_base_of<spicy::trait::isLiteral, T>::value; }

template<typename T>
bool Model<T>::isNonTerminal() const { return std::is_base_of<spicy::trait::isNonTerminal, T>::value; }

template<typename T>
bool Model<T>::isTerminal() const { return std::is_base_of<spicy::trait::isTerminal, T>::value; }

template<typename T>
Production_ 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 bool Production_::nullable() const { return data()->nullable(); }

inline bool Production_::eodOk() const { return data()->eodOk(); }

inline bool Production_::atomic() const { return data()->atomic(); }

inline std::optional<Type> Production_::type() const { return data()->type(); }

inline std::string Production_::render() const { return data()->render(); }

inline Expression Production_::expression() const { return data()->expression(); }

inline int64_t Production_::tokenID() const { return data()->tokenID(); }

inline std::vector<std::vector<Production>> Production_::rhss() const { return data()->rhss(); }

inline const Location& Production_::location() const { return data()->location(); }

inline const std::string& Production_::symbol() const { return data()->symbol(); }

inline void Production_::setSymbol(const std::string& s)  { return data()->setSymbol(s); }

inline std::optional<Expression> Production_::filter() const { return data()->filter(); }

inline void Production_::setFilter(const Expression& filter)  { return data()->setFilter(filter); }

inline std::optional<Expression> Production_::sink() const { return data()->sink(); }

inline void Production_::setSink(const Expression& sink)  { return data()->setSink(sink); }

inline const production::Meta& Production_::meta() const { return data()->meta(); }

inline void Production_::setMeta(production::Meta m)  { return data()->setMeta(std::move(m)); }

inline std::shared_ptr<production::Meta> Production_::_metaInstance() const { return data()->_metaInstance(); }

inline void Production_::_setMetaInstance(std::shared_ptr<production::Meta> m)  { return data()->_setMetaInstance(std::move(m)); }

inline bool Production_::isLiteral() const { return data()->isLiteral(); }

inline bool Production_::isNonTerminal() const { return data()->isNonTerminal(); }

inline bool Production_::isTerminal() const { return data()->isTerminal(); }

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

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



