/////////////////////////////////////////////////////////////////////////////// // // 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 // Standard includes #if defined(DEBUG) #include #else #define NDEBUG #undef assert #define assert(x) #endif #include #include #include #include #include #include #include // mostly for MS Visual Studio compiler #include "PureScript_GC.hh" namespace PureScript { using cstring = const char *; using nullptr_t = std::nullptr_t; // Workaround for missing C++11 version in gcc class runtime_error : public std::runtime_error { public: runtime_error(const char message[]) : std::runtime_error(std::string(message)) {} }; const bool undefined = false; const size_t constructor = 0; // A variant data class designed to provide some features of dynamic typing. // class any { public: enum Type { Thunk = 0x00, Integer = 0x01, Double = 0x02, Character = 0x03, Boolean = 0x04, StringLiteral = 0x05, Function = 0x06, EffFunction = 0x07, Shared = 0x08, String = 0x08, Map = 0x09, Data = 0x0A, Array = 0x0B, Closure = 0x0C, EffClosure = 0x0D, Pointer = 0x0E }; private: mutable Type type; public: struct as_thunk { }; static constexpr as_thunk unthunk = as_thunk{}; using map_pair = std::pair; using map = std::vector; using data = std::vector; 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 eff_closure { public: virtual auto operator()() const -> any = 0; virtual ~eff_closure() {} }; template class _eff_closure : public eff_closure { const T lambda; public: _eff_closure(const T& l) noexcept : lambda(l) {} auto operator()() const -> any override { return lambda(); } }; public: template using shared = SHARED_TYPE(T); template inline static auto make_shared(Args&&... args) -> SHARED_TYPE(T) { return MAKE_SHARED(T)(std::forward(args)...); } private: union { mutable thunk t; mutable long i; mutable double d; mutable char c; mutable bool b; mutable cstring r; mutable fn f; mutable eff_fn e; mutable shared s; mutable shared m; mutable shared v; mutable shared a; mutable shared l; mutable shared el; mutable shared p; }; public: any(const long val) noexcept : type(Type::Integer), i(val) {} any(const int val) noexcept : type(Type::Integer), i(val) {} any(const unsigned int val) noexcept : type(Type::Integer), i(val) {} any(const double val) noexcept : type(Type::Double), d(val) {} any(const char val) noexcept : type(Type::Character), c(val) {} template ::value>::type> any(const T val) noexcept : type(Type::Boolean), b(val) {} template any(const char (&val)[N]) noexcept : type(Type::StringLiteral), r(val) {} any(char * val) : type(Type::String), s(make_shared(val)) {} any(const std::string& val) : type(Type::String), s(make_shared(val)) {} any(std::string&& val) noexcept : type(Type::String), s(make_shared(std::move(val))) {} any(const shared& val) noexcept : type(Type::String), s(val) {} any(shared&& val) noexcept : type(Type::String), s(std::move(val)) {} any(const map& val) : type(Type::Map), m(make_shared(val)) {} any(map&& val) noexcept : type(Type::Map), m(make_shared(std::move(val))) {} any(const data& val) : type(Type::Data), v(make_shared(val)) {} any(data&& val) noexcept : type(Type::Data), v(make_shared(std::move(val))) {} any(const array& val) : type(Type::Array), a(make_shared(val)) {} any(array&& val) noexcept : type(Type::Array), a(make_shared(std::move(val))) {} template any(const T& val, typename std::enable_if::value>::type* = 0) noexcept : type(Type::Function), f(val) {} template ::value && !std::is_convertible::value>::type> any(const T& val, typename std::enable_if,T>::value>::type* = 0) : type(Type::Closure), l(make_shared<_closure>(val)) {} template any(const T& val, typename std::enable_if::value>::type* = 0) noexcept : type(Type::EffFunction), e(val) {} template ::value && !std::is_convertible::value>::type> any(const T& val, typename std::enable_if,T>::value>::type* = 0) : type(Type::EffClosure), el(make_shared<_eff_closure>(val)) {} template any(const T& val, typename std::enable_if::value>::type* = 0) noexcept : type(Type::Thunk), t(val) {} template ::type>::value>::type> any(const T& val, typename std::enable_if::type* = 0) noexcept : type(Type::Pointer), p(val) {} template any(T&& val, typename std::enable_if,T>::value>::type* = 0) noexcept : type(Type::Pointer), p(std::move(val)) {} any(std::nullptr_t) noexcept : type(Type::Pointer), p(nullptr) {} #if !defined(USE_GC) private: auto copy(const any& other) const noexcept -> void { if (type & Type::Shared) { new (&p) shared(other.p); } else { f = other.f; } } auto move(any&& other) const noexcept -> void { if (type & Type::Shared) { new (&p) shared(std::move(other.p)); } else { f = other.f; } } auto destruct() -> void { if (type & Type::Shared) { p.~shared(); } } public: any(const any& other) noexcept : type(other.type) { copy(other); } any(any&& other) noexcept : type(other.type) { move(std::move(other)); } auto operator=(const any& rhs) noexcept -> any& { destruct(); type = rhs.type; copy(rhs); return *this; } auto operator=(any&& rhs) noexcept -> any& { destruct(); type = rhs.type; move(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 long() const; operator double() const; operator bool() const; operator char() const; operator cstring() const; operator const map&() const; operator const data&() const; operator const array&() const; auto operator[](const char[]) const -> const any&; auto operator[](const size_t) const -> const any&; auto operator[](const any&) const -> const any&; auto contains(const char[]) const -> bool; auto extractPointer() const -> void*; 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 DEFINE_INT_OPERATOR(op, rty) \ inline friend auto operator op (const any& lhs, int rhs) -> rty { \ return (long)lhs op (long)rhs; \ } \ inline friend auto operator op (int lhs, const any& rhs) -> rty { \ return (long)lhs op (long)rhs; \ } #define DECLARE_COMPARISON_OPERATOR(op) \ friend auto operator op (const any&, const any&) -> bool; \ DEFINE_OPERATOR(op, long, bool) \ DEFINE_OPERATOR(op, double, bool) \ DEFINE_OPERATOR(op, char, bool) \ DEFINE_INT_OPERATOR(op, 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(+, long, long) DEFINE_OPERATOR(+, double, double) DEFINE_OPERATOR(+, char, char) DEFINE_INT_OPERATOR(+, long) 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(-, long, long) DEFINE_OPERATOR(-, double, double) DEFINE_OPERATOR(-, char, char) DEFINE_INT_OPERATOR(-, long) friend auto operator*(const any&, const any&) -> any; DEFINE_OPERATOR(*, long, long) DEFINE_OPERATOR(*, double, double) DEFINE_OPERATOR(*, char, char) DEFINE_INT_OPERATOR(*, long) friend auto operator/(const any&, const any&) -> any; DEFINE_OPERATOR(/, long, long) DEFINE_OPERATOR(/, double, double) DEFINE_OPERATOR(/, char, char) DEFINE_INT_OPERATOR(/, long) friend auto operator%(const any&, const any&) -> any; DEFINE_OPERATOR(%, long, long) DEFINE_OPERATOR(%, char, char) DEFINE_INT_OPERATOR(%, long) friend auto operator-(const any&) -> any; // unary negate }; 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 || std::is_same::value || std::is_same::value, const T&>::type { return a; } template ::value>::type> inline auto cast(const any& a) -> typename std::enable_if::value && !std::is_same::value && !std::is_same::value, T&>::type { return *static_cast(a.extractPointer()); } } // namespace PureScript #undef WITH_ALLOCATOR #undef SHARED_TYPE #undef MAKE_SHARED #undef IS_POINTER_TYPE #undef DEFINE_OPERATOR #undef DEFINE_INT_OPERATOR #undef DECLARE_COMPARISON_OPERATOR #endif // PureScript_HH