/////////////////////////////////////////////////////////////////////////////// // // Module : purescript.hh // Copyright : (c) Andy Arvanitis 2015 // License : MIT // // Maintainer : Andy Arvanitis // Stability : experimental // Portability : // // Basic types and functions to support purescript-to-C++1x rendering // /////////////////////////////////////////////////////////////////////////////// // #ifndef PureScript_HH #define PureScript_HH #if !defined(__cplusplus) #error This is a C++ file! #endif // Standard includes #if defined(DEBUG) #include #include #define IF_DEBUG(x) x #else #define NDEBUG #undef assert #define assert(x) #define IF_DEBUG(_) #endif #include #include #include #include #include #include #include // mostly for MS Visual Studio compiler #include "purescript_memory.hh" namespace PureScript { using cstring = const char *; using std::nullptr_t; using std::runtime_error; namespace Private { struct SymbolGeneratorAnchor {}; template struct SymbolGenerator { constexpr static SymbolGeneratorAnchor anchor = SymbolGeneratorAnchor{}; }; template constexpr SymbolGeneratorAnchor SymbolGenerator::anchor; using Symbol = const SymbolGeneratorAnchor *; } #define define_symbol(S) namespace PureScript { \ namespace Private { \ namespace Symbols { \ struct S_ ## S {}; \ } \ } \ } #define symbol(S) (&Private::SymbolGenerator<::PureScript::Private::Symbols::S_ ## S>::anchor) constexpr bool undefined = false; // TODO: Not a real limit, just used for simpler accessors static constexpr size_t unknown_size = 64; // A variant data class designed to provide some features of dynamic typing. // class any { public: enum class Tag { Thunk = 0x10, Integer, Double, Character, Boolean, StringLiteral, Function, EffFunction, RawPointer, String, Map, Data, Array, Closure, EffClosure, Pointer }; private: mutable Tag tag; public: struct as_thunk { }; static constexpr as_thunk unthunk = as_thunk{}; using map_pair = std::pair; template using map = std::array; template using data = std::array; using array = std::deque; using fn = auto (*)(const any&) -> any; using eff_fn = auto (*)() -> any; using thunk = auto (*)(const as_thunk) -> const any&; private: class Closure { public: virtual auto operator()(const any&) const -> any = 0; virtual ~Closure(); }; template class Closure_ : public Closure { const T lambda; public: Closure_(const T& l) noexcept : lambda(l) {} auto operator()(const any& arg) const -> any override { return lambda(arg); } }; class EffClosure { public: virtual auto operator()() const -> any = 0; virtual ~EffClosure(); }; template class EffClosure_ : public EffClosure { const T lambda; public: EffClosure_(const T& l) noexcept : lambda(l) {} auto operator()() const -> any override { return lambda(); } }; private: union { mutable thunk t; mutable int i; mutable double d; mutable char c; mutable bool b; mutable cstring r; mutable fn f; mutable eff_fn e; mutable void * u; mutable managed s; mutable managed a; mutable managed l; mutable managed k; mutable managed p; }; public: any(const int val) noexcept : tag(Tag::Integer), i(val) {} any(const long val) noexcept : tag(Tag::Integer), i(static_cast(val)) { assert(val >= std::numeric_limits::min() && val <= std::numeric_limits::max()); } any(const double val) noexcept : tag(Tag::Double), d(val) {} any(const char val) noexcept : tag(Tag::Character), c(val) {} template ::value>::type> any(const T val) noexcept : tag(Tag::Boolean), b(val) {} template any(const char (&val)[N]) noexcept : tag(Tag::StringLiteral), r(val) {} any(char * val) : tag(Tag::String), s(make_managed(val)) {} any(const std::string& val) : tag(Tag::String), s(make_managed(val)) {} any(std::string&& val) noexcept : tag(Tag::String), s(make_managed(std::move(val))) {} any(const managed& val) noexcept : tag(Tag::String), s(val) {} any(managed&& val) noexcept : tag(Tag::String), s(std::move(val)) {} template any(map&& val) noexcept : tag(Tag::Map), p(make_managed>(std::move(val))) {} template any(data&& val) noexcept : tag(Tag::Data), p(make_managed>(std::move(val))) {} any(const array& val) : tag(Tag::Array), a(make_managed(val)) {} any(array&& val) noexcept : tag(Tag::Array), a(make_managed(std::move(val))) {} template any(const T& val, typename std::enable_if::value>::type* = 0) noexcept : tag(Tag::Function), f(val) {} template ::value && !std::is_convertible::value>::type> any(const T& val, typename std::enable_if,T>::value>::type* = 0) : tag(Tag::Closure), l(make_managed>(val)) {} template any(const T& val, typename std::enable_if::value>::type* = 0) noexcept : tag(Tag::EffFunction), e(val) {} template ::value && !std::is_convertible::value>::type> any(const T& val, typename std::enable_if,T>::value>::type* = 0) : tag(Tag::EffClosure), k(make_managed>(val)) {} template any(const T& val, typename std::enable_if::value>::type* = 0) noexcept : tag(Tag::Thunk), t(val) {} template ::type>::value>::type> any(const T& val, typename std::enable_if::type* = 0) noexcept : tag(Tag::Pointer), p(val) {} template any(T&& val, typename std::enable_if,T>::value>::type* = 0) noexcept : tag(Tag::Pointer), p(std::move(val)) {} // Explicit void* to raw pointer value template any(const T& val, typename std::enable_if::value>::type* = 0) noexcept : tag(Tag::RawPointer), u(val) {} any(nullptr_t) noexcept : tag(Tag::RawPointer), u(nullptr) {} #if !defined(USE_GC) private: template ::value>::type> static constexpr auto move_if_rvalue(U&& u) noexcept -> U&& { return static_cast(u); } template ::value>::type> static constexpr auto move_if_rvalue(U& u) noexcept -> U& { return static_cast(u); } template auto assign(T&& other) const noexcept -> void { switch (other.tag) { case Tag::Thunk: t = other.t; break; case Tag::Integer: i = other.i; break; case Tag::Double: d = other.d; break; case Tag::Character: c = other.c; break; case Tag::Boolean: b = other.b; break; case Tag::StringLiteral: r = other.r; break; case Tag::Function: f = other.f; break; case Tag::EffFunction: e = other.e; break; case Tag::RawPointer: u = other.u; break; case Tag::String: new (&s) managed(move_if_rvalue(other.s)); break; case Tag::Array: new (&a) managed(move_if_rvalue(other.a)); break; case Tag::Closure: new (&l) managed(move_if_rvalue(other.l)); break; case Tag::EffClosure: new (&k) managed(move_if_rvalue(other.k)); break; case Tag::Map: case Tag::Data: case Tag::Pointer: new (&p) managed(move_if_rvalue(other.p)); break; default: assert(false && "Bad 'any' tag"); break; } } auto destruct() -> void { switch (tag) { case Tag::String: s.~managed(); break; case Tag::Array: a.~managed(); break; case Tag::Closure: l.~managed(); break; case Tag::EffClosure: k.~managed(); break; case Tag::Map: case Tag::Data: case Tag::Pointer: p.~managed(); break; default: break; } } public: any(const any& other) noexcept : tag(other.tag) { assign(other); } any(any&& other) noexcept : tag(other.tag) { assign(std::move(other)); } auto operator=(const any& rhs) noexcept -> any& { destruct(); tag = rhs.tag; assign(rhs); return *this; } auto operator=(any&& rhs) noexcept -> any& { destruct(); tag = rhs.tag; assign(std::move(rhs)); return *this; } ~any() { destruct(); } #endif // !defined(USE_GC) public: any() = delete; auto operator()(const any&) const -> any; auto operator()(const as_thunk) const -> const any&; auto operator()() const -> any; operator int() const; operator double() const; operator bool() const; operator char() const; operator size_t() const; operator cstring() const; operator const array&() const; auto operator[](const size_t) const -> const any&; auto size() const -> size_t; auto empty() const -> bool; auto contains(const Private::Symbol) const -> bool; auto extractPointer(IF_DEBUG(const Tag)) const -> void*; auto rawPointer() const -> void* { const any& variant = unthunkVariant(*this); assert(tag == Tag::RawPointer); return variant.u; } static auto unthunkVariant(const any&) -> const any&; #define DEFINE_OPERATOR(op, ty, rty) \ inline friend auto operator op (const any& lhs, ty rhs) -> rty { \ return (ty)lhs op rhs; \ } \ inline friend auto operator op (ty lhs, const any& rhs) -> rty { \ return lhs op (ty)rhs; \ } \ #define DECLARE_COMPARISON_OPERATOR(op) \ friend auto operator op (const any&, const any&) -> bool; \ DEFINE_OPERATOR(op, int, bool) \ DEFINE_OPERATOR(op, double, bool) \ DEFINE_OPERATOR(op, char, bool) \ DEFINE_OPERATOR(op, bool, bool) \ friend auto operator op (const any&, const char * const) -> bool; \ friend auto operator op (const char * const, const any&) -> bool; DECLARE_COMPARISON_OPERATOR(==) DECLARE_COMPARISON_OPERATOR(!=) DECLARE_COMPARISON_OPERATOR(<) DECLARE_COMPARISON_OPERATOR(<=) DECLARE_COMPARISON_OPERATOR(>) DECLARE_COMPARISON_OPERATOR(>=) friend auto operator+(const any&, const any&) -> any; DEFINE_OPERATOR(+, int, int) DEFINE_OPERATOR(+, double, double) DEFINE_OPERATOR(+, char, char) friend auto operator+(const any& lhs, const char * const rhs) -> std::string; friend auto operator+(const char * const lhs, const any& rhs) -> std::string; friend auto operator-(const any&, const any&) -> any; DEFINE_OPERATOR(-, int, int) DEFINE_OPERATOR(-, double, double) DEFINE_OPERATOR(-, char, char) friend auto operator*(const any&, const any&) -> any; DEFINE_OPERATOR(*, int, int) DEFINE_OPERATOR(*, double, double) DEFINE_OPERATOR(*, char, char) friend auto operator/(const any&, const any&) -> any; DEFINE_OPERATOR(/, int, int) DEFINE_OPERATOR(/, double, double) DEFINE_OPERATOR(/, char, char) friend auto operator%(const any&, const any&) -> any; DEFINE_OPERATOR(%, int, int) DEFINE_OPERATOR(%, char, char) friend auto operator-(const any&) -> any; // unary negate }; // class any namespace Private { template struct TagHelper {}; template struct TagHelper> { static constexpr any::Tag tag = any::Tag::Map; }; template struct TagHelper> { static constexpr any::Tag tag = any::Tag::Data; }; template struct TagHelper { static constexpr any::Tag tag = any::Tag::Pointer; }; } // Pass-through template constexpr auto cast(const T& a) -> const T& { return a; } template inline auto cast(const any& a) -> typename std::enable_if::value || std::is_same::value, T>::type { return a; } template inline auto cast(const any& a) -> typename std::enable_if::value, const T&>::type { return a; } template ::value>::type> inline auto cast(const any& a) -> typename std::enable_if::value, T&>::type { return *static_cast(a.extractPointer(IF_DEBUG(Private::TagHelper::tag))); } template inline auto cast(const any& a) -> typename std::enable_if::value, T>::type { return a.rawPointer(); } namespace map { template inline auto get(const T& a) -> typename std::enable_if::value, const any&>::type { return a[N].second; } template inline auto get(const any& a) -> const any& { return cast>(a)[N].second; } template inline auto get(const Private::Symbol key, const any::map& a) -> const any& { static_assert(N > 0, "map size must be greater than zero"); typename std::remove_reference::type::size_type i = 0; do { if (a[i].first == key) { return a[i].second; } } while (a[++i].first != nullptr); assert(false && "map key not found"); static const any invalid_key(nullptr); return invalid_key; } inline auto get(const Private::Symbol key, const any& a) -> const any& { return get(key, cast>(a)); } } namespace data { template inline auto get(const T& a) -> typename std::enable_if::value, const any&>::type { return a[N]; } template inline auto get(const any& a) -> const any& { return cast>(a)[N]; } template inline auto ctor(const T& a) -> int { return get<0>(a); } } } // namespace PureScript #undef WITH_ALLOCATOR #undef IS_POINTER_TYPE #undef DEFINE_OPERATOR #undef DECLARE_COMPARISON_OPERATOR #endif // PureScript_HH