git.iko.soy/rowtype-yoga/purescript-native
on 4e7cfee help sign in
581 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 <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 private:
84 static constexpr auto _managed_ = 0x10;
86 public:
87 enum class Tag {
88 Unknown,
89 Thunk,
90 Integer,
91 Double,
92 Character,
93 Boolean,
94 Function,
95 EffFunction,
96 RawPointer,
97 String = _managed_,
98 Dictionary,
99 Data,
100 Array,
101 Record,
102 Closure,
103 EffClosure,
104 Pointer,
105 Invalid
106 };
108 private:
109 mutable Tag tag;
111 inline static auto isManaged(const Tag tag) -> decltype(_managed_) {
112 return static_cast<decltype(_managed_)>(tag) & _managed_;
113 }
114 static_assert(static_cast<decltype(_managed_)>(Tag::Invalid) & _managed_, "outside of range");
116 public:
117 struct as_thunk {
118 };
119 static constexpr as_thunk unthunk = as_thunk{};
121 using dict_pair = std::pair<const Private::Symbol, const any>;
122 template <size_t N>
123 using dict = std::array<const dict_pair, N>;
125 template <size_t N>
126 using data = std::array<const any, N>;
128 using array = std::deque<any WITH_ALLOCATOR(any)>;
130 struct cstr_cmp {
131 auto operator ()(const char *, const char *) const -> bool;
132 };
133 using record = std::map<const char *,
134 any,
135 cstr_cmp
136 WITH_ALLOCATOR_PAIR(char * const, any)>;
138 using fn = auto (*)(const any&) -> any;
139 using eff_fn = auto (*)() -> any;
140 using thunk = auto (*)(const as_thunk) -> const any&;
142 private:
143 class Closure {
144 public:
145 virtual auto operator()(const any&) const -> any = 0;
146 virtual ~Closure();
147 };
149 template <typename T>
150 class Closure_ : public Closure {
151 const T lambda;
152 public:
153 Closure_(const T& l) noexcept : lambda(l) {}
154 auto operator()(const any& arg) const -> any override {
155 return lambda(arg);
156 }
157 };
159 class EffClosure {
160 public:
161 virtual auto operator()() const -> any = 0;
162 virtual ~EffClosure();
163 };
165 template <typename T>
166 class EffClosure_ : public EffClosure {
167 const T lambda;
168 public:
169 EffClosure_(const T& l) noexcept : lambda(l) {}
170 auto operator()() const -> any override {
171 return lambda();
172 }
173 };
175 private:
176 union {
177 mutable thunk t;
178 mutable int i;
179 mutable double d;
180 mutable char32_t c;
181 mutable bool b;
182 mutable fn f;
183 mutable eff_fn e;
184 mutable void * v;
185 mutable uint64_t u;
186 mutable managed<void> p;
187 };
189 public:
191 any(const int val) noexcept : tag(Tag::Integer), i(val) {}
192 any(const long val) noexcept : tag(Tag::Integer), i(static_cast<decltype(i)>(val)) {
193 assert(val >= std::numeric_limits<decltype(i)>::min() &&
194 val <= std::numeric_limits<decltype(i)>::max());
195 }
196 any(const double val) noexcept : tag(Tag::Double), d(val) {}
197 any(const char32_t val) noexcept : tag(Tag::Character), c(val) {}
198 any(const char val) noexcept : tag(Tag::Character), c(val) {}
200 template <typename T, typename = typename std::enable_if<std::is_same<bool,T>::value>::type>
201 any(const T val) noexcept : tag(Tag::Boolean), b(val) {}
203 any(const char * val) : tag(Tag::String), p(make_managed_and_finalized<string>(val)) {}
204 any(const string& val) : tag(Tag::String), p(make_managed_and_finalized<string>(val)) {}
205 any(string&& val) noexcept : tag(Tag::String), p(make_managed_and_finalized<string>(std::move(val))) {}
207 any(const managed<string>& val) noexcept : tag(Tag::String), p(val) {}
208 any(managed<string>&& val) noexcept : tag(Tag::String), p(std::move(val)) {}
210 template <size_t N>
211 any(dict<N>&& val) noexcept : tag(Tag::Dictionary), p(make_managed<dict<N>>(std::move(val))) {}
213 template <size_t N>
214 any(data<N>&& val) noexcept : tag(Tag::Data), p(make_managed<data<N>>(std::move(val))) {}
216 any(const array& val) : tag(Tag::Array), p(make_managed<array>(val)) {}
217 any(array&& val) noexcept : tag(Tag::Array), p(make_managed<array>(std::move(val))) {}
219 any(const record& val) : tag(Tag::Record), p(make_managed<record>(val)) {}
220 any(record&& val) noexcept : tag(Tag::Record), p(make_managed<record>(std::move(val))) {}
222 template <typename T>
223 any(const T& val, typename std::enable_if<std::is_convertible<T,fn>::value>::type* = 0) noexcept
224 : tag(Tag::Function), f(val) {}
226 template <typename T, typename = typename std::enable_if<!std::is_same<any,T>::value &&
227 !std::is_convertible<T,fn>::value>::type>
228 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any(const any&)>,T>::value>::type* = 0)
229 : tag(Tag::Closure), p(make_managed<Closure_<T>>(val)) {}
231 template <typename T>
232 any(const T& val, typename std::enable_if<std::is_convertible<T,eff_fn>::value>::type* = 0) noexcept
233 : tag(Tag::EffFunction), e(val) {}
235 template <typename T,
236 typename = typename std::enable_if<!std::is_same<any,T>::value &&
237 !std::is_convertible<T,eff_fn>::value>::type>
238 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any()>,T>::value>::type* = 0)
239 : tag(Tag::EffClosure), p(make_managed<EffClosure_<T>>(val)) {}
241 template <typename T>
242 any(const T& val, typename std::enable_if<std::is_convertible<T,thunk>::value>::type* = 0) noexcept
243 : tag(Tag::Thunk), t(val) {}
245 template <typename T,
246 typename = typename std::enable_if<std::is_class<typename std::remove_pointer<T>::type>::value>::type>
247 any(const T& val, typename std::enable_if<IS_POINTER_TYPE(T)::value>::type* = 0) noexcept
248 : tag(Tag::Pointer), p(val) {}
250 template <typename T>
251 any(T&& val, typename std::enable_if<std::is_assignable<managed<void>,T>::value>::type* = 0) noexcept
252 : tag(Tag::Pointer), p(std::move(val)) {}
254 // Explicit void* to raw pointer value
255 template <typename T>
256 any(const T& val, typename std::enable_if<std::is_same<T,void*>::value>::type* = 0) noexcept
257 : tag(Tag::RawPointer), v(val) {}
259 any(nullptr_t) noexcept : tag(Tag::RawPointer), v(nullptr) {}
262#if !defined(USE_GC)
263 private:
264 auto destruct() const noexcept -> void {
265 if (isManaged(tag)) {
266 p.~managed<void>();
267 }
268 }
270 static_assert(sizeof(u) >= sizeof(t), "size assumption");
271 static_assert(sizeof(u) >= sizeof(i), "size assumption");
272 static_assert(sizeof(u) >= sizeof(d), "size assumption");
273 static_assert(sizeof(u) >= sizeof(c), "size assumption");
274 static_assert(sizeof(u) >= sizeof(b), "size assumption");
275 static_assert(sizeof(u) >= sizeof(f), "size assumption");
276 static_assert(sizeof(u) >= sizeof(e), "size assumption");
277 static_assert(sizeof(u) >= sizeof(v), "size assumption");
279 auto copyAssign(const any& other) const noexcept -> void {
280 if (isManaged(other.tag)) {
281 new (&p) managed<void>(other.p);
282 } else {
283 u = other.u;
284 }
285 }
286 auto moveAssign(any& other) const noexcept -> void {
287 if (isManaged(other.tag)) {
288 new (&p) managed<void>(std::move(other.p));
289 } else {
290 u = other.u;
291 }
292 }
294 public:
295 any(const any& other) noexcept : tag(other.tag) {
296 copyAssign(other);
297 }
299 any(any&& other) noexcept : tag(other.tag) {
300 moveAssign(other);
301 }
303 auto operator=(const any& rhs) noexcept -> any& {
304 destruct();
305 tag = rhs.tag;
306 copyAssign(rhs);
307 return *this;
308 }
310 auto operator=(any&& rhs) noexcept -> any& {
311 destruct();
312 tag = rhs.tag;
313 moveAssign(rhs);
314 return *this;
315 }
317 ~any() {
318 destruct();
319 }
320#endif // !defined(USE_GC)
322 public:
323 any() = delete;
325 auto operator()(const any&) const -> any;
326 auto operator()(const as_thunk) const -> const any&;
327 auto operator()() const -> any;
329 operator int() const;
330 operator double() const;
331 operator bool() const;
332 operator char32_t() const;
333 operator char() const;
334 operator size_t() const;
335 operator const string&() const;
336 operator const char *() const;
337 operator const array&() const;
338 operator const record&() const;
340 auto operator[](const size_t) const -> const any&;
341 auto at(const char *) const -> const any&;
342 auto size() const -> size_t;
343 auto empty() const -> bool;
344 auto contains(const Private::Symbol) const -> bool;
346 auto extractPointer(IF_DEBUG(const Tag)) const -> void*;
348 auto rawPointer() const -> void* {
349 const any& variant = unthunkVariant(*this);
350 assert(tag == Tag::RawPointer);
351 return variant.v;
352 }
354 static auto unthunkVariant(const any&) -> const any&;
356 #define DEFINE_OPERATOR(op) \
357 template <typename T, \
358 typename = typename std::enable_if<!std::is_same<T,any>::value && \
359 !std::is_same<T,const char *>::value>::type> \
360 inline friend auto operator op (const any& lhs, T rhs) -> T { \
361 return static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(lhs) op rhs; \
362 } \
363 template <typename T, \
364 typename = typename std::enable_if<!std::is_same<T,any>::value && \
365 !std::is_same<T,const char *>::value>::type> \
366 inline friend auto operator op (T lhs, const any& rhs) -> T { \
367 return lhs op static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(rhs); \
368 } \
369 friend auto operator op (const any&, const any&) -> any; \
371 #define DEFINE_COMPARISON_OPERATOR(op) \
372 template <typename T, \
373 typename = typename std::enable_if<!std::is_same<T,any>::value && \
374 !std::is_same<T,const char *>::value>::type> \
375 inline friend auto operator op (const any& lhs, T rhs) -> bool { \
376 return static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(lhs) op rhs; \
377 } \
378 template <typename T, \
379 typename = typename std::enable_if<!std::is_same<T,any>::value && \
380 !std::is_same<T,const char *>::value>::type> \
381 inline friend auto operator op (T lhs, const any& rhs) -> bool { \
382 return lhs op static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(rhs); \
383 } \
384 inline friend auto operator op (const any& lhs, const char * rhs) -> bool { \
385 return static_cast<const string&>(lhs) op rhs; \
386 } \
387 inline friend auto operator op (const char * lhs, const any& rhs) -> bool { \
388 return lhs op static_cast<const string&>(rhs); \
389 } \
390 friend auto operator op (const any&, const any&) -> bool; \
392 DEFINE_COMPARISON_OPERATOR(==)
393 DEFINE_COMPARISON_OPERATOR(!=)
394 DEFINE_COMPARISON_OPERATOR(<)
395 DEFINE_COMPARISON_OPERATOR(<=)
396 DEFINE_COMPARISON_OPERATOR(>)
397 DEFINE_COMPARISON_OPERATOR(>=)
399 DEFINE_OPERATOR(+)
400 DEFINE_OPERATOR(-)
401 DEFINE_OPERATOR(*)
402 DEFINE_OPERATOR(/)
403 DEFINE_OPERATOR(%)
405 inline friend auto operator+(const any& lhs, const char * rhs) -> string {
406 return static_cast<const string&>(lhs) + rhs;
407 }
408 inline friend auto operator+(const char * lhs, const any& rhs) -> string {
409 return lhs + static_cast<const string&>(rhs);
410 }
412 friend auto operator-(const any&) -> any; // unary negate
414}; // class any
416namespace Private {
417 template <typename T, typename U=void>
418 struct TagHelper {};
420 template <>
421 struct TagHelper<string> {
422 static constexpr any::Tag tag = any::Tag::String;
423 };
425 template <>
426 struct TagHelper<const char *> {
427 static constexpr any::Tag tag = any::Tag::String;
428 };
430 template <size_t N>
431 struct TagHelper<any::dict<N>> {
432 static constexpr any::Tag tag = any::Tag::Dictionary;
433 };
435 template <size_t N>
436 struct TagHelper<any::data<N>> {
437 static constexpr any::Tag tag = any::Tag::Data;
438 };
440 template <>
441 struct TagHelper<any::array> {
442 static constexpr any::Tag tag = any::Tag::Array;
443 };
445 template <>
446 struct TagHelper<any::record> {
447 static constexpr any::Tag tag = any::Tag::Record;
448 };
450 template <typename T>
451 struct TagHelper<T> {
452 static constexpr any::Tag tag = any::Tag::Pointer;
453 };
456// Pass-through
457template <typename T>
458constexpr auto cast(const T& a) -> const T& {
459 return a;
462template <typename T>
463inline auto cast(const any& a) ->
464 typename std::enable_if<std::is_arithmetic<T>::value ||
465 std::is_same<T,const char *>::value, T>::type {
466 return a;
469template <typename T,
470 typename = typename std::enable_if<std::is_class<T>::value>::type>
471inline auto cast(const any& a) -> T& {
472 return *static_cast<T*>(a.extractPointer(IF_DEBUG(Private::TagHelper<T>::tag)));
475template <typename T,
476 typename = typename std::enable_if<std::is_pointer<T>::value &&
477 !std::is_same<T,void*>::value &&
478 !std::is_same<T,const char *>::value>::type>
479inline auto cast(const any& a) -> T {
480 return static_cast<T>(a.extractPointer(IF_DEBUG(Private::TagHelper<T>::tag)));
483template <typename T>
484inline auto cast(const any& a) ->
485 typename std::enable_if<std::is_same<void*, T>::value, T>::type {
486 return a.rawPointer();
489namespace dict {
490 template <size_t N, typename T>
491 inline auto get(const T& a) ->
492 typename std::enable_if<!std::is_same<any, T>::value, const any&>::type {
493 return a[N].second;
494 }
496 template <size_t N>
497 inline auto get(const any& a) -> const any& {
498 return cast<any::dict<N+1>>(a)[N].second;
499 }
501 template <size_t N>
502 inline auto get(const Private::Symbol key, const any::dict<N>& a) -> const any& {
503 static_assert(N > 0, "dict size must be greater than zero");
504 typename std::remove_reference<decltype(a)>::type::size_type i = 0;
505 do {
506 if (a[i].first == key) {
507 return a[i].second;
508 }
509 } while (a[++i].first != nullptr);
510 assert(false && "dict key not found");
511 static const any invalid_key(nullptr);
512 return invalid_key;
513 }
515 inline auto get(const Private::Symbol key, const any& a) -> const any& {
516 return get(key, cast<any::dict<unknown_size>>(a));
517 }
519 #define P(N) any::dict_pair&& arg ## N
520 #define T(N) any::dict<N+1>
521 #define E(N) std::forward<decltype(arg ## N)>(arg ## N)
522 #define M(...) {{ __VA_ARGS__, { nullptr, nullptr } }}
524 inline auto make(P(1)) -> T(1) {
525 return T(1) M(E(1));
526 }
527 inline auto make(P(1), P(2)) -> T(2) {
528 return T(2) M(E(1), E(2));
529 }
530 inline auto make(P(1), P(2), P(3)) -> T(3) {
531 return T(3) M(E(1), E(2), E(3));
532 }
533 inline auto make(P(1), P(2), P(3), P(4)) -> T(4) {
534 return T(4) M(E(1), E(2), E(3), E(4));
535 }
536 inline auto make(P(1), P(2), P(3), P(4), P(5)) -> T(5) {
537 return T(5) M(E(1), E(2), E(3), E(4), E(5));
538 }
539 inline auto make(P(1), P(2), P(3), P(4), P(5), P(6)) -> T(6) {
540 return T(6) M(E(1), E(2), E(3), E(4), E(5), E(6));
541 }
542 inline auto make(P(1), P(2), P(3), P(4), P(5), P(6), P(7)) -> T(7) {
543 return T(7) M(E(1), E(2), E(3), E(4), E(5), E(6), E(7));
544 }
545 inline auto make(P(1), P(2), P(3), P(4), P(5), P(6), P(7), P(8)) -> T(8) {
546 return T(8) M(E(1), E(2), E(3), E(4), E(5), E(6), E(7), E(8));
547 }
549 #undef P
550 #undef T
551 #undef E
552 #undef M
555namespace data {
556 template <size_t N, typename T>
557 inline auto get(const T& a) ->
558 typename std::enable_if<!std::is_same<any, T>::value, const any&>::type {
559 return a[N];
560 }
562 template <size_t N>
563 inline auto get(const any& a) -> const any& {
564 return cast<any::data<N+1>>(a)[N];
565 }
567 template <typename T>
568 inline auto ctor(const T& a) -> int {
569 return get<0>(a);
570 }
573} // namespace PureScript
575#undef WITH_ALLOCATOR
576#undef WITH_ALLOCATOR_PAIR
577#undef IS_POINTER_TYPE
578#undef DEFINE_OPERATOR
579#undef DEFINE_COMPARISON_OPERATOR
581#endif // PureScript_HH