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 }