git.iko.soy/rowtype-yoga/purescript-native
on aded024 help sign in
464 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#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;
49struct symbol_generator_anchor {};
50using symbol_t = const symbol_generator_anchor *;
52template <typename T>
53struct symbol_generator {
54 constexpr static symbol_generator_anchor anchor = symbol_generator_anchor{};
55};
56template <typename T>
57constexpr symbol_generator_anchor symbol_generator<T>::anchor;
59#define SYMBOL(S) &symbol_generator<symbol::S>::anchor
61// Workaround for missing C++11 version in gcc
62class runtime_error : public std::runtime_error {
63public:
64 runtime_error(const char message[]) : std::runtime_error(std::string(message)) {}
65};
67constexpr bool undefined = false;
68constexpr size_t constructor = 0;
70// Not a real limit, just used for simpler accessors
71static constexpr size_t unknown_size = 64;
73// A variant data class designed to provide some features of dynamic typing.
74//
75class any {
77 public:
78 enum class tag_t {
79 Thunk = 0x10,
80 Integer,
81 Double,
82 Character,
83 Boolean,
84 StringLiteral,
85 Function,
86 EffFunction,
87 RawPointer,
88 String,
89 Map,
90 Data,
91 Array,
92 Closure,
93 EffClosure,
94 Pointer
95 };
97 private:
98 mutable tag_t tag;
100 public:
101 struct as_thunk {
102 };
103 static constexpr as_thunk unthunk = as_thunk{};
105 using map_pair = std::pair<const symbol_t, const any>;
107 template <size_t N>
108 using map = std::array<const map_pair, N>;
110 template <size_t N>
111 using data = std::array<const any, N>;
113 using array = std::deque<any WITH_ALLOCATOR(any)>;
114 using fn = auto (*)(const any&) -> any;
115 using eff_fn = auto (*)() -> any;
116 using thunk = auto (*)(const as_thunk) -> const any&;
118 private:
119 class closure {
120 public:
121 virtual auto operator()(const any&) const -> any = 0;
122 virtual ~closure() {}
123 };
125 template <typename T>
126 class _closure : public closure {
127 const T lambda;
128 public:
129 _closure(const T& l) noexcept : lambda(l) {}
130 auto operator()(const any& arg) const -> any override {
131 return lambda(arg);
132 }
133 };
135 class eff_closure {
136 public:
137 virtual auto operator()() const -> any = 0;
138 virtual ~eff_closure() {}
139 };
141 template <typename T>
142 class _eff_closure : public eff_closure {
143 const T lambda;
144 public:
145 _eff_closure(const T& l) noexcept : lambda(l) {}
146 auto operator()() const -> any override {
147 return lambda();
148 }
149 };
151 private:
152 union {
153 mutable thunk t;
154 mutable int i;
155 mutable double d;
156 mutable char c;
157 mutable bool b;
158 mutable cstring r;
159 mutable fn f;
160 mutable eff_fn e;
161 mutable void * u;
162 mutable managed<std::string> s;
163 mutable managed<array> a;
164 mutable managed<closure> l;
165 mutable managed<eff_closure> k;
166 mutable managed<void> p;
167 };
169 public:
171 any(const int val) noexcept : tag(tag_t::Integer), i(val) {}
172 any(const long val) noexcept : tag(tag_t::Integer), i(static_cast<decltype(i)>(val)) {
173 assert(val >= std::numeric_limits<decltype(i)>::min() &&
174 val <= std::numeric_limits<decltype(i)>::max());
175 }
177 any(const double val) noexcept : tag(tag_t::Double), d(val) {}
178 any(const char val) noexcept : tag(tag_t::Character), c(val) {}
180 template <typename T, typename = typename std::enable_if<std::is_same<bool,T>::value>::type>
181 any(const T val) noexcept : tag(tag_t::Boolean), b(val) {}
183 template <size_t N>
184 any(const char (&val)[N]) noexcept : tag(tag_t::StringLiteral), r(val) {}
185 any(char * val) : tag(tag_t::String), s(make_managed<std::string>(val)) {}
187 any(const std::string& val) : tag(tag_t::String), s(make_managed<std::string>(val)) {}
188 any(std::string&& val) noexcept : tag(tag_t::String), s(make_managed<std::string>(std::move(val))) {}
190 any(const managed<std::string>& val) noexcept : tag(tag_t::String), s(val) {}
191 any(managed<std::string>&& val) noexcept : tag(tag_t::String), s(std::move(val)) {}
193 template <size_t N>
194 any(map<N>&& val) noexcept : tag(tag_t::Map), p(make_managed<map<N>>(std::move(val))) {}
196 template <size_t N>
197 any(data<N>&& val) noexcept : tag(tag_t::Data), p(make_managed<data<N>>(std::move(val))) {}
199 any(const array& val) : tag(tag_t::Array), a(make_managed<array>(val)) {}
200 any(array&& val) noexcept : tag(tag_t::Array), a(make_managed<array>(std::move(val))) {}
202 template <typename T>
203 any(const T& val, typename std::enable_if<std::is_convertible<T,fn>::value>::type* = 0) noexcept
204 : tag(tag_t::Function), f(val) {}
206 template <typename T, typename = typename std::enable_if<!std::is_same<any,T>::value &&
207 !std::is_convertible<T,fn>::value>::type>
208 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any(const any&)>,T>::value>::type* = 0)
209 : tag(tag_t::Closure), l(make_managed<_closure<T>>(val)) {}
211 template <typename T>
212 any(const T& val, typename std::enable_if<std::is_convertible<T,eff_fn>::value>::type* = 0) noexcept
213 : tag(tag_t::EffFunction), e(val) {}
215 template <typename T,
216 typename = typename std::enable_if<!std::is_same<any,T>::value &&
217 !std::is_convertible<T,eff_fn>::value>::type>
218 any(const T& val, typename std::enable_if<std::is_assignable<std::function<any()>,T>::value>::type* = 0)
219 : tag(tag_t::EffClosure), k(make_managed<_eff_closure<T>>(val)) {}
221 template <typename T>
222 any(const T& val, typename std::enable_if<std::is_convertible<T,thunk>::value>::type* = 0) noexcept
223 : tag(tag_t::Thunk), t(val) {}
225 template <typename T,
226 typename = typename std::enable_if<std::is_class<typename std::remove_pointer<T>::type>::value>::type>
227 any(const T& val, typename std::enable_if<IS_POINTER_TYPE(T)::value>::type* = 0) noexcept
228 : tag(tag_t::Pointer), p(val) {}
230 template <typename T>
231 any(T&& val, typename std::enable_if<std::is_assignable<managed<void>,T>::value>::type* = 0) noexcept
232 : tag(tag_t::Pointer), p(std::move(val)) {}
234 // Explicit void* to raw pointer value
235 template <typename T>
236 any(const T& val, typename std::enable_if<std::is_same<T,void*>::value>::type* = 0) noexcept
237 : tag(tag_t::RawPointer), u(val) {}
239 any(nullptr_t) noexcept : tag(tag_t::RawPointer), u(nullptr) {}
242#if !defined(USE_GC)
243 private:
244 template <typename T,
245 typename U,
246 typename = typename std::enable_if<!std::is_reference<T>::value>::type>
247 static constexpr auto move_if_rvalue(U&& u) noexcept -> U&& {
248 return static_cast<U&&>(u);
249 }
251 template <typename T,
252 typename U,
253 typename = typename std::enable_if<std::is_reference<T>::value>::type>
254 static constexpr auto move_if_rvalue(U& u) noexcept -> U& {
255 return static_cast<U&>(u);
256 }
258 template <typename T>
259 auto assign(T&& other) const noexcept -> void {
260 switch (other.tag) {
261 case tag_t::Thunk: t = other.t; break;
262 case tag_t::Integer: i = other.i; break;
263 case tag_t::Double: d = other.d; break;
264 case tag_t::Character: c = other.c; break;
265 case tag_t::Boolean: b = other.b; break;
266 case tag_t::StringLiteral: r = other.r; break;
267 case tag_t::Function: f = other.f; break;
268 case tag_t::EffFunction: e = other.e; break;
269 case tag_t::RawPointer: u = other.u; break;
271 case tag_t::String: new (&s) managed<std::string>(move_if_rvalue<T>(other.s)); break;
272 case tag_t::Array: new (&a) managed<array>(move_if_rvalue<T>(other.a)); break;
273 case tag_t::Closure: new (&l) managed<closure>(move_if_rvalue<T>(other.l)); break;
274 case tag_t::EffClosure: new (&k) managed<eff_closure>(move_if_rvalue<T>(other.k)); break;
275 case tag_t::Map:
276 case tag_t::Data:
277 case tag_t::Pointer: new (&p) managed<void>(move_if_rvalue<T>(other.p)); break;
279 default: assert(false && "Bad 'any' tag"); break;
280 }
281 }
283 auto destruct() -> void {
284 switch (tag) {
285 case tag_t::String: s.~managed<std::string>(); break;
286 case tag_t::Array: a.~managed<array>(); break;
287 case tag_t::Closure: l.~managed<closure>(); break;
288 case tag_t::EffClosure: k.~managed<eff_closure>(); break;
289 case tag_t::Map:
290 case tag_t::Data:
291 case tag_t::Pointer: p.~managed<void>(); break;
293 default: break;
294 }
295 }
297 public:
298 any(const any& other) noexcept : tag(other.tag) {
299 assign(other);
300 }
302 any(any&& other) noexcept : tag(other.tag) {
303 assign(std::move(other));
304 }
306 auto operator=(const any& rhs) noexcept -> any& {
307 destruct();
308 tag = rhs.tag;
309 assign(rhs);
310 return *this;
311 }
313 auto operator=(any&& rhs) noexcept -> any& {
314 destruct();
315 tag = rhs.tag;
316 assign(std::move(rhs));
317 return *this;
318 }
320 ~any() {
321 destruct();
322 }
323#endif // !defined(USE_GC)
325 public:
326 any() = delete;
328 auto operator()(const any&) const -> any;
329 auto operator()(const as_thunk) const -> const any&;
330 auto operator()() const -> any;
332 operator int() const;
333 operator double() const;
334 operator bool() const;
335 operator char() const;
336 operator cstring() const;
337 operator const array&() const;
339 auto operator[](const symbol_t) const -> const any&;
340 auto operator[](const size_t) const -> const any&;
341 auto operator[](const any&) const -> const any&;
343 auto contains(const symbol_t) const -> bool;
345 auto extractPointer(IF_DEBUG(const tag_t)) const -> void*;
347 auto rawPointer() const -> void* {
348 const any& variant = unthunkVariant(*this);
349 assert(tag == tag_t::RawPointer);
350 return variant.u;
351 }
353 static auto unthunkVariant(const any&) -> const any&;
355 #define DEFINE_OPERATOR(op, ty, rty) \
356 inline friend auto operator op (const any& lhs, ty rhs) -> rty { \
357 return (ty)lhs op rhs; \
358 } \
359 inline friend auto operator op (ty lhs, const any& rhs) -> rty { \
360 return lhs op (ty)rhs; \
361 } \
363 #define DECLARE_COMPARISON_OPERATOR(op) \
364 friend auto operator op (const any&, const any&) -> bool; \
365 DEFINE_OPERATOR(op, int, bool) \
366 DEFINE_OPERATOR(op, double, bool) \
367 DEFINE_OPERATOR(op, char, bool) \
368 DEFINE_OPERATOR(op, bool, bool) \
369 friend auto operator op (const any&, const char * const) -> bool; \
370 friend auto operator op (const char * const, const any&) -> bool;
372 DECLARE_COMPARISON_OPERATOR(==)
373 DECLARE_COMPARISON_OPERATOR(!=)
374 DECLARE_COMPARISON_OPERATOR(<)
375 DECLARE_COMPARISON_OPERATOR(<=)
376 DECLARE_COMPARISON_OPERATOR(>)
377 DECLARE_COMPARISON_OPERATOR(>=)
379 friend auto operator+(const any&, const any&) -> any;
381 DEFINE_OPERATOR(+, int, int)
382 DEFINE_OPERATOR(+, double, double)
383 DEFINE_OPERATOR(+, char, char)
384 friend auto operator+(const any& lhs, const char * const rhs) -> std::string;
385 friend auto operator+(const char * const lhs, const any& rhs) -> std::string;
387 friend auto operator-(const any&, const any&) -> any;
389 DEFINE_OPERATOR(-, int, int)
390 DEFINE_OPERATOR(-, double, double)
391 DEFINE_OPERATOR(-, char, char)
393 friend auto operator*(const any&, const any&) -> any;
395 DEFINE_OPERATOR(*, int, int)
396 DEFINE_OPERATOR(*, double, double)
397 DEFINE_OPERATOR(*, char, char)
399 friend auto operator/(const any&, const any&) -> any;
401 DEFINE_OPERATOR(/, int, int)
402 DEFINE_OPERATOR(/, double, double)
403 DEFINE_OPERATOR(/, char, char)
405 friend auto operator%(const any&, const any&) -> any;
407 DEFINE_OPERATOR(%, int, int)
408 DEFINE_OPERATOR(%, char, char)
410 friend auto operator-(const any&) -> any; // unary negate
412}; // class any
414template <typename T, typename U=void>
415struct tag_helper {};
417template <size_t N>
418struct tag_helper<any::map<N>> {
419 static constexpr any::tag_t tag = any::tag_t::Map;
420};
422template <size_t N>
423struct tag_helper<any::data<N>> {
424 static constexpr any::tag_t tag = any::tag_t::Data;
425};
427template <typename T>
428struct tag_helper<T> {
429 static constexpr any::tag_t tag = any::tag_t::Pointer;
430};
432template <typename T>
433inline auto cast(const any& a) ->
434 typename std::enable_if<std::is_arithmetic<T>::value ||
435 std::is_same<T,cstring>::value, T>::type {
436 return a;
439template <typename T>
440inline auto cast(const any& a) ->
441 typename std::enable_if<std::is_same<T, any::array>::value, const T&>::type {
442 return a;
445template <typename T, typename = typename std::enable_if<std::is_class<T>::value>::type>
446inline auto cast(const any& a) ->
447 typename std::enable_if<!std::is_same<T, any::array>::value, T&>::type {
448 return *static_cast<T*>(a.extractPointer(IF_DEBUG(tag_helper<T>::tag)));
451template <typename T>
452inline auto cast(const any& a) ->
453 typename std::enable_if<std::is_same<void*, T>::value, T>::type {
454 return a.rawPointer();
457} // namespace PureScript
459#undef WITH_ALLOCATOR
460#undef IS_POINTER_TYPE
461#undef DEFINE_OPERATOR
462#undef DECLARE_COMPARISON_OPERATOR
464#endif // PureScript_HH