1 module vayne.value;
2 
3 
4 import std.algorithm;
5 import std.array;
6 import std.conv;
7 import std.datetime;
8 import std.format;
9 import std.meta;
10 import std.range;
11 import std.traits;
12 import std.typecons;
13 
14 
15 import vayne.hash;
16 
17 
18 private struct IgnoreAttribute {}
19 private struct NameAttribute { const(char)[] name; }
20 
21 
22 @property IgnoreAttribute ignore() {
23 	return IgnoreAttribute();
24 }
25 
26 
27 @property NameAttribute bind(const(char)[] name)  {
28 	return NameAttribute(name);
29 }
30 
31 
32 private enum NotCallableNames = ["__ctor", "__xdtor", "__postblit", "__xpostblit", "opAssign", "opIndexAssign", "opCast", "opDollar", "opIndex", "opSlice", "opApply", "opCmp"];
33 private enum NotBindableNames = ["opIndex"];
34 
35 
36 private template isCompatibleStorageClass(size_t Class) {
37 	enum isCompatibleStorageClass = Class == 0;
38 }
39 
40 
41 private template isCompatibleArgType(T) {
42 	static if (is(T == enum)) {
43 		enum isCompatibleArgType = isCompatibleArgType!(OriginalType!T);
44 	} else {
45 		enum isCompatibleArgType = !isSomeFunction!T && (isScalarType!T || isSomeString!(OriginalType!T) || isBoolean!T || is(Unqual!T == Value) || (isArray!T && is(Unqual!(typeof(T.init[0])) == Value)) || (isAssociativeArray!T && is(Unqual!(KeyType!T) == Value) && is(Unqual!(ValueType!T) == Value)));
46 	}
47 }
48 
49 
50 private template isCompatibleReturnType(F) {
51 	alias R = ReturnType!F;
52 	enum isCompatibleReturnType = !isSomeFunction!R && ((isScalarType!R || isSomeString!(OriginalType!R) || isBoolean!R || is(Unqual!R == Value) || isArray!R || isAssociativeArray!R || is(R == class) || is(R == interface) ||
53 		(is(R == struct) && (
54 			((functionAttributes!F & FunctionAttribute.ref_) != 0) ||
55 			is(typeof({ auto x = new Unqual!R; *x = Unqual!R.init; }))
56 		))
57 	));
58 }
59 
60 
61 private template isCompatibleFunction(T) {
62 	enum isCompatibleFunction = isCompatibleReturnType!T && allSatisfy!(isCompatibleArgType, ParameterTypeTuple!T) && allSatisfy!(isCompatibleStorageClass, ParameterStorageClassTuple!T) &&
63 		((functionAttributes!T & FunctionAttribute.inout_) == 0);
64 }
65 
66 
67 private static void functionWrapper(T)(void* ptr, void* self, Value[] args, ref Value ret) if (isSomeFunction!T) {
68 	alias ParameterTypeTuple!T Args;
69 
70 	static assert ((variadicFunctionStyle!T != Variadic.d && variadicFunctionStyle!T != Variadic.c), "Non-typesafe variadic functions are not supported.");
71 
72 	static if (variadicFunctionStyle!T == Variadic.typesafe) {
73 		enum variadic = true;
74 		enum requiredArgs = Args.length - 1;
75 	} else {
76 		enum variadic = false;
77 		enum requiredArgs = Args.length;
78 	}
79 
80 	static if (isFunctionPointer!T) {
81 		T func = cast(T)ptr;
82 	} else {
83 		T func;
84 		func.ptr = self;
85 		func.funcptr = cast(typeof(func.funcptr))ptr;
86 	}
87 
88 	static if ((Args.length == 1) && isArray!(Args[0]) && is(Unqual!(ElementType!(Args[0])) == Value)) {
89 		auto argValues = args;
90 	} else {
91 		if ((variadic && (args.length < requiredArgs)) || (!variadic && args.length != requiredArgs))
92 			throw new Exception(format("expected %d arguments but got %d", requiredArgs, args.length));
93 
94 		Args argValues;
95 
96 		foreach (i, Arg; Args) {
97 			alias ArgU = Unqual!Arg;
98 			static if (is(Arg == enum)) {
99 				*cast(ArgU*)&argValues[i] = cast(ArgU)args[i].get!(Unqual!(OriginalType!ArgU));
100 			} else {
101 				*cast(ArgU*)&argValues[i] = args[i].get!ArgU;
102 			}
103 		}
104 	}
105 
106 	alias R = Unqual!(ReturnType!T);
107 	static if (is(R == void)) {
108 		func(argValues);
109 	} else {
110 		static if (is(R == struct)) {
111 			static if ((functionAttributes!T & FunctionAttribute.ref_) != 0) {
112 				ret = Value(func(argValues));
113 			} else {
114 				auto nret = new R;
115 				*nret = func(argValues);
116 				ret = Value(nret);
117 			}
118 		} else {
119 			ret = Value(func(argValues));
120 		}
121 	}
122 }
123 
124 
125 struct Value {
126 	enum Type : ubyte {
127 		Undefined,
128 		Null,
129 		Bool,
130 		Integer,
131 		Float,
132 		Function,
133 		String,
134 		Array,
135 		AssocArray,
136 		Object,
137 		Pointer,
138 	}
139 
140 	this(typeof(null)) {
141 		type_ = Type.Null;
142 	}
143 
144 	this(Value x) {
145 		type_ = x.type_;
146 		storage_ = x.storage_;
147 	}
148 
149 	this(T)(T x) if (isBoolean!T) {
150 		type_ = Type.Bool;
151 		storage_.b = x;
152 	}
153 
154 	this(T)(T x) if (isScalarType!T && !isBoolean!T && !isFloatingPoint!T) {
155 		type_ = Type.Integer;
156 		storage_.l = cast(long)x;
157 	}
158 
159 	this(T)(T x) if (isScalarType!T && !isBoolean!T && isFloatingPoint!T) {
160 		type_ = Type.Float;
161 		storage_.d = cast(double)x;
162 	}
163 
164 	this(T)(T x) if (isSomeString!(OriginalType!T)) {
165 		type_ = Type.String;
166 		storage_.s = x;
167 	}
168 
169 	this(T)(T x) if (isArray!T && !isSomeString!(OriginalType!T)) {
170 		type_ = Type.Array;
171 		static if (!is(Unqual!(ElementType!T) == Value)) {
172 			auto arr = uninitializedArray!(Value[])(x.length);
173 			static if (is(Unqual!(ElementType!T) == void)) {
174 				foreach (i, ref v; cast(ubyte[])x)
175 					arr[i] = Value(v);
176 			} else {
177 				foreach (i, ref v; x)
178 					arr[i] = Value(v);
179 			}
180 			storage_.a = arr;
181 		} else {
182 			storage_.a = cast(Value[])x;
183 		}
184 	}
185 
186 	this(T)(T x) if (isSomeFunction!T && isCompatibleFunction!T) {
187 		if (x !is null) {
188 			type_ = Type.Function;
189 			static if (isFunctionPointer!T) {
190 				storage_.f.ptr = x;
191 			} else {
192 				storage_.f.self = x.ptr;
193 				storage_.f.ptr = cast(void*)x.funcptr;
194 			}
195 			storage_.f.wrapper = &functionWrapper!T;
196 		} else {
197 			type_ = Type.Null;
198 		}
199 	}
200 
201 	this(T)(T x) if (isAssociativeArray!T) {
202 		type_ = Type.AssocArray;
203 		foreach (ref k, ref v; x)
204 			storage_.aa[Value(k)] = Value(v);
205 	}
206 
207 	this(T)(auto ref T x) if (isInstanceOf!(Nullable, T) || isInstanceOf!(NullableRef, T)) {
208 		if (x.isNull) {
209 			this(null);
210 		} else {
211 			this(x.get);
212 		}
213 	}
214 
215 	this(T)(T x) if (isPointer!T && !isSomeFunction!T) {
216 		static if (is(Unqual!(PointerTarget!T) == void) || isPointer!(PointerTarget!T)) {
217 			type_ = Type.Pointer;
218 			storage_.p = cast(void*)x;
219 		} else {
220 			if (x is null) {
221 				this(null);
222 			} else {
223 				this(*x);
224 			}
225 		}
226 	}
227 
228 	this(T)(ref T x) if (is(Unqual!T == struct) && !(isInstanceOf!(Nullable, T) || isInstanceOf!(NullableRef, T))) {
229 		static if (is(Unqual!(typeof(x.toVayneValue())) == Value)) {
230 			this = x.toVayneValue();
231 		} else {
232 			type_ = Type.Object;
233 			bindMembers(x);
234 		}
235 	}
236 
237 	this(T)(T x) if (is(Unqual!T == class) || is(Unqual!T == interface)) {
238 		if (x !is null) {
239 			static if (is(Unqual!(typeof(x.toVayneValue())) == Value)) {
240 				this = x.toVayneValue();
241 			} else {
242 				type_ = Type.Object;
243 				bindMembers(x);
244 			}
245 		} else {
246 			type_ = Type.Null;
247 		}
248 	}
249 
250 
251 	private void bindMembers(T)(ref T x) {
252 		foreach (Member; FieldNameTuple!T) {
253 			static if (Member != "" && is(typeof(__traits(getMember, x, Member))) && __traits(getProtection, __traits(getMember, x, Member)) == "public" && !hasUDA!(__traits(getMember, T, Member), IgnoreAttribute)) {
254 				static if (hasUDA!(__traits(getMember, x, Member), NameAttribute)) {
255 					enum name = getUDAs!(__traits(getMember, x, Member), NameAttribute)[0].name;
256 				} else {
257 					enum name = Member;
258 				}
259 
260 				static if (!isSomeFunction!(typeof(__traits(getMember, x, Member)))) {
261 					storage_.o[name] = Value(__traits(getMember, x, Member));
262 				} else static if (isCompatibleFunction!(typeof(__traits(getMember, x, Member)))) {
263 					if (__traits(getMember, x, Member) is !null) {
264 						storage_.o[name] = Value(__traits(getMember, x, Member));
265 					}
266 				}
267 			}
268 		}
269 
270 		foreach (Member; __traits(derivedMembers, T)) {
271 			enum getMember = "&(*cast(Unqual!(typeof(x))*)&x)." ~ Member;
272 
273 			static if (is(FunctionTypeOf!(__traits(getMember, T, Member))) && (__traits(getProtection, __traits(getMember, x, Member)) == "public") && !hasUDA!(__traits(getMember, T, Member), IgnoreAttribute)) {
274 				static if (is(typeof(() { auto a = mixin(getMember); }))) {
275 					alias MemberType = typeof(mixin(getMember));
276 					enum callable = !NotCallableNames.canFind(Member);
277 					static if (callable && isCompatibleFunction!MemberType) {
278 						enum bindable = !NotBindableNames.canFind(Member);
279 
280 						alias Args = ParameterTypeTuple!MemberType;
281 
282 						auto addr = mixin(getMember);
283 
284 						static if (hasUDA!(__traits(getMember, x, Member), NameAttribute)) {
285 							enum name = getUDAs!(__traits(getMember, x, Member), NameAttribute)[0].name;
286 						} else {
287 							enum name = Member;
288 						}
289 
290 						static if (bindable) {
291 							storage_.o[name] = Value(addr);
292 						}
293 
294 						static if ((Member == "toString") && (Args.length == 0)) {
295 							storage_.o["__tostring"] = Value(addr);
296 						} else static if ((Member == "opIndex")) {
297 							//storage_.o["__index"] = Value(addr);
298 						}
299 					}
300 				}
301 			}
302 		}
303 	}
304 
305 	@property Type type() const pure nothrow {
306 		return type_;
307 	}
308 
309 	@property auto length() const {
310 		final switch(type) with (Type) {
311 		case Null:
312 		case Undefined:
313 		case Bool:
314 		case Integer:
315 		case Float:
316 		case Function:
317 		case Pointer:
318 			throw new Exception(format("length op not allowed for type %s", type));
319 		case String:
320 		case Array:
321 			return storage_.a.length;
322 		case AssocArray:
323 			return storage_.aa.length;
324 		case Object:
325 			return storage_.o.length;
326 		}
327 	}
328 
329 	auto compareOp(string op)(in Value other) const {
330 		if (type != other.type) {
331 			if ((type == Type.Integer) && (other.type == Type.Float)) {
332 				return mixin("storage_.l " ~ op ~ " other.storage_.d");
333 			} else if ((type == Type.Float) && (other.type == Type.Integer)) {
334 				return mixin("storage_.d " ~ op ~ " other.storage_.l");
335 			}
336 
337 			throw new Exception(format("compare op '%s' not allowed between types %s and %s", op, type, other.type));
338 		}
339 
340 		final switch (type) with (Type) {
341 		case Null:
342 		case Undefined:
343 			return true;
344 		case Bool:
345 			return mixin("storage_.b" ~ op ~ "other.storage_.b");
346 		case Integer:
347 			return mixin("storage_.l " ~ op ~ " other.storage_.l");
348 		case Float:
349 			return mixin("storage_.d " ~ op ~ " other.storage_.d");
350 		case String:
351 			return mixin("storage_.s" ~ op ~ "other.storage_.s");
352 		case Function:
353 			return mixin("storage_.f.ptr" ~ op ~ "other.storage_.f.ptr");
354 		case Array:
355 			return mixin("storage_.a" ~ op ~ "other.storage_.a");
356 		case Pointer:
357 			return mixin("storage_.p" ~ op ~ "other.storage_.p");
358 		case AssocArray:
359 		case Object:
360 			break;
361 		}
362 
363 		throw new Exception(format("compare op '%s' not allowed for type %s", op, type));
364 	}
365 
366 	auto concatOp(in Value other) const {
367 		if (type != other.type || type != Type.String)
368 			throw new Exception(format("concat not allowed between types %s and %s", type, other.type));
369 		return Value(storage_.s ~ other.storage_.s);
370 	}
371 
372 	auto binaryOp(string op)(Value other) {
373 		if (type != other.type) {
374 			if ((type == Type.Integer) && (other.type == Type.Float)) {
375 				return Value(mixin("storage_.l " ~ op ~ " other.storage_.d"));
376 			} else if ((type == Type.Float) && (other.type == Type.Integer)) {
377 				return Value(mixin("storage_.d " ~ op ~ " other.storage_.l"));
378 			}
379 			throw new Exception(format("binary op '%s' not allowed between types %s and %s", op, type, other.type));
380 		}
381 
382 		final switch (type) with (Type) {
383 		case Bool:
384 			return Value(mixin("storage_.b " ~ op ~ " other.storage_.b"));
385 		case Integer:
386 			return Value(mixin("storage_.l " ~ op ~ " other.storage_.l"));
387 		case Float:
388 			return Value(mixin("storage_.d " ~ op ~ " other.storage_.d"));
389 		case Null:
390 		case Undefined:
391 		case String:
392 		case Function:
393 		case Array:
394 		case AssocArray:
395 		case Object:
396 		case Pointer:
397 			break;
398 		}
399 
400 		throw new Exception(format("binary op '%s' not allowed for type %s", op, type));
401 	}
402 
403 	void unaryOp(string op)() {
404 		final switch (type) with (Type) {
405 		case Integer:
406 			storage_.l = mixin(op ~ "storage_.l");
407 			break;
408 		case Float:
409 			storage_.d = mixin(op ~ "storage_.d");
410 			break;
411 		case Null:
412 		case Undefined:
413 		case Bool:
414 		case String:
415 		case Function:
416 		case Array:
417 		case AssocArray:
418 		case Object:
419 		case Pointer:
420 			throw new Exception(format("unary op '%s' not allowed for type %s", op, type));
421 		}
422 	}
423 
424 	bool opEquals(const Value other) const {
425 		if (type_ == other.type_)
426 			return compareOp!("==")(other);
427 		return false;
428 	}
429 
430 	size_t toHash() const nothrow @safe {
431 		return () @trusted {
432 			try {
433 				final switch (type) with (Type) {
434 				case Integer:
435 					return vayne.hash.hashOf(storage_.l);
436 				case Float:
437 					return vayne.hash.hashOf(storage_.d);
438 				case Null:
439 				case Undefined:
440 					return 0;
441 				case Bool:
442 					return vayne.hash.hashOf(storage_.b);
443 				case String:
444 					return vayne.hash.hashOf(storage_.s);
445 				case Function:
446 					return vayne.hash.hashOf(storage_.f);
447 				case Array:
448 					return vayne.hash.hashOf(storage_.a);
449 				case AssocArray:
450 					return vayne.hash.hashOf(storage_.aa);
451 				case Object:
452 					return vayne.hash.hashOf(storage_.o);
453 				case Pointer:
454 					return vayne.hash.hashOf(storage_.p);
455 				}
456 			} catch (Exception e) {
457 				return 0;
458 			}
459 		}();
460 	}
461 
462 	string toString() const {
463 		final switch (type) with (Type) {
464 		case Undefined:
465 			return "undefined";
466 		case Null:
467 			return "null";
468 		case Bool:
469 			return storage_.b.to!string;
470 		case Integer:
471 			return storage_.l.to!string;
472 		case Float:
473 			return storage_.d.to!string;
474 		case String:
475 			return storage_.s.to!string;
476 		case Function:
477 			static if ((void*).sizeof == 4) {
478 				return format("[function 0x%08x:0x%08x]", storage_.f.self, storage_.f.ptr);
479 			} else {
480 				return format("[function 0x%016x:0x%016x]", storage_.f.self, storage_.f.ptr);
481 			}
482 		case Array:
483 			return storage_.a.to!string;
484 		case AssocArray:
485 			return storage_.aa.to!string;
486 		case Object:
487 			if (auto tostring = "__tostring" in storage_.o) {
488 				Value result;
489 				tostring.call(result, null);
490 				return result.get!string;
491 			}
492 			return storage_.o.to!string;
493 		case Pointer:
494 			static if ((void*).sizeof == 4) {
495 				return format("[pointer 0x%08x]", storage_.p);
496 			} else {
497 				return format("[pointer 0x%016x]", storage_.p);
498 			}
499 		}
500 	}
501 
502 	T get(T)() const if (is(Unqual!T == Value)) {
503 		return this;
504 	}
505 
506 	T get(T)() const if (isSomeString!(OriginalType!T)) {
507 		final switch (type) with (Type) {
508 		case Undefined:
509 		case Null:
510 		case Function:
511 		case Array:
512 		case AssocArray:
513 		case Pointer:
514 			throw new Exception(format("cannot convert %s to string", type));
515 		case Object:
516 			if (auto tostring = "__tostring" in storage_.o) {
517 				Value result;
518 				tostring.call(result, null);
519 				return result.get!T;
520 			}
521 			goto case AssocArray;
522 		case Bool:
523 			return storage_.b.to!T;
524 		case Integer:
525 			return storage_.l.to!T;
526 		case Float:
527 			return storage_.d.to!T;
528 		case String:
529 			return storage_.s.to!T;
530 		}
531 	}
532 
533 	T get(T)() const if (isScalarType!T && !isBoolean!T) {
534 		final switch (type) with (Type) {
535 		case Bool:
536 			return cast(T)(storage_.b ? 1 : 0);
537 		case Integer:
538 			return cast(T)storage_.l;
539 		case Float:
540 			return cast(T)storage_.d;
541 		case String:
542 			return storage_.s.to!T;
543 		case Pointer:
544 			return cast(T)(cast(size_t)storage_.p);
545 		case Null:
546 		case Undefined:
547 		case Function:
548 		case Array:
549 		case AssocArray:
550 		case Object:
551 			throw new Exception(format("cannot convert %s to scalar", type));
552 		}
553 	}
554 
555 	T get(T)() const if (isBoolean!T) {
556 		final switch (type) with (Type) {
557 		case Null:
558 			return false;
559 		case Bool:
560 			return storage_.b;
561 		case Integer:
562 			return storage_.l != 0;
563 		case Float:
564 			return storage_.d != 0.0;
565 		case String:
566 			return storage_.s.length != 0;
567 		case Pointer:
568 			return storage_.p != null;
569 		case Undefined:
570 		case Function:
571 		case Array:
572 		case AssocArray:
573 		case Object:
574 			throw new Exception(format("cannot convert %s to boolean", type));
575 		}
576 	}
577 
578 	T get(T)() const if (isPointer!T) {
579 		final switch (type) with (Type) {
580 		case Null:
581 			return null;
582 		case Pointer:
583 			return cast(T)storage_.p;
584 		case Undefined:
585 		case Bool:
586 		case Integer:
587 		case String:
588 		case Float:
589 		case Function:
590 		case Array:
591 		case AssocArray:
592 		case Object:
593 			throw new Exception(format("cannot convert %s to boolean", type));
594 		}
595 	}
596 
597 	T get(T)() const if (is(Unqual!T == Function)) {
598 		final switch (type) with (Type) {
599 		case Null:
600 		case Undefined:
601 		case Bool:
602 		case Integer:
603 		case Float:
604 		case String:
605 		case Array:
606 		case AssocArray:
607 		case Object:
608 		case Pointer:
609 			throw new Exception(format("cannot convert %s to function", type));
610 		case Function:
611 			return cast(T)storage_.f;
612 		}
613 	}
614 
615 	T get(T)() const if (is(Unqual!T == Date)) {
616 		final switch (type) with (Type) {
617 		case Null:
618 		case Undefined:
619 		case Bool:
620 		case Integer:
621 		case Float:
622 		case String:
623 		case Array:
624 		case Pointer:
625 		case Function:
626 			throw new Exception(format("cannot convert %s to date", type));
627 		case Object:
628 			return Date(storage_.o["year"], storage_.o["month"], storage_.o["day"]);
629 		case AssocArray:
630 			return Date(storage_.aa["year"], storage_.aa["month"], storage_.aa["day"]);
631 		}
632 	}
633 
634 	ref auto keys() const {
635 		final switch (type) with (Type) {
636 		case Null:
637 		case Undefined:
638 		case Bool:
639 		case Integer:
640 		case Float:
641 		case Function:
642 		case Pointer:
643 			throw new Exception(format("keys not allowed for type %s", type));
644 		case String:
645 		case Array:
646 			return Value(iota(0,length).array);
647 		case AssocArray:
648 			return Value(storage_.aa.keys);
649 		case Object:
650 			return Value(storage_.o.keys);
651 		}
652 	}
653 
654 	ref auto values() const {
655 		final switch (type) with (Type) {
656 		case Null:
657 		case Undefined:
658 		case Bool:
659 		case Integer:
660 		case Float:
661 		case Function:
662 		case Pointer:
663 			throw new Exception(format("values not allowed for type %s", type));
664 		case String:
665 		case Array:
666 			return this;
667 		case AssocArray:
668 			return Value(storage_.aa.values);
669 		case Object:
670 			return Value(storage_.o.values);
671 		}
672 	}
673 
674 	bool has(in Value index) const {
675 		final switch (type) with (Type) {
676 		case Null:
677 		case Undefined:
678 		case Bool:
679 		case Integer:
680 		case Float:
681 		case Function:
682 		case Pointer:
683 			throw new Exception(format("indexing not allowed for type %s, key '%s'", type, index));
684 		case String:
685 			auto i = index.get!ulong;
686 			return i < length;
687 		case Array:
688 			auto i = index.get!ulong;
689 			return (i < length);
690 		case AssocArray:
691 			auto i = index;
692 			return ((i in storage_.aa) != null);
693 		case Object:
694 			auto i = index.get!string;
695 			return ((i in storage_.o) != null);
696 		}
697 	}
698 
699 	bool has(in Value index, Value* pout) const {
700 		final switch (type) with (Type) {
701 		case Null:
702 		case Undefined:
703 		case Bool:
704 		case Integer:
705 		case Float:
706 		case Function:
707 		case Pointer:
708 			throw new Exception(format("indexing not allowed for type %s, key '%s'", type, index));
709 		case String:
710 			auto i = index.get!size_t;
711 			if (i < length) {
712 				*pout = Value(storage_.s[i..i + 1]);
713 				return true;
714 			}
715 			return false;
716 		case Array:
717 			auto i = index.get!size_t;
718 			if (i < length) {
719 				*pout = (cast(Value*)storage_.a)[i];
720 				return true;
721 			}
722 			return false;
723 		case AssocArray:
724 			auto i = index;
725 			if (auto pvalue = i in storage_.aa) {
726 				*pout = *pvalue;
727 				return true;
728 			}
729 			return false;
730 		case Object:
731 			auto i = index.get!string;
732 			if (auto pvalue = i in storage_.o) {
733 				*pout = *pvalue;
734 				return true;
735 			}
736 			return false;
737 		}
738 	}
739 
740 	Value opIndex(in Value index) const {
741 		final switch (type) with (Type) {
742 		case Null:
743 		case Undefined:
744 		case Bool:
745 		case Integer:
746 		case Float:
747 		case Function:
748 		case Pointer:
749 			throw new Exception(format("indexing not allowed for type %s, key '%s'", type, index));
750 		case String:
751 			auto i = index.get!size_t;
752 			if (i < length)
753 				return Value(storage_.s[i..i + 1]);
754 			throw new Exception(format("out of range index %d for length %d", i, length));
755 		case Array:
756 			auto i = index.get!size_t;
757 			if (i < length)
758 				return (cast(Value*)storage_.a)[i];
759 			throw new Exception(format("out of range index %d for length %d", i, length));
760 		case AssocArray:
761 			auto i = index;
762 			if (auto pvalue = i in storage_.aa)
763 				return *pvalue;
764 			throw new Exception(format("undefined key '%s' for associative array", i));
765 		case Object:
766 			auto i = index.get!string;
767 			if (auto pvalue = i in storage_.o)
768 				return *pvalue;
769 			throw new Exception(format("unknown member '%s' for object", i));
770 		}
771 	}
772 
773 	Value opIndex(in string index) const {
774 		final switch (type) with (Type) {
775 		case Null:
776 		case Undefined:
777 		case Bool:
778 		case Integer:
779 		case Float:
780 		case Function:
781 		case Pointer:
782 			throw new Exception(format("indexing not allowed for type %s, key '%s'", type, index));
783 		case String:
784 			throw new Exception(format("indexing with string key not allowed for type %s, key '%s'", type, index));
785 		case Array:
786 			throw new Exception(format("indexing with string key not allowed for type %s, key '%s'", type, index));
787 		case AssocArray:
788 			auto i = Value(index);
789 			if (auto pvalue = i in storage_.aa)
790 				return *pvalue;
791 			throw new Exception(format("undefined key '%s' for associative array", i));
792 		case Object:
793 			if (auto pvalue = index in storage_.o)
794 				return *pvalue;
795 			throw new Exception(format("unknown member '%s' for object", index));
796 		}
797 	}
798 
799 	Value opIndex(in size_t index) const {
800 		final switch (type) with (Type) {
801 		case Null:
802 		case Undefined:
803 		case Bool:
804 		case Integer:
805 		case Float:
806 		case Function:
807 		case Pointer:
808 			throw new Exception(format("indexing not allowed for type %s, key '%s'", type, index));
809 		case Object:
810 			throw new Exception(format("indexing with integer key not allowed for type %s, key '%s'", type, index));
811 		case String:
812 			if (index < length)
813 				return Value(storage_.s[index..index + 1]);
814 			throw new Exception(format("out of range index %d for length %d", index, length));
815 		case Array:
816 			if (index < length)
817 				return (cast(Value*)storage_.a)[index];
818 			throw new Exception(format("out of range index %d for length %d", index, length));
819 		case AssocArray:
820 			if (auto pvalue = Value(index) in storage_.aa)
821 				return *pvalue;
822 			throw new Exception(format("undefined key '%s' for associative array", index));
823 		}
824 	}
825 
826 	void opIndexAssign(Value value, in string index) {
827 		final switch (type) with (Type) {
828 		case Null:
829 		case Undefined:
830 		case Bool:
831 		case Integer:
832 		case Float:
833 		case Function:
834 		case Pointer:
835 			throw new Exception(format("index assign not allowed for type %s, key '%s'", type, index));
836 		case String:
837 			throw new Exception(format("index assign with string key not allowed for type %s, key '%s'", type, index));
838 		case Array:
839 			throw new Exception(format("index assign with string key not allowed for type %s, key '%s'", type, index));
840 		case AssocArray:
841 			storage_.aa[Value(index)] = value;
842 			break;
843 		case Object:
844 			storage_.o[index] = value;
845 			break;
846 		}
847 	}
848 
849 	void opIndexAssign(Value value, in size_t index) {
850 		final switch (type) with (Type) {
851 		case Null:
852 		case Undefined:
853 		case Bool:
854 		case Integer:
855 		case Float:
856 		case Function:
857 		case String:
858 		case Pointer:
859 			throw new Exception(format("index assign not allowed for type %s, key '%s'", type, index));
860 		case Object:
861 			throw new Exception(format("index assign with integer key not allowed for type %s, key '%s'", type, index));
862 		case Array:
863 			if (index < length) {
864 				storage_.a[index] = value;
865 				break;
866 			}
867 			throw new Exception(format("out of range index %d for length %d", index, length));
868 		case AssocArray:
869 			storage_.aa[Value(index)] = value;
870 			break;
871 		}
872 	}
873 
874 	Value opSlice(in Value start, in Value end) const {
875 		return opSlice(start.get!size_t, end.get!size_t);
876 	}
877 
878 	Value opSlice(in size_t start, in size_t end) const {
879 		final switch (type) with (Type) {
880 		case Null:
881 		case Undefined:
882 		case Bool:
883 		case Integer:
884 		case Float:
885 		case Function:
886 		case Pointer:
887 		case AssocArray:
888 		case Object:
889 			throw new Exception(format("slicing not allowed for type %s", type));
890 		case String:
891 			return Value(storage_.s[start..end]);
892 		case Array:
893 			return Value(storage_.a[start..end]);
894 		}
895 	}
896 
897 	int opApply(int delegate(Value) dg) {
898 		final switch (type) with (Type) {
899 		case Null:
900 		case Undefined:
901 		case Bool:
902 		case Integer:
903 		case Float:
904 		case Function:
905 		case Pointer:
906 			throw new Exception(format("iteration not allowed for type %s", type));
907 		case String:
908 			foreach (v; storage_.s) {
909 				if (auto r = dg(Value(v)))
910 					return r;
911 			}
912 			break;
913 		case Array:
914 			foreach (v; storage_.a) {
915 				if (auto r = dg(v))
916 					return r;
917 			}
918 			break;
919 		case AssocArray:
920 			foreach (v; storage_.aa) {
921 				if (auto r = dg(v))
922 					return r;
923 			}
924 			break;
925 		case Object:
926 			foreach (v; storage_.o) {
927 				if (auto r = dg(v))
928 					return r;
929 			}
930 			break;
931 		}
932 		return 0;
933 	}
934 
935 	int opApply(int delegate(size_t, Value) dg) {
936 		final switch (type) with (Type) {
937 		case Null:
938 		case Undefined:
939 		case Bool:
940 		case Integer:
941 		case Float:
942 		case Function:
943 		case Pointer:
944 			throw new Exception(format("iteration not allowed for type %s", type));
945 		case String:
946 			foreach (i, v; storage_.s) {
947 				if (auto r = dg(i, Value(v)))
948 					return r;
949 			}
950 			break;
951 		case Array:
952 			foreach (i, v; storage_.a) {
953 				if (auto r = dg(i, v))
954 					return r;
955 			}
956 			break;
957 		case AssocArray:
958 			foreach (i, v; storage_.aa) {
959 				if (auto r = dg(i.get!size_t, v))
960 					return r;
961 			}
962 			break;
963 		case Object:
964 			size_t i;
965 			foreach (v; storage_.o) {
966 				if (auto r = dg(i++, v))
967 					return r;
968 			}
969 			break;
970 		}
971 		return 0;
972 	}
973 
974 	int opApply(int delegate(Value, Value) dg) {
975 		final switch (type) with (Type) {
976 		case Null:
977 		case Undefined:
978 		case Bool:
979 		case Integer:
980 		case Float:
981 		case Function:
982 		case Pointer:
983 			throw new Exception(format("iteration not allowed for type %s", type));
984 		case String:
985 			foreach (k, v; storage_.s) {
986 				if (auto r = dg(Value(k), Value(v)))
987 					return r;
988 			}
989 			break;
990 		case Array:
991 			foreach (k, v; storage_.a) {
992 				if (auto r = dg(Value(k), v))
993 					return r;
994 			}
995 			break;
996 		case AssocArray:
997 			foreach (k, v; storage_.aa) {
998 				if (auto r = dg(k, v))
999 					return r;
1000 			}
1001 			break;
1002 		case Object:
1003 			foreach (k, v; storage_.o) {
1004 				if (auto r = dg(Value(k), v))
1005 					return r;
1006 			}
1007 			break;
1008 		}
1009 		return 0;
1010 	}
1011 
1012 	int opApply(int delegate(string, Value) dg) {
1013 		final switch (type) with (Type) {
1014 		case Null:
1015 		case Undefined:
1016 		case Bool:
1017 		case Integer:
1018 		case Float:
1019 		case Function:
1020 		case Pointer:
1021 			throw new Exception(format("iteration not allowed for type %s", type));
1022 		case String:
1023 			throw new Exception(format("iteration with string key not allowed for type %s", type));
1024 		case Array:
1025 			throw new Exception(format("iteration with string key not allowed for type %s", type));
1026 		case AssocArray:
1027 			foreach (k, v; storage_.aa) {
1028 				if (auto r = dg(k.get!string, v))
1029 					return r;
1030 			}
1031 			break;
1032 		case Object:
1033 			foreach (k, v; storage_.o) {
1034 				if (auto r = dg(k, v))
1035 					return r;
1036 			}
1037 			break;
1038 		}
1039 		return 0;
1040 	}
1041 
1042 	static auto emptyObject() {
1043 		Value value;
1044 		value.type_ = Type.Object;
1045 		return value;
1046 	}
1047 
1048 	static auto emptyAssocArray() {
1049 		Value value;
1050 		value.type_ = Type.AssocArray;
1051 		return value;
1052 	}
1053 
1054 	void call(ref Value ret, Value[] args) const {
1055 		auto func = get!Function();
1056 		func.wrapper(func.ptr, func.self, args, ret);
1057 	}
1058 
1059 	void call(Args...)(ref Value ret, Args args) const {
1060 		Value[Args.length] argValues = [ args ];
1061 		auto func = get!Function();
1062 		func.wrapper(func.ptr, func.self, argValues, ret);
1063 	}
1064 
1065 	static struct Function {
1066 		void* self;
1067 		void* ptr;
1068 		void function(void* ptr, void* self, Value[], ref Value) wrapper;
1069 	}
1070 
1071 	union Storage {
1072 		bool b;
1073 		long l;
1074 		double d;
1075 		string s;
1076 		Value[] a;
1077 		Value[Value] aa;
1078 		Value[string] o; // TODO: use a custom hash map for proper inlining and access to internals
1079 		Function f;
1080 		void* p;
1081 	}
1082 
1083 	private Type type_;
1084 	private Storage storage_;
1085 }