// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details. #pragma once #include #include #include #include #include #include #include #include #include #include namespace hilti::detail::cxx { class Formatter; namespace element { enum class Type { Expression, Type, Linkage, Attribute }; } // namespace element /** An element of type `T` in the compiler's intermediary C++ representation. */ template class Element { public: Element() = default; Element(std::string s) : _s(std::move(s)) {} Element(const char* s) : _s(s) {} Element& operator=(const std::string& s) { _s = s; return *this; } Element& operator=(std::string&& s) { _s = std::move(s); return *this; } Element& operator=(const char* s) { _s = s; return *this; } bool isMultiLine() const { return _s.find('\n') != std::string::npos; } operator std::string() const { return _s; } operator std::string_view() const { return _s; } explicit operator bool() const { return ! _s.empty(); } bool operator<(const Element& s) const { return _s < s._s; } bool operator==(const Element& s) const { return _s == s._s; } bool operator!=(const Element& s) const { return ! operator==(s); } private: std::string _s; }; using Attribute = Element; /**< C++ function attribute */ using Linkage = Element; /**< C++ linkage specification */ using Type = Element; /**< C++ type */ /** * Represents a C++-side expression, stored as a string of the C++ code along * with a associated "side". */ class Expression { public: Expression() = default; Expression(std::string s, Side side = Side::RHS) : _s(std::move(s)), _side(side) {} Expression(const char* s, Side side = Side::RHS) : _s(s), _side(side) {} bool isLhs() const { return _side == Side::LHS; } operator std::string() const { return _s; } operator std::string_view() const { return _s; } explicit operator bool() const { return ! _s.empty(); } bool operator<(const Expression& s) const { return _s < s._s; } bool operator==(const Expression& s) const { return _s == s._s; } bool operator!=(const Expression& s) const { return ! operator==(s); } private: std::string _s; Side _side = Side::LHS; }; extern std::optional normalizeID(std::string_view id); /** A C++ ID. */ class ID : public detail::IDBase { public: using Base = detail::IDBase; using Base::IDBase; ID() = default; explicit ID(const ::hilti::ID& id) : Base(id.str()) {} ID& operator=(const ::hilti::ID& id) { *this = ID(id); return *this; } /** Wrapper to construct an ID from an already normalized string name. */ static ID fromNormalized(std::string_view id) { return ID(id, Base::AlreadyNormalized()); } }; namespace declaration { // Joint base class for all C++ declarations. struct DeclarationBase { DeclarationBase(cxx::ID id) : id(std::move(id)) {} virtual ~DeclarationBase() = default; cxx::ID id; // Outputs the C++ representation of the declaration. virtual void emit(Formatter& f) const = 0; protected: bool operator==(const DeclarationBase& other) const { return id == other.id; } // for derived classes to use }; /** A C++ `@include` specific action. */ struct IncludeFile : public DeclarationBase { IncludeFile(std::string file) : DeclarationBase({}), file(std::move(file)) {} std::string file; void emit(Formatter& f) const final; bool operator<(const IncludeFile& other) const { return file < other.file; } bool operator==(const IncludeFile& other) const { return file == other.file && DeclarationBase::operator==(other); } bool operator!=(const IncludeFile& other) const { return ! operator==(other); } }; /** Declaration of a local C++ variable. */ struct Local : public DeclarationBase { Local(cxx::ID id = {}, cxx::Type type = {}, std::vector args = {}, std::optional init = {}, Linkage linkage = {}) : DeclarationBase(std::move(id)), type(std::move(type)), args(std::move(args)), init(std::move(init)), linkage(std::move(linkage)) {} cxx::Type type; std::vector args; std::optional init; Linkage linkage; // Returns true if the ID starts with two underscores, which is the // namespace reserved for internal IDs. bool isInternal() const { return util::startsWith(id.local(), "__"); } // Returns true if the ID starts with "_anon", which is the marker we use // for anonymous fields that make it out into the generated struct. bool isAnonymous() const { return util::startsWith(id.local(), "_anon"); } void emit(Formatter& f) const final; bool operator==(const Local& other) const { return type == other.type && args == other.args && init == other.init && linkage == other.linkage && DeclarationBase::operator==(other); } bool operator!=(const Local& other) const { return ! operator==(other); } std::string str() const; operator std::string() const { return str(); } }; /** Declaration of a global C++ variable. */ struct Global : public DeclarationBase { cxx::Type type; std::vector args; std::optional init; Linkage linkage; Global(cxx::ID id = {}, cxx::Type type = {}, std::vector args = {}, std::optional init = {}, Linkage linkage = {}) : DeclarationBase(std::move(id)), type(std::move(type)), args(std::move(args)), init(std::move(init)), linkage(std::move(linkage)) {} void emit(Formatter& f) const final; bool operator==(const Global& other) const { return type == other.type && args == other.args && init == other.init && linkage == other.linkage && DeclarationBase::operator==(other); } bool operator!=(const Global& other) const { return ! operator==(other); } std::string str() const; operator std::string() const { return str(); } }; /** Declaration of a C++ constant. */ struct Constant : public DeclarationBase { cxx::Type type; std::optional init; Linkage linkage; Constant(cxx::ID id = {}, cxx::Type type = {}, std::optional init = {}, Linkage linkage = {}) : DeclarationBase(std::move(id)), type(std::move(type)), init(std::move(init)), linkage(std::move(linkage)) {} void emit(Formatter& f) const final; bool operator<(const Constant& s) const { return id < s.id; } bool operator==(const Constant& other) const { return type == other.type && init == other.init && linkage == other.linkage && DeclarationBase::operator==(other); } bool operator!=(const Constant& other) const { return ! operator==(other); } }; /** Declaration of a C++ type. */ struct Type : public DeclarationBase { cxx::Type type; std::string code; bool no_using = false; // turned on automatically for types starting with "struct" bool public_ = false; // declare the type in a public section of the generated C++ code Type(cxx::ID id = {}, cxx::Type type = {}, std::string code = {}, bool no_using = false, bool public_ = false) : DeclarationBase(std::move(id)), type(std::move(type)), code(std::move(code)), no_using(no_using), public_(public_) {} void emit(Formatter& f) const final; bool operator==(const Type& other) const { return type == other.type && code == other.code && no_using == other.no_using && DeclarationBase::operator==(other); } bool operator!=(const Type& other) const { return ! operator==(other); } }; /** Declaration of a C++ function argument. */ struct Argument : public DeclarationBase { cxx::Type type; std::optional default_; cxx::Type internal_type = ""; operator std::string() const { return id ? util::fmt("%s %s", type, id) : std::string(type); } Argument(cxx::ID id = {}, cxx::Type type = {}, std::optional default_ = {}, cxx::Type internal_type = "") : DeclarationBase(std::move(id)), type(std::move(type)), default_(std::move(default_)), internal_type(std::move(internal_type)) {} void emit(Formatter& f) const final; bool operator==(const Argument& other) const { return type == other.type && default_ == other.default_ && internal_type == other.internal_type && DeclarationBase::operator==(other); } bool operator!=(const Argument& other) const { return ! operator==(other); } }; } // namespace declaration /** A C++ statement block. */ class Block { public: Block() {} Block(std::vector stmts); void addStatement(std::string stmt); void addStatementAtFront(std::string stmt); void addBlock(Block child); void addComment(const std::string& stmt, bool sep_before = true, bool sep_after = false); void addLocal(const declaration::Local& v); void addTmp(const declaration::Local& v); void addReturn(const Expression& expr = Expression()); void addIf(const Expression& cond, Block true_); void addIf(const Expression& init, const Expression& cond, cxx::Block true_); void addIf(const Expression& cond, Block true_, Block false_); void addIf(const Expression& init, const Expression& cond, Block true_, Block false_); void addElseIf(const Expression& cond, Block true_); void addElse(Block true_); void addFor(const Expression& init, const Expression& cond, const Expression& next, const cxx::Block& body); void addForRange(bool const_, const ID& id, const Expression& seq, const cxx::Block& body); // void addForRange(const Expression& init, bool const_, const ID& id, const Expression& seq, cxx::Block body); // // C++20 ... void addWhile(const Expression& cond, const Block& body); void addLambda(const std::string& name, const std::string& signature, Block body); void addSwitch(const Expression& cond, const std::vector>& cases_, std::optional default_ = {}); void appendFromBlock(Block b); void addTry(Block body, std::vector> catches); bool ensureBracesForBlock() const { return _ensure_braces_for_block; } void setEnsureBracesforBlock() { _ensure_braces_for_block = true; } size_t size(bool ignore_comments = false) const; Block& operator+=(const Block& other); explicit operator bool() const { return ! (_stmts.empty() && _tmps.empty()); } friend ::hilti::detail::cxx::Formatter& operator<<(Formatter& f, const Block& x); bool operator==(const Block& other) const { return _stmts == other._stmts; } bool operator!=(const Block& other) const { return ! operator==(other); } private: using Flags = unsigned int; std::vector> _stmts; std::vector _tmps; bool _ensure_braces_for_block = false; }; namespace declaration { /** Declaration of a C++ function. */ struct Function : public DeclarationBase { /** Tag marking an inline function for overload resolution. */ using Inline = struct {}; /** Type of function being declared. */ enum Type { Free, // global, free function Method // struct method }; Type ftype; cxx::Type result; std::vector args; Linkage linkage; std::optional body; std::optional inline_body; std::string prototype(bool qualify) const; std::string parameters() const; Function(Type ftype, cxx::Type result, cxx::ID id, std::vector args, Linkage linkage, std::optional body = {}) : DeclarationBase(std::move(id)), ftype(ftype), result(std::move(result)), args(std::move(args)), linkage(std::move(linkage)), body(std::move(body)) {} Function(Type ftype, cxx::Type result, cxx::ID id, std::vector args, Linkage linkage, Inline, Block inline_body) : DeclarationBase(std::move(id)), ftype(ftype), result(std::move(result)), args(std::move(args)), linkage(std::move(linkage)), inline_body(std::move(inline_body)) {} void emit(Formatter& f) const final; bool operator==(const Function& other) const { return ftype == other.ftype && result == other.result && args == other.args && linkage == other.linkage && inline_body == other.inline_body && body == other.body && DeclarationBase::operator==(other); } bool operator!=(const Function& other) const { return ! operator==(other); } }; } // namespace declaration namespace type { namespace struct_ { using Member = std::variant; inline bool operator<(const Member& m1, const Member& m2) { auto id = [](auto m) { if ( auto x = std::get_if(&m) ) return x->id; if ( auto x = std::get_if(&m) ) return x->id; throw std::bad_variant_access(); }; return id(m1) < id(m2); } } // namespace struct_ /** A C++ struct type. */ struct Struct { std::vector args; std::vector members; cxx::ID type_name; std::optional self; cxx::Block ctor; bool add_ctors = false; std::string str() const; std::string code() const; operator std::string() const { return str(); } operator cxx::Type() const { return str(); } }; namespace union_ { using Member = struct_::Member; } // namespace union_ /** A C++ union type. */ struct Union { std::vector members; cxx::ID type_name; std::string str() const; operator std::string() const { return str(); } operator cxx::Type() const { return str(); } }; namespace enum_ { using Label = std::pair; } // namespace enum_ /** A C++ enum type. */ struct Enum { std::vector labels; cxx::ID type_name; std::string str() const; operator std::string() const { return str(); } operator cxx::Type() const { return str(); } }; } // namespace type inline std::ostream& operator<<(std::ostream& o, const ID& i) { return o << std::string(i); } inline std::ostream& operator<<(std::ostream& o, const Linkage& l) { return o << std::string(l); } inline std::ostream& operator<<(std::ostream& o, const Type& t) { return o << std::string(t); } inline std::ostream& operator<<(std::ostream& o, const Attribute& a) { return o << std::string(a); } inline std::ostream& operator<<(std::ostream& o, const declaration::Argument& t) { return o << std::string(t); } inline std::ostream& operator<<(std::ostream& o, const Expression& e) { return o << std::string(e); } extern Formatter& operator<<(Formatter& f, const Block& x); extern Formatter& operator<<(Formatter& f, const Expression& x); extern Formatter& operator<<(Formatter& f, const ID& x); extern Formatter& operator<<(Formatter& f, const Function& x); extern Formatter& operator<<(Formatter& f, const Type& x); inline Formatter& operator<<(Formatter& f, const declaration::DeclarationBase& x) { x.emit(f); return f; } } // namespace hilti::detail::cxx