1 /** 2 * The module contains the object UniNode 3 * 4 * Copyright: (c) 2015-2020, Milofon Project. 5 * License: Subject to the terms of the BSD 3-Clause License, as written in the included LICENSE.md file. 6 * Author: <m.galanin@milofon.pro> Maksim Galanin 7 * Date: 2020-01-12 8 */ 9 10 module uninode.node; 11 12 private 13 { 14 import std.algorithm.searching: canFind; 15 import std.meta : AliasSeq, allSatisfy, staticMap; 16 import std.format : fmt = format; 17 import std.typecons : Tuple, Nullable; 18 import std.exception : enforce; 19 import std..string : capitalize; 20 import std.conv : to, ConvOverflowException; 21 import std.array : appender, join; 22 import std.traits; 23 } 24 25 26 alias Bytes = immutable(ubyte)[]; 27 28 29 /** 30 * A [UniNode] implementation 31 */ 32 struct UniNodeImpl(Node) 33 { 34 private pure nothrow @safe @nogc 35 { 36 union Storage 37 { 38 bool boolean; 39 ulong uinteger; 40 long integer; 41 double floating; 42 string text; 43 Bytes raw; 44 Node[] sequence; 45 Node[string] mapping; 46 } 47 48 alias GetFieldPair(string F) = AliasSeq!(typeof(__traits(getMember, Storage, F)), F); 49 alias Types = Tuple!(staticMap!(GetFieldPair, FieldNameTuple!Storage)); 50 alias AllTypes = Tuple!(staticMap!(GetFieldPair, FieldNameTuple!Storage), 51 typeof(null), "nil"); 52 53 ref inout(T) _val(T)() inout pure nothrow @trusted @nogc 54 if (isUniNodeType!(T, Node)) 55 { 56 return __traits(getMember, _storage, Types.fieldNames[NodeTag!(Node, T)]); 57 } 58 59 Storage _storage; 60 Tag _tag = Tag.nil; 61 } 62 63 /** 64 * UniNodeImpl Tag 65 */ 66 mixin("enum Tag : ubyte {" ~ [AllTypes.fieldNames].join(", ") ~ "}"); 67 68 /** 69 * Check type node 70 */ 71 bool can(Tag tag) inout pure nothrow @safe @nogc 72 { 73 return _tag == tag; 74 } 75 76 /** 77 * Auto generage can functions 78 */ 79 static foreach (string name; AllTypes.fieldNames) 80 { 81 /** 82 * Check node is null 83 */ 84 mixin("bool can", name.capitalize, `() inout pure nothrow @safe @nogc 85 { 86 return _tag == Tag.`, name, `; 87 }`); 88 } 89 90 /** 91 * Return tag Node 92 */ 93 const(Tag) tag() inout pure nothrow @safe @nogc 94 { 95 return _tag; 96 } 97 98 /** 99 * Constructs a UniNode 100 */ 101 this(typeof(null)) inout pure nothrow @safe @nogc 102 { 103 _tag = Tag.nil; 104 } 105 106 /** 107 * Constructs a UniNode 108 * 109 * Params: 110 * value = ctor value 111 */ 112 this(T)(auto ref T value) inout pure nothrow @trusted @nogc 113 if (isUniNodeInnerType!(T) && !isRawData!T) 114 { 115 static if (isBoolean!T) 116 { 117 _storage.boolean = value; 118 _tag = Tag.boolean; 119 } 120 else static if(isSignedNumeric!T) 121 { 122 _storage.integer = value; 123 _tag = Tag.integer; 124 } 125 else static if(isUnsignedNumeric!T) 126 { 127 _storage.uinteger = value; 128 _tag = Tag.uinteger; 129 } 130 else static if(isFloatingPoint!T) 131 { 132 _storage.floating = value; 133 _tag = Tag.floating; 134 } 135 else static if(is(Unqual!T == string)) 136 { 137 _storage.text = value; 138 _tag = Tag.text; 139 } 140 else 141 _tag = Tag.nil; 142 } 143 144 /** 145 * Constructs a UniNode 146 * with gc 147 * 148 * Params: 149 * value = ctor value 150 */ 151 this(T)(auto ref T value) inout pure nothrow @trusted 152 if (isRawData!T || isUniNodeArray!(T, Node)) 153 { 154 static if(isRawData!T) 155 { 156 static if (isStaticArray!T || isMutable!T) 157 _storage.raw = value.idup; 158 else 159 { 160 alias ST = typeof(_storage.raw); 161 _storage.raw = cast(ST)value; 162 } 163 _tag = Tag.raw; 164 } 165 else static if (isUniNodeArray!(T, Node)) 166 { 167 alias ST = typeof(_storage.sequence); 168 _storage.sequence = cast(ST)value.dup; 169 _tag = Tag.sequence; 170 } 171 else 172 _tag = Tag.nil; 173 } 174 175 /** 176 * Constructs a UniNode 177 * with gc and throw 178 * 179 * Params: 180 * value = ctor value 181 */ 182 this(T)(auto ref T value) inout pure @trusted 183 if (isUniNodeMapping!(T, Node)) 184 { 185 alias ST = typeof(_storage.mapping); 186 _storage.mapping = cast(ST)value.dup; 187 _tag = Tag.mapping; 188 } 189 190 /** 191 * Constructs a UniNode sequence from arguments 192 * 193 * Params: 194 * value = ctor value 195 */ 196 this(T...)(auto ref T value) inout pure nothrow @trusted 197 if (T.length > 1 && allSatisfy!(isUniNodeInnerType, T)) 198 { 199 alias ST = typeof(_storage.sequence); 200 Node[] seq = new Node[value.length]; 201 static foreach (idx, val; value) 202 seq[idx] = Node(val); 203 _storage.sequence = cast(ST)seq; 204 _tag = Tag.sequence; 205 } 206 207 /** 208 * Construct empty Node sequence 209 */ 210 static Node emptySequence() nothrow @safe 211 { 212 return Node(cast(Node[])null); 213 } 214 215 /** 216 * Construct empty Node mapping 217 */ 218 static Node emptyMapping() @safe 219 { 220 return Node(cast(Node[string])null); 221 } 222 223 /** 224 * Convert UniNode to sequence 225 */ 226 inout(Node[]) getSequence() inout pure @safe 227 { 228 enforceUniNode(can(Tag.sequence), 229 fmt!("Trying to convert sequence but have %s.")(_tag), __FILE__, __LINE__); 230 return _val!(inout(Node[])); 231 } 232 233 /** 234 * Convert UniNode to mapping 235 */ 236 inout(Node[string]) getMapping() inout pure @safe 237 { 238 enforceUniNode(can(Tag.mapping), 239 fmt!("Trying to convert mapping but have %s.")(_tag), __FILE__, __LINE__); 240 return _val!(inout(Node[string])); 241 } 242 243 /** 244 * Convert UniNode to primitive type 245 */ 246 inout(T) get(T)() inout pure @safe 247 { 248 T wrapTo(A)(auto ref A val) inout pure @safe 249 { 250 try 251 return val.to!T; 252 catch (ConvOverflowException e) 253 throw new UniNodeException(e.msg); 254 } 255 256 void checkTag(T)(Tag target, string file = __FILE__, size_t line = __LINE__) 257 inout pure @safe 258 { 259 enforceUniNode(_tag == target, 260 fmt!("Trying to convert %s but have %s.")(T.stringof, _tag), file, line); 261 } 262 263 static if (isSignedNumeric!T) 264 { 265 if (canUinteger) 266 { 267 immutable val = _val!ulong; 268 enforceUniNode(val < T.max, "Unsigned value great max"); 269 return wrapTo(val); 270 } 271 checkTag!T(Tag.integer); 272 immutable val = _val!long; 273 return wrapTo(val); 274 } 275 else static if (isUnsignedNumeric!T) 276 { 277 if (canInteger) 278 { 279 immutable val = _val!long; 280 enforceUniNode(val >= 0, "Signed value less zero"); 281 return wrapTo(val); 282 } 283 checkTag!T(Tag.uinteger); 284 immutable val = _val!ulong; 285 return wrapTo(val); 286 } 287 else static if (isBoolean!T) 288 { 289 if (canUinteger) 290 return _val!ulong != 0; 291 if (canInteger) 292 return _val!long != 0; 293 else 294 { 295 checkTag!T(Tag.boolean); 296 return _val!bool; 297 } 298 } 299 else static if (isFloatingPoint!T) 300 { 301 if (canUinteger) 302 { 303 immutable val = _val!ulong; 304 return wrapTo(val); 305 } 306 if (canInteger) 307 { 308 immutable val = _val!long; 309 return wrapTo(val); 310 } 311 else 312 { 313 checkTag!T(Tag.floating); 314 return _val!double; 315 } 316 } 317 else static if (is(T == string)) 318 { 319 if (canRaw) 320 return cast(string)_val!Bytes; 321 checkTag!T(Tag.text); 322 return _val!string.to!T; 323 } 324 else static if (isRawData!T) 325 { 326 checkTag!T(Tag.raw); 327 immutable val = _val!Bytes; 328 static if (isStaticArray!T) 329 return cast(inout(T))val[0..T.length]; 330 else 331 return val.to!T; 332 } 333 else static if (isUniNodeArray!(T, Node)) 334 return getSequence(); 335 else static if (isUniNodeMapping!(T, Node)) 336 return getMapping(); 337 else 338 throw new UniNodeException(fmt!"Not support type '%s'"(T.stringof)); 339 } 340 341 /** 342 * Convert UniNode to optional primitive type 343 */ 344 Nullable!(const(T)) opt(T)() const pure nothrow @safe 345 { 346 alias RT = Nullable!(const(T)); 347 try 348 return RT(get!T); 349 catch (Exception e) 350 return RT.init; 351 } 352 353 /** 354 * Convert UniNode to optional primitive type 355 */ 356 Nullable!(T) opt(T)() pure nothrow @safe 357 { 358 alias RT = Nullable!(T); 359 try 360 return RT(get!T); 361 catch (Exception e) 362 return RT.init; 363 } 364 365 /** 366 * Convert UniNode to primitive type or return alternative value 367 */ 368 inout(T) getOrElse(T)(T alt) inout pure nothrow @safe 369 { 370 try 371 return get!T; 372 catch (Exception e) 373 return alt; 374 } 375 376 /** 377 * Convert UniNode to optional sequence 378 */ 379 Nullable!(const(Node[])) optSequence() const pure nothrow @safe 380 { 381 alias RT = Nullable!(const(Node[])); 382 try 383 return RT(getSequence()); 384 catch (Exception e) 385 return RT.init; 386 } 387 388 /** 389 * Convert UniNode to optional sequence 390 */ 391 Nullable!(Node[]) optSequence() pure nothrow @safe 392 { 393 alias RT = Nullable!(Node[]); 394 try 395 return RT(getSequence()); 396 catch (Exception e) 397 return RT.init; 398 } 399 400 /** 401 * Convert UniNode to optional mapping 402 */ 403 Nullable!(const(Node[string])) optMapping() const pure nothrow @safe 404 { 405 alias RT = Nullable!(const(Node[string])); 406 try 407 return RT(getMapping()); 408 catch (Exception e) 409 return RT.init; 410 } 411 412 /** 413 * Convert UniNode to optional mapping 414 */ 415 Nullable!(Node[string]) optMapping() pure nothrow @safe 416 { 417 alias RT = Nullable!(Node[string]); 418 try 419 return RT(getMapping()); 420 catch (Exception e) 421 return RT.init; 422 } 423 424 /** 425 * Implement index operator by Node array 426 */ 427 inout(Node) opIndex(size_t idx) inout @safe 428 { 429 enforceUniNode(can(Tag.sequence), 430 fmt!("Trying to convert sequence but have %s.")(_tag), __FILE__, __LINE__); 431 return _val!(inout(Node[]))[idx]; 432 } 433 434 /** 435 * Implement index operator by Node object 436 */ 437 inout(Node) opIndex(string key) inout @safe 438 { 439 enforceUniNode(can(Tag.mapping), 440 fmt!("Trying to convert mapping but have %s.")(_tag), __FILE__, __LINE__); 441 return _val!(inout(Node[string]))[key]; 442 } 443 444 /** 445 * Implement index assign operator by Node sequence 446 */ 447 void opIndexAssign(T)(auto ref T val, size_t idx) @safe 448 if (isUniNodeInnerType!T || isUniNode!T) 449 { 450 enforceUniNode(can(Tag.sequence), 451 fmt!("Trying to convert sequence but have %s.")(_tag), __FILE__, __LINE__); 452 static if (isUniNode!T) 453 _val!(inout(Node[]))[idx] = val; 454 else 455 _val!(inout(Node[]))[idx] = Node(val); 456 } 457 458 /** 459 * Implement index assign operator by Node mapping 460 */ 461 void opIndexAssign(T)(auto ref T val, string key) @safe 462 if (isUniNodeInnerType!T || isUniNode!T) 463 { 464 enforceUniNode(can(Tag.mapping), 465 fmt!("Trying to convert mapping but have %s.")(_tag), __FILE__, __LINE__); 466 static if (isUniNode!T) 467 _val!(inout(Node[string]))[key] = val; 468 else 469 _val!(inout(Node[string]))[key] = Node(val); 470 } 471 472 /** 473 * Implement operator ~= by UniNode array 474 */ 475 void opOpAssign(string op)(auto ref Node elem) @safe 476 if (op == "~") 477 { 478 enforceUniNode(can(Tag.sequence), 479 fmt!("Trying to convert sequence but have %s.")(_tag), __FILE__, __LINE__); 480 _val!(Node[]) ~= elem; 481 } 482 483 /** 484 * Implement operator ~= by UniNode array 485 */ 486 void opOpAssign(string op)(auto ref Node[] elem) @safe 487 if (op == "~") 488 { 489 opOpAssign!op(Node(elem)); 490 } 491 492 /** 493 * Particular keys in an Node can be removed with the remove 494 */ 495 void remove(string key) @safe 496 { 497 enforceUniNode(can(Tag.mapping), 498 fmt!("Trying to convert mapping but have %s.")(_tag), __FILE__, __LINE__); 499 _val!(Node[string]).remove(key); 500 } 501 502 /** 503 * Inserting if not present 504 */ 505 Node require(T)(string key, auto ref T val) @safe 506 if (isUniNodeInnerType!T || isUniNode!T) 507 { 508 enforceUniNode(can(Tag.mapping), 509 fmt!("Trying to convert mapping but have %s.")(_tag), __FILE__, __LINE__); 510 if (auto ret = key in _val!(Node[string])) 511 return *ret; 512 else 513 { 514 static if (isUniNode!T) 515 return _val!(Node[string])[key] = val; 516 else 517 return _val!(Node[string])[key] = Node(val); 518 } 519 } 520 521 /** 522 * Implement operator in for mapping 523 */ 524 inout(Node)* opBinaryRight(string op)(string key) inout @safe 525 if (op == "in") 526 { 527 enforceUniNode(_tag == Tag.mapping, 528 fmt!("Trying to convert mapping but have %s.")(_tag), __FILE__, __LINE__); 529 return key in _val!(inout(Node[string])); 530 } 531 532 /** 533 * Iteration by Node mapping 534 */ 535 int opApply(D)(D dg) inout 536 if (isCallable!D && (Parameters!D.length == 1 && isUniNode!(Parameters!D[0])) 537 || (Parameters!D.length == 2 && isUniNode!(Parameters!D[1]))) 538 { 539 alias Params = Parameters!D; 540 541 ref P toDelegateParam(P)(ref inout(Node) node) @trusted 542 { 543 return *(cast(P*)&node); 544 } 545 546 static if (Params.length == 1 && isUniNode!(Params[0])) 547 { 548 foreach (ref inout(Node) node; _val!(inout(Node[]))()) 549 { 550 if (auto ret = dg(toDelegateParam!(Params[0])(node))) 551 return ret; 552 } 553 } 554 else static if (Params.length == 2 && isUniNode!(Params[1])) 555 { 556 static if (isSomeString!(Params[0])) 557 { 558 foreach (Params[0] key, ref inout(Node) node; _val!(inout(Node[string]))()) 559 if (auto ret = dg(key, toDelegateParam!(Params[1])(node))) 560 return ret; 561 } 562 else 563 { 564 foreach (Params[0] key, ref inout(Node) node; _val!(inout(Node[]))) 565 if (auto ret = dg(key, toDelegateParam!(Params[1])(node))) 566 return ret; 567 } 568 } 569 return 0; 570 } 571 572 /** 573 * Returns the hash of the `Node`'s current value. 574 */ 575 size_t toHash() const nothrow @trusted 576 { 577 final switch (_tag) 578 { 579 static foreach (tid, T; AllTypes.Types) 580 { 581 case tid: 582 static if (is(T == typeof(null))) 583 return typeid(T).getHash(null); 584 else 585 { 586 auto val = _val!T; 587 return typeid(T).getHash(&val); 588 } 589 } 590 } 591 } 592 593 /** 594 * Returns the length sequence types 595 */ 596 size_t length() const pure @property 597 { 598 return this.match!( 599 (string val) => val.length, 600 (Bytes val) => val.length, 601 (const(Node)[] val) => val.length, 602 (const(Node[string]) val) => val.length, 603 () { throw new UniNodeException("Expected " ~ Node.stringof ~ " not length"); } 604 ); 605 } 606 607 /** 608 * Compares two `UniNode`s for equality. 609 */ 610 bool opEquals()(auto ref const(Node) rhs) const @safe 611 { 612 return this.match!((value) { 613 return rhs.match!((rhsValue) { 614 static if (is(typeof(value) == typeof(rhsValue))) 615 return value == rhsValue; 616 else static if (isNumeric!(typeof(value)) 617 && isNumeric!(typeof(rhsValue))) 618 return value == rhsValue; 619 else 620 return false; 621 }); 622 }); 623 } 624 625 /** 626 * Returns string representaion 627 */ 628 string toString() const @safe 629 { 630 auto buff = appender!string; 631 632 void toStringNode(UniNodeImpl!Node node) @safe const 633 { 634 node.match!( 635 (bool v) => buff.put(fmt!"bool(%s)"(v)), 636 (long v) => buff.put(fmt!"int(%s)"(v)), 637 (ulong v) => buff.put(fmt!"uint(%s)"(v)), 638 (double v) => buff.put(fmt!"float(%s)"(v)), 639 (string v) => buff.put(fmt!"text(%s)"(v)), 640 (Bytes v) => buff.put(fmt!"raw(%s)"(v)), 641 (const(Node)[] v) { 642 buff.put("["); 643 const len = v.length; 644 size_t count; 645 foreach (ref const(Node) nodeV; v) 646 { 647 count++; 648 toStringNode(nodeV); 649 if (count < len) 650 buff.put(", "); 651 } 652 buff.put("]"); 653 }, 654 (const(Node[string]) v) { 655 buff.put("{"); 656 const len = v.length; 657 size_t count; 658 foreach (string key, ref const(Node) nodeV; v) 659 { 660 count++; 661 buff.put(key ~ ":"); 662 toStringNode(nodeV); 663 if (count < len) 664 buff.put(", "); 665 } 666 buff.put("}"); 667 }, 668 () => buff.put("nil") 669 ); 670 } 671 672 toStringNode(this); 673 return buff.data; 674 } 675 } 676 677 678 /** 679 * A [UniNode] struct 680 */ 681 struct UniNode 682 { 683 private 684 { 685 alias Node = UniNodeImpl!UniNode; 686 alias node this; 687 } 688 689 /** 690 * Node implementation 691 */ 692 Node node; 693 694 /** 695 * Common constructor 696 */ 697 this(T)(auto ref T val) inout pure nothrow @safe @nogc 698 if ((isUniNodeInnerType!T && !isRawData!T) || is (T == typeof(null))) 699 { 700 node = Node(val); 701 } 702 703 /** 704 * Common constructor 705 */ 706 this(T)(auto ref T val) inout pure nothrow @safe 707 if (isRawData!T || isUniNodeArray!(T, Node)) 708 { 709 node = Node(val); 710 } 711 712 /** 713 * Common constructor 714 */ 715 this(T)(auto ref T val) inout pure @safe 716 if (isUniNodeMapping!(T, Node)) 717 { 718 node = Node(val); 719 } 720 721 /** 722 * Sequence constructor 723 */ 724 this(T...)(auto ref T val) inout pure nothrow @safe 725 if (T.length > 0 && allSatisfy!(isUniNodeInnerType, T)) 726 { 727 node = Node(val); 728 } 729 730 /** 731 * Compares two `UniNode`s for equality. 732 */ 733 bool opEquals(const(UniNode) rhs) const pure @safe 734 { 735 return node.opEquals(rhs); 736 } 737 738 /** 739 * Returns the hash of the `UniNode`'s current value. 740 */ 741 size_t toHash() const nothrow @safe 742 { 743 return node.toHash(); 744 } 745 } 746 747 748 /** 749 * Thrown when an unhandled type is encountered. 750 */ 751 class UniNodeException : Exception 752 { 753 /** 754 * common constructor 755 */ 756 pure nothrow @safe @nogc 757 this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) 758 { 759 super(msg, file, line, next); 760 } 761 } 762 763 764 /** 765 * Calls a type-appropriate function with the value held in a [UniNode]. 766 */ 767 template match(handlers...) 768 if (handlers.length) 769 { 770 /** 771 * The actual `match` function. 772 * 773 * Params: 774 * self = A [UniNode] object 775 */ 776 auto match(Node)(auto ref Node node) 777 if (is(Node : UniNodeImpl!Th, Th)) 778 { 779 return matchImpl!(handlers)(node); 780 } 781 } 782 783 784 /** 785 * Checking is UniNode 786 */ 787 template isUniNode(T) 788 { 789 alias UT = Unqual!T; 790 static if (__traits(compiles, is(UT : UniNodeImpl!UT))) 791 enum isUniNode = is(UT : UniNodeImpl!UT); 792 else 793 enum isUniNode = false; 794 } 795 796 @("Checking is UniNode") 797 @safe unittest 798 { 799 alias UniUni = UniNode; 800 assert (isUniNode!UniUni); 801 assert (isUniNode!UniNode); 802 assert (!isUniNode!int); 803 assert (isUniNode!(const(UniNode))); 804 } 805 806 807 package: 808 809 810 alias enforceUniNode = enforce!UniNodeException; 811 812 813 /** 814 * Checking for uninode 815 */ 816 template isUniNodeType(T, N) 817 { 818 enum isUniNodeType = isUniNodeInnerType!T 819 || isUniNodeArray!(T, N) || isUniNodeMapping!(T, N); 820 } 821 822 @("Checking for uninode type") 823 @safe unittest 824 { 825 static foreach(T; Fields!(UniNode.Storage)) 826 assert(isUniNodeType!(T, UniNode), "Type " ~ T.stringof ~ " not UniNode"); 827 } 828 829 830 /** 831 * Checking for inner types 832 */ 833 template isUniNodeInnerType(T) 834 { 835 alias TU = Unqual!T; 836 enum isUniNodeInnerType = isNumeric!TU || isBoolean!TU || 837 is(TU == string) || isRawData!TU; 838 } 839 840 @("Checking for inner types") 841 @safe unittest 842 { 843 static foreach(T; AliasSeq!(int, long, uint, ulong, bool, string)) 844 assert (isUniNodeInnerType!(T)); 845 assert (isUniNodeInnerType!string); 846 assert (!isUniNodeInnerType!(typeof(null))); 847 } 848 849 850 /** 851 * Checking for binary data 852 */ 853 template isRawData(T) 854 { 855 enum isRawData = isArray!T && is(Unqual!(ForeachType!T) == ubyte); 856 } 857 858 @("Checking for binary data") 859 @safe unittest 860 { 861 assert (isRawData!(Bytes)); 862 } 863 864 865 /** 866 * Checking for array 867 */ 868 template isUniNodeArray(T, N) 869 { 870 enum isUniNodeArray = isArray!T && is(Unqual!(ForeachType!T) : Unqual!N); 871 } 872 873 @("Checking for array") 874 @safe unittest 875 { 876 assert (isUniNodeArray!(UniNode[], UniNode)); 877 } 878 879 880 /** 881 * Checking for object 882 */ 883 template isUniNodeMapping(T, N) 884 { 885 enum isUniNodeMapping = isAssociativeArray!T 886 && is(Unqual!(ForeachType!T) : Unqual!N) && is(KeyType!T == string); 887 } 888 889 @("Checking for object") 890 @safe unittest 891 { 892 assert (isUniNodeMapping!(UniNode[string], UniNode)); 893 } 894 895 896 private: 897 898 899 /** 900 * True if `handler` is a potential match for `T`, otherwise false. 901 */ 902 enum bool canMatch(alias handler, T) = is(typeof((T arg) => handler(arg))); 903 904 @("Should work canMatch") 905 @safe unittest 906 { 907 static struct OverloadSet 908 { 909 static void fun(int n) {} 910 static void fun(double d) {} 911 } 912 913 assert(canMatch!(OverloadSet.fun, int)); 914 assert(canMatch!(OverloadSet.fun, double)); 915 } 916 917 918 @("Checking all types") 919 @safe unittest 920 { 921 assert(allSatisfy!(isCopyable, UniNode.Storage)); 922 assert(!allSatisfy!(hasElaborateCopyConstructor, UniNode.Storage)); 923 assert(!allSatisfy!(hasElaborateDestructor, UniNode.Storage)); 924 } 925 926 927 /** 928 * Checking for an integer signed number 929 */ 930 template isSignedNumeric(T) 931 { 932 enum isSignedNumeric = isNumeric!T && isSigned!T && !isFloatingPoint!T; 933 } 934 935 @("Checking for an integer signed number") 936 @safe unittest 937 { 938 static foreach(T; AliasSeq!(byte, int, short, long)) 939 assert(isSignedNumeric!T); 940 } 941 942 943 /** 944 * Checking for an integer unsigned number 945 */ 946 template isUnsignedNumeric(T) 947 { 948 enum isUnsignedNumeric = isUnsigned!T && !isFloatingPoint!T; 949 } 950 951 @("Checking for an integer unsigned number") 952 @safe unittest 953 { 954 static foreach(T; AliasSeq!(ubyte, uint, ushort, ulong)) 955 assert(isUnsignedNumeric!T); 956 } 957 958 959 /** 960 * Language type to uninode inner tag 961 */ 962 template NodeTag(Node, T) 963 if (isUniNodeType!(T, Node)) 964 { 965 static if (isBoolean!T) 966 enum NodeTag = Node.Tag.boolean; 967 else static if (isSignedNumeric!T) 968 enum NodeTag = Node.Tag.integer; 969 else static if (isUnsignedNumeric!T) 970 enum NodeTag = Node.Tag.uinteger; 971 else static if (isFloatingPoint!T) 972 enum NodeTag = Node.Tag.floating; 973 else static if (isSomeString!T) 974 enum NodeTag = Node.Tag.text; 975 else static if (isRawData!T) 976 enum NodeTag = Node.Tag.raw; 977 else static if (isUniNodeArray!(T, Node)) 978 enum NodeTag = Node.Tag.sequence; 979 else static if (isUniNodeMapping!(T, Node)) 980 enum NodeTag = Node.Tag.mapping; 981 else 982 enum NodeTag = Node.Tag.nil; 983 } 984 985 @("NodeTag test") 986 @safe unittest 987 { 988 static assert (NodeTag!(UniNode, bool) == UniNode.Tag.boolean); 989 static assert (NodeTag!(UniNode, int) == UniNode.Tag.integer); 990 static assert (NodeTag!(UniNode, uint) == UniNode.Tag.uinteger); 991 static assert (NodeTag!(UniNode, float) == UniNode.Tag.floating); 992 static assert (NodeTag!(UniNode, string) == UniNode.Tag.text); 993 static assert (NodeTag!(UniNode, ubyte[]) == UniNode.Tag.raw); 994 static assert (NodeTag!(UniNode, UniNode[]) == UniNode.Tag.sequence); 995 static assert (NodeTag!(UniNode, UniNode[string]) == UniNode.Tag.mapping); 996 } 997 998 999 /** 1000 * Match implementation 1001 */ 1002 template matchImpl(handlers...) 1003 if (handlers.length) 1004 { 1005 // Converts an unsigned integer to a compile-time string constant. 1006 enum toCtString(ulong n) = n.stringof[0 .. $ - 2]; 1007 1008 auto matchImpl(Node)(auto ref Node node) 1009 if (is(Node : UniNodeImpl!T, T)) 1010 { 1011 alias AllTypes = Node.AllTypes.Types; 1012 enum MatchType : ubyte { NO, TPL, FUN, EMP } 1013 struct Match 1014 { 1015 MatchType type; 1016 size_t hid; 1017 ubyte dist; 1018 } 1019 1020 enum defaultMatch = Match(MatchType.NO, 0, ubyte.max); 1021 1022 template HandlerMatch(alias handler, size_t hid, T) 1023 { 1024 static if (isCallable!handler) 1025 { 1026 alias params = Parameters!handler; 1027 static if (params.length == 1) 1028 { 1029 enum dist = GetDistance!(Node, T, params[0]); 1030 enum type = dist < ubyte.max ? MatchType.FUN : MatchType.NO; 1031 enum HandlerMatch = Match(type, hid, dist); 1032 } 1033 else static if (params.length == 0) 1034 enum HandlerMatch = Match(MatchType.EMP, hid, ubyte.max); 1035 else 1036 enum HandlerMatch = defaultMatch; 1037 } 1038 else static if (canMatch!(handler, T)) 1039 enum HandlerMatch = Match(MatchType.TPL, hid, ubyte.max-1); 1040 else 1041 enum HandlerMatch = defaultMatch; 1042 } 1043 1044 enum matches = () { 1045 Match[AllTypes.length] matches; 1046 1047 foreach (tid, T; AllTypes) 1048 { 1049 foreach (hid, handler; handlers) 1050 { 1051 enum m = HandlerMatch!(handler, hid, T); 1052 if (matches[tid].type != MatchType.NO) 1053 { 1054 if (matches[tid].dist > m.dist) 1055 matches[tid] = m; 1056 } 1057 else 1058 matches[tid] = m; 1059 } 1060 } 1061 return matches; 1062 } (); 1063 1064 // Check for unreachable handlers 1065 static foreach(hid, handler; handlers) 1066 { 1067 static assert(matches[].canFind!(m => m.type != MatchType.NO && m.hid == hid), 1068 "handler #" ~ toCtString!hid ~ " " ~ 1069 "of type `" ~ ( __traits(isTemplate, handler) 1070 ? "template" 1071 : typeof(handler).stringof 1072 ) ~ "` " ~ 1073 "never matches" 1074 ); 1075 } 1076 1077 // Workaround for dlang issue 19993 1078 static foreach (size_t hid, handler; handlers) { 1079 mixin("alias handler", toCtString!hid, " = handler;"); 1080 } 1081 1082 final switch (node._tag) 1083 { 1084 static foreach (tid, T; AllTypes) 1085 { 1086 case tid: 1087 static if (matches[tid].type == MatchType.TPL) 1088 static if (is(T == typeof(null))) 1089 return mixin("handler", 1090 toCtString!(matches[tid].hid))(null); 1091 else 1092 return mixin("handler", 1093 toCtString!(matches[tid].hid))(node.get!T); 1094 else static if (matches[tid].type == MatchType.EMP) 1095 { 1096 alias h = handlers[matches[tid].hid]; 1097 static if (is(ReturnType!h == void)) 1098 { 1099 mixin("handler", toCtString!(matches[tid].hid))(); 1100 return 0; 1101 } 1102 else 1103 return mixin("handler", toCtString!(matches[tid].hid))(); 1104 } 1105 else static if (matches[tid].type == MatchType.FUN) 1106 { 1107 alias h = handlers[matches[tid].hid]; 1108 alias PT = Unqual!(Parameters!h[0]); 1109 static if (is(ReturnType!h == void)) 1110 { 1111 static if (isUniNodeArray!(PT, Node) || isUniNodeMapping!(PT, Node)) 1112 mixin("handler", 1113 toCtString!(matches[tid].hid))(node._val!T); 1114 else 1115 mixin("handler", 1116 toCtString!(matches[tid].hid))(node.get!(PT)); 1117 return 0; 1118 } 1119 else 1120 { 1121 static if (isUniNodeArray!(PT, Node) && isUniNodeMapping!(PT, Node)) 1122 return mixin("handler", 1123 toCtString!(matches[tid].hid))(node._val!(T)); 1124 else 1125 return mixin("handler", 1126 toCtString!(matches[tid].hid))(node.get!(PT)); 1127 } 1128 } 1129 else 1130 { 1131 static if(exhaustive) 1132 static assert(false, 1133 "No matching handler for type `" ~ T.stringof ~ "`"); 1134 else 1135 throw new MatchException( 1136 "No matching handler for type `" ~ T.stringof ~ "`"); 1137 } 1138 } 1139 } 1140 1141 assert (false); 1142 } 1143 } 1144 1145 1146 /** 1147 * Get distande by types 1148 */ 1149 template GetDistance(Node, Org, Trg) 1150 if (isUniNodeType!(Org, Node) || is (Org == typeof(null))) 1151 { 1152 static if (isBoolean!Org && isBoolean!Trg) 1153 enum GetDistance = 0; 1154 else static if (isSignedNumeric!Org && isSignedNumeric!Trg) 1155 enum GetDistance = Org.sizeof - Trg.sizeof; 1156 else static if (isUnsignedNumeric!Org && isUnsignedNumeric!Trg) 1157 enum GetDistance = Org.sizeof - Trg.sizeof; 1158 else static if (isFloatingPoint!Org && isFloatingPoint!Trg) 1159 enum GetDistance = Org.sizeof - Trg.sizeof; 1160 else static if (is(Org == string) && is (Trg == string)) 1161 enum GetDistance = 0; 1162 else static if (isRawData!Org && isRawData!Trg) 1163 enum GetDistance = 0; 1164 else static if (isUniNodeArray!(Org, Node) && isUniNodeArray!(Trg, Node)) 1165 enum GetDistance = 0; 1166 else static if (isUniNodeMapping!(Org, Node) && isUniNodeMapping!(Trg, Node)) 1167 enum GetDistance = 0; 1168 else static if (is(Org == typeof(null)) && is(Trg == typeof(null))) 1169 enum GetDistance = 0; 1170 else 1171 enum GetDistance = ubyte.max; 1172 } 1173 1174 1175 auto assumeSafe(F)(F fun) 1176 if (isFunctionPointer!F || isDelegate!F) 1177 { 1178 static if (functionAttributes!F & FunctionAttribute.safe) 1179 return fun; 1180 else 1181 return (ParameterTypeTuple!F args) @trusted { 1182 return fun(args); 1183 }; 1184 } 1185