git.iko.soy/rowtype-yoga/purescript-native
on 17867a2 help sign in
532 lines 16 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::nullptr_t;
49namespace Private {
50 struct symbol_generator_anchor_t {};
52 template <typename T>
53 struct symbol_generator {
54 constexpr static symbol_generator_anchor_t anchor = symbol_generator_anchor_t{};
55 };
56 template <typename T>
57 constexpr symbol_generator_anchor_t symbol_generator<T>::anchor;
60using symbol_t = const Private::symbol_generator_anchor_t *;
62#define define_symbol(S) namespace PureScript { \
63 namespace Private { \
64 namespace Symbol { \
65 struct S_ ## S {}; \
66 } \
67 } \
68 }
69#define symbol(S) (&Private::symbol_generator<::PureScript::Private::Symbol::S_ ## S>::anchor)
71// Workaround for missing C++11 version in gcc
72class runtime_error : public std::runtime_error {
73public:
74 runtime_error(const char message[]) : std::runtime_error(std::string(message)) {}
75};
77constexpr bool undefined = false;
79// Not a real limit, just used for simpler accessors
80static constexpr size_t unknown_size = 64;
82// A variant data class designed to provide some features of dynamic typing.
83//
84class any {
86 public:
87 enum class tag_t {
88 Thunk = 0x10,
89 Integer,
90 Double,
91 Character,
92 Boolean,
93 StringLiteral,
94 Function,
95 EffFunction,
96 RawPointer,
97 String,
98 Map,
99 Data,
100 Array,
101 Closure,
102 EffClosure,
103 Pointer
104 };
106 private:
107 mutable tag_t tag;
109 public:
110 struct as_thunk {
111 };
112 static constexpr as_thunk unthunk = as_thunk{};
114 using map_pair = std::pair<const symbol_t, const any>;
116 template <size_t N>
117 using map = std::array<const map_pair, N>;
119 template <size_t N>
120 using data = std::array<const any, N>;
122 using array = std::deque<any WITH_ALLOCATOR(any)>;
123 using fn = auto (*)(const any&) -> any;
124 using eff_fn = auto (*)() -> any;
125 using thunk = auto (*)(const as_thunk) -> const any&;
127 private:
128 class closure {
129 public:
130 virtual auto operator()(const any&) const -> any = 0;
131 virtual ~closure() {}
132 };
134 template <typename T>
135 class closure_ : public closure {
136 const T lambda;
137 public:
138 closure_(const T& l) noexcept : lambda(l) {}
139 auto operator()(const any& arg) const -> any override {
140 return lambda(arg);
141 }
142 };
144 class eff_closure {
145 public:
146 virtual auto operator()() const -> any = 0;
147 virtual ~eff_closure() {}
148 };
150 template <typename T>
151 class eff_closure_ : public eff_closure {
152 const T lambda;
153 public:
154 eff_closure_(const T& l) noexcept : lambda(l) {}
155 auto operator()() const -> any override {
156 return lambda();
157 }
158 };
160 private:
161 union {
162 mutable thunk t;
163 mutable int i;
164 mutable double d;
165 mutable char c;
166 mutable bool b;
167 mutable cstring r;
168 mutable fn f;
169 mutable eff_fn e;
170 mutable void * u;
171 mutable managed<std::string> s;
172 mutable managed<array> a;
173 mutable managed<closure> l;
174 mutable managed<eff_closure> k;
175 mutable managed<void> p;
176 };
178 public:
180 any(const int val) noexcept : tag(tag_t::Integer), i(val) {}
181 any(const long val) noexcept : tag(tag_t::Integer), i(static_cast<decltype(i)>(val)) {
182 assert(val >= std::numeric_limits<decltype(i)>::min() &&
183 val <= std::numeric_limits<decltype(i)>::max());
184 }
186 any(const double val) noexcept : tag(tag_t::Double), d(val) {}
187 any(const char val) noexcept : tag(tag_t::Character), c(val) {}
189 template <typename T, typename = typename std::enable_if<std::is_same<bool,T>::value>::type>
190 any(const T val) noexcept : tag(tag_t::Boolean), b(val) {}
192 template <size_t N>
193 any(const char (&val)[N]) noexcept : tag(tag_t::StringLiteral), r(val) {}
194 any(char * val) : tag(tag_t::String), s(make_managed<std::string>(val)) {}
196 any(const std::string& val) : tag(tag_t::String), s(make_managed<std::string>(val)) {}
197 any(std::string&& val) noexcept : tag(tag_t::String), s(make_managed<std::string>(std::move(val))) {}
199 any(const managed<std::string>& val) noexcept : tag(tag_t::String), s(val) {}
200 any(managed<std::string>&& val) noexcept : tag(tag_t::String), s(std::move(val)) {}
202 template <size_t N>
203 any(map<N>&& val) noexcept : tag(tag_t::Map), p(make_managed<map<N>>(std::move(val))) {}
205 template <size_t N>
206 any(data<N>&& val) noexcept : tag(tag_t::Data), p(make_managed<data<N>>(std::move(val))) {}
208 any(const array& val) : tag(tag_t::Array), a(make_managed<array>(val)) {}
209 any(array&& val) noexcept : tag(tag_t::Array), a(make_managed<array>(std::move(val))) {}
211 template <typename T>
212 any(const T& val, typename std::enable_if<std::is_convertible<T,fn>::value>::type* = 0) noexcept
213 : tag(tag_t::Function), f(val) {}
215 template <typename T, typename = typename std::enable_if<!std::is_same<any,T>::value &&
216 !std::is_convertible<T,fn>::value>::type>
217 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any(const any&)>,T>::value>::type* = 0)
218 : tag(tag_t::Closure), l(make_managed<closure_<T>>(val)) {}
220 template <typename T>
221 any(const T& val, typename std::enable_if<std::is_convertible<T,eff_fn>::value>::type* = 0) noexcept
222 : tag(tag_t::EffFunction), e(val) {}
224 template <typename T,
225 typename = typename std::enable_if<!std::is_same<any,T>::value &&
226 !std::is_convertible<T,eff_fn>::value>::type>
227 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any()>,T>::value>::type* = 0)
228 : tag(tag_t::EffClosure), k(make_managed<eff_closure_<T>>(val)) {}
230 template <typename T>
231 any(const T& val, typename std::enable_if<std::is_convertible<T,thunk>::value>::type* = 0) noexcept
232 : tag(tag_t::Thunk), t(val) {}
234 template <typename T,
235 typename = typename std::enable_if<std::is_class<typename std::remove_pointer<T>::type>::value>::type>
236 any(const T& val, typename std::enable_if<IS_POINTER_TYPE(T)::value>::type* = 0) noexcept
237 : tag(tag_t::Pointer), p(val) {}
239 template <typename T>
240 any(T&& val, typename std::enable_if<std::is_assignable<managed<void>,T>::value>::type* = 0) noexcept
241 : tag(tag_t::Pointer), p(std::move(val)) {}
243 // Explicit void* to raw pointer value
244 template <typename T>
245 any(const T& val, typename std::enable_if<std::is_same<T,void*>::value>::type* = 0) noexcept
246 : tag(tag_t::RawPointer), u(val) {}
248 any(nullptr_t) noexcept : tag(tag_t::RawPointer), u(nullptr) {}
251#if !defined(USE_GC)
252 private:
253 template <typename T,
254 typename U,
255 typename = typename std::enable_if<!std::is_reference<T>::value>::type>
256 static constexpr auto move_if_rvalue(U&& u) noexcept -> U&& {
257 return static_cast<U&&>(u);
258 }
260 template <typename T,
261 typename U,
262 typename = typename std::enable_if<std::is_reference<T>::value>::type>
263 static constexpr auto move_if_rvalue(U& u) noexcept -> U& {
264 return static_cast<U&>(u);
265 }
267 template <typename T>
268 auto assign(T&& other) const noexcept -> void {
269 switch (other.tag) {
270 case tag_t::Thunk: t = other.t; break;
271 case tag_t::Integer: i = other.i; break;
272 case tag_t::Double: d = other.d; break;
273 case tag_t::Character: c = other.c; break;
274 case tag_t::Boolean: b = other.b; break;
275 case tag_t::StringLiteral: r = other.r; break;
276 case tag_t::Function: f = other.f; break;
277 case tag_t::EffFunction: e = other.e; break;
278 case tag_t::RawPointer: u = other.u; break;
280 case tag_t::String: new (&s) managed<std::string>(move_if_rvalue<T>(other.s)); break;
281 case tag_t::Array: new (&a) managed<array>(move_if_rvalue<T>(other.a)); break;
282 case tag_t::Closure: new (&l) managed<closure>(move_if_rvalue<T>(other.l)); break;
283 case tag_t::EffClosure: new (&k) managed<eff_closure>(move_if_rvalue<T>(other.k)); break;
284 case tag_t::Map:
285 case tag_t::Data:
286 case tag_t::Pointer: new (&p) managed<void>(move_if_rvalue<T>(other.p)); break;
288 default: assert(false && "Bad 'any' tag"); break;
289 }
290 }
292 auto destruct() -> void {
293 switch (tag) {
294 case tag_t::String: s.~managed<std::string>(); break;
295 case tag_t::Array: a.~managed<array>(); break;
296 case tag_t::Closure: l.~managed<closure>(); break;
297 case tag_t::EffClosure: k.~managed<eff_closure>(); break;
298 case tag_t::Map:
299 case tag_t::Data:
300 case tag_t::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 char() const;
345 operator cstring() const;
346 operator const array&() const;
348 auto operator[](const symbol_t) const -> const any&;
349 auto operator[](const size_t) const -> const any&;
350 auto operator[](const any&) const -> const any&;
352 auto size() const -> size_t;
353 auto empty() const -> bool;
354 auto contains(const symbol_t) const -> bool;
356 auto extractPointer(IF_DEBUG(const tag_t)) const -> void*;
358 auto rawPointer() const -> void* {
359 const any& variant = unthunkVariant(*this);
360 assert(tag == tag_t::RawPointer);
361 return variant.u;
362 }
364 static auto unthunkVariant(const any&) -> const any&;
366 #define DEFINE_OPERATOR(op, ty, rty) \
367 inline friend auto operator op (const any& lhs, ty rhs) -> rty { \
368 return (ty)lhs op rhs; \
369 } \
370 inline friend auto operator op (ty lhs, const any& rhs) -> rty { \
371 return lhs op (ty)rhs; \
372 } \
374 #define DECLARE_COMPARISON_OPERATOR(op) \
375 friend auto operator op (const any&, const any&) -> bool; \
376 DEFINE_OPERATOR(op, int, bool) \
377 DEFINE_OPERATOR(op, double, bool) \
378 DEFINE_OPERATOR(op, char, bool) \
379 DEFINE_OPERATOR(op, bool, bool) \
380 friend auto operator op (const any&, const char * const) -> bool; \
381 friend auto operator op (const char * const, const any&) -> bool;
383 DECLARE_COMPARISON_OPERATOR(==)
384 DECLARE_COMPARISON_OPERATOR(!=)
385 DECLARE_COMPARISON_OPERATOR(<)
386 DECLARE_COMPARISON_OPERATOR(<=)
387 DECLARE_COMPARISON_OPERATOR(>)
388 DECLARE_COMPARISON_OPERATOR(>=)
390 friend auto operator+(const any&, const any&) -> any;
392 DEFINE_OPERATOR(+, int, int)
393 DEFINE_OPERATOR(+, double, double)
394 DEFINE_OPERATOR(+, char, char)
395 friend auto operator+(const any& lhs, const char * const rhs) -> std::string;
396 friend auto operator+(const char * const lhs, const any& rhs) -> std::string;
398 friend auto operator-(const any&, const any&) -> any;
400 DEFINE_OPERATOR(-, int, int)
401 DEFINE_OPERATOR(-, double, double)
402 DEFINE_OPERATOR(-, char, char)
404 friend auto operator*(const any&, const any&) -> any;
406 DEFINE_OPERATOR(*, int, int)
407 DEFINE_OPERATOR(*, double, double)
408 DEFINE_OPERATOR(*, char, char)
410 friend auto operator/(const any&, const any&) -> any;
412 DEFINE_OPERATOR(/, int, int)
413 DEFINE_OPERATOR(/, double, double)
414 DEFINE_OPERATOR(/, char, char)
416 friend auto operator%(const any&, const any&) -> any;
418 DEFINE_OPERATOR(%, int, int)
419 DEFINE_OPERATOR(%, char, char)
421 friend auto operator-(const any&) -> any; // unary negate
423}; // class any
425namespace Private {
426 template <typename T, typename U=void>
427 struct tag_helper_t {};
429 template <size_t N>
430 struct tag_helper_t<any::map<N>> {
431 static constexpr any::tag_t tag = any::tag_t::Map;
432 };
434 template <size_t N>
435 struct tag_helper_t<any::data<N>> {
436 static constexpr any::tag_t tag = any::tag_t::Data;
437 };
439 template <typename T>
440 struct tag_helper_t<T> {
441 static constexpr any::tag_t tag = any::tag_t::Pointer;
442 };
445// Pass-through
446template <typename T>
447constexpr auto cast(const T& a) -> const T& {
448 return a;
451template <typename T>
452inline auto cast(const any& a) ->
453 typename std::enable_if<std::is_arithmetic<T>::value ||
454 std::is_same<T,cstring>::value, T>::type {
455 return a;
458template <typename T>
459inline auto cast(const any& a) ->
460 typename std::enable_if<std::is_same<T, any::array>::value, const T&>::type {
461 return a;
464template <typename T, typename = typename std::enable_if<std::is_class<T>::value>::type>
465inline auto cast(const any& a) ->
466 typename std::enable_if<!std::is_same<T, any::array>::value, T&>::type {
467 return *static_cast<T*>(a.extractPointer(IF_DEBUG(Private::tag_helper_t<T>::tag)));
470template <typename T>
471inline auto cast(const any& a) ->
472 typename std::enable_if<std::is_same<void*, T>::value, T>::type {
473 return a.rawPointer();
476namespace map {
477 template <size_t N, typename T>
478 inline auto get(const T& a) ->
479 typename std::enable_if<!std::is_same<any, T>::value, const any&>::type {
480 return a[N].second;
481 }
483 template <size_t N>
484 inline auto get(const any& a) -> const any& {
485 return cast<any::map<N+1>>(a)[N].second;
486 }
488 template <size_t N>
489 inline auto get(const symbol_t key, const any::map<N>& a) -> const any& {
490 static_assert(N > 0, "map size must be greater than zero");
491 typename std::remove_reference<decltype(a)>::type::size_type i = 0;
492 do {
493 if (a[i].first == key) {
494 return a[i].second;
495 }
496 } while (a[++i].first != nullptr);
497 assert(false && "map key not found");
498 static const any invalid_key(nullptr);
499 return invalid_key;
500 }
502 inline auto get(const symbol_t key, const any& a) -> const any& {
503 return get(key, cast<any::map<unknown_size>>(a));
504 }
507namespace data {
508 template <size_t N, typename T>
509 inline auto get(const T& a) ->
510 typename std::enable_if<!std::is_same<any, T>::value, const any&>::type {
511 return a[N];
512 }
514 template <size_t N>
515 inline auto get(const any& a) -> const any& {
516 return cast<any::data<N+1>>(a)[N];
517 }
519 template <typename T>
520 inline auto ctor(const T& a) -> int {
521 return get<0>(a);
522 }
525} // namespace PureScript
527#undef WITH_ALLOCATOR
528#undef IS_POINTER_TYPE
529#undef DEFINE_OPERATOR
530#undef DECLARE_COMPARISON_OPERATOR
532#endif // PureScript_HH