1 module uim.json.parser; 2 // 3 //// Written in the D programming language. 4 // 5 ///** 6 //JavaScript Object Notation 7 // 8 //Synopsis: 9 //---- 10 // //parse a file or string of json into a usable structure 11 // string s = "{ \"language\": \"D\", \"rating\": 3.14, \"code\": \"42\" }"; 12 // JSNValue j = parseJSN(s); 13 // writeln("Language: ", j["language"].str(), 14 // " Rating: ", j["rating"].floating() 15 // ); 16 // 17 // // j and j["language"] return JSNValue, 18 // // j["language"].str returns a string 19 // 20 // //check a type 21 // long x; 22 // if (const(JSNValue)* code = "code" in j) 23 // { 24 // if (code.type() == JSN_TYPE.INTEGER) 25 // x = code.integer; 26 // else 27 // x = to!int(code.str); 28 // } 29 // 30 // // create a json struct 31 // JSNValue jj = [ "language": "D" ]; 32 // // rating doesnt exist yet, so use .object to assign 33 // jj.object["rating"] = JSNValue(3.14); 34 // // create an array to assign to list 35 // jj.object["list"] = JSNValue( ["a", "b", "c"] ); 36 // // list already exists, so .object optional 37 // jj["list"].array ~= JSNValue("D"); 38 // 39 // s = j.toString(); 40 // writeln(s); 41 //---- 42 // 43 //Copyright: Copyright Jeremie Pelletier 2008 - 2009. 44 //License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 45 //Authors: Jeremie Pelletier, David Herberth 46 //References: $(LINK http://json.org/) 47 //Source: $(PHOBOSSRC std/_json.d) 48 //*/ 49 ///* 50 // Copyright Jeremie Pelletier 2008 - 2009. 51 //Distributed under the Boost Software License, Version 1.0. 52 // (See accompanying file LICENSE_1_0.txt or copy at 53 // http://www.boost.org/LICENSE_1_0.txt) 54 //*/ 55 //module std.json; 56 // 57 //import std.conv; 58 //import std.range.primitives; 59 //import std.array; 60 //import std.traits; 61 // 62 ///** 63 //String literals used to represent special float values within JSN strings. 64 //*/ 65 //enum JSNFloatLiteral : string 66 //{ 67 // nan = "NaN", /// string representation of floating-point NaN 68 // inf = "Infinite", /// string representation of floating-point Infinity 69 // negativeInf = "-Infinite", /// string representation of floating-point negative Infinity 70 //} 71 // 72 ///** 73 //Flags that control how json is encoded and parsed. 74 //*/ 75 //enum JSNOptions 76 //{ 77 // none, /// standard parsing 78 // specialFloatLiterals = 0x1, /// encode NaN and Inf float values as strings 79 // escapeNonAsciiChars = 0x2 /// encode non ascii characters with an unicode escape sequence 80 //} 81 // 82 ///** 83 //JSN type enumeration 84 //*/ 85 //enum JSN_TYPE : byte 86 //{ 87 // /// Indicates the type of a $(D JSNValue). 88 // NULL, 89 // STRING, /// ditto 90 // INTEGER, /// ditto 91 // UINTEGER,/// ditto 92 // FLOAT, /// ditto 93 // OBJECT, /// ditto 94 // ARRAY, /// ditto 95 // TRUE, /// ditto 96 // FALSE /// ditto 97 //} 98 // 99 ///** 100 //JSN value node 101 //*/ 102 //struct JSNValue 103 //{ 104 // import std.exception : enforceEx, enforce; 105 // 106 // union Store 107 // { 108 // string str; 109 // long integer; 110 // ulong uinteger; 111 // double floating; 112 // JSNValue[string] object; 113 // JSNValue[] array; 114 // } 115 // private Store store; 116 // private JSN_TYPE type_tag; 117 // 118 // /** 119 // Returns the JSN_TYPE of the value stored in this structure. 120 // */ 121 // @property JSN_TYPE type() const pure nothrow @safe @nogc 122 // { 123 // return type_tag; 124 // } 125 // /// 126 // unittest 127 // { 128 // string s = "{ \"language\": \"D\" }"; 129 // JSNValue j = parseJSN(s); 130 // assert(j.type == JSN_TYPE.OBJECT); 131 // assert(j["language"].type == JSN_TYPE.STRING); 132 // } 133 // 134 // 135 // 136 // /// Value getter/setter for $(D JSN_TYPE.STRING). 137 // /// Throws: $(D JSNException) for read access if $(D type) is not 138 // /// $(D JSN_TYPE.STRING). 139 // @property string str() const pure @trusted 140 // { 141 // enforce!JSNException(type == JSN_TYPE.STRING, 142 // "JSNValue is not a string"); 143 // return store.str; 144 // } 145 // /// ditto 146 // @property string str(string v) pure nothrow @nogc @safe 147 // { 148 // assign(v); 149 // return v; 150 // } 151 // 152 // /// Value getter/setter for $(D JSN_TYPE.INTEGER). 153 // /// Throws: $(D JSNException) for read access if $(D type) is not 154 // /// $(D JSN_TYPE.INTEGER). 155 // @property inout(long) integer() inout pure @safe 156 // { 157 // enforce!JSNException(type == JSN_TYPE.INTEGER, 158 // "JSNValue is not an integer"); 159 // return store.integer; 160 // } 161 // /// ditto 162 // @property long integer(long v) pure nothrow @safe @nogc 163 // { 164 // assign(v); 165 // return store.integer; 166 // } 167 // 168 // /// Value getter/setter for $(D JSN_TYPE.UINTEGER). 169 // /// Throws: $(D JSNException) for read access if $(D type) is not 170 // /// $(D JSN_TYPE.UINTEGER). 171 // @property inout(ulong) uinteger() inout pure @safe 172 // { 173 // enforce!JSNException(type == JSN_TYPE.UINTEGER, 174 // "JSNValue is not an unsigned integer"); 175 // return store.uinteger; 176 // } 177 // /// ditto 178 // @property ulong uinteger(ulong v) pure nothrow @safe @nogc 179 // { 180 // assign(v); 181 // return store.uinteger; 182 // } 183 // 184 // /// Value getter/setter for $(D JSN_TYPE.FLOAT). Note that despite 185 // /// the name, this is a $(B 64)-bit `double`, not a 32-bit `float`. 186 // /// Throws: $(D JSNException) for read access if $(D type) is not 187 // /// $(D JSN_TYPE.FLOAT). 188 // @property inout(double) floating() inout pure @safe 189 // { 190 // enforce!JSNException(type == JSN_TYPE.FLOAT, 191 // "JSNValue is not a floating type"); 192 // return store.floating; 193 // } 194 // /// ditto 195 // @property double floating(double v) pure nothrow @safe @nogc 196 // { 197 // assign(v); 198 // return store.floating; 199 // } 200 // 201 // /// Value getter/setter for $(D JSN_TYPE.OBJECT). 202 // /// Throws: $(D JSNException) for read access if $(D type) is not 203 // /// $(D JSN_TYPE.OBJECT). 204 // /* Note: this is @system because of the following pattern: 205 // --- 206 // auto a = &(json.object()); 207 // json.uinteger = 0; // overwrite AA pointer 208 // (*a)["hello"] = "world"; // segmentation fault 209 // --- 210 // */ 211 // @property ref inout(JSNValue[string]) object() inout pure @system 212 // { 213 // enforce!JSNException(type == JSN_TYPE.OBJECT, 214 // "JSNValue is not an object"); 215 // return store.object; 216 // } 217 // /// ditto 218 // @property JSNValue[string] object(JSNValue[string] v) pure nothrow @nogc @safe 219 // { 220 // assign(v); 221 // return v; 222 // } 223 // 224 // /// Value getter for $(D JSN_TYPE.OBJECT). 225 // /// Unlike $(D object), this retrieves the object by value and can be used in @safe code. 226 // /// 227 // /// A caveat is that, if the returned value is null, modifications will not be visible: 228 // /// --- 229 // /// JSNValue json; 230 // /// json.object = null; 231 // /// json.objectNoRef["hello"] = JSNValue("world"); 232 // /// assert("hello" !in json.object); 233 // /// --- 234 // /// 235 // /// Throws: $(D JSNException) for read access if $(D type) is not 236 // /// $(D JSN_TYPE.OBJECT). 237 // @property inout(JSNValue[string]) objectNoRef() inout pure @trusted 238 // { 239 // enforce!JSNException(type == JSN_TYPE.OBJECT, 240 // "JSNValue is not an object"); 241 // return store.object; 242 // } 243 // 244 // /// Value getter/setter for $(D JSN_TYPE.ARRAY). 245 // /// Throws: $(D JSNException) for read access if $(D type) is not 246 // /// $(D JSN_TYPE.ARRAY). 247 // /* Note: this is @system because of the following pattern: 248 // --- 249 // auto a = &(json.array()); 250 // json.uinteger = 0; // overwrite array pointer 251 // (*a)[0] = "world"; // segmentation fault 252 // --- 253 // */ 254 // @property ref inout(JSNValue[]) array() inout pure @system 255 // { 256 // enforce!JSNException(type == JSN_TYPE.ARRAY, 257 // "JSNValue is not an array"); 258 // return store.array; 259 // } 260 // /// ditto 261 // @property JSNValue[] array(JSNValue[] v) pure nothrow @nogc @safe 262 // { 263 // assign(v); 264 // return v; 265 // } 266 // 267 // /// Value getter for $(D JSN_TYPE.ARRAY). 268 // /// Unlike $(D array), this retrieves the array by value and can be used in @safe code. 269 // /// 270 // /// A caveat is that, if you append to the returned array, the new values aren't visible in the 271 // /// JSNValue: 272 // /// --- 273 // /// JSNValue json; 274 // /// json.array = [JSNValue("hello")]; 275 // /// json.arrayNoRef ~= JSNValue("world"); 276 // /// assert(json.array.length == 1); 277 // /// --- 278 // /// 279 // /// Throws: $(D JSNException) for read access if $(D type) is not 280 // /// $(D JSN_TYPE.ARRAY). 281 // @property inout(JSNValue[]) arrayNoRef() inout pure @trusted 282 // { 283 // enforce!JSNException(type == JSN_TYPE.ARRAY, 284 // "JSNValue is not an array"); 285 // return store.array; 286 // } 287 // 288 // /// Test whether the type is $(D JSN_TYPE.NULL) 289 // @property bool isNull() const pure nothrow @safe @nogc 290 // { 291 // return type == JSN_TYPE.NULL; 292 // } 293 // 294 // private void assign(T)(T arg) @safe 295 // { 296 // static if (is(T : typeof(null))) 297 // { 298 // type_tag = JSN_TYPE.NULL; 299 // } 300 // else static if (is(T : string)) 301 // { 302 // type_tag = JSN_TYPE.STRING; 303 // store.str = arg; 304 // } 305 // else static if (isSomeString!T) // issue 15884 306 // { 307 // type_tag = JSN_TYPE.STRING; 308 // // FIXME: std.array.array(Range) is not deduced as 'pure' 309 // () @trusted { 310 // import std.utf : byUTF; 311 // store.str = cast(immutable)(arg.byUTF!char.array); 312 // }(); 313 // } 314 // else static if (is(T : bool)) 315 // { 316 // type_tag = arg ? JSN_TYPE.TRUE : JSN_TYPE.FALSE; 317 // } 318 // else static if (is(T : ulong) && isUnsigned!T) 319 // { 320 // type_tag = JSN_TYPE.UINTEGER; 321 // store.uinteger = arg; 322 // } 323 // else static if (is(T : long)) 324 // { 325 // type_tag = JSN_TYPE.INTEGER; 326 // store.integer = arg; 327 // } 328 // else static if (isFloatingPoint!T) 329 // { 330 // type_tag = JSN_TYPE.FLOAT; 331 // store.floating = arg; 332 // } 333 // else static if (is(T : Value[Key], Key, Value)) 334 // { 335 // static assert(is(Key : string), "AA key must be string"); 336 // type_tag = JSN_TYPE.OBJECT; 337 // static if (is(Value : JSNValue)) 338 // { 339 // store.object = arg; 340 // } 341 // else 342 // { 343 // JSNValue[string] aa; 344 // foreach (key, value; arg) 345 // aa[key] = JSNValue(value); 346 // store.object = aa; 347 // } 348 // } 349 // else static if (isArray!T) 350 // { 351 // type_tag = JSN_TYPE.ARRAY; 352 // static if (is(ElementEncodingType!T : JSNValue)) 353 // { 354 // store.array = arg; 355 // } 356 // else 357 // { 358 // JSNValue[] new_arg = new JSNValue[arg.length]; 359 // foreach (i, e; arg) 360 // new_arg[i] = JSNValue(e); 361 // store.array = new_arg; 362 // } 363 // } 364 // else static if (is(T : JSNValue)) 365 // { 366 // type_tag = arg.type; 367 // store = arg.store; 368 // } 369 // else 370 // { 371 // static assert(false, text(`unable to convert type "`, T.stringof, `" to json`)); 372 // } 373 // } 374 // 375 // private void assignRef(T)(ref T arg) if (isStaticArray!T) 376 // { 377 // type_tag = JSN_TYPE.ARRAY; 378 // static if (is(ElementEncodingType!T : JSNValue)) 379 // { 380 // store.array = arg; 381 // } 382 // else 383 // { 384 // JSNValue[] new_arg = new JSNValue[arg.length]; 385 // foreach (i, e; arg) 386 // new_arg[i] = JSNValue(e); 387 // store.array = new_arg; 388 // } 389 // } 390 // 391 // /** 392 // * Constructor for $(D JSNValue). If $(D arg) is a $(D JSNValue) 393 // * its value and type will be copied to the new $(D JSNValue). 394 // * Note that this is a shallow copy: if type is $(D JSN_TYPE.OBJECT) 395 // * or $(D JSN_TYPE.ARRAY) then only the reference to the data will 396 // * be copied. 397 // * Otherwise, $(D arg) must be implicitly convertible to one of the 398 // * following types: $(D typeof(null)), $(D string), $(D ulong), 399 // * $(D long), $(D double), an associative array $(D V[K]) for any $(D V) 400 // * and $(D K) i.e. a JSN object, any array or $(D bool). The type will 401 // * be set accordingly. 402 // */ 403 // this(T)(T arg) if (!isStaticArray!T) 404 // { 405 // assign(arg); 406 // } 407 // /// Ditto 408 // this(T)(ref T arg) if (isStaticArray!T) 409 // { 410 // assignRef(arg); 411 // } 412 // /// Ditto 413 // this(T : JSNValue)(inout T arg) inout 414 // { 415 // store = arg.store; 416 // type_tag = arg.type; 417 // } 418 // /// 419 // unittest 420 // { 421 // JSNValue j = JSNValue( "a string" ); 422 // j = JSNValue(42); 423 // 424 // j = JSNValue( [1, 2, 3] ); 425 // assert(j.type == JSN_TYPE.ARRAY); 426 // 427 // j = JSNValue( ["language": "D"] ); 428 // assert(j.type == JSN_TYPE.OBJECT); 429 // } 430 // 431 // void opAssign(T)(T arg) if (!isStaticArray!T && !is(T : JSNValue)) 432 // { 433 // assign(arg); 434 // } 435 // 436 // void opAssign(T)(ref T arg) if (isStaticArray!T) 437 // { 438 // assignRef(arg); 439 // } 440 // 441 // /// Array syntax for json arrays. 442 // /// Throws: $(D JSNException) if $(D type) is not $(D JSN_TYPE.ARRAY). 443 // ref inout(JSNValue) opIndex(size_t i) inout pure @safe 444 // { 445 // auto a = this.arrayNoRef; 446 // enforceEx!JSNException(i < a.length, 447 // "JSNValue array index is out of range"); 448 // return a[i]; 449 // } 450 // /// 451 // unittest 452 // { 453 // JSNValue j = JSNValue( [42, 43, 44] ); 454 // assert( j[0].integer == 42 ); 455 // assert( j[1].integer == 43 ); 456 // } 457 // 458 // /// Hash syntax for json objects. 459 // /// Throws: $(D JSNException) if $(D type) is not $(D JSN_TYPE.OBJECT). 460 // ref inout(JSNValue) opIndex(string k) inout pure @safe 461 // { 462 // auto o = this.objectNoRef; 463 // return *enforce!JSNException(k in o, 464 // "Key not found: " ~ k); 465 // } 466 // /// 467 // unittest 468 // { 469 // JSNValue j = JSNValue( ["language": "D"] ); 470 // assert( j["language"].str == "D" ); 471 // } 472 // 473 // /// Operator sets $(D value) for element of JSN object by $(D key). 474 // /// 475 // /// If JSN value is null, then operator initializes it with object and then 476 // /// sets $(D value) for it. 477 // /// 478 // /// Throws: $(D JSNException) if $(D type) is not $(D JSN_TYPE.OBJECT) 479 // /// or $(D JSN_TYPE.NULL). 480 // void opIndexAssign(T)(auto ref T value, string key) pure 481 // { 482 // enforceEx!JSNException(type == JSN_TYPE.OBJECT || type == JSN_TYPE.NULL, 483 // "JSNValue must be object or null"); 484 // JSNValue[string] aa = null; 485 // if (type == JSN_TYPE.OBJECT) 486 // { 487 // aa = this.objectNoRef; 488 // } 489 // 490 // aa[key] = value; 491 // this.object = aa; 492 // } 493 // /// 494 // unittest 495 // { 496 // JSNValue j = JSNValue( ["language": "D"] ); 497 // j["language"].str = "Perl"; 498 // assert( j["language"].str == "Perl" ); 499 // } 500 // 501 // void opIndexAssign(T)(T arg, size_t i) pure 502 // { 503 // auto a = this.arrayNoRef; 504 // enforceEx!JSNException(i < a.length, 505 // "JSNValue array index is out of range"); 506 // a[i] = arg; 507 // this.array = a; 508 // } 509 // /// 510 // unittest 511 // { 512 // JSNValue j = JSNValue( ["Perl", "C"] ); 513 // j[1].str = "D"; 514 // assert( j[1].str == "D" ); 515 // } 516 // 517 // JSNValue opBinary(string op : "~", T)(T arg) @safe 518 // { 519 // auto a = this.arrayNoRef; 520 // static if (isArray!T) 521 // { 522 // return JSNValue(a ~ JSNValue(arg).arrayNoRef); 523 // } 524 // else static if (is(T : JSNValue)) 525 // { 526 // return JSNValue(a ~ arg.arrayNoRef); 527 // } 528 // else 529 // { 530 // static assert(false, "argument is not an array or a JSNValue array"); 531 // } 532 // } 533 // 534 // void opOpAssign(string op : "~", T)(T arg) @safe 535 // { 536 // auto a = this.arrayNoRef; 537 // static if (isArray!T) 538 // { 539 // a ~= JSNValue(arg).arrayNoRef; 540 // } 541 // else static if (is(T : JSNValue)) 542 // { 543 // a ~= arg.arrayNoRef; 544 // } 545 // else 546 // { 547 // static assert(false, "argument is not an array or a JSNValue array"); 548 // } 549 // this.array = a; 550 // } 551 // 552 // /** 553 // * Support for the $(D in) operator. 554 // * 555 // * Tests wether a key can be found in an object. 556 // * 557 // * Returns: 558 // * when found, the $(D const(JSNValue)*) that matches to the key, 559 // * otherwise $(D null). 560 // * 561 // * Throws: $(D JSNException) if the right hand side argument $(D JSN_TYPE) 562 // * is not $(D OBJECT). 563 // */ 564 // auto opBinaryRight(string op : "in")(string k) const @safe 565 // { 566 // return k in this.objectNoRef; 567 // } 568 // /// 569 // unittest 570 // { 571 // JSNValue j = [ "language": "D", "author": "walter" ]; 572 // string a = ("author" in j).str; 573 // } 574 // 575 // bool opEquals(const JSNValue rhs) const @nogc nothrow pure @safe 576 // { 577 // return opEquals(rhs); 578 // } 579 // 580 // bool opEquals(ref const JSNValue rhs) const @nogc nothrow pure @trusted 581 // { 582 // // Default doesn't work well since store is a union. Compare only 583 // // what should be in store. 584 // // This is @trusted to remain nogc, nothrow, fast, and usable from @safe code. 585 // if (type_tag != rhs.type_tag) return false; 586 // 587 // final switch (type_tag) 588 // { 589 // case JSN_TYPE.STRING: 590 // return store.str == rhs.store.str; 591 // case JSN_TYPE.INTEGER: 592 // return store.integer == rhs.store.integer; 593 // case JSN_TYPE.UINTEGER: 594 // return store.uinteger == rhs.store.uinteger; 595 // case JSN_TYPE.FLOAT: 596 // return store.floating == rhs.store.floating; 597 // case JSN_TYPE.OBJECT: 598 // return store.object == rhs.store.object; 599 // case JSN_TYPE.ARRAY: 600 // return store.array == rhs.store.array; 601 // case JSN_TYPE.TRUE: 602 // case JSN_TYPE.FALSE: 603 // case JSN_TYPE.NULL: 604 // return true; 605 // } 606 // } 607 // 608 // /// Implements the foreach $(D opApply) interface for json arrays. 609 // int opApply(int delegate(size_t index, ref JSNValue) dg) @system 610 // { 611 // int result; 612 // 613 // foreach (size_t index, ref value; array) 614 // { 615 // result = dg(index, value); 616 // if (result) 617 // break; 618 // } 619 // 620 // return result; 621 // } 622 // 623 // /// Implements the foreach $(D opApply) interface for json objects. 624 // int opApply(int delegate(string key, ref JSNValue) dg) @system 625 // { 626 // enforce!JSNException(type == JSN_TYPE.OBJECT, 627 // "JSNValue is not an object"); 628 // int result; 629 // 630 // foreach (string key, ref value; object) 631 // { 632 // result = dg(key, value); 633 // if (result) 634 // break; 635 // } 636 // 637 // return result; 638 // } 639 // 640 // /// Implicitly calls $(D toJSN) on this JSNValue. 641 // /// 642 // /// $(I options) can be used to tweak the conversion behavior. 643 // string toString(in JSNOptions options = JSNOptions.none) const @safe 644 // { 645 // return toJSN(this, false, options); 646 // } 647 // 648 // /// Implicitly calls $(D toJSN) on this JSNValue, like $(D toString), but 649 // /// also passes $(I true) as $(I pretty) argument. 650 // /// 651 // /// $(I options) can be used to tweak the conversion behavior 652 // string toPrettyString(in JSNOptions options = JSNOptions.none) const @safe 653 // { 654 // return toJSN(this, true, options); 655 // } 656 //} 657 // 658 ///** 659 //Parses a serialized string and returns a tree of JSN values. 660 //Throws: $(LREF JSNException) if the depth exceeds the max depth. 661 //Params: 662 // json = json-formatted string to parse 663 // maxDepth = maximum depth of nesting allowed, -1 disables depth checking 664 // options = enable decoding string representations of NaN/Inf as float values 665 //*/ 666 //JSNValue parseJSN(T)(T json, int maxDepth = -1, JSNOptions options = JSNOptions.none) 667 // if (isInputRange!T) 668 //{ 669 // import std.ascii : isWhite, isDigit, isHexDigit, toUpper, toLower; 670 // import std.utf : toUTF8; 671 // 672 // JSNValue root; 673 // root.type_tag = JSN_TYPE.NULL; 674 // 675 // if (json.empty) return root; 676 // 677 // int depth = -1; 678 // dchar next = 0; 679 // int line = 1, pos = 0; 680 // 681 // /* */ 682 // void error(string msg) 683 // { 684 // throw new JSNException(msg, line, pos); 685 // } 686 // 687 // dchar popChar() 688 // { 689 // if (json.empty) error("Unexpected end of data."); 690 // dchar c = json.front; 691 // json.popFront(); 692 // 693 // if (c == '\n') { 694 // line++; 695 // pos = 0; 696 // } 697 // else { 698 // pos++; 699 // } 700 // 701 // return c; 702 // } 703 // 704 // dchar peekChar() { 705 // if (!next) { 706 // if (json.empty) return '\0'; 707 // next = popChar(); 708 // } 709 // return next; 710 // } 711 // 712 // void skipWhitespace() { 713 // while (isWhite(peekChar())) next = 0; 714 // } 715 // 716 // dchar getChar(bool SkipWhitespace = false)() { 717 // static if (SkipWhitespace) skipWhitespace(); 718 // 719 // dchar c; 720 // if (next) { 721 // c = next; 722 // next = 0; 723 // } 724 // else c = popChar(); 725 // 726 // return c; 727 // } 728 // 729 // void checkChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c) { 730 // static if (SkipWhitespace) skipWhitespace(); 731 // auto c2 = getChar(); 732 // static if (!CaseSensitive) c2 = toLower(c2); 733 // 734 // if (c2 != c) error(text("Found '", c2, "' when expecting '", c, "'.")); 735 // } 736 // 737 // bool testChar(bool SkipWhitespace = true, bool CaseSensitive = true)(char c) { 738 // static if (SkipWhitespace) skipWhitespace(); 739 // auto c2 = peekChar(); 740 // static if (!CaseSensitive) c2 = toLower(c2); 741 // 742 // if (c2 != c) return false; 743 // 744 // getChar(); 745 // return true; 746 // } 747 // 748 // string parseString() { 749 // auto str = appender!string(); 750 // 751 // Next: 752 // switch (peekChar()) { 753 // case '"': 754 // getChar(); 755 // break; 756 // 757 // case '\\': 758 // getChar(); 759 // auto c = getChar(); 760 // switch (c) { 761 // case '"': str.put('"'); break; 762 // case '\\': str.put('\\'); break; 763 // case '/': str.put('/'); break; 764 // case 'b': str.put('\b'); break; 765 // case 'f': str.put('\f'); break; 766 // case 'n': str.put('\n'); break; 767 // case 'r': str.put('\r'); break; 768 // case 't': str.put('\t'); break; 769 // case 'u': 770 // dchar val = 0; 771 // foreach_reverse (i; 0 .. 4) 772 // { 773 // auto hex = toUpper(getChar()); 774 // if (!isHexDigit(hex)) error("Expecting hex character"); 775 // val += (isDigit(hex) ? hex - '0' : hex - ('A' - 10)) << (4 * i); 776 // } 777 // char[4] buf; 778 // str.put(toUTF8(buf, val)); 779 // break; 780 // 781 // default: 782 // error(text("Invalid escape sequence '\\", c, "'.")); 783 // } 784 // goto Next; 785 // 786 // default: 787 // auto c = getChar(); 788 // appendJSNChar(str, c, options, &error); 789 // goto Next; 790 // } 791 // 792 // return str.data.length ? str.data : ""; 793 // } 794 // 795 // bool tryGetSpecialFloat(string str, out double val) { 796 // switch (str) 797 // { 798 // case JSNFloatLiteral.nan: 799 // val = double.nan; 800 // return true; 801 // case JSNFloatLiteral.inf: 802 // val = double.infinity; 803 // return true; 804 // case JSNFloatLiteral.negativeInf: 805 // val = -double.infinity; 806 // return true; 807 // default: 808 // return false; 809 // } 810 // } 811 // 812 // void parseValue(ref JSNValue value) 813 // { 814 // auto c = getChar!true(); 815 // 816 // switch (c) 817 // { 818 // case '{': 819 // if (testChar('}')) 820 // { 821 // value.object = null; 822 // break; 823 // } 824 // 825 // JSNValue[string] obj; 826 // do 827 // { 828 // checkChar('"'); 829 // string name = parseString(); 830 // checkChar(':'); 831 // JSNValue member; 832 // parseValue(member); 833 // obj[name] = member; 834 // } 835 // while (testChar(',')); 836 // value.object = obj; 837 // 838 // checkChar('}'); 839 // break; 840 // 841 // case '[': 842 // if (testChar(']')) 843 // { 844 // value.type_tag = JSN_TYPE.ARRAY; 845 // break; 846 // } 847 // 848 // JSNValue[] arr; 849 // do 850 // { 851 // JSNValue element; 852 // parseValue(element); 853 // arr ~= element; 854 // } 855 // while (testChar(',')); 856 // 857 // checkChar(']'); 858 // value.array = arr; 859 // break; 860 // 861 // case '"': 862 // auto str = parseString(); 863 // 864 // // if special float parsing is enabled, check if string represents NaN/Inf 865 // if ((options & JSNOptions.specialFloatLiterals) && 866 // tryGetSpecialFloat(str, value.store.floating)) 867 // { 868 // // found a special float, its value was placed in value.store.floating 869 // value.type_tag = JSN_TYPE.FLOAT; 870 // break; 871 // } 872 // 873 // value.type_tag = JSN_TYPE.STRING; 874 // value.store.str = str; 875 // break; 876 // 877 // case '0': .. case '9': 878 // case '-': 879 // auto number = appender!string(); 880 // bool isFloat, isNegative; 881 // 882 // void readInteger() 883 // { 884 // if (!isDigit(c)) error("Digit expected"); 885 // 886 // Next: number.put(c); 887 // 888 // if (isDigit(peekChar())) 889 // { 890 // c = getChar(); 891 // goto Next; 892 // } 893 // } 894 // 895 // if (c == '-') 896 // { 897 // number.put('-'); 898 // c = getChar(); 899 // isNegative = true; 900 // } 901 // 902 // readInteger(); 903 // 904 // if (testChar('.')) 905 // { 906 // isFloat = true; 907 // number.put('.'); 908 // c = getChar(); 909 // readInteger(); 910 // } 911 // if (testChar!(false, false)('e')) 912 // { 913 // isFloat = true; 914 // number.put('e'); 915 // if (testChar('+')) number.put('+'); 916 // else if (testChar('-')) number.put('-'); 917 // c = getChar(); 918 // readInteger(); 919 // } 920 // 921 // string data = number.data; 922 // if (isFloat) 923 // { 924 // value.type_tag = JSN_TYPE.FLOAT; 925 // value.store.floating = parse!double(data); 926 // } 927 // else 928 // { 929 // if (isNegative) 930 // value.store.integer = parse!long(data); 931 // else 932 // value.store.uinteger = parse!ulong(data); 933 // 934 // value.type_tag = !isNegative && value.store.uinteger & (1UL << 63) ? 935 // JSN_TYPE.UINTEGER : JSN_TYPE.INTEGER; 936 // } 937 // break; 938 // 939 // case 't': 940 // case 'T': 941 // value.type_tag = JSN_TYPE.TRUE; 942 // checkChar!(false, false)('r'); 943 // checkChar!(false, false)('u'); 944 // checkChar!(false, false)('e'); 945 // break; 946 // 947 // case 'f': 948 // case 'F': 949 // value.type_tag = JSN_TYPE.FALSE; 950 // checkChar!(false, false)('a'); 951 // checkChar!(false, false)('l'); 952 // checkChar!(false, false)('s'); 953 // checkChar!(false, false)('e'); 954 // break; 955 // 956 // case 'n': 957 // case 'N': 958 // value.type_tag = JSN_TYPE.NULL; 959 // checkChar!(false, false)('u'); 960 // checkChar!(false, false)('l'); 961 // checkChar!(false, false)('l'); 962 // break; 963 // 964 // default: 965 // error(text("Unexpected character '", c, "'.")); 966 // } 967 // 968 // depth--; 969 // } 970 // 971 // parseValue(root); 972 // return root; 973 //} 974 // 975 // 976 ///* 977 //Parses a serialized string and returns a tree of JSN values. 978 //Throws: $(REF JSNException, std,json) if the depth exceeds the max depth. 979 //Params: 980 // json = json-formatted string to parse 981 // options = enable decoding string representations of NaN/Inf as float values 982 //*/ 983 //JSNValue parseJSN(T)(T json, JSNOptions options) 984 // if (isInputRange!T) 985 //{ 986 // return parseJSN!T(json, -1, options); 987 //} 988 // 989 //deprecated( 990 // "Please use the overload that takes a ref JSNValue rather than a pointer. This overload will " 991 // ~ "be removed in November 2017.") 992 // string toJSN(in JSNValue* root, in bool pretty = false, in JSNOptions options = JSNOptions.none) @safe 993 //{ 994 // return toJSN(*root, pretty, options); 995 //} 996 // 997 ///** 998 //Takes a tree of JSN values and returns the serialized string. 999 // 1000 //Any Object types will be serialized in a key-sorted order. 1001 // 1002 //If $(D pretty) is false no whitespaces are generated. 1003 //If $(D pretty) is true serialized string is formatted to be human-readable. 1004 //Set the $(specialFloatLiterals) flag is set in $(D options) to encode NaN/Infinity as strings. 1005 //*/ 1006 //string toJSN(const ref JSNValue root, in bool pretty = false, in JSNOptions options = JSNOptions.none) @safe 1007 //{ 1008 // auto json = appender!string(); 1009 // 1010 // void toString(string str) @safe 1011 // { 1012 // json.put('"'); 1013 // 1014 // foreach (dchar c; str) 1015 // { 1016 // switch (c) 1017 // { 1018 // case '"': json.put("\\\""); break; 1019 // case '\\': json.put("\\\\"); break; 1020 // case '/': json.put("\\/"); break; 1021 // case '\b': json.put("\\b"); break; 1022 // case '\f': json.put("\\f"); break; 1023 // case '\n': json.put("\\n"); break; 1024 // case '\r': json.put("\\r"); break; 1025 // case '\t': json.put("\\t"); break; 1026 // default: 1027 // appendJSNChar(json, c, options, 1028 // (msg) { throw new JSNException(msg); }); 1029 // } 1030 // } 1031 // 1032 // json.put('"'); 1033 // } 1034 // 1035 // void toValue(ref in JSNValue value, ulong indentLevel) @safe 1036 // { 1037 // void putTabs(ulong additionalIndent = 0) 1038 // { 1039 // if (pretty) 1040 // foreach (i; 0 .. indentLevel + additionalIndent) 1041 // json.put(" "); 1042 // } 1043 // void putEOL() 1044 // { 1045 // if (pretty) 1046 // json.put('\n'); 1047 // } 1048 // void putCharAndEOL(char ch) 1049 // { 1050 // json.put(ch); 1051 // putEOL(); 1052 // } 1053 // 1054 // final switch (value.type) 1055 // { 1056 // case JSN_TYPE.OBJECT: 1057 // auto obj = value.objectNoRef; 1058 // if (!obj.length) 1059 // { 1060 // json.put("{}"); 1061 // } 1062 // else 1063 // { 1064 // putCharAndEOL('{'); 1065 // bool first = true; 1066 // 1067 // void emit(R)(R names) 1068 // { 1069 // foreach (name; names) 1070 // { 1071 // auto member = obj[name]; 1072 // if (!first) 1073 // putCharAndEOL(','); 1074 // first = false; 1075 // putTabs(1); 1076 // toString(name); 1077 // json.put(':'); 1078 // if (pretty) 1079 // json.put(' '); 1080 // toValue(member, indentLevel + 1); 1081 // } 1082 // } 1083 // 1084 // import std.algorithm : sort; 1085 // import std.array; 1086 // // @@@BUG@@@ 14439 1087 // // auto names = obj.keys; // aa.keys can't be called in @safe code 1088 // auto names = new string[obj.length]; 1089 // size_t i = 0; 1090 // foreach (k, v; obj) 1091 // { 1092 // names[i] = k; 1093 // i++; 1094 // } 1095 // sort(names); 1096 // emit(names); 1097 // 1098 // putEOL(); 1099 // putTabs(); 1100 // json.put('}'); 1101 // } 1102 // break; 1103 // 1104 // case JSN_TYPE.ARRAY: 1105 // auto arr = value.arrayNoRef; 1106 // if (arr.empty) 1107 // { 1108 // json.put("[]"); 1109 // } 1110 // else 1111 // { 1112 // putCharAndEOL('['); 1113 // foreach (i, el; arr) 1114 // { 1115 // if (i) 1116 // putCharAndEOL(','); 1117 // putTabs(1); 1118 // toValue(el, indentLevel + 1); 1119 // } 1120 // putEOL(); 1121 // putTabs(); 1122 // json.put(']'); 1123 // } 1124 // break; 1125 // 1126 // case JSN_TYPE.STRING: 1127 // toString(value.str); 1128 // break; 1129 // 1130 // case JSN_TYPE.INTEGER: 1131 // json.put(to!string(value.store.integer)); 1132 // break; 1133 // 1134 // case JSN_TYPE.UINTEGER: 1135 // json.put(to!string(value.store.uinteger)); 1136 // break; 1137 // 1138 // case JSN_TYPE.FLOAT: 1139 // import std.math : isNaN, isInfinity; 1140 // 1141 // auto val = value.store.floating; 1142 // 1143 // if (val.isNaN) 1144 // { 1145 // if (options & JSNOptions.specialFloatLiterals) 1146 // { 1147 // toString(JSNFloatLiteral.nan); 1148 // } 1149 // else 1150 // { 1151 // throw new JSNException( 1152 // "Cannot encode NaN. Consider passing the specialFloatLiterals flag."); 1153 // } 1154 // } 1155 // else if (val.isInfinity) 1156 // { 1157 // if (options & JSNOptions.specialFloatLiterals) 1158 // { 1159 // toString((val > 0) ? JSNFloatLiteral.inf : JSNFloatLiteral.negativeInf); 1160 // } 1161 // else 1162 // { 1163 // throw new JSNException( 1164 // "Cannot encode Infinity. Consider passing the specialFloatLiterals flag."); 1165 // } 1166 // } 1167 // else 1168 // { 1169 // import std.format : format; 1170 // // The correct formula for the number of decimal digits needed for lossless round 1171 // // trips is actually: 1172 // // ceil(log(pow(2.0, double.mant_dig - 1)) / log(10.0) + 1) == (double.dig + 2) 1173 // // Anything less will round off (1 + double.epsilon) 1174 // json.put("%.18g".format(val)); 1175 // } 1176 // break; 1177 // 1178 // case JSN_TYPE.TRUE: 1179 // json.put("true"); 1180 // break; 1181 // 1182 // case JSN_TYPE.FALSE: 1183 // json.put("false"); 1184 // break; 1185 // 1186 // case JSN_TYPE.NULL: 1187 // json.put("null"); 1188 // break; 1189 // } 1190 // } 1191 // 1192 // toValue(root, 0); 1193 // return json.data; 1194 //} 1195 // 1196 //private void appendJSNChar(ref Appender!string dst, dchar c, JSNOptions opts, 1197 // scope void delegate(string) error) @safe 1198 //{ 1199 // import std.uni : isControl; 1200 // 1201 // with (JSNOptions) if (isControl(c) || 1202 // ((opts & escapeNonAsciiChars) >= escapeNonAsciiChars && c >= 0x80)) 1203 // { 1204 // dst.put("\\u"); 1205 // foreach_reverse (i; 0 .. 4) 1206 // { 1207 // char ch = (c >>> (4 * i)) & 0x0f; 1208 // ch += ch < 10 ? '0' : 'A' - 10; 1209 // dst.put(ch); 1210 // } 1211 // } 1212 // else 1213 // { 1214 // dst.put(c); 1215 // } 1216 //} 1217 // 1218 // 1219 // 1220 ///** 1221 //Exception thrown on JSN errors 1222 //*/ 1223 //class JSNException : Exception 1224 //{ 1225 // this(string msg, int line = 0, int pos = 0) pure nothrow @safe 1226 // { 1227 // if (line) 1228 // super(text(msg, " (Line ", line, ":", pos, ")")); 1229 // else 1230 // super(msg); 1231 // } 1232 // 1233 // this(string msg, string file, size_t line) pure nothrow @safe 1234 // { 1235 // super(msg, file, line); 1236 // } 1237 //} 1238 //