git.iko.soy/rowtype-yoga/purescript-native
on da472aa help sign in
526 lines 15 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;
48using std::runtime_error;
50namespace Private {
51 struct SymbolGeneratorAnchor {};
53 template <typename T>
54 struct SymbolGenerator {
55 constexpr static SymbolGeneratorAnchor anchor = SymbolGeneratorAnchor{};
56 };
57 template <typename T>
58 constexpr SymbolGeneratorAnchor SymbolGenerator<T>::anchor;
60 using Symbol = const SymbolGeneratorAnchor *;
63#define define_symbol(S) namespace PureScript { \
64 namespace Private { \
65 namespace Symbols { \
66 struct S_ ## S {}; \
67 } \
68 } \
69 }
70#define symbol(S) (&Private::SymbolGenerator<::PureScript::Private::Symbols::S_ ## S>::anchor)
72constexpr bool undefined = false;
74// TODO: Not a real limit, just used for simpler accessors
75static constexpr size_t unknown_size = 64;
77// A variant data class designed to provide some features of dynamic typing.
78//
79class any {
81 public:
82 enum class Tag {
83 Thunk = 0x10,
84 Integer,
85 Double,
86 Character,
87 Boolean,
88 StringLiteral,
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 char c;
161 mutable bool b;
162 mutable cstring r;
163 mutable fn f;
164 mutable eff_fn e;
165 mutable void * u;
166 mutable managed<std::string> s;
167 mutable managed<array> a;
168 mutable managed<Closure> l;
169 mutable managed<EffClosure> k;
170 mutable managed<void> p;
171 };
173 public:
175 any(const int val) noexcept : tag(Tag::Integer), i(val) {}
176 any(const long val) noexcept : tag(Tag::Integer), i(static_cast<decltype(i)>(val)) {
177 assert(val >= std::numeric_limits<decltype(i)>::min() &&
178 val <= std::numeric_limits<decltype(i)>::max());
179 }
181 any(const double val) noexcept : tag(Tag::Double), d(val) {}
182 any(const char val) noexcept : tag(Tag::Character), c(val) {}
184 template <typename T, typename = typename std::enable_if<std::is_same<bool,T>::value>::type>
185 any(const T val) noexcept : tag(Tag::Boolean), b(val) {}
187 template <size_t N>
188 any(const char (&val)[N]) noexcept : tag(Tag::StringLiteral), r(val) {}
189 any(char * val) : tag(Tag::String), s(make_managed<std::string>(val)) {}
191 any(const std::string& val) : tag(Tag::String), s(make_managed<std::string>(val)) {}
192 any(std::string&& val) noexcept : tag(Tag::String), s(make_managed<std::string>(std::move(val))) {}
194 any(const managed<std::string>& val) noexcept : tag(Tag::String), s(val) {}
195 any(managed<std::string>&& val) noexcept : tag(Tag::String), s(std::move(val)) {}
197 template <size_t N>
198 any(map<N>&& val) noexcept : tag(Tag::Map), p(make_managed<map<N>>(std::move(val))) {}
200 template <size_t N>
201 any(data<N>&& val) noexcept : tag(Tag::Data), p(make_managed<data<N>>(std::move(val))) {}
203 any(const array& val) : tag(Tag::Array), a(make_managed<array>(val)) {}
204 any(array&& val) noexcept : tag(Tag::Array), a(make_managed<array>(std::move(val))) {}
206 template <typename T>
207 any(const T& val, typename std::enable_if<std::is_convertible<T,fn>::value>::type* = 0) noexcept
208 : tag(Tag::Function), f(val) {}
210 template <typename T, typename = typename std::enable_if<!std::is_same<any,T>::value &&
211 !std::is_convertible<T,fn>::value>::type>
212 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any(const any&)>,T>::value>::type* = 0)
213 : tag(Tag::Closure), l(make_managed<Closure_<T>>(val)) {}
215 template <typename T>
216 any(const T& val, typename std::enable_if<std::is_convertible<T,eff_fn>::value>::type* = 0) noexcept
217 : tag(Tag::EffFunction), e(val) {}
219 template <typename T,
220 typename = typename std::enable_if<!std::is_same<any,T>::value &&
221 !std::is_convertible<T,eff_fn>::value>::type>
222 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any()>,T>::value>::type* = 0)
223 : tag(Tag::EffClosure), k(make_managed<EffClosure_<T>>(val)) {}
225 template <typename T>
226 any(const T& val, typename std::enable_if<std::is_convertible<T,thunk>::value>::type* = 0) noexcept
227 : tag(Tag::Thunk), t(val) {}
229 template <typename T,
230 typename = typename std::enable_if<std::is_class<typename std::remove_pointer<T>::type>::value>::type>
231 any(const T& val, typename std::enable_if<IS_POINTER_TYPE(T)::value>::type* = 0) noexcept
232 : tag(Tag::Pointer), p(val) {}
234 template <typename T>
235 any(T&& val, typename std::enable_if<std::is_assignable<managed<void>,T>::value>::type* = 0) noexcept
236 : tag(Tag::Pointer), p(std::move(val)) {}
238 // Explicit void* to raw pointer value
239 template <typename T>
240 any(const T& val, typename std::enable_if<std::is_same<T,void*>::value>::type* = 0) noexcept
241 : tag(Tag::RawPointer), u(val) {}
243 any(nullptr_t) noexcept : tag(Tag::RawPointer), u(nullptr) {}
246#if !defined(USE_GC)
247 private:
248 template <typename T,
249 typename U,
250 typename = typename std::enable_if<!std::is_reference<T>::value>::type>
251 static constexpr auto move_if_rvalue(U&& u) noexcept -> U&& {
252 return static_cast<U&&>(u);
253 }
255 template <typename T,
256 typename U,
257 typename = typename std::enable_if<std::is_reference<T>::value>::type>
258 static constexpr auto move_if_rvalue(U& u) noexcept -> U& {
259 return static_cast<U&>(u);
260 }
262 template <typename T>
263 auto assign(T&& other) const noexcept -> void {
264 switch (other.tag) {
265 case Tag::Thunk: t = other.t; break;
266 case Tag::Integer: i = other.i; break;
267 case Tag::Double: d = other.d; break;
268 case Tag::Character: c = other.c; break;
269 case Tag::Boolean: b = other.b; break;
270 case Tag::StringLiteral: r = other.r; break;
271 case Tag::Function: f = other.f; break;
272 case Tag::EffFunction: e = other.e; break;
273 case Tag::RawPointer: u = other.u; break;
275 case Tag::String: new (&s) managed<std::string>(move_if_rvalue<T>(other.s)); break;
276 case Tag::Array: new (&a) managed<array>(move_if_rvalue<T>(other.a)); break;
277 case Tag::Closure: new (&l) managed<Closure>(move_if_rvalue<T>(other.l)); break;
278 case Tag::EffClosure: new (&k) managed<EffClosure>(move_if_rvalue<T>(other.k)); break;
279 case Tag::Map:
280 case Tag::Data:
281 case Tag::Pointer: new (&p) managed<void>(move_if_rvalue<T>(other.p)); break;
283 default: assert(false && "Bad 'any' tag"); break;
284 }
285 }
287 auto destruct() -> void {
288 switch (tag) {
289 case Tag::String: s.~managed<std::string>(); break;
290 case Tag::Array: a.~managed<array>(); break;
291 case Tag::Closure: l.~managed<Closure>(); break;
292 case Tag::EffClosure: k.~managed<EffClosure>(); break;
293 case Tag::Map:
294 case Tag::Data:
295 case Tag::Pointer: p.~managed<void>(); break;
297 default: break;
298 }
299 }
301 public:
302 any(const any& other) noexcept : tag(other.tag) {
303 assign(other);
304 }
306 any(any&& other) noexcept : tag(other.tag) {
307 assign(std::move(other));
308 }
310 auto operator=(const any& rhs) noexcept -> any& {
311 destruct();
312 tag = rhs.tag;
313 assign(rhs);
314 return *this;
315 }
317 auto operator=(any&& rhs) noexcept -> any& {
318 destruct();
319 tag = rhs.tag;
320 assign(std::move(rhs));
321 return *this;
322 }
324 ~any() {
325 destruct();
326 }
327#endif // !defined(USE_GC)
329 public:
330 any() = delete;
332 auto operator()(const any&) const -> any;
333 auto operator()(const as_thunk) const -> const any&;
334 auto operator()() const -> any;
336 operator int() const;
337 operator double() const;
338 operator bool() const;
339 operator char() const;
340 operator size_t() const;
341 operator cstring() const;
342 operator const array&() const;
344 auto operator[](const size_t) const -> const any&;
346 auto size() const -> size_t;
347 auto empty() const -> bool;
348 auto contains(const Private::Symbol) const -> bool;
350 auto extractPointer(IF_DEBUG(const Tag)) const -> void*;
352 auto rawPointer() const -> void* {
353 const any& variant = unthunkVariant(*this);
354 assert(tag == Tag::RawPointer);
355 return variant.u;
356 }
358 static auto unthunkVariant(const any&) -> const any&;
360 #define DEFINE_OPERATOR(op, ty, rty) \
361 inline friend auto operator op (const any& lhs, ty rhs) -> rty { \
362 return (ty)lhs op rhs; \
363 } \
364 inline friend auto operator op (ty lhs, const any& rhs) -> rty { \
365 return lhs op (ty)rhs; \
366 } \
368 #define DECLARE_COMPARISON_OPERATOR(op) \
369 friend auto operator op (const any&, const any&) -> bool; \
370 DEFINE_OPERATOR(op, int, bool) \
371 DEFINE_OPERATOR(op, double, bool) \
372 DEFINE_OPERATOR(op, char, bool) \
373 DEFINE_OPERATOR(op, bool, bool) \
374 friend auto operator op (const any&, const char * const) -> bool; \
375 friend auto operator op (const char * const, const any&) -> bool;
377 DECLARE_COMPARISON_OPERATOR(==)
378 DECLARE_COMPARISON_OPERATOR(!=)
379 DECLARE_COMPARISON_OPERATOR(<)
380 DECLARE_COMPARISON_OPERATOR(<=)
381 DECLARE_COMPARISON_OPERATOR(>)
382 DECLARE_COMPARISON_OPERATOR(>=)
384 friend auto operator+(const any&, const any&) -> any;
386 DEFINE_OPERATOR(+, int, int)
387 DEFINE_OPERATOR(+, double, double)
388 DEFINE_OPERATOR(+, char, char)
389 friend auto operator+(const any& lhs, const char * const rhs) -> std::string;
390 friend auto operator+(const char * const lhs, const any& rhs) -> std::string;
392 friend auto operator-(const any&, const any&) -> any;
394 DEFINE_OPERATOR(-, int, int)
395 DEFINE_OPERATOR(-, double, double)
396 DEFINE_OPERATOR(-, char, char)
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(%, char, char)
415 friend auto operator-(const any&) -> any; // unary negate
417}; // class any
419namespace Private {
420 template <typename T, typename U=void>
421 struct TagHelper {};
423 template <size_t N>
424 struct TagHelper<any::map<N>> {
425 static constexpr any::Tag tag = any::Tag::Map;
426 };
428 template <size_t N>
429 struct TagHelper<any::data<N>> {
430 static constexpr any::Tag tag = any::Tag::Data;
431 };
433 template <typename T>
434 struct TagHelper<T> {
435 static constexpr any::Tag tag = any::Tag::Pointer;
436 };
439// Pass-through
440template <typename T>
441constexpr auto cast(const T& a) -> const T& {
442 return a;
445template <typename T>
446inline auto cast(const any& a) ->
447 typename std::enable_if<std::is_arithmetic<T>::value ||
448 std::is_same<T,cstring>::value, T>::type {
449 return a;
452template <typename T>
453inline auto cast(const any& a) ->
454 typename std::enable_if<std::is_same<T, any::array>::value, const T&>::type {
455 return a;
458template <typename T, typename = typename std::enable_if<std::is_class<T>::value>::type>
459inline auto cast(const any& a) ->
460 typename std::enable_if<!std::is_same<T, any::array>::value, T&>::type {
461 return *static_cast<T*>(a.extractPointer(IF_DEBUG(Private::TagHelper<T>::tag)));
464template <typename T>
465inline auto cast(const any& a) ->
466 typename std::enable_if<std::is_same<void*, T>::value, T>::type {
467 return a.rawPointer();
470namespace map {
471 template <size_t N, typename T>
472 inline auto get(const T& a) ->
473 typename std::enable_if<!std::is_same<any, T>::value, const any&>::type {
474 return a[N].second;
475 }
477 template <size_t N>
478 inline auto get(const any& a) -> const any& {
479 return cast<any::map<N+1>>(a)[N].second;
480 }
482 template <size_t N>
483 inline auto get(const Private::Symbol key, const any::map<N>& a) -> const any& {
484 static_assert(N > 0, "map size must be greater than zero");
485 typename std::remove_reference<decltype(a)>::type::size_type i = 0;
486 do {
487 if (a[i].first == key) {
488 return a[i].second;
489 }
490 } while (a[++i].first != nullptr);
491 assert(false && "map key not found");
492 static const any invalid_key(nullptr);
493 return invalid_key;
494 }
496 inline auto get(const Private::Symbol key, const any& a) -> const any& {
497 return get(key, cast<any::map<unknown_size>>(a));
498 }
501namespace data {
502 template <size_t N, typename T>
503 inline auto get(const T& a) ->
504 typename std::enable_if<!std::is_same<any, T>::value, const any&>::type {
505 return a[N];
506 }
508 template <size_t N>
509 inline auto get(const any& a) -> const any& {
510 return cast<any::data<N+1>>(a)[N];
511 }
513 template <typename T>
514 inline auto ctor(const T& a) -> int {
515 return get<0>(a);
516 }
519} // namespace PureScript
521#undef WITH_ALLOCATOR
522#undef IS_POINTER_TYPE
523#undef DEFINE_OPERATOR
524#undef DECLARE_COMPARISON_OPERATOR
526#endif // PureScript_HH