git.iko.soy/rowtype-yoga/purescript-native
on fc1751c help sign in
456 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 <climits>
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};
63const bool undefined = false;
64const size_t constructor = 0;
66// A variant data class designed to provide some features of dynamic typing.
67//
68class any {
70 public:
71 enum class Type {
72 Thunk = 0x10,
73 Integer,
74 Double,
75 Character,
76 Boolean,
77 StringLiteral,
78 Function,
79 EffFunction,
80 RawPointer,
81 String,
82 Map,
83 Data,
84 Array,
85 Closure,
86 EffClosure,
87 Pointer
88 };
90 private:
91 mutable Type type;
93 public:
94 struct as_thunk {
95 };
96 static constexpr as_thunk unthunk = as_thunk{};
98 using map_pair = std::pair<const symbol_t, const any>;
100 template <size_t N>
101 using map = std::array<const map_pair, N>;
103 template <size_t N>
104 using data = std::array<const any, N>;
106 using array = std::deque<any WITH_ALLOCATOR(any)>;
107 using fn = auto (*)(const any&) -> any;
108 using eff_fn = auto (*)() -> any;
109 using thunk = auto (*)(const as_thunk) -> const any&;
111 private:
112 class closure {
113 public:
114 virtual auto operator()(const any&) const -> any = 0;
115 virtual ~closure() {}
116 };
118 template <typename T>
119 class _closure : public closure {
120 const T lambda;
121 public:
122 _closure(const T& l) noexcept : lambda(l) {}
123 auto operator()(const any& arg) const -> any override {
124 return lambda(arg);
125 }
126 };
128 class eff_closure {
129 public:
130 virtual auto operator()() const -> any = 0;
131 virtual ~eff_closure() {}
132 };
134 template <typename T>
135 class _eff_closure : public eff_closure {
136 const T lambda;
137 public:
138 _eff_closure(const T& l) noexcept : lambda(l) {}
139 auto operator()() const -> any override {
140 return lambda();
141 }
142 };
144 private:
145 union {
146 mutable thunk t;
147 mutable int i;
148 mutable double d;
149 mutable char c;
150 mutable bool b;
151 mutable cstring r;
152 mutable fn f;
153 mutable eff_fn e;
154 mutable void * u;
155 mutable managed<std::string> s;
156 mutable managed<array> a;
157 mutable managed<closure> l;
158 mutable managed<eff_closure> k;
159 mutable managed<void> p;
160 };
162 public:
164 any(const int val) noexcept : type(Type::Integer), i(val) {}
165 any(const long int_value) noexcept : type(Type::Integer), i(int_value) {
166 assert(int_value >= INT_MIN && int_value <= INT_MAX);
167 }
169 any(const double val) noexcept : type(Type::Double), d(val) {}
170 any(const char val) noexcept : type(Type::Character), c(val) {}
172 template <typename T, typename = typename std::enable_if<std::is_same<bool,T>::value>::type>
173 any(const T val) noexcept : type(Type::Boolean), b(val) {}
175 template <size_t N>
176 any(const char (&val)[N]) noexcept : type(Type::StringLiteral), r(val) {}
177 any(char * val) : type(Type::String), s(make_managed<std::string>(val)) {}
179 any(const std::string& val) : type(Type::String), s(make_managed<std::string>(val)) {}
180 any(std::string&& val) noexcept : type(Type::String), s(make_managed<std::string>(std::move(val))) {}
182 any(const managed<std::string>& val) noexcept : type(Type::String), s(val) {}
183 any(managed<std::string>&& val) noexcept : type(Type::String), s(std::move(val)) {}
185 template <size_t N>
186 any(map<N>&& val) noexcept : type(Type::Map), p(make_managed<map<N>>(std::move(val))) {}
188 template <size_t N>
189 any(data<N>&& val) noexcept : type(Type::Data), p(make_managed<data<N>>(std::move(val))) {}
191 any(const array& val) : type(Type::Array), a(make_managed<array>(val)) {}
192 any(array&& val) noexcept : type(Type::Array), a(make_managed<array>(std::move(val))) {}
194 template <typename T>
195 any(const T& val, typename std::enable_if<std::is_convertible<T,fn>::value>::type* = 0) noexcept
196 : type(Type::Function), f(val) {}
198 template <typename T, typename = typename std::enable_if<!std::is_same<any,T>::value &&
199 !std::is_convertible<T,fn>::value>::type>
200 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any(const any&)>,T>::value>::type* = 0)
201 : type(Type::Closure), l(make_managed<_closure<T>>(val)) {}
203 template <typename T>
204 any(const T& val, typename std::enable_if<std::is_convertible<T,eff_fn>::value>::type* = 0) noexcept
205 : type(Type::EffFunction), e(val) {}
207 template <typename T,
208 typename = typename std::enable_if<!std::is_same<any,T>::value &&
209 !std::is_convertible<T,eff_fn>::value>::type>
210 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any()>,T>::value>::type* = 0)
211 : type(Type::EffClosure), k(make_managed<_eff_closure<T>>(val)) {}
213 template <typename T>
214 any(const T& val, typename std::enable_if<std::is_convertible<T,thunk>::value>::type* = 0) noexcept
215 : type(Type::Thunk), t(val) {}
217 template <typename T,
218 typename = typename std::enable_if<std::is_class<typename std::remove_pointer<T>::type>::value>::type>
219 any(const T& val, typename std::enable_if<IS_POINTER_TYPE(T)::value>::type* = 0) noexcept
220 : type(Type::Pointer), p(val) {}
222 template <typename T>
223 any(T&& val, typename std::enable_if<std::is_assignable<managed<void>,T>::value>::type* = 0) noexcept
224 : type(Type::Pointer), p(std::move(val)) {}
226 // Explicit void* to raw pointer value
227 template <typename T>
228 any(const T& val, typename std::enable_if<std::is_same<T,void*>::value>::type* = 0) noexcept
229 : type(Type::RawPointer), u(val) {}
231 any(std::nullptr_t) noexcept : type(Type::RawPointer), u(nullptr) {}
234#if !defined(USE_GC)
235 private:
236 template <typename T,
237 typename U,
238 typename = typename std::enable_if<!std::is_reference<T>::value>::type>
239 static constexpr auto move_if_rvalue(U&& u) noexcept -> U&& {
240 return static_cast<U&&>(u);
241 }
243 template <typename T,
244 typename U,
245 typename = typename std::enable_if<std::is_reference<T>::value>::type>
246 static constexpr auto move_if_rvalue(U& u) noexcept -> U& {
247 return static_cast<U&>(u);
248 }
250 template <typename T>
251 auto assign(T&& other) const noexcept -> void {
252 switch (other.type) {
253 case Type::Thunk: t = other.t; break;
254 case Type::Integer: i = other.i; break;
255 case Type::Double: d = other.d; break;
256 case Type::Character: c = other.c; break;
257 case Type::Boolean: b = other.b; break;
258 case Type::StringLiteral: r = other.r; break;
259 case Type::Function: f = other.f; break;
260 case Type::EffFunction: e = other.e; break;
261 case Type::RawPointer: u = other.u; break;
263 case Type::String: new (&s) managed<std::string>(move_if_rvalue<T>(other.s)); break;
264 case Type::Array: new (&a) managed<array>(move_if_rvalue<T>(other.a)); break;
265 case Type::Closure: new (&l) managed<closure>(move_if_rvalue<T>(other.l)); break;
266 case Type::EffClosure: new (&k) managed<eff_closure>(move_if_rvalue<T>(other.k)); break;
267 case Type::Map:
268 case Type::Data:
269 case Type::Pointer: new (&p) managed<void>(move_if_rvalue<T>(other.p)); break;
271 default: assert(false && "Bad 'any' type"); break;
272 }
273 }
275 auto destruct() -> void {
276 switch (type) {
277 case Type::String: s.~managed<std::string>(); break;
278 case Type::Array: a.~managed<array>(); break;
279 case Type::Closure: l.~managed<closure>(); break;
280 case Type::EffClosure: k.~managed<eff_closure>(); break;
281 case Type::Map:
282 case Type::Data:
283 case Type::Pointer: p.~managed<void>(); break;
285 default: break;
286 }
287 }
289 public:
290 any(const any& other) noexcept : type(other.type) {
291 assign(other);
292 }
294 any(any&& other) noexcept : type(other.type) {
295 assign(std::move(other));
296 }
298 auto operator=(const any& rhs) noexcept -> any& {
299 destruct();
300 type = rhs.type;
301 assign(rhs);
302 return *this;
303 }
305 auto operator=(any&& rhs) noexcept -> any& {
306 destruct();
307 type = rhs.type;
308 assign(std::move(rhs));
309 return *this;
310 }
312 ~any() {
313 destruct();
314 }
315#endif // !defined(USE_GC)
317 public:
318 any() = delete;
320 auto operator()(const any&) const -> any;
321 auto operator()(const as_thunk) const -> const any&;
322 auto operator()() const -> any;
324 operator int() const;
325 operator double() const;
326 operator bool() const;
327 operator char() const;
328 operator cstring() const;
329 operator const array&() const;
331 auto operator[](const symbol_t) const -> const any&;
332 auto operator[](const size_t) const -> const any&;
333 auto operator[](const any&) const -> const any&;
335 auto contains(const symbol_t) const -> bool;
337 auto extractPointer(IF_DEBUG(const Type)) const -> void*;
339 auto rawPointer() const -> void* {
340 const any& variant = unthunkVariant(*this);
341 assert(type == Type::RawPointer);
342 return variant.u;
343 }
345 static auto unthunkVariant(const any&) -> const any&;
347 #define DEFINE_OPERATOR(op, ty, rty) \
348 inline friend auto operator op (const any& lhs, ty rhs) -> rty { \
349 return (ty)lhs op rhs; \
350 } \
351 inline friend auto operator op (ty lhs, const any& rhs) -> rty { \
352 return lhs op (ty)rhs; \
353 } \
355 #define DECLARE_COMPARISON_OPERATOR(op) \
356 friend auto operator op (const any&, const any&) -> bool; \
357 DEFINE_OPERATOR(op, int, bool) \
358 DEFINE_OPERATOR(op, double, bool) \
359 DEFINE_OPERATOR(op, char, bool) \
360 DEFINE_OPERATOR(op, bool, bool) \
361 friend auto operator op (const any&, const char * const) -> bool; \
362 friend auto operator op (const char * const, const any&) -> bool;
364 DECLARE_COMPARISON_OPERATOR(==)
365 DECLARE_COMPARISON_OPERATOR(!=)
366 DECLARE_COMPARISON_OPERATOR(<)
367 DECLARE_COMPARISON_OPERATOR(<=)
368 DECLARE_COMPARISON_OPERATOR(>)
369 DECLARE_COMPARISON_OPERATOR(>=)
371 friend auto operator+(const any&, const any&) -> any;
373 DEFINE_OPERATOR(+, int, int)
374 DEFINE_OPERATOR(+, double, double)
375 DEFINE_OPERATOR(+, char, char)
376 friend auto operator+(const any& lhs, const char * const rhs) -> std::string;
377 friend auto operator+(const char * const lhs, const any& rhs) -> std::string;
379 friend auto operator-(const any&, const any&) -> any;
381 DEFINE_OPERATOR(-, int, int)
382 DEFINE_OPERATOR(-, double, double)
383 DEFINE_OPERATOR(-, char, char)
385 friend auto operator*(const any&, const any&) -> any;
387 DEFINE_OPERATOR(*, int, int)
388 DEFINE_OPERATOR(*, double, double)
389 DEFINE_OPERATOR(*, char, char)
391 friend auto operator/(const any&, const any&) -> any;
393 DEFINE_OPERATOR(/, int, int)
394 DEFINE_OPERATOR(/, double, double)
395 DEFINE_OPERATOR(/, char, char)
397 friend auto operator%(const any&, const any&) -> any;
399 DEFINE_OPERATOR(%, int, int)
400 DEFINE_OPERATOR(%, char, char)
402 friend auto operator-(const any&) -> any; // unary negate
404}; // class any
406template <typename T, typename U=void>
407struct tag_helper {};
409template <size_t N>
410struct tag_helper<any::map<N>> {
411 static constexpr any::Type tag = any::Type::Map;
412};
414template <size_t N>
415struct tag_helper<any::data<N>> {
416 static constexpr any::Type tag = any::Type::Data;
417};
419template <typename T>
420struct tag_helper<T> {
421 static constexpr any::Type tag = any::Type::Pointer;
422};
424template <typename T>
425inline auto cast(const any& a) ->
426 typename std::enable_if<std::is_arithmetic<T>::value ||
427 std::is_same<T,cstring>::value, T>::type {
428 return a;
431template <typename T>
432inline auto cast(const any& a) ->
433 typename std::enable_if<std::is_same<T, any::array>::value, const T&>::type {
434 return a;
437template <typename T, typename = typename std::enable_if<std::is_class<T>::value>::type>
438inline auto cast(const any& a) ->
439 typename std::enable_if<!std::is_same<T, any::array>::value, T&>::type {
440 return *static_cast<T*>(a.extractPointer(IF_DEBUG(tag_helper<T>::tag)));
443template <typename T>
444inline auto cast(const any& a) ->
445 typename std::enable_if<std::is_same<void*, T>::value, T>::type {
446 return a.rawPointer();
449} // namespace PureScript
451#undef WITH_ALLOCATOR
452#undef IS_POINTER_TYPE
453#undef DEFINE_OPERATOR
454#undef DECLARE_COMPARISON_OPERATOR
456#endif // PureScript_HH