455 lines
16 KiB
C++
455 lines
16 KiB
C++
// Copyright (c) 2020-now by the Zeek Project. See LICENSE for details.
|
|
|
|
#pragma once
|
|
|
|
#include <optional>
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <utility>
|
|
#include <variant>
|
|
#include <vector>
|
|
|
|
#include <hilti/ast/id.h>
|
|
#include <hilti/ast/type.h>
|
|
#include <hilti/base/id-base.h>
|
|
#include <hilti/base/util.h>
|
|
|
|
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<element::Type T>
|
|
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<element::Type::Attribute>; /**< C++ function attribute */
|
|
using Linkage = Element<element::Type::Linkage>; /**< C++ linkage specification */
|
|
using Type = Element<element::Type::Type>; /**< 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<std::string> normalizeID(std::string_view id);
|
|
|
|
/** A C++ ID. */
|
|
class ID : public detail::IDBase<ID, normalizeID> {
|
|
public:
|
|
using Base = detail::IDBase<ID, normalizeID>;
|
|
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<cxx::Expression> args = {},
|
|
std::optional<cxx::Expression> 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<cxx::Expression> args;
|
|
std::optional<cxx::Expression> 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<cxx::Expression> args;
|
|
std::optional<cxx::Expression> init;
|
|
Linkage linkage;
|
|
|
|
Global(cxx::ID id = {}, cxx::Type type = {}, std::vector<cxx::Expression> args = {},
|
|
std::optional<cxx::Expression> 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<cxx::Expression> init;
|
|
Linkage linkage;
|
|
|
|
Constant(cxx::ID id = {}, cxx::Type type = {}, std::optional<cxx::Expression> 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<cxx::Expression> 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<cxx::Expression> 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<std::string> 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<std::pair<Expression, Block>>& cases_,
|
|
std::optional<Block> default_ = {});
|
|
void appendFromBlock(Block b);
|
|
void addTry(Block body, std::vector<std::pair<declaration::Argument, Block>> 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<std::tuple<std::string, Block, Flags>> _stmts;
|
|
std::vector<std::string> _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<Argument> args;
|
|
Linkage linkage;
|
|
std::optional<Block> body;
|
|
std::optional<Block> inline_body;
|
|
|
|
std::string prototype(bool qualify) const;
|
|
std::string parameters() const;
|
|
|
|
Function(Type ftype, cxx::Type result, cxx::ID id, std::vector<Argument> args, Linkage linkage,
|
|
std::optional<Block> 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<Argument> 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<declaration::Local, declaration::Function>;
|
|
|
|
inline bool operator<(const Member& m1, const Member& m2) {
|
|
auto id = [](auto m) {
|
|
if ( auto x = std::get_if<declaration::Local>(&m) )
|
|
return x->id;
|
|
if ( auto x = std::get_if<declaration::Function>(&m) )
|
|
return x->id;
|
|
|
|
throw std::bad_variant_access();
|
|
};
|
|
|
|
return id(m1) < id(m2);
|
|
}
|
|
|
|
} // namespace struct_
|
|
|
|
/** A C++ struct type. */
|
|
struct Struct {
|
|
std::vector<declaration::Argument> args;
|
|
std::vector<struct_::Member> members;
|
|
cxx::ID type_name;
|
|
std::optional<cxx::Type> 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<union_::Member> 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<cxx::ID, int>;
|
|
} // namespace enum_
|
|
|
|
/** A C++ enum type. */
|
|
struct Enum {
|
|
std::vector<enum_::Label> 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
|