git.iko.soy/rowtype-yoga/purescript-native
on e1c490b help sign in
572 lines 18 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#if !defined(__cplusplus)
19#error This is a C++ file!
20#endif
22// Standard includes
24#if defined(DEBUG)
25 #include <cassert>
26 #include <limits>
27 #define IF_DEBUG(x) x
28#else
29 #define NDEBUG
30 #undef assert
31 #define assert(x)
32 #define IF_DEBUG(_)
33#endif
35#include <functional>
36#include <string>
37#include <array>
38#include <deque>
39#include <utility>
40#include <stdexcept>
41#include <iso646.h> // mostly for MS Visual Studio compiler
42#include "purescript_memory.hh"
44namespace PureScript {
46using cstring = const char *;
47using std::string;
48using std::nullptr_t;
49using std::runtime_error;
51namespace Private {
52 struct SymbolGeneratorAnchor {};
54 template <typename T>
55 struct SymbolGenerator {
56 constexpr static SymbolGeneratorAnchor anchor = SymbolGeneratorAnchor{};
57 };
58 template <typename T>
59 constexpr SymbolGeneratorAnchor SymbolGenerator<T>::anchor;
61 using Symbol = const SymbolGeneratorAnchor *;
64#define define_symbol(S) namespace PureScript { \
65 namespace Private { \
66 namespace Symbols { \
67 struct S_ ## S {}; \
68 } \
69 } \
70 }
71#define symbol(S) (&Private::SymbolGenerator<::PureScript::Private::Symbols::S_ ## S>::anchor)
73constexpr bool undefined = false;
75// TODO: Not a real limit, just used for simpler accessors
76static constexpr size_t unknown_size = 64;
78// A variant data class designed to provide some features of dynamic typing.
79//
80class any {
82 public:
83 enum class Tag {
84 Thunk = 0x10,
85 Integer,
86 Double,
87 Character,
88 Boolean,
89 Function,
90 EffFunction,
91 RawPointer,
92 String,
93 Map,
94 Data,
95 Array,
96 Closure,
97 EffClosure,
98 Pointer
99 };
101 private:
102 mutable Tag tag;
104 public:
105 struct as_thunk {
106 };
107 static constexpr as_thunk unthunk = as_thunk{};
109 using map_pair = std::pair<const Private::Symbol, const any>;
111 template <size_t N>
112 using map = std::array<const map_pair, N>;
114 template <size_t N>
115 using data = std::array<const any, N>;
117 using array = std::deque<any WITH_ALLOCATOR(any)>;
118 using fn = auto (*)(const any&) -> any;
119 using eff_fn = auto (*)() -> any;
120 using thunk = auto (*)(const as_thunk) -> const any&;
122 private:
123 class Closure {
124 public:
125 virtual auto operator()(const any&) const -> any = 0;
126 virtual ~Closure();
127 };
129 template <typename T>
130 class Closure_ : public Closure {
131 const T lambda;
132 public:
133 Closure_(const T& l) noexcept : lambda(l) {}
134 auto operator()(const any& arg) const -> any override {
135 return lambda(arg);
136 }
137 };
139 class EffClosure {
140 public:
141 virtual auto operator()() const -> any = 0;
142 virtual ~EffClosure();
143 };
145 template <typename T>
146 class EffClosure_ : public EffClosure {
147 const T lambda;
148 public:
149 EffClosure_(const T& l) noexcept : lambda(l) {}
150 auto operator()() const -> any override {
151 return lambda();
152 }
153 };
155 private:
156 union {
157 mutable thunk t;
158 mutable int i;
159 mutable double d;
160 mutable char32_t c;
161 mutable bool b;
162 mutable fn f;
163 mutable eff_fn e;
164 mutable void * v;
165 mutable managed<string> s;
166 mutable managed<array> a;
167 mutable managed<Closure> l;
168 mutable managed<EffClosure> k;
169 mutable managed<void> p;
170 };
172 public:
174 any(const int val) noexcept : tag(Tag::Integer), i(val) {}
175 any(const long val) noexcept : tag(Tag::Integer), i(static_cast<decltype(i)>(val)) {
176 assert(val >= std::numeric_limits<decltype(i)>::min() &&
177 val <= std::numeric_limits<decltype(i)>::max());
178 }
179 any(const double val) noexcept : tag(Tag::Double), d(val) {}
180 any(const char32_t val) noexcept : tag(Tag::Character), c(val) {}
181 any(const char val) noexcept : tag(Tag::Character), c(val) {}
183 template <typename T, typename = typename std::enable_if<std::is_same<bool,T>::value>::type>
184 any(const T val) noexcept : tag(Tag::Boolean), b(val) {}
186 any(const char * val) : tag(Tag::String), s(make_managed<string>(val)) {}
187 any(const string& val) : tag(Tag::String), s(make_managed<string>(val)) {}
188 any(string&& val) noexcept : tag(Tag::String), s(make_managed<string>(std::move(val))) {}
190 any(const managed<string>& val) noexcept : tag(Tag::String), s(val) {}
191 any(managed<string>&& val) noexcept : tag(Tag::String), s(std::move(val)) {}
193 template <size_t N>
194 any(map<N>&& val) noexcept : tag(Tag::Map), p(make_managed<map<N>>(std::move(val))) {}
196 template <size_t N>
197 any(data<N>&& val) noexcept : tag(Tag::Data), p(make_managed<data<N>>(std::move(val))) {}
199 any(const array& val) : tag(Tag::Array), a(make_managed<array>(val)) {}
200 any(array&& val) noexcept : tag(Tag::Array), a(make_managed<array>(std::move(val))) {}
202 template <typename T>
203 any(const T& val, typename std::enable_if<std::is_convertible<T,fn>::value>::type* = 0) noexcept
204 : tag(Tag::Function), f(val) {}
206 template <typename T, typename = typename std::enable_if<!std::is_same<any,T>::value &&
207 !std::is_convertible<T,fn>::value>::type>
208 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any(const any&)>,T>::value>::type* = 0)
209 : tag(Tag::Closure), l(make_managed<Closure_<T>>(val)) {}
211 template <typename T>
212 any(const T& val, typename std::enable_if<std::is_convertible<T,eff_fn>::value>::type* = 0) noexcept
213 : tag(Tag::EffFunction), e(val) {}
215 template <typename T,
216 typename = typename std::enable_if<!std::is_same<any,T>::value &&
217 !std::is_convertible<T,eff_fn>::value>::type>
218 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any()>,T>::value>::type* = 0)
219 : tag(Tag::EffClosure), k(make_managed<EffClosure_<T>>(val)) {}
221 template <typename T>
222 any(const T& val, typename std::enable_if<std::is_convertible<T,thunk>::value>::type* = 0) noexcept
223 : tag(Tag::Thunk), t(val) {}
225 template <typename T,
226 typename = typename std::enable_if<std::is_class<typename std::remove_pointer<T>::type>::value>::type>
227 any(const T& val, typename std::enable_if<IS_POINTER_TYPE(T)::value>::type* = 0) noexcept
228 : tag(Tag::Pointer), p(val) {}
230 template <typename T>
231 any(T&& val, typename std::enable_if<std::is_assignable<managed<void>,T>::value>::type* = 0) noexcept
232 : tag(Tag::Pointer), p(std::move(val)) {}
234 // Explicit void* to raw pointer value
235 template <typename T>
236 any(const T& val, typename std::enable_if<std::is_same<T,void*>::value>::type* = 0) noexcept
237 : tag(Tag::RawPointer), v(val) {}
239 any(nullptr_t) noexcept : tag(Tag::RawPointer), v(nullptr) {}
242#if !defined(USE_GC)
243 private:
244 template <typename T,
245 typename U,
246 typename = typename std::enable_if<!std::is_reference<T>::value>::type>
247 static constexpr auto move_if_rvalue(U&& u) noexcept -> U&& {
248 return static_cast<U&&>(u);
249 }
251 template <typename T,
252 typename U,
253 typename = typename std::enable_if<std::is_reference<T>::value>::type>
254 static constexpr auto move_if_rvalue(U& u) noexcept -> U& {
255 return static_cast<U&>(u);
256 }
258 template <typename T>
259 auto assign(T&& other) const noexcept -> void {
260 switch (other.tag) {
261 case Tag::Thunk: t = other.t; break;
262 case Tag::Integer: i = other.i; break;
263 case Tag::Double: d = other.d; break;
264 case Tag::Character: c = other.c; break;
265 case Tag::Boolean: b = other.b; break;
266 case Tag::Function: f = other.f; break;
267 case Tag::EffFunction: e = other.e; break;
268 case Tag::RawPointer: v = other.v; break;
270 case Tag::String: new (&s) managed<string>(move_if_rvalue<T>(other.s)); break;
271 case Tag::Array: new (&a) managed<array>(move_if_rvalue<T>(other.a)); break;
272 case Tag::Closure: new (&l) managed<Closure>(move_if_rvalue<T>(other.l)); break;
273 case Tag::EffClosure: new (&k) managed<EffClosure>(move_if_rvalue<T>(other.k)); break;
274 case Tag::Map:
275 case Tag::Data:
276 case Tag::Pointer: new (&p) managed<void>(move_if_rvalue<T>(other.p)); break;
278 default: assert(false && "Bad 'any' tag"); break;
279 }
280 }
282 auto destruct() -> void {
283 switch (tag) {
284 case Tag::String: s.~managed<string>(); break;
285 case Tag::Array: a.~managed<array>(); break;
286 case Tag::Closure: l.~managed<Closure>(); break;
287 case Tag::EffClosure: k.~managed<EffClosure>(); break;
288 case Tag::Map:
289 case Tag::Data:
290 case Tag::Pointer: p.~managed<void>(); break;
292 default: break;
293 }
294 }
296 public:
297 any(const any& other) noexcept : tag(other.tag) {
298 assign(other);
299 }
301 any(any&& other) noexcept : tag(other.tag) {
302 assign(std::move(other));
303 }
305 auto operator=(const any& rhs) noexcept -> any& {
306 destruct();
307 tag = rhs.tag;
308 assign(rhs);
309 return *this;
310 }
312 auto operator=(any&& rhs) noexcept -> any& {
313 destruct();
314 tag = rhs.tag;
315 assign(std::move(rhs));
316 return *this;
317 }
319 ~any() {
320 destruct();
321 }
322#endif // !defined(USE_GC)
324 public:
325 any() = delete;
327 auto operator()(const any&) const -> any;
328 auto operator()(const as_thunk) const -> const any&;
329 auto operator()() const -> any;
331 operator int() const;
332 operator double() const;
333 operator bool() const;
334 operator char32_t() const;
335 operator char() const;
336 operator size_t() const;
337 operator const string&() const;
338 operator const char *() const;
339 operator const array&() const;
341 auto operator[](const size_t) const -> const any&;
343 auto size() const -> size_t;
344 auto empty() const -> bool;
345 auto contains(const Private::Symbol) const -> bool;
347 auto extractPointer(IF_DEBUG(const Tag)) const -> void*;
349 auto rawPointer() const -> void* {
350 const any& variant = unthunkVariant(*this);
351 assert(tag == Tag::RawPointer);
352 return variant.v;
353 }
355 static auto unthunkVariant(const any&) -> const any&;
357 #define DEFINE_OPERATOR(op) \
358 template <typename T, \
359 typename = typename std::enable_if<!std::is_same<T,any>::value && \
360 !std::is_same<T,const char *>::value>::type> \
361 inline friend auto operator op (const any& lhs, T rhs) -> T { \
362 return static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(lhs) op rhs; \
363 } \
364 template <typename T, \
365 typename = typename std::enable_if<!std::is_same<T,any>::value && \
366 !std::is_same<T,const char *>::value>::type> \
367 inline friend auto operator op (T lhs, const any& rhs) -> T { \
368 return lhs op static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(rhs); \
369 } \
370 friend auto operator op (const any&, const any&) -> any; \
372 #define DEFINE_COMPARISON_OPERATOR(op) \
373 template <typename T, \
374 typename = typename std::enable_if<!std::is_same<T,any>::value && \
375 !std::is_same<T,const char *>::value>::type> \
376 inline friend auto operator op (const any& lhs, T rhs) -> bool { \
377 return static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(lhs) op rhs; \
378 } \
379 template <typename T, \
380 typename = typename std::enable_if<!std::is_same<T,any>::value && \
381 !std::is_same<T,const char *>::value>::type> \
382 inline friend auto operator op (T lhs, const any& rhs) -> bool { \
383 return lhs op static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(rhs); \
384 } \
385 inline friend auto operator op (const any& lhs, const char * rhs) -> bool { \
386 return static_cast<const string&>(lhs) op rhs; \
387 } \
388 inline friend auto operator op (const char * lhs, const any& rhs) -> bool { \
389 return lhs op static_cast<const string&>(rhs); \
390 } \
391 friend auto operator op (const any&, const any&) -> bool; \
393 DEFINE_COMPARISON_OPERATOR(==)
394 DEFINE_COMPARISON_OPERATOR(!=)
395 DEFINE_COMPARISON_OPERATOR(<)
396 DEFINE_COMPARISON_OPERATOR(<=)
397 DEFINE_COMPARISON_OPERATOR(>)
398 DEFINE_COMPARISON_OPERATOR(>=)
400 DEFINE_OPERATOR(+)
401 DEFINE_OPERATOR(-)
402 DEFINE_OPERATOR(*)
403 DEFINE_OPERATOR(/)
404 DEFINE_OPERATOR(%)
406 inline friend auto operator+(const any& lhs, const char * rhs) -> string {
407 return static_cast<const string&>(lhs) + rhs;
408 }
409 inline friend auto operator+(const char * lhs, const any& rhs) -> string {
410 return lhs + static_cast<const string&>(rhs);
411 }
413 friend auto operator-(const any&) -> any; // unary negate
415}; // class any
417namespace Private {
418 template <typename T, typename U=void>
419 struct TagHelper {};
421 template <size_t N>
422 struct TagHelper<any::map<N>> {
423 static constexpr any::Tag tag = any::Tag::Map;
424 };
426 template <size_t N>
427 struct TagHelper<any::data<N>> {
428 static constexpr any::Tag tag = any::Tag::Data;
429 };
431 template <typename T>
432 struct TagHelper<T> {
433 static constexpr any::Tag tag = any::Tag::Pointer;
434 };
437// Pass-through
438template <typename T>
439constexpr auto cast(const T& a) -> const T& {
440 return a;
443template <typename T>
444inline auto cast(const any& a) ->
445 typename std::enable_if<std::is_arithmetic<T>::value ||
446 std::is_same<T,const char *>::value, T>::type {
447 return a;
450template <typename T>
451inline auto cast(const any& a) ->
452 typename std::enable_if<std::is_same<T, string>::value ||
453 std::is_same<T, any::array>::value, const T&>::type {
454 return a;
457template <typename T,
458 typename = typename std::enable_if<std::is_class<T>::value &&
459 !std::is_same<T,string>::value &&
460 !std::is_same<T,any::array>::value>::type>
461inline auto cast(const any& a) -> T& {
462 return *static_cast<T*>(a.extractPointer(IF_DEBUG(Private::TagHelper<T>::tag)));
465template <typename T,
466 typename = typename std::enable_if<std::is_pointer<T>::value &&
467 !std::is_same<T,void*>::value &&
468 !std::is_same<T,const char *>::value>::type>
469inline auto cast(const any& a) -> T {
470 return static_cast<T>(a.extractPointer(IF_DEBUG(Private::TagHelper<T>::tag)));
473template <typename T>
474inline auto cast(const any& a) ->
475 typename std::enable_if<std::is_same<void*, T>::value, T>::type {
476 return a.rawPointer();
479namespace map {
480 template <size_t N, typename T>
481 inline auto get(const T& a) ->
482 typename std::enable_if<!std::is_same<any, T>::value, const any&>::type {
483 return a[N].second;
484 }
486 template <size_t N>
487 inline auto get(const any& a) -> const any& {
488 return cast<any::map<N+1>>(a)[N].second;
489 }
491 template <size_t N>
492 inline auto get(const Private::Symbol key, const any::map<N>& a) -> const any& {
493 static_assert(N > 0, "map size must be greater than zero");
494 typename std::remove_reference<decltype(a)>::type::size_type i = 0;
495 do {
496 if (a[i].first == key) {
497 return a[i].second;
498 }
499 } while (a[++i].first != nullptr);
500 assert(false && "map key not found");
501 static const any invalid_key(nullptr);
502 return invalid_key;
503 }
505 inline auto get(const Private::Symbol key, const any& a) -> const any& {
506 return get(key, cast<any::map<unknown_size>>(a));
507 }
509 #define P(N) any::map_pair&& arg ## N
510 #define T(N) any::map<N+1>
511 #define E(N) std::forward<decltype(arg ## N)>(arg ## N)
512 #define M(...) {{ __VA_ARGS__, { nullptr, nullptr } }}
514 inline auto make(P(1)) -> T(1) {
515 return T(1) M(E(1));
516 }
517 inline auto make(P(1), P(2)) -> T(2) {
518 return T(2) M(E(1), E(2));
519 }
520 inline auto make(P(1), P(2), P(3)) -> T(3) {
521 return T(3) M(E(1), E(2), E(3));
522 }
523 inline auto make(P(1), P(2), P(3), P(4)) -> T(4) {
524 return T(4) M(E(1), E(2), E(3), E(4));
525 }
526 inline auto make(P(1), P(2), P(3), P(4), P(5)) -> T(5) {
527 return T(5) M(E(1), E(2), E(3), E(4), E(5));
528 }
529 inline auto make(P(1), P(2), P(3), P(4), P(5), P(6)) -> T(6) {
530 return T(6) M(E(1), E(2), E(3), E(4), E(5), E(6));
531 }
532 inline auto make(P(1), P(2), P(3), P(4), P(5), P(6), P(7)) -> T(7) {
533 return T(7) M(E(1), E(2), E(3), E(4), E(5), E(6), E(7));
534 }
535 inline auto make(P(1), P(2), P(3), P(4), P(5), P(6), P(7), P(8)) -> T(8) {
536 return T(8) M(E(1), E(2), E(3), E(4), E(5), E(6), E(7), E(8));
537 }
539 #undef P
540 #undef T
541 #undef E
542 #undef M
545namespace record = map;
547namespace data {
548 template <size_t N, typename T>
549 inline auto get(const T& a) ->
550 typename std::enable_if<!std::is_same<any, T>::value, const any&>::type {
551 return a[N];
552 }
554 template <size_t N>
555 inline auto get(const any& a) -> const any& {
556 return cast<any::data<N+1>>(a)[N];
557 }
559 template <typename T>
560 inline auto ctor(const T& a) -> int {
561 return get<0>(a);
562 }
565} // namespace PureScript
567#undef WITH_ALLOCATOR
568#undef IS_POINTER_TYPE
569#undef DEFINE_OPERATOR
570#undef DEFINE_COMPARISON_OPERATOR
572#endif // PureScript_HH