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 }