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