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