git.iko.soy/rowtype-yoga/purescript-native
on 5bc7fab help sign in
584 lines 19 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 <unordered_map>
40#include <utility>
41#include <stdexcept>
42#include <iso646.h> // mostly for MS Visual Studio compiler
43#include "purescript_memory.hh"
45namespace PureScript {
47using cstring = const char *;
48using std::string;
49using std::nullptr_t;
50using std::runtime_error;
52namespace Private {
53 struct SymbolGeneratorAnchor {};
55 template <typename T>
56 struct SymbolGenerator {
57 constexpr static SymbolGeneratorAnchor anchor = SymbolGeneratorAnchor{};
58 };
59 template <typename T>
60 constexpr SymbolGeneratorAnchor SymbolGenerator<T>::anchor;
62 using Symbol = const SymbolGeneratorAnchor *;
65#define DEFINE_SYMBOL(S) namespace PureScript { \
66 namespace Private { \
67 namespace Symbols { \
68 struct S_ ## S {}; \
69 } \
70 } \
71 }
72#define SYM(S) (&Private::SymbolGenerator<::PureScript::Private::Symbols::S_ ## S>::anchor)
74constexpr bool undefined = false;
76// TODO: Not a real limit, just used for simpler accessors
77static constexpr size_t unknown_size = 64;
79// A variant data class designed to provide some features of dynamic typing.
80//
81class any {
83 public:
84 enum class Tag {
85 Thunk = 0x10,
86 Integer,
87 Double,
88 Character,
89 Boolean,
90 Function,
91 EffFunction,
92 RawPointer,
93 String,
94 Map,
95 Data,
96 Array,
97 Record,
98 Closure,
99 EffClosure,
100 Pointer
101 };
103 private:
104 mutable Tag tag;
106 public:
107 struct as_thunk {
108 };
109 static constexpr as_thunk unthunk = as_thunk{};
111 using dict_pair = std::pair<const Private::Symbol, const any>;
113 template <size_t N>
114 using dict = std::array<const dict_pair, N>;
116 template <size_t N>
117 using data = std::array<const any, N>;
119 using array = std::deque<any WITH_ALLOCATOR(any)>;
120 using record = std::unordered_map<std::string, any WITH_ALLOCATOR_PAIR(std::string, any)>;
122 using fn = auto (*)(const any&) -> any;
123 using eff_fn = auto (*)() -> any;
124 using thunk = auto (*)(const as_thunk) -> const any&;
126 private:
127 class Closure {
128 public:
129 virtual auto operator()(const any&) const -> any = 0;
130 virtual ~Closure();
131 };
133 template <typename T>
134 class Closure_ : public Closure {
135 const T lambda;
136 public:
137 Closure_(const T& l) noexcept : lambda(l) {}
138 auto operator()(const any& arg) const -> any override {
139 return lambda(arg);
140 }
141 };
143 class EffClosure {
144 public:
145 virtual auto operator()() const -> any = 0;
146 virtual ~EffClosure();
147 };
149 template <typename T>
150 class EffClosure_ : public EffClosure {
151 const T lambda;
152 public:
153 EffClosure_(const T& l) noexcept : lambda(l) {}
154 auto operator()() const -> any override {
155 return lambda();
156 }
157 };
159 private:
160 union {
161 mutable thunk t;
162 mutable int i;
163 mutable double d;
164 mutable char32_t c;
165 mutable bool b;
166 mutable fn f;
167 mutable eff_fn e;
168 mutable void * v;
169 mutable managed<string> s;
170 mutable managed<array> a;
171 mutable managed<record> r;
172 mutable managed<Closure> l;
173 mutable managed<EffClosure> k;
174 mutable managed<void> p;
175 };
177 public:
179 any(const int val) noexcept : tag(Tag::Integer), i(val) {}
180 any(const long val) noexcept : tag(Tag::Integer), i(static_cast<decltype(i)>(val)) {
181 assert(val >= std::numeric_limits<decltype(i)>::min() &&
182 val <= std::numeric_limits<decltype(i)>::max());
183 }
184 any(const double val) noexcept : tag(Tag::Double), d(val) {}
185 any(const char32_t val) noexcept : tag(Tag::Character), c(val) {}
186 any(const char val) noexcept : tag(Tag::Character), c(val) {}
188 template <typename T, typename = typename std::enable_if<std::is_same<bool,T>::value>::type>
189 any(const T val) noexcept : tag(Tag::Boolean), b(val) {}
191 any(const char * val) : tag(Tag::String), s(make_managed_and_finalized<string>(val)) {}
192 any(const string& val) : tag(Tag::String), s(make_managed_and_finalized<string>(val)) {}
193 any(string&& val) noexcept : tag(Tag::String), s(make_managed_and_finalized<string>(std::move(val))) {}
195 any(const managed<string>& val) noexcept : tag(Tag::String), s(val) {}
196 any(managed<string>&& val) noexcept : tag(Tag::String), s(std::move(val)) {}
198 template <size_t N>
199 any(dict<N>&& val) noexcept : tag(Tag::Map), p(make_managed<dict<N>>(std::move(val))) {}
201 template <size_t N>
202 any(data<N>&& val) noexcept : tag(Tag::Data), p(make_managed<data<N>>(std::move(val))) {}
204 any(const array& val) : tag(Tag::Array), a(make_managed<array>(val)) {}
205 any(array&& val) noexcept : tag(Tag::Array), a(make_managed<array>(std::move(val))) {}
207 any(const record& val) : tag(Tag::Record), r(make_managed<record>(val)) {}
208 any(record&& val) noexcept : tag(Tag::Record), r(make_managed<record>(std::move(val))) {}
210 template <typename T>
211 any(const T& val, typename std::enable_if<std::is_convertible<T,fn>::value>::type* = 0) noexcept
212 : tag(Tag::Function), f(val) {}
214 template <typename T, typename = typename std::enable_if<!std::is_same<any,T>::value &&
215 !std::is_convertible<T,fn>::value>::type>
216 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any(const any&)>,T>::value>::type* = 0)
217 : tag(Tag::Closure), l(make_managed<Closure_<T>>(val)) {}
219 template <typename T>
220 any(const T& val, typename std::enable_if<std::is_convertible<T,eff_fn>::value>::type* = 0) noexcept
221 : tag(Tag::EffFunction), e(val) {}
223 template <typename T,
224 typename = typename std::enable_if<!std::is_same<any,T>::value &&
225 !std::is_convertible<T,eff_fn>::value>::type>
226 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any()>,T>::value>::type* = 0)
227 : tag(Tag::EffClosure), k(make_managed<EffClosure_<T>>(val)) {}
229 template <typename T>
230 any(const T& val, typename std::enable_if<std::is_convertible<T,thunk>::value>::type* = 0) noexcept
231 : tag(Tag::Thunk), t(val) {}
233 template <typename T,
234 typename = typename std::enable_if<std::is_class<typename std::remove_pointer<T>::type>::value>::type>
235 any(const T& val, typename std::enable_if<IS_POINTER_TYPE(T)::value>::type* = 0) noexcept
236 : tag(Tag::Pointer), p(val) {}
238 template <typename T>
239 any(T&& val, typename std::enable_if<std::is_assignable<managed<void>,T>::value>::type* = 0) noexcept
240 : tag(Tag::Pointer), p(std::move(val)) {}
242 // Explicit void* to raw pointer value
243 template <typename T>
244 any(const T& val, typename std::enable_if<std::is_same<T,void*>::value>::type* = 0) noexcept
245 : tag(Tag::RawPointer), v(val) {}
247 any(nullptr_t) noexcept : tag(Tag::RawPointer), v(nullptr) {}
250#if !defined(USE_GC)
251 private:
252 template <typename T,
253 typename U,
254 typename = typename std::enable_if<!std::is_reference<T>::value>::type>
255 static constexpr auto move_if_rvalue(U&& u) noexcept -> U&& {
256 return static_cast<U&&>(u);
257 }
259 template <typename T,
260 typename U,
261 typename = typename std::enable_if<std::is_reference<T>::value>::type>
262 static constexpr auto move_if_rvalue(U& u) noexcept -> U& {
263 return static_cast<U&>(u);
264 }
266 template <typename T>
267 auto assign(T&& other) const noexcept -> void {
268 switch (other.tag) {
269 case Tag::Thunk: t = other.t; break;
270 case Tag::Integer: i = other.i; break;
271 case Tag::Double: d = other.d; break;
272 case Tag::Character: c = other.c; break;
273 case Tag::Boolean: b = other.b; break;
274 case Tag::Function: f = other.f; break;
275 case Tag::EffFunction: e = other.e; break;
276 case Tag::RawPointer: v = other.v; break;
278 case Tag::String: new (&s) managed<string>(move_if_rvalue<T>(other.s)); break;
279 case Tag::Array: new (&a) managed<array>(move_if_rvalue<T>(other.a)); break;
280 case Tag::Record: new (&r) managed<record>(move_if_rvalue<T>(other.r)); break;
281 case Tag::Closure: new (&l) managed<Closure>(move_if_rvalue<T>(other.l)); break;
282 case Tag::EffClosure: new (&k) managed<EffClosure>(move_if_rvalue<T>(other.k)); break;
283 case Tag::Map:
284 case Tag::Data:
285 case Tag::Pointer: new (&p) managed<void>(move_if_rvalue<T>(other.p)); break;
287 default: assert(false && "Bad 'any' tag"); break;
288 }
289 }
291 auto destruct() -> void {
292 switch (tag) {
293 case Tag::String: s.~managed<string>(); break;
294 case Tag::Array: a.~managed<array>(); break;
295 case Tag::Record: r.~managed<record>(); break;
296 case Tag::Closure: l.~managed<Closure>(); break;
297 case Tag::EffClosure: k.~managed<EffClosure>(); break;
298 case Tag::Map:
299 case Tag::Data:
300 case Tag::Pointer: p.~managed<void>(); break;
302 default: break;
303 }
304 }
306 public:
307 any(const any& other) noexcept : tag(other.tag) {
308 assign(other);
309 }
311 any(any&& other) noexcept : tag(other.tag) {
312 assign(std::move(other));
313 }
315 auto operator=(const any& rhs) noexcept -> any& {
316 destruct();
317 tag = rhs.tag;
318 assign(rhs);
319 return *this;
320 }
322 auto operator=(any&& rhs) noexcept -> any& {
323 destruct();
324 tag = rhs.tag;
325 assign(std::move(rhs));
326 return *this;
327 }
329 ~any() {
330 destruct();
331 }
332#endif // !defined(USE_GC)
334 public:
335 any() = delete;
337 auto operator()(const any&) const -> any;
338 auto operator()(const as_thunk) const -> const any&;
339 auto operator()() const -> any;
341 operator int() const;
342 operator double() const;
343 operator bool() const;
344 operator char32_t() const;
345 operator char() const;
346 operator size_t() const;
347 operator const string&() const;
348 operator const char *() const;
349 operator const array&() const;
350 operator const record&() const;
352 auto operator[](const size_t) const -> const any&;
353 auto at(const std::string&) const -> const any&;
354 auto size() const -> size_t;
355 auto empty() const -> bool;
356 auto contains(const Private::Symbol) const -> bool;
358 auto extractPointer(IF_DEBUG(const Tag)) const -> void*;
360 auto rawPointer() const -> void* {
361 const any& variant = unthunkVariant(*this);
362 assert(tag == Tag::RawPointer);
363 return variant.v;
364 }
366 static auto unthunkVariant(const any&) -> const any&;
368 #define DEFINE_OPERATOR(op) \
369 template <typename T, \
370 typename = typename std::enable_if<!std::is_same<T,any>::value && \
371 !std::is_same<T,const char *>::value>::type> \
372 inline friend auto operator op (const any& lhs, T rhs) -> T { \
373 return static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(lhs) op rhs; \
374 } \
375 template <typename T, \
376 typename = typename std::enable_if<!std::is_same<T,any>::value && \
377 !std::is_same<T,const char *>::value>::type> \
378 inline friend auto operator op (T lhs, const any& rhs) -> T { \
379 return lhs op static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(rhs); \
380 } \
381 friend auto operator op (const any&, const any&) -> any; \
383 #define DEFINE_COMPARISON_OPERATOR(op) \
384 template <typename T, \
385 typename = typename std::enable_if<!std::is_same<T,any>::value && \
386 !std::is_same<T,const char *>::value>::type> \
387 inline friend auto operator op (const any& lhs, T rhs) -> bool { \
388 return static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(lhs) op rhs; \
389 } \
390 template <typename T, \
391 typename = typename std::enable_if<!std::is_same<T,any>::value && \
392 !std::is_same<T,const char *>::value>::type> \
393 inline friend auto operator op (T lhs, const any& rhs) -> bool { \
394 return lhs op static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(rhs); \
395 } \
396 inline friend auto operator op (const any& lhs, const char * rhs) -> bool { \
397 return static_cast<const string&>(lhs) op rhs; \
398 } \
399 inline friend auto operator op (const char * lhs, const any& rhs) -> bool { \
400 return lhs op static_cast<const string&>(rhs); \
401 } \
402 friend auto operator op (const any&, const any&) -> bool; \
404 DEFINE_COMPARISON_OPERATOR(==)
405 DEFINE_COMPARISON_OPERATOR(!=)
406 DEFINE_COMPARISON_OPERATOR(<)
407 DEFINE_COMPARISON_OPERATOR(<=)
408 DEFINE_COMPARISON_OPERATOR(>)
409 DEFINE_COMPARISON_OPERATOR(>=)
411 DEFINE_OPERATOR(+)
412 DEFINE_OPERATOR(-)
413 DEFINE_OPERATOR(*)
414 DEFINE_OPERATOR(/)
415 DEFINE_OPERATOR(%)
417 inline friend auto operator+(const any& lhs, const char * rhs) -> string {
418 return static_cast<const string&>(lhs) + rhs;
419 }
420 inline friend auto operator+(const char * lhs, const any& rhs) -> string {
421 return lhs + static_cast<const string&>(rhs);
422 }
424 friend auto operator-(const any&) -> any; // unary negate
426}; // class any
428namespace Private {
429 template <typename T, typename U=void>
430 struct TagHelper {};
432 template <size_t N>
433 struct TagHelper<any::dict<N>> {
434 static constexpr any::Tag tag = any::Tag::Map;
435 };
437 template <size_t N>
438 struct TagHelper<any::data<N>> {
439 static constexpr any::Tag tag = any::Tag::Data;
440 };
442 template <typename T>
443 struct TagHelper<T> {
444 static constexpr any::Tag tag = any::Tag::Pointer;
445 };
448// Pass-through
449template <typename T>
450constexpr auto cast(const T& a) -> const T& {
451 return a;
454template <typename T>
455inline auto cast(const any& a) ->
456 typename std::enable_if<std::is_arithmetic<T>::value ||
457 std::is_same<T,const char *>::value, T>::type {
458 return a;
461template <typename T>
462inline auto cast(const any& a) ->
463 typename std::enable_if<std::is_same<T, string>::value ||
464 std::is_same<T, any::array>::value ||
465 std::is_same<T, any::record>::value, const T&>::type {
466 return a;
469template <typename T,
470 typename = typename std::enable_if<std::is_class<T>::value &&
471 !std::is_same<T,string>::value &&
472 !std::is_same<T,any::array>::value &&
473 !std::is_same<T,any::record>::value>::type>
474inline auto cast(const any& a) -> T& {
475 return *static_cast<T*>(a.extractPointer(IF_DEBUG(Private::TagHelper<T>::tag)));
478template <typename T,
479 typename = typename std::enable_if<std::is_pointer<T>::value &&
480 !std::is_same<T,void*>::value &&
481 !std::is_same<T,const char *>::value>::type>
482inline auto cast(const any& a) -> T {
483 return static_cast<T>(a.extractPointer(IF_DEBUG(Private::TagHelper<T>::tag)));
486template <typename T>
487inline auto cast(const any& a) ->
488 typename std::enable_if<std::is_same<void*, T>::value, T>::type {
489 return a.rawPointer();
492namespace dict {
493 template <size_t N, typename T>
494 inline auto get(const T& a) ->
495 typename std::enable_if<!std::is_same<any, T>::value, const any&>::type {
496 return a[N].second;
497 }
499 template <size_t N>
500 inline auto get(const any& a) -> const any& {
501 return cast<any::dict<N+1>>(a)[N].second;
502 }
504 template <size_t N>
505 inline auto get(const Private::Symbol key, const any::dict<N>& a) -> const any& {
506 static_assert(N > 0, "dict size must be greater than zero");
507 typename std::remove_reference<decltype(a)>::type::size_type i = 0;
508 do {
509 if (a[i].first == key) {
510 return a[i].second;
511 }
512 } while (a[++i].first != nullptr);
513 assert(false && "dict key not found");
514 static const any invalid_key(nullptr);
515 return invalid_key;
516 }
518 inline auto get(const Private::Symbol key, const any& a) -> const any& {
519 return get(key, cast<any::dict<unknown_size>>(a));
520 }
522 #define P(N) any::dict_pair&& arg ## N
523 #define T(N) any::dict<N+1>
524 #define E(N) std::forward<decltype(arg ## N)>(arg ## N)
525 #define M(...) {{ __VA_ARGS__, { nullptr, nullptr } }}
527 inline auto make(P(1)) -> T(1) {
528 return T(1) M(E(1));
529 }
530 inline auto make(P(1), P(2)) -> T(2) {
531 return T(2) M(E(1), E(2));
532 }
533 inline auto make(P(1), P(2), P(3)) -> T(3) {
534 return T(3) M(E(1), E(2), E(3));
535 }
536 inline auto make(P(1), P(2), P(3), P(4)) -> T(4) {
537 return T(4) M(E(1), E(2), E(3), E(4));
538 }
539 inline auto make(P(1), P(2), P(3), P(4), P(5)) -> T(5) {
540 return T(5) M(E(1), E(2), E(3), E(4), E(5));
541 }
542 inline auto make(P(1), P(2), P(3), P(4), P(5), P(6)) -> T(6) {
543 return T(6) M(E(1), E(2), E(3), E(4), E(5), E(6));
544 }
545 inline auto make(P(1), P(2), P(3), P(4), P(5), P(6), P(7)) -> T(7) {
546 return T(7) M(E(1), E(2), E(3), E(4), E(5), E(6), E(7));
547 }
548 inline auto make(P(1), P(2), P(3), P(4), P(5), P(6), P(7), P(8)) -> T(8) {
549 return T(8) M(E(1), E(2), E(3), E(4), E(5), E(6), E(7), E(8));
550 }
552 #undef P
553 #undef T
554 #undef E
555 #undef M
558namespace data {
559 template <size_t N, typename T>
560 inline auto get(const T& a) ->
561 typename std::enable_if<!std::is_same<any, T>::value, const any&>::type {
562 return a[N];
563 }
565 template <size_t N>
566 inline auto get(const any& a) -> const any& {
567 return cast<any::data<N+1>>(a)[N];
568 }
570 template <typename T>
571 inline auto ctor(const T& a) -> int {
572 return get<0>(a);
573 }
576} // namespace PureScript
578#undef WITH_ALLOCATOR
579#undef WITH_ALLOCATOR_PAIR
580#undef IS_POINTER_TYPE
581#undef DEFINE_OPERATOR
582#undef DEFINE_COMPARISON_OPERATOR
584#endif // PureScript_HH