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 }