git.iko.soy/rowtype-yoga/purescript-native
on 36a2ed7 help sign in
577 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 *; // TODO: deprecated
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 size_t val) noexcept : tag(Tag::Integer), i(static_cast<decltype(i)>(val)) {
197 assert(val <= std::numeric_limits<decltype(i)>::max());
198 }
199 any(const double val) noexcept : tag(Tag::Double), d(val) {}
200 any(const char32_t val) noexcept : tag(Tag::Character), c(val) {}
201 any(const char val) noexcept : tag(Tag::Character), c(val) {}
203 template <typename T, typename = typename std::enable_if<std::is_same<bool,T>::value>::type>
204 any(const T val) noexcept : tag(Tag::Boolean), b(val) {}
206 any(const char * val) : tag(Tag::String), p(make_managed_and_finalized<string>(val)) {}
207 any(const string& val) : tag(Tag::String), p(make_managed_and_finalized<string>(val)) {}
208 any(string&& val) noexcept : tag(Tag::String), p(make_managed_and_finalized<string>(std::move(val))) {}
210 any(const managed<string>& val) noexcept : tag(Tag::String), p(val) {}
211 any(managed<string>&& val) noexcept : tag(Tag::String), p(std::move(val)) {}
213 template <size_t N>
214 any(dict<N>&& val) noexcept : tag(Tag::Dictionary), p(make_managed<dict<N>>(std::move(val))) {}
216 template <size_t N>
217 any(data<N>&& val) noexcept : tag(Tag::Data), p(make_managed<data<N>>(std::move(val))) {}
219 any(const array& val) : tag(Tag::Array), p(make_managed<array>(val)) {}
220 any(array&& val) noexcept : tag(Tag::Array), p(make_managed<array>(std::move(val))) {}
222 any(const record& val) : tag(Tag::Record), p(make_managed<record>(val)) {}
223 any(record&& val) noexcept : tag(Tag::Record), p(make_managed<record>(std::move(val))) {}
225 template <typename T>
226 any(const T& val, typename std::enable_if<std::is_convertible<T,fn>::value>::type* = 0) noexcept
227 : tag(Tag::Function), f(val) {}
229 template <typename T, typename = typename std::enable_if<!std::is_same<any,T>::value &&
230 !std::is_convertible<T,fn>::value>::type>
231 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any(const any&)>,T>::value>::type* = 0)
232 : tag(Tag::Closure), p(make_managed<Closure_<T>>(val)) {}
234 template <typename T>
235 any(const T& val, typename std::enable_if<std::is_convertible<T,eff_fn>::value>::type* = 0) noexcept
236 : tag(Tag::EffFunction), e(val) {}
238 template <typename T,
239 typename = typename std::enable_if<!std::is_same<any,T>::value &&
240 !std::is_convertible<T,eff_fn>::value>::type>
241 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any()>,T>::value>::type* = 0)
242 : tag(Tag::EffClosure), p(make_managed<EffClosure_<T>>(val)) {}
244 template <typename T>
245 any(const T& val, typename std::enable_if<std::is_convertible<T,thunk>::value>::type* = 0) noexcept
246 : tag(Tag::Thunk), t(val) {}
248 template <typename T,
249 typename = typename std::enable_if<std::is_class<typename std::remove_pointer<T>::type>::value>::type>
250 any(const T& val, typename std::enable_if<IS_POINTER_TYPE(T)::value>::type* = 0) noexcept
251 : tag(Tag::Pointer), p(val) {}
253 template <typename T>
254 any(T&& val, typename std::enable_if<std::is_assignable<managed<void>,T>::value>::type* = 0) noexcept
255 : tag(Tag::Pointer), p(std::move(val)) {}
257 any(void * val) noexcept : tag(Tag::RawPointer), v(val) {}
258 any(nullptr_t) noexcept : tag(Tag::RawPointer), v(nullptr) {}
260#if !defined(USE_GC)
261 private:
262 auto destruct() const noexcept -> void {
263 if (isManaged(tag)) {
264 p.~managed<void>();
265 }
266 }
268 static_assert(sizeof(any::u) >= sizeof(any::t), "size assumption");
269 static_assert(sizeof(any::u) >= sizeof(any::i), "size assumption");
270 static_assert(sizeof(any::u) >= sizeof(any::d), "size assumption");
271 static_assert(sizeof(any::u) >= sizeof(any::c), "size assumption");
272 static_assert(sizeof(any::u) >= sizeof(any::b), "size assumption");
273 static_assert(sizeof(any::u) >= sizeof(any::f), "size assumption");
274 static_assert(sizeof(any::u) >= sizeof(any::e), "size assumption");
275 static_assert(sizeof(any::u) >= sizeof(any::v), "size assumption");
277 auto copyAssign(const any& other) const noexcept -> void {
278 if (isManaged(other.tag)) {
279 new (&p) managed<void>(other.p);
280 } else {
281 u = other.u;
282 }
283 }
284 auto moveAssign(any& other) const noexcept -> void {
285 if (isManaged(other.tag)) {
286 new (&p) managed<void>(std::move(other.p));
287 } else {
288 u = other.u;
289 }
290 }
292 public:
293 any(const any& other) noexcept : tag(other.tag) {
294 copyAssign(other);
295 }
297 any(any&& other) noexcept : tag(other.tag) {
298 moveAssign(other);
299 }
301 auto operator=(const any& rhs) noexcept -> any& {
302 destruct();
303 tag = rhs.tag;
304 copyAssign(rhs);
305 return *this;
306 }
308 auto operator=(any&& rhs) noexcept -> any& {
309 destruct();
310 tag = rhs.tag;
311 moveAssign(rhs);
312 return *this;
313 }
315 ~any() {
316 destruct();
317 }
318#endif // !defined(USE_GC)
320 public:
321 any() = delete;
323 auto operator()(const any&) const -> any;
324 auto operator()(const as_thunk) const -> const any&;
325 auto operator()() const -> any;
327 operator int() const;
328 operator double() const;
329 operator bool() const;
330 operator char32_t() const;
331 operator char() const;
332 operator size_t() const;
333 operator const string&() const;
334 operator const char *() const;
335 operator const array&() const;
336 operator const record&() const;
338 operator void *() const {
339 return rawPointer();
340 }
342 template <typename T,
343 typename = typename std::enable_if<std::is_pointer<T>::value &&
344 std::is_class<typename std::remove_pointer<T>::type>::value>::type>
345 operator T() const {
346 return static_cast<T>(rawPointer());
347 }
349 auto operator[](const int) const -> const any&;
350 auto at(const char *) const -> const any&;
351 auto size() const -> size_t;
352 auto empty() const -> bool;
353 auto contains(const Private::Symbol) const -> bool;
355 auto extractPointer(IF_DEBUG(const Tag)) const -> void*;
357 auto rawPointer() const -> void* {
358 const any& variant = unthunkVariant(*this);
359 assert(tag == Tag::RawPointer);
360 return variant.v;
361 }
363 static auto unthunkVariant(const any&) -> const any&;
365 #define DEFINE_OPERATOR(op) \
366 template <typename T, \
367 typename = typename std::enable_if<!std::is_same<T,any>::value && \
368 !std::is_same<T,const char *>::value>::type> \
369 inline friend auto operator op (const any& lhs, T rhs) -> T { \
370 return static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(lhs) op rhs; \
371 } \
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 (T lhs, const any& rhs) -> T { \
376 return lhs op static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(rhs); \
377 } \
378 friend auto operator op (const any&, const any&) -> any; \
380 #define DEFINE_COMPARISON_OPERATOR(op) \
381 template <typename T, \
382 typename = typename std::enable_if<!std::is_same<T,any>::value && \
383 !std::is_same<T,const char *>::value>::type> \
384 inline friend auto operator op (const any& lhs, T rhs) -> bool { \
385 return static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(lhs) op rhs; \
386 } \
387 template <typename T, \
388 typename = typename std::enable_if<!std::is_same<T,any>::value && \
389 !std::is_same<T,const char *>::value>::type> \
390 inline friend auto operator op (T lhs, const any& rhs) -> bool { \
391 return lhs op static_cast<typename std::add_lvalue_reference<typename std::add_const<T>::type >::type>(rhs); \
392 } \
393 inline friend auto operator op (const any& lhs, const char * rhs) -> bool { \
394 return static_cast<const string&>(lhs) op rhs; \
395 } \
396 inline friend auto operator op (const char * lhs, const any& rhs) -> bool { \
397 return lhs op static_cast<const string&>(rhs); \
398 } \
399 friend auto operator op (const any&, const any&) -> bool; \
401 DEFINE_COMPARISON_OPERATOR(==)
402 DEFINE_COMPARISON_OPERATOR(!=)
403 DEFINE_COMPARISON_OPERATOR(<)
404 DEFINE_COMPARISON_OPERATOR(<=)
405 DEFINE_COMPARISON_OPERATOR(>)
406 DEFINE_COMPARISON_OPERATOR(>=)
408 DEFINE_OPERATOR(+)
409 DEFINE_OPERATOR(-)
410 DEFINE_OPERATOR(*)
411 DEFINE_OPERATOR(/)
412 DEFINE_OPERATOR(%)
414 inline friend auto operator+(const any& lhs, const char * rhs) -> string {
415 return static_cast<const string&>(lhs) + rhs;
416 }
417 inline friend auto operator+(const char * lhs, const any& rhs) -> string {
418 return lhs + static_cast<const string&>(rhs);
419 }
421 friend auto operator-(const any&) -> any; // unary negate
423}; // class any
425namespace Private {
426 template <typename T, typename U=void>
427 struct TagHelper {};
429 template <>
430 struct TagHelper<string> {
431 static constexpr any::Tag tag = any::Tag::String;
432 };
434 template <>
435 struct TagHelper<const char *> {
436 static constexpr any::Tag tag = any::Tag::String;
437 };
439 template <size_t N>
440 struct TagHelper<any::dict<N>> {
441 static constexpr any::Tag tag = any::Tag::Dictionary;
442 };
444 template <size_t N>
445 struct TagHelper<any::data<N>> {
446 static constexpr any::Tag tag = any::Tag::Data;
447 };
449 template <>
450 struct TagHelper<any::array> {
451 static constexpr any::Tag tag = any::Tag::Array;
452 };
454 template <>
455 struct TagHelper<any::record> {
456 static constexpr any::Tag tag = any::Tag::Record;
457 };
459 template <typename T>
460 struct TagHelper<T> {
461 static constexpr any::Tag tag = any::Tag::Pointer;
462 };
465template <typename T,
466 typename = typename std::enable_if<std::is_class<T>::value>::type>
467inline auto cast_managed(const any& a) -> T& {
468 return *static_cast<T*>(a.extractPointer(IF_DEBUG(Private::TagHelper<T>::tag)));
471// TODO: Deprecated - use static_cast
472template <typename T,
473 typename = typename std::enable_if<!std::is_class<T>::value>::type>
474inline auto cast(const any& a) -> T {
475 return static_cast<T>(a);
478// TODO: Deprecated - use cast_managed
479template <typename T,
480 typename = typename std::enable_if<std::is_class<T>::value>::type>
481inline auto cast(const any& a) -> T& {
482 return cast_managed<T>(a);
485namespace dict {
486 template <size_t N, typename T>
487 inline auto get(const T& a) ->
488 typename std::enable_if<!std::is_same<any, T>::value, const any&>::type {
489 return a[N].second;
490 }
492 template <size_t N>
493 inline auto get(const any& a) -> const any& {
494 return cast_managed<any::dict<N+1>>(a)[N].second;
495 }
497 template <size_t N>
498 inline auto get(const Private::Symbol key, const any::dict<N>& a) -> const any& {
499 static_assert(N > 0, "dict size must be greater than zero");
500 typename std::remove_reference<decltype(a)>::type::size_type i = 0;
501 do {
502 if (a[i].first == key) {
503 return a[i].second;
504 }
505 } while (a[++i].first != nullptr);
506 assert(false && "dict key not found");
507 static const any invalid_key(nullptr);
508 return invalid_key;
509 }
511 inline auto get(const Private::Symbol key, const any& a) -> const any& {
512 return get(key, cast_managed<any::dict<unknown_size>>(a));
513 }
515 #define P(N) any::dict_pair&& arg ## N
516 #define T(N) any::dict<N+1>
517 #define E(N) std::forward<decltype(arg ## N)>(arg ## N)
518 #define M(...) {{ __VA_ARGS__, { nullptr, nullptr } }}
520 inline auto make(P(1)) -> T(1) {
521 return T(1) M(E(1));
522 }
523 inline auto make(P(1), P(2)) -> T(2) {
524 return T(2) M(E(1), E(2));
525 }
526 inline auto make(P(1), P(2), P(3)) -> T(3) {
527 return T(3) M(E(1), E(2), E(3));
528 }
529 inline auto make(P(1), P(2), P(3), P(4)) -> T(4) {
530 return T(4) M(E(1), E(2), E(3), E(4));
531 }
532 inline auto make(P(1), P(2), P(3), P(4), P(5)) -> T(5) {
533 return T(5) M(E(1), E(2), E(3), E(4), E(5));
534 }
535 inline auto make(P(1), P(2), P(3), P(4), P(5), P(6)) -> T(6) {
536 return T(6) M(E(1), E(2), E(3), E(4), E(5), E(6));
537 }
538 inline auto make(P(1), P(2), P(3), P(4), P(5), P(6), P(7)) -> T(7) {
539 return T(7) M(E(1), E(2), E(3), E(4), E(5), E(6), E(7));
540 }
541 inline auto make(P(1), P(2), P(3), P(4), P(5), P(6), P(7), P(8)) -> T(8) {
542 return T(8) M(E(1), E(2), E(3), E(4), E(5), E(6), E(7), E(8));
543 }
545 #undef P
546 #undef T
547 #undef E
548 #undef M
551namespace data {
552 template <size_t N, typename T>
553 inline auto get(const T& a) ->
554 typename std::enable_if<!std::is_same<any, T>::value, const any&>::type {
555 return a[N];
556 }
558 template <size_t N>
559 inline auto get(const any& a) -> const any& {
560 return cast_managed<any::data<N+1>>(a)[N];
561 }
563 template <typename T>
564 inline auto ctor(const T& a) -> int {
565 return get<0>(a);
566 }
569} // namespace PureScript
571#undef WITH_ALLOCATOR
572#undef WITH_ALLOCATOR_PAIR
573#undef IS_POINTER_TYPE
574#undef DEFINE_OPERATOR
575#undef DEFINE_COMPARISON_OPERATOR
577#endif // PureScript_HH