#pragma once #include <string> #include <vector> #include <memory> #include <type_traits> #include "Lib/Utils.hh" #include "Lib/Script/FrontEnd/Token.hh" #include "Lib/Script/FrontEnd/Tokens.hh" #include "Lib/Script/FrontEnd/Location.hh" namespace Vivy::Script { /* Structural And Declarative Objects */ class IrRoot; // A root, with all the needed modules, etc class IrModule; // A module (single vvs file) class IrAttribute; // Attribute placed on the module, function, etc class IrImport; // An import statement class IrOption; // An option to pass to jobs (no global vars) /* Types */ class IrType; class IrTAss; // Ass types, SYLLABE, LINE, etc class IrTOption; // An option type, need to have the same shape class IrTPrimitive; // Primitive types /* Jobs */ class IrJob; class IrImportedJob; // An imported job /* Functions */ class IrFunction; class IrImportedFunction; // A utility to hide the imported functions /* Variables */ class IrVariable; class IrVPrimitive; // A simple base variable class IrVTable; // A variable with fields (a struct) /* Instructions */ class IrInstruction; class IrILoop; // Any loop class IrIAffectation; // Variable affectation class IrIReturn; // Return statement class IrIDeclaration; // Variable declaration class IrIConditional; // If/Else/Elif statements /* Expression */ class IrExpression; class IrEConstExpr; // A constant expression, known at compile time class IrECall; // A function call class IrECompOp; // A comparaison (!=, <=, ...) class IrEArithmeticOp; // Simple operations (+, *, ...) class IrELogicOp; // A logic operation (and, or, ...) class IrEVariableRef; // Reference to a variable, local or argument /* A signature for a callable element */ class IrCallableSignature; /* Base class forward declaration needed for concepts */ class IrElement; /* ** Base concept for an IR element class. */ template <typename T> concept IrElementObject = std::is_base_of<IrElement, T>::value; template <typename... T> concept IrElementObjects = std::conjunction<std::is_base_of<IrElement, T>...>::value; /* ** IrElementConstructible object. Used to create an element from ** the passed arguments (`typename... Args`). */ template <typename T, typename... Args> concept IrElementConstructible = IrElementObject<T> && requires(T irElem, Args &&...args) { T(std::forward<Args>(args)...); }; /* ** IrElementDefaultConstructible object. Used to create an element ** from nothing. */ template <typename T> concept IrElementDefaultConstructible = std::is_base_of<IrElement, T>::value && has_any_default_constructor<T>::value; /* ** IrElementParsable object. Used for the create with the pointer ** to the tokens passed. Also removes the ** `virtual void parse(std::vector<Token>*)` abstract method. */ template <typename T> concept IrElementParsable = std::is_base_of<IrElement, T>::value && requires(T irElem) { irElem.parse(new std::vector<Token>); }; /* Base class definition: IrElement */ class IrElement { IrElement *parentElement = nullptr; IrAttribute *elementAttributes = nullptr; std::vector<IrElement *> childElements; protected: IrElement() noexcept = default; IrElement(IrElement *p) noexcept; void detachElementFromParent() noexcept; void addChild(IrElement *) noexcept; void throwUnexpectedToken(const Token &tok) const; public: virtual ~IrElement() noexcept; virtual const IrElement *parent() const noexcept; virtual IrElement *parent() noexcept; void setParent(IrElement *p) noexcept; void setAttribute(IrAttribute *a) noexcept; virtual std::string toString() const noexcept = 0; /* ** Get attributes for an element! */ std::vector<StrV> attribute(const std::string &name) const noexcept; std::vector<StrV> attribute(StrV name) const noexcept; /* ** Create an IrElement by parsing a token stream representing the file. ** May throw an exception because parse is not noexcept. Always return a ** non null pointer, will throw an exception on failure. ** ** TODO: Use concepts IrElementParsable and IrElementConstructible. */ template <typename T> requires IrElementParsable<T> && IrElementDefaultConstructible<T> static T *create(IrElement *p, std::vector<Token> *tokens) { using DeleteCall = decltype(&IrElement::destroy<T>); std::unique_ptr<T, DeleteCall> ret(new T, IrElement::destroy<T>); ret->setParent(p != nullptr ? p : ret.get()); ret->parse(tokens); return ret.release(); } template <typename T, typename... Args> requires IrElementConstructible<T, Args...> && IrElementParsable<T> static T *create(IrElement *p, std::vector<Token> *tokens, Args &&...args) { using DeleteCall = decltype(&IrElement::destroy<T>); std::unique_ptr<T, DeleteCall> ret(new T(std::forward<Args>(args)...), IrElement::destroy<T>); ret->setParent(p != nullptr ? p : ret.get()); ret->parse(tokens); return ret.release(); } template <typename T> requires IrElementParsable<T> static T *create(IrElement *p) { using DeleteCall = decltype(&IrElement::destroy<T>); std::unique_ptr<T, DeleteCall> ret(new T, IrElement::destroy<T>); ret->setParent(p != nullptr ? p : ret.get()); return ret.release(); } template <typename T, typename... Args> requires IrElementConstructible<T, Args...> static T *create(IrElement *p, Args &&...args) { using DeleteCall = decltype(&IrElement::destroy<T>); std::unique_ptr<T, DeleteCall> ret(new T(std::forward<Args>(args)...), IrElement::destroy<T>); ret->setParent(p != nullptr ? p : ret.get()); return ret.release(); } /* A safer call to delete, to also remove the children from its parent */ template <typename T> requires IrElementObject<T> static void destroy(T *const e) noexcept { if (e != nullptr) { e->detachElementFromParent(); delete e; } } }; /* ** Define an IrElement sub-class. Sets the correct visibility for the ** constructors and add IrElement as a friend for the create static ** method. */ #define VIVY_IR_ELEMENT(clazz) \ protected: \ friend class IrElement; \ template <typename... T> friend struct HasAnyConstructor; \ \ private: }