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