/////////////////////////////////////////////////////////////////////////////// // // 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 #include // mostly for MS Visual Studio compiler #define DECLARE_OPERATOR(op, ty, rty) \ friend auto operator op (const any&, ty) -> rty; \ friend auto operator op (ty, const any&) -> rty; #define DECLARE_COMPARISON_OPERATOR(op) \ friend auto operator op (const any&, const any&) -> bool; \ DECLARE_OPERATOR(op, long, bool) \ DECLARE_OPERATOR(op, double, bool) \ DECLARE_OPERATOR(op, char, bool) \ DECLARE_OPERATOR(op, const char *, bool) namespace PureScript { using string = 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 class Type : int8_t { Unknown, Integer, Double, Character, Boolean, StringLiteral, String, Map, Data, Array, Function, Closure, EffFunction, Thunk, Pointer }; private: mutable Type type; mutable bool is_shared; public: struct as_thunk { }; static constexpr as_thunk unthunk = as_thunk{}; using map = std::vector>; using data = std::vector; using array = std::deque; using fn = auto (*)(const any&) -> 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 { return lambda(arg); } }; class eff_fn { public: virtual auto operator()() const -> any = 0; }; template class _eff_fn : public eff_fn { const T lambda; public: _eff_fn(const T& l) noexcept : lambda(l) {} auto operator()() const -> any { return lambda(); } }; public: template using shared = std::shared_ptr; template inline static auto make_shared(Args&&... args) -> decltype(std::make_shared(std::forward(args)...)) { return std::make_shared(std::forward(args)...); } private: union { mutable long i; mutable double d; mutable char c; mutable bool b; mutable string r; mutable shared s; mutable shared m; mutable shared v; mutable shared a; mutable fn f; mutable shared l; mutable shared e; mutable thunk t; mutable shared p; }; public: any(const long val) : type(Type::Integer), is_shared(false), i(val) {} any(const int val) : type(Type::Integer), is_shared(false), i(val) {} any(const unsigned int val) : type(Type::Integer), is_shared(false), i(val) {} any(const double val) : type(Type::Double), is_shared(false), d(val) {} any(const char val) : type(Type::Character), is_shared(false), c(val) {} template ::value>::type> any(const T val) : type(Type::Boolean), is_shared(false), b(val) {} template any(const char (&val)[N]) : type(Type::StringLiteral), is_shared(false), r(val) {} any(char * val) : type(Type::String), is_shared(true), s(make_shared(val)) {} any(const std::string& val) : type(Type::String), is_shared(true), s(make_shared(val)) {} any(std::string&& val) : type(Type::String), is_shared(true), s(make_shared(std::move(val))) {} any(const shared& val) : type(Type::String), is_shared(true), s(val) {} any(shared&& val) noexcept : type(Type::String), is_shared(true), s(std::move(val)) {} any(const map& val) : type(Type::Map), is_shared(true), m(make_shared(val)) {} any(map&& val) noexcept : type(Type::Map), is_shared(true), m(make_shared(std::move(val))) {} any(const data& val) : type(Type::Data), is_shared(true), v(make_shared(val)) {} any(data&& val) noexcept : type(Type::Data), is_shared(true), v(make_shared(std::move(val))) {} any(const array& val) : type(Type::Array), is_shared(true), a(make_shared(val)) {} any(array&& val) noexcept : type(Type::Array), is_shared(true), a(make_shared(std::move(val))) {} template any(const T& val, typename std::enable_if::value>::type* = 0) : type(Type::Function), is_shared(false), f(val) {} template ::value && !std::is_convertible::value>::type> any(const T& val, typename std::enable_if,T>::value>::type* = 0) : type(Type::Closure), is_shared(true), l(make_shared<_closure>(val)) {} template any(const T& val, typename std::enable_if,T>::value>::type* = 0) : type(Type::EffFunction), is_shared(true), e(make_shared<_eff_fn>(val)) {} template any(const T& val, typename std::enable_if::value>::type* = 0) : type(Type::Thunk), is_shared(false), t(val) {} template any(const T& val, typename std::enable_if,T>::value>::type* = 0) : type(Type::Pointer), is_shared(true), p(val) {} template any(T&& val, typename std::enable_if,T>::value>::type* = 0) noexcept : type(Type::Pointer), is_shared(true), p(std::move(val)) {} any(std::nullptr_t) : type(Type::Pointer), is_shared(true), p(nullptr) {} private: auto copy(const any& other) const -> void { if (is_shared) { new (&p) shared(other.p); } else { f = other.f; } } auto move(any& other) const noexcept -> void { if (is_shared) { new (&p) shared(std::move(other.p)); } else { f = other.f; } } auto destruct() noexcept -> void { if (is_shared) { p.~shared(); } } public: any(const any& other) : type(other.type), is_shared(other.is_shared) { copy(other); } any(any&& other) noexcept : type(other.type), is_shared(other.is_shared) { move(other); } auto operator=(const any& rhs) -> any& { destruct(); type = rhs.type; is_shared = rhs.is_shared; copy(rhs); return *this; } auto operator=(any&& rhs) noexcept -> any& { destruct(); type = rhs.type; is_shared = rhs.is_shared; move(rhs); return *this; } any() = delete; ~any() { destruct(); } template auto cast() const -> typename std::enable_if::value, T>::type; template auto cast() const -> typename std::enable_if::value, T>::type; template auto cast() const -> typename std::enable_if::value, T>::type; template auto cast() const -> typename std::enable_if::value, T>::type; template auto cast() const -> typename std::enable_if::value, T>::type; template auto cast() const -> typename std::enable_if::value, const T&>::type; template auto cast() const -> typename std::enable_if::value, const T&>::type; template auto cast() const -> typename std::enable_if::value, const T&>::type; template auto cast() const -> typename std::enable_if,T>::value, typename T::element_type*>::type { return static_cast(extractPointer()); } 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 string() 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&; DECLARE_COMPARISON_OPERATOR(==) DECLARE_COMPARISON_OPERATOR(!=) DECLARE_COMPARISON_OPERATOR(<) DECLARE_COMPARISON_OPERATOR(<=) DECLARE_COMPARISON_OPERATOR(>) DECLARE_COMPARISON_OPERATOR(>=) // For MS Visual Studio compiler: broken typed-enum workaround DECLARE_OPERATOR(==, int, bool) DECLARE_OPERATOR(!=, int, bool) friend auto operator+(const any&, const any&) -> any; DECLARE_OPERATOR(+, long, long) DECLARE_OPERATOR(+, double, double) DECLARE_OPERATOR(+, char, char) DECLARE_OPERATOR(+, const char *, std::string) friend auto operator-(const any&, const any&) -> any; DECLARE_OPERATOR(-, long, long) DECLARE_OPERATOR(-, double, double) DECLARE_OPERATOR(-, char, char) friend auto operator*(const any&, const any&) -> any; DECLARE_OPERATOR(*, long, long) DECLARE_OPERATOR(*, double, double) DECLARE_OPERATOR(*, char, char) friend auto operator/(const any&, const any&) -> any; DECLARE_OPERATOR(/, long, long) DECLARE_OPERATOR(/, double, double) DECLARE_OPERATOR(/, char, char) friend auto operator%(const any&, const any&) -> any; DECLARE_OPERATOR(%, long, long) DECLARE_OPERATOR(%, char, char) friend auto operator-(const any&) -> any; // unary negate }; // For MS Visual Studio compiler: broken typed-enum workaround inline auto operator==(const any& lhs, int rhs) -> bool { return lhs == (long)rhs; } inline auto operator!=(const any& lhs, int rhs) -> bool { return lhs != (long)rhs; } } // namespace PureScript #undef DECLARE_OPERATOR #undef DECLARE_COMPARISON_OPERATOR #endif // PureScript_HH