git.iko.soy/rowtype-yoga/purescript-native
on bb06cee help sign in
460 lines 14 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// Standard includes
20#if defined(DEBUG)
21 #include <cassert>
22 #include <limits>
23 #define IF_DEBUG(x) x
24#else
25 #define NDEBUG
26 #undef assert
27 #define assert(x)
28 #define IF_DEBUG(_)
29#endif
31#include <functional>
32#include <string>
33#include <array>
34#include <deque>
35#include <utility>
36#include <stdexcept>
37#include <iso646.h> // mostly for MS Visual Studio compiler
38#include "purescript_memory.hh"
40namespace PureScript {
42using cstring = const char *;
43using std::nullptr_t;
45struct symbol_generator_anchor {};
46using symbol_t = const symbol_generator_anchor *;
48template <typename T>
49struct symbol_generator {
50 constexpr static symbol_generator_anchor anchor = symbol_generator_anchor{};
51};
52template <typename T>
53constexpr symbol_generator_anchor symbol_generator<T>::anchor;
55#define SYMBOL(S) &symbol_generator<symbol::S>::anchor
57// Workaround for missing C++11 version in gcc
58class runtime_error : public std::runtime_error {
59public:
60 runtime_error(const char message[]) : std::runtime_error(std::string(message)) {}
61};
63constexpr bool undefined = false;
64constexpr size_t constructor = 0;
66// Not a real limit, just used for simpler accessors
67static constexpr size_t unknown_size = 64;
69// A variant data class designed to provide some features of dynamic typing.
70//
71class any {
73 public:
74 enum class tag_t {
75 Thunk = 0x10,
76 Integer,
77 Double,
78 Character,
79 Boolean,
80 StringLiteral,
81 Function,
82 EffFunction,
83 RawPointer,
84 String,
85 Map,
86 Data,
87 Array,
88 Closure,
89 EffClosure,
90 Pointer
91 };
93 private:
94 mutable tag_t tag;
96 public:
97 struct as_thunk {
98 };
99 static constexpr as_thunk unthunk = as_thunk{};
101 using map_pair = std::pair<const symbol_t, const any>;
103 template <size_t N>
104 using map = std::array<const map_pair, N>;
106 template <size_t N>
107 using data = std::array<const any, N>;
109 using array = std::deque<any WITH_ALLOCATOR(any)>;
110 using fn = auto (*)(const any&) -> any;
111 using eff_fn = auto (*)() -> any;
112 using thunk = auto (*)(const as_thunk) -> const any&;
114 private:
115 class closure {
116 public:
117 virtual auto operator()(const any&) const -> any = 0;
118 virtual ~closure() {}
119 };
121 template <typename T>
122 class _closure : public closure {
123 const T lambda;
124 public:
125 _closure(const T& l) noexcept : lambda(l) {}
126 auto operator()(const any& arg) const -> any override {
127 return lambda(arg);
128 }
129 };
131 class eff_closure {
132 public:
133 virtual auto operator()() const -> any = 0;
134 virtual ~eff_closure() {}
135 };
137 template <typename T>
138 class _eff_closure : public eff_closure {
139 const T lambda;
140 public:
141 _eff_closure(const T& l) noexcept : lambda(l) {}
142 auto operator()() const -> any override {
143 return lambda();
144 }
145 };
147 private:
148 union {
149 mutable thunk t;
150 mutable int i;
151 mutable double d;
152 mutable char c;
153 mutable bool b;
154 mutable cstring r;
155 mutable fn f;
156 mutable eff_fn e;
157 mutable void * u;
158 mutable managed<std::string> s;
159 mutable managed<array> a;
160 mutable managed<closure> l;
161 mutable managed<eff_closure> k;
162 mutable managed<void> p;
163 };
165 public:
167 any(const int val) noexcept : tag(tag_t::Integer), i(val) {}
168 any(const long val) noexcept : tag(tag_t::Integer), i(static_cast<decltype(i)>(val)) {
169 assert(val >= std::numeric_limits<decltype(i)>::min() &&
170 val <= std::numeric_limits<decltype(i)>::max());
171 }
173 any(const double val) noexcept : tag(tag_t::Double), d(val) {}
174 any(const char val) noexcept : tag(tag_t::Character), c(val) {}
176 template <typename T, typename = typename std::enable_if<std::is_same<bool,T>::value>::type>
177 any(const T val) noexcept : tag(tag_t::Boolean), b(val) {}
179 template <size_t N>
180 any(const char (&val)[N]) noexcept : tag(tag_t::StringLiteral), r(val) {}
181 any(char * val) : tag(tag_t::String), s(make_managed<std::string>(val)) {}
183 any(const std::string& val) : tag(tag_t::String), s(make_managed<std::string>(val)) {}
184 any(std::string&& val) noexcept : tag(tag_t::String), s(make_managed<std::string>(std::move(val))) {}
186 any(const managed<std::string>& val) noexcept : tag(tag_t::String), s(val) {}
187 any(managed<std::string>&& val) noexcept : tag(tag_t::String), s(std::move(val)) {}
189 template <size_t N>
190 any(map<N>&& val) noexcept : tag(tag_t::Map), p(make_managed<map<N>>(std::move(val))) {}
192 template <size_t N>
193 any(data<N>&& val) noexcept : tag(tag_t::Data), p(make_managed<data<N>>(std::move(val))) {}
195 any(const array& val) : tag(tag_t::Array), a(make_managed<array>(val)) {}
196 any(array&& val) noexcept : tag(tag_t::Array), a(make_managed<array>(std::move(val))) {}
198 template <typename T>
199 any(const T& val, typename std::enable_if<std::is_convertible<T,fn>::value>::type* = 0) noexcept
200 : tag(tag_t::Function), f(val) {}
202 template <typename T, typename = typename std::enable_if<!std::is_same<any,T>::value &&
203 !std::is_convertible<T,fn>::value>::type>
204 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any(const any&)>,T>::value>::type* = 0)
205 : tag(tag_t::Closure), l(make_managed<_closure<T>>(val)) {}
207 template <typename T>
208 any(const T& val, typename std::enable_if<std::is_convertible<T,eff_fn>::value>::type* = 0) noexcept
209 : tag(tag_t::EffFunction), e(val) {}
211 template <typename T,
212 typename = typename std::enable_if<!std::is_same<any,T>::value &&
213 !std::is_convertible<T,eff_fn>::value>::type>
214 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any()>,T>::value>::type* = 0)
215 : tag(tag_t::EffClosure), k(make_managed<_eff_closure<T>>(val)) {}
217 template <typename T>
218 any(const T& val, typename std::enable_if<std::is_convertible<T,thunk>::value>::type* = 0) noexcept
219 : tag(tag_t::Thunk), t(val) {}
221 template <typename T,
222 typename = typename std::enable_if<std::is_class<typename std::remove_pointer<T>::type>::value>::type>
223 any(const T& val, typename std::enable_if<IS_POINTER_TYPE(T)::value>::type* = 0) noexcept
224 : tag(tag_t::Pointer), p(val) {}
226 template <typename T>
227 any(T&& val, typename std::enable_if<std::is_assignable<managed<void>,T>::value>::type* = 0) noexcept
228 : tag(tag_t::Pointer), p(std::move(val)) {}
230 // Explicit void* to raw pointer value
231 template <typename T>
232 any(const T& val, typename std::enable_if<std::is_same<T,void*>::value>::type* = 0) noexcept
233 : tag(tag_t::RawPointer), u(val) {}
235 any(nullptr_t) noexcept : tag(tag_t::RawPointer), u(nullptr) {}
238#if !defined(USE_GC)
239 private:
240 template <typename T,
241 typename U,
242 typename = typename std::enable_if<!std::is_reference<T>::value>::type>
243 static constexpr auto move_if_rvalue(U&& u) noexcept -> U&& {
244 return static_cast<U&&>(u);
245 }
247 template <typename T,
248 typename U,
249 typename = typename std::enable_if<std::is_reference<T>::value>::type>
250 static constexpr auto move_if_rvalue(U& u) noexcept -> U& {
251 return static_cast<U&>(u);
252 }
254 template <typename T>
255 auto assign(T&& other) const noexcept -> void {
256 switch (other.tag) {
257 case tag_t::Thunk: t = other.t; break;
258 case tag_t::Integer: i = other.i; break;
259 case tag_t::Double: d = other.d; break;
260 case tag_t::Character: c = other.c; break;
261 case tag_t::Boolean: b = other.b; break;
262 case tag_t::StringLiteral: r = other.r; break;
263 case tag_t::Function: f = other.f; break;
264 case tag_t::EffFunction: e = other.e; break;
265 case tag_t::RawPointer: u = other.u; break;
267 case tag_t::String: new (&s) managed<std::string>(move_if_rvalue<T>(other.s)); break;
268 case tag_t::Array: new (&a) managed<array>(move_if_rvalue<T>(other.a)); break;
269 case tag_t::Closure: new (&l) managed<closure>(move_if_rvalue<T>(other.l)); break;
270 case tag_t::EffClosure: new (&k) managed<eff_closure>(move_if_rvalue<T>(other.k)); break;
271 case tag_t::Map:
272 case tag_t::Data:
273 case tag_t::Pointer: new (&p) managed<void>(move_if_rvalue<T>(other.p)); break;
275 default: assert(false && "Bad 'any' tag"); break;
276 }
277 }
279 auto destruct() -> void {
280 switch (tag) {
281 case tag_t::String: s.~managed<std::string>(); break;
282 case tag_t::Array: a.~managed<array>(); break;
283 case tag_t::Closure: l.~managed<closure>(); break;
284 case tag_t::EffClosure: k.~managed<eff_closure>(); break;
285 case tag_t::Map:
286 case tag_t::Data:
287 case tag_t::Pointer: p.~managed<void>(); break;
289 default: break;
290 }
291 }
293 public:
294 any(const any& other) noexcept : tag(other.tag) {
295 assign(other);
296 }
298 any(any&& other) noexcept : tag(other.tag) {
299 assign(std::move(other));
300 }
302 auto operator=(const any& rhs) noexcept -> any& {
303 destruct();
304 tag = rhs.tag;
305 assign(rhs);
306 return *this;
307 }
309 auto operator=(any&& rhs) noexcept -> any& {
310 destruct();
311 tag = rhs.tag;
312 assign(std::move(rhs));
313 return *this;
314 }
316 ~any() {
317 destruct();
318 }
319#endif // !defined(USE_GC)
321 public:
322 any() = delete;
324 auto operator()(const any&) const -> any;
325 auto operator()(const as_thunk) const -> const any&;
326 auto operator()() const -> any;
328 operator int() const;
329 operator double() const;
330 operator bool() const;
331 operator char() const;
332 operator cstring() const;
333 operator const array&() const;
335 auto operator[](const symbol_t) const -> const any&;
336 auto operator[](const size_t) const -> const any&;
337 auto operator[](const any&) const -> const any&;
339 auto contains(const symbol_t) const -> bool;
341 auto extractPointer(IF_DEBUG(const tag_t)) const -> void*;
343 auto rawPointer() const -> void* {
344 const any& variant = unthunkVariant(*this);
345 assert(tag == tag_t::RawPointer);
346 return variant.u;
347 }
349 static auto unthunkVariant(const any&) -> const any&;
351 #define DEFINE_OPERATOR(op, ty, rty) \
352 inline friend auto operator op (const any& lhs, ty rhs) -> rty { \
353 return (ty)lhs op rhs; \
354 } \
355 inline friend auto operator op (ty lhs, const any& rhs) -> rty { \
356 return lhs op (ty)rhs; \
357 } \
359 #define DECLARE_COMPARISON_OPERATOR(op) \
360 friend auto operator op (const any&, const any&) -> bool; \
361 DEFINE_OPERATOR(op, int, bool) \
362 DEFINE_OPERATOR(op, double, bool) \
363 DEFINE_OPERATOR(op, char, bool) \
364 DEFINE_OPERATOR(op, bool, bool) \
365 friend auto operator op (const any&, const char * const) -> bool; \
366 friend auto operator op (const char * const, const any&) -> bool;
368 DECLARE_COMPARISON_OPERATOR(==)
369 DECLARE_COMPARISON_OPERATOR(!=)
370 DECLARE_COMPARISON_OPERATOR(<)
371 DECLARE_COMPARISON_OPERATOR(<=)
372 DECLARE_COMPARISON_OPERATOR(>)
373 DECLARE_COMPARISON_OPERATOR(>=)
375 friend auto operator+(const any&, const any&) -> any;
377 DEFINE_OPERATOR(+, int, int)
378 DEFINE_OPERATOR(+, double, double)
379 DEFINE_OPERATOR(+, char, char)
380 friend auto operator+(const any& lhs, const char * const rhs) -> std::string;
381 friend auto operator+(const char * const lhs, const any& rhs) -> std::string;
383 friend auto operator-(const any&, const any&) -> any;
385 DEFINE_OPERATOR(-, int, int)
386 DEFINE_OPERATOR(-, double, double)
387 DEFINE_OPERATOR(-, char, char)
389 friend auto operator*(const any&, const any&) -> any;
391 DEFINE_OPERATOR(*, int, int)
392 DEFINE_OPERATOR(*, double, double)
393 DEFINE_OPERATOR(*, char, char)
395 friend auto operator/(const any&, const any&) -> any;
397 DEFINE_OPERATOR(/, int, int)
398 DEFINE_OPERATOR(/, double, double)
399 DEFINE_OPERATOR(/, char, char)
401 friend auto operator%(const any&, const any&) -> any;
403 DEFINE_OPERATOR(%, int, int)
404 DEFINE_OPERATOR(%, char, char)
406 friend auto operator-(const any&) -> any; // unary negate
408}; // class any
410template <typename T, typename U=void>
411struct tag_helper {};
413template <size_t N>
414struct tag_helper<any::map<N>> {
415 static constexpr any::tag_t tag = any::tag_t::Map;
416};
418template <size_t N>
419struct tag_helper<any::data<N>> {
420 static constexpr any::tag_t tag = any::tag_t::Data;
421};
423template <typename T>
424struct tag_helper<T> {
425 static constexpr any::tag_t tag = any::tag_t::Pointer;
426};
428template <typename T>
429inline auto cast(const any& a) ->
430 typename std::enable_if<std::is_arithmetic<T>::value ||
431 std::is_same<T,cstring>::value, T>::type {
432 return a;
435template <typename T>
436inline auto cast(const any& a) ->
437 typename std::enable_if<std::is_same<T, any::array>::value, const T&>::type {
438 return a;
441template <typename T, typename = typename std::enable_if<std::is_class<T>::value>::type>
442inline auto cast(const any& a) ->
443 typename std::enable_if<!std::is_same<T, any::array>::value, T&>::type {
444 return *static_cast<T*>(a.extractPointer(IF_DEBUG(tag_helper<T>::tag)));
447template <typename T>
448inline auto cast(const any& a) ->
449 typename std::enable_if<std::is_same<void*, T>::value, T>::type {
450 return a.rawPointer();
453} // namespace PureScript
455#undef WITH_ALLOCATOR
456#undef IS_POINTER_TYPE
457#undef DEFINE_OPERATOR
458#undef DECLARE_COMPARISON_OPERATOR
460#endif // PureScript_HH