git.iko.soy/rowtype-yoga/purescript-native
on d681b45 help sign in
426 lines 14 kB history raw
1///////////////////////////////////////////////////////////////////////////////
2//
3// Module : purescript.hh
4// Copyright : (c) Andy Arvanitis 2015
5// License : MIT
6//
7// Maintainer : Andy Arvanitis <andy.arvanitis@gmail.com>
8// Stability : experimental
9// Portability :
10//
11// Basic types and functions to support purescript-to-C++1x rendering
12//
13///////////////////////////////////////////////////////////////////////////////
14//
15#ifndef PureScript_HH
16#define PureScript_HH
18// Standard includes
20#if defined(DEBUG)
21 #include <cassert>
22 #include <climits>
23#else
24 #define NDEBUG
25 #undef assert
26 #define assert(x)
27#endif
29#include <functional>
30#include <string>
31#include <vector>
32#include <deque>
33#include <utility>
34#include <stdexcept>
35#include <iso646.h> // mostly for MS Visual Studio compiler
36#include "purescript_memory.hh"
38namespace PureScript {
40using cstring = const char *;
41using std::nullptr_t;
43// Workaround for missing C++11 version in gcc
44class runtime_error : public std::runtime_error {
45public:
46 runtime_error(const char message[]) : std::runtime_error(std::string(message)) {}
47};
49const bool undefined = false;
50const size_t constructor = 0;
52// A variant data class designed to provide some features of dynamic typing.
53//
54class any {
56 public:
57 enum class Type {
58 Thunk = 0x10,
59 Integer,
60 Double,
61 Character,
62 Boolean,
63 StringLiteral,
64 Function,
65 EffFunction,
66 RawPointer,
67 String,
68 Map,
69 Data,
70 Array,
71 Closure,
72 EffClosure,
73 Pointer
74 };
76 private:
77 mutable Type type;
79 public:
80 struct as_thunk {
81 };
82 static constexpr as_thunk unthunk = as_thunk{};
84 using map_pair = std::pair<const char * const, const any>;
85 using map = std::vector<map_pair WITH_ALLOCATOR(map_pair)>;
86 using data = std::vector<any WITH_ALLOCATOR(any)>;
87 using array = std::deque<any WITH_ALLOCATOR(any)>;
88 using fn = auto (*)(const any&) -> any;
89 using eff_fn = auto (*)() -> any;
90 using thunk = auto (*)(const as_thunk) -> const any&;
92 private:
93 class closure {
94 public:
95 virtual auto operator()(const any&) const -> any = 0;
96 virtual ~closure() {}
97 };
99 template <typename T>
100 class _closure : public closure {
101 const T lambda;
102 public:
103 _closure(const T& l) noexcept : lambda(l) {}
104 auto operator()(const any& arg) const -> any override {
105 return lambda(arg);
106 }
107 };
109 class eff_closure {
110 public:
111 virtual auto operator()() const -> any = 0;
112 virtual ~eff_closure() {}
113 };
115 template <typename T>
116 class _eff_closure : public eff_closure {
117 const T lambda;
118 public:
119 _eff_closure(const T& l) noexcept : lambda(l) {}
120 auto operator()() const -> any override {
121 return lambda();
122 }
123 };
125 private:
126 union {
127 mutable thunk t;
128 mutable int i;
129 mutable double d;
130 mutable char c;
131 mutable bool b;
132 mutable cstring r;
133 mutable fn f;
134 mutable eff_fn e;
135 mutable void * u;
136 mutable managed<std::string> s;
137 mutable managed<map> m;
138 mutable managed<data> v;
139 mutable managed<array> a;
140 mutable managed<closure> l;
141 mutable managed<eff_closure> k;
142 mutable managed<void> p;
143 };
145 public:
147 any(const int val) noexcept : type(Type::Integer), i(val) {}
148 any(const long int_value) noexcept : type(Type::Integer), i(int_value) {
149 assert(int_value >= INT_MIN && int_value <= INT_MAX);
150 }
152 any(const double val) noexcept : type(Type::Double), d(val) {}
153 any(const char val) noexcept : type(Type::Character), c(val) {}
155 template <typename T, typename = typename std::enable_if<std::is_same<bool,T>::value>::type>
156 any(const T val) noexcept : type(Type::Boolean), b(val) {}
158 template <size_t N>
159 any(const char (&val)[N]) noexcept : type(Type::StringLiteral), r(val) {}
160 any(char * val) : type(Type::String), s(make_managed<std::string>(val)) {}
162 any(const std::string& val) : type(Type::String), s(make_managed<std::string>(val)) {}
163 any(std::string&& val) noexcept : type(Type::String), s(make_managed<std::string>(std::move(val))) {}
165 any(const managed<std::string>& val) noexcept : type(Type::String), s(val) {}
166 any(managed<std::string>&& val) noexcept : type(Type::String), s(std::move(val)) {}
168 any(const map& val) : type(Type::Map), m(make_managed<map>(val)) {}
169 any(map&& val) noexcept : type(Type::Map), m(make_managed<map>(std::move(val))) {}
171 any(const data& val) : type(Type::Data), v(make_managed<data>(val)) {}
172 any(data&& val) noexcept : type(Type::Data), v(make_managed<data>(std::move(val))) {}
174 any(const array& val) : type(Type::Array), a(make_managed<array>(val)) {}
175 any(array&& val) noexcept : type(Type::Array), a(make_managed<array>(std::move(val))) {}
177 template <typename T>
178 any(const T& val, typename std::enable_if<std::is_convertible<T,fn>::value>::type* = 0) noexcept
179 : type(Type::Function), f(val) {}
181 template <typename T, typename = typename std::enable_if<!std::is_same<any,T>::value &&
182 !std::is_convertible<T,fn>::value>::type>
183 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any(const any&)>,T>::value>::type* = 0)
184 : type(Type::Closure), l(make_managed<_closure<T>>(val)) {}
186 template <typename T>
187 any(const T& val, typename std::enable_if<std::is_convertible<T,eff_fn>::value>::type* = 0) noexcept
188 : type(Type::EffFunction), e(val) {}
190 template <typename T,
191 typename = typename std::enable_if<!std::is_same<any,T>::value &&
192 !std::is_convertible<T,eff_fn>::value>::type>
193 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any()>,T>::value>::type* = 0)
194 : type(Type::EffClosure), k(make_managed<_eff_closure<T>>(val)) {}
196 template <typename T>
197 any(const T& val, typename std::enable_if<std::is_convertible<T,thunk>::value>::type* = 0) noexcept
198 : type(Type::Thunk), t(val) {}
200 template <typename T,
201 typename = typename std::enable_if<std::is_class<typename std::remove_pointer<T>::type>::value>::type>
202 any(const T& val, typename std::enable_if<IS_POINTER_TYPE(T)::value>::type* = 0) noexcept
203 : type(Type::Pointer), p(val) {}
205 template <typename T>
206 any(T&& val, typename std::enable_if<std::is_assignable<managed<void>,T>::value>::type* = 0) noexcept
207 : type(Type::Pointer), p(std::move(val)) {}
209 // Explicit void* to raw pointer value
210 template <typename T>
211 any(const T& val, typename std::enable_if<std::is_same<T,void*>::value>::type* = 0) noexcept
212 : type(Type::RawPointer), u(val) {}
214 any(std::nullptr_t) noexcept : type(Type::Pointer), p(nullptr) {}
217#if !defined(USE_GC)
218 private:
219 template <typename T,
220 typename U,
221 typename = typename std::enable_if<!std::is_reference<T>::value>::type>
222 static constexpr auto move_if_rvalue(U&& u) noexcept -> U&& {
223 return static_cast<U&&>(u);
224 }
226 template <typename T,
227 typename U,
228 typename = typename std::enable_if<std::is_reference<T>::value>::type>
229 static constexpr auto move_if_rvalue(U& u) noexcept -> U& {
230 return static_cast<U&>(u);
231 }
233 template <typename T>
234 auto assign(T&& other) const noexcept -> void {
235 switch (other.type) {
236 case Type::Thunk: t = other.t; break;
237 case Type::Integer: i = other.i; break;
238 case Type::Double: d = other.d; break;
239 case Type::Character: c = other.c; break;
240 case Type::Boolean: b = other.b; break;
241 case Type::StringLiteral: r = other.r; break;
242 case Type::Function: f = other.f; break;
243 case Type::EffFunction: e = other.e; break;
244 case Type::RawPointer: u = other.u; break;
246 case Type::String: new (&s) managed<std::string>(move_if_rvalue<T>(other.s)); break;
247 case Type::Map: new (&m) managed<map>(move_if_rvalue<T>(other.m)); break;
248 case Type::Data: new (&v) managed<data>(move_if_rvalue<T>(other.v)); break;
249 case Type::Array: new (&a) managed<array>(move_if_rvalue<T>(other.a)); break;
250 case Type::Closure: new (&l) managed<closure>(move_if_rvalue<T>(other.l)); break;
251 case Type::EffClosure: new (&k) managed<eff_closure>(move_if_rvalue<T>(other.k)); break;
252 case Type::Pointer: new (&p) managed<void>(move_if_rvalue<T>(other.p)); break;
254 default: assert(false && "Bad 'any' type"); break;
255 }
256 }
258 auto destruct() -> void {
259 switch (type) {
260 case Type::String: s.~managed<std::string>(); break;
261 case Type::Map: m.~managed<map>(); break;
262 case Type::Data: v.~managed<data>(); break;
263 case Type::Array: a.~managed<array>(); break;
264 case Type::Closure: l.~managed<closure>(); break;
265 case Type::EffClosure: k.~managed<eff_closure>(); break;
266 case Type::Pointer: p.~managed<void>(); break;
268 default: break;
269 }
270 }
272 public:
273 any(const any& other) noexcept : type(other.type) {
274 assign(other);
275 }
277 any(any&& other) noexcept : type(other.type) {
278 assign(std::move(other));
279 }
281 auto operator=(const any& rhs) noexcept -> any& {
282 destruct();
283 type = rhs.type;
284 assign(rhs);
285 return *this;
286 }
288 auto operator=(any&& rhs) noexcept -> any& {
289 destruct();
290 type = rhs.type;
291 assign(std::move(rhs));
292 return *this;
293 }
295 ~any() {
296 destruct();
297 }
298#endif // !defined(USE_GC)
300 public:
301 any() = delete;
303 auto operator()(const any&) const -> any;
304 auto operator()(const as_thunk) const -> const any&;
305 auto operator()() const -> any;
307 operator int() const;
308 operator double() const;
309 operator bool() const;
310 operator char() const;
311 operator cstring() const;
312 operator const map&() const;
313 operator const data&() const;
314 operator const array&() const;
316 auto operator[](const char[]) const -> const any&;
317 auto operator[](const size_t) const -> const any&;
318 auto operator[](const any&) const -> const any&;
320 auto contains(const char[]) const -> bool;
322 auto extractPointer() const -> void*;
324 auto rawPointer() const -> void* {
325 const any& variant = unthunkVariant(*this);
326 assert(type == Type::RawPointer);
327 return variant.u;
328 }
330 static auto unthunkVariant(const any&) -> const any&;
332 #define DEFINE_OPERATOR(op, ty, rty) \
333 inline friend auto operator op (const any& lhs, ty rhs) -> rty { \
334 return (ty)lhs op rhs; \
335 } \
336 inline friend auto operator op (ty lhs, const any& rhs) -> rty { \
337 return lhs op (ty)rhs; \
338 } \
340 #define DECLARE_COMPARISON_OPERATOR(op) \
341 friend auto operator op (const any&, const any&) -> bool; \
342 DEFINE_OPERATOR(op, int, bool) \
343 DEFINE_OPERATOR(op, double, bool) \
344 DEFINE_OPERATOR(op, char, bool) \
345 DEFINE_OPERATOR(op, bool, bool) \
346 friend auto operator op (const any&, const char * const) -> bool; \
347 friend auto operator op (const char * const, const any&) -> bool;
349 DECLARE_COMPARISON_OPERATOR(==)
350 DECLARE_COMPARISON_OPERATOR(!=)
351 DECLARE_COMPARISON_OPERATOR(<)
352 DECLARE_COMPARISON_OPERATOR(<=)
353 DECLARE_COMPARISON_OPERATOR(>)
354 DECLARE_COMPARISON_OPERATOR(>=)
356 friend auto operator+(const any&, const any&) -> any;
358 DEFINE_OPERATOR(+, int, int)
359 DEFINE_OPERATOR(+, double, double)
360 DEFINE_OPERATOR(+, char, char)
361 friend auto operator+(const any& lhs, const char * const rhs) -> std::string;
362 friend auto operator+(const char * const lhs, const any& rhs) -> std::string;
364 friend auto operator-(const any&, const any&) -> any;
366 DEFINE_OPERATOR(-, int, int)
367 DEFINE_OPERATOR(-, double, double)
368 DEFINE_OPERATOR(-, char, char)
370 friend auto operator*(const any&, const any&) -> any;
372 DEFINE_OPERATOR(*, int, int)
373 DEFINE_OPERATOR(*, double, double)
374 DEFINE_OPERATOR(*, char, char)
376 friend auto operator/(const any&, const any&) -> any;
378 DEFINE_OPERATOR(/, int, int)
379 DEFINE_OPERATOR(/, double, double)
380 DEFINE_OPERATOR(/, char, char)
382 friend auto operator%(const any&, const any&) -> any;
384 DEFINE_OPERATOR(%, int, int)
385 DEFINE_OPERATOR(%, char, char)
387 friend auto operator-(const any&) -> any; // unary negate
388};
390template <typename T>
391inline auto cast(const any& a) ->
392 typename std::enable_if<std::is_arithmetic<T>::value ||
393 std::is_same<T,cstring>::value, T>::type {
394 return a;
397template <typename T>
398inline auto cast(const any& a) ->
399 typename std::enable_if<std::is_same<T, any::map>::value ||
400 std::is_same<T, any::data>::value ||
401 std::is_same<T, any::array>::value, const T&>::type {
402 return a;
405template <typename T, typename = typename std::enable_if<std::is_class<T>::value>::type>
406inline auto cast(const any& a) ->
407 typename std::enable_if<!std::is_same<T, any::map>::value &&
408 !std::is_same<T, any::data>::value &&
409 !std::is_same<T, any::array>::value, T&>::type {
410 return *static_cast<T*>(a.extractPointer());
413template <typename T>
414inline auto cast(const any& a) ->
415 typename std::enable_if<std::is_same<void*, T>::value, T>::type {
416 return a.rawPointer();
419} // namespace PureScript
421#undef WITH_ALLOCATOR
422#undef IS_POINTER_TYPE
423#undef DEFINE_OPERATOR
424#undef DECLARE_COMPARISON_OPERATOR
426#endif // PureScript_HH