1 /**
2  * Provides various means for parsing JSON documents.
3  *
4  * This module contains two different parser implementations. The first
5  * implementation returns a single JSON document in the form of a
6  * $(D JSONValue), while the second implementation returns a stream
7  * of nodes. The stream based parser is particularly useful for
8  * deserializing with few allocations or for processing large documents.
9  *
10  * Copyright: Copyright 2012 - 2015, Sönke Ludwig.
11  * License:   $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
12  * Authors:   Sönke Ludwig
13  * Source:    $(PHOBOSSRC std/data/json/parser.d)
14  */
15 module funkwerk.stdx.data.json.parser;
16 
17 ///
18 unittest
19 {
20     import std.algorithm : equal, map;
21     import std.format : format;
22 
23     // Parse a JSON string to a single value
24     JSONValue value = toJSONValue(`{"name": "D", "kind": "language"}`);
25 
26     // Parse a JSON string to a node stream
27     auto nodes = parseJSONStream(`{"name": "D", "kind": "language"}`);
28     with (JSONParserNodeKind) {
29         assert(nodes.map!(n => n.kind).equal(
30             [objectStart, key, literal, key, literal, objectEnd]));
31     }
32 
33     // Parse a list of tokens instead of a string
34     auto tokens = lexJSON(`{"name": "D", "kind": "language"}`);
35     JSONValue value2 = toJSONValue(tokens);
36     assert(value == value2, format!"%s != %s"(value, value2));
37 }
38 
39 import std.array : appender;
40 import std.range : ElementType, isInputRange;
41 import std.traits : isIntegral, isSomeChar;
42 
43 import funkwerk.stdx.data.json.lexer;
44 import funkwerk.stdx.data.json.value;
45 
46 /// The default amount of nesting in the input allowed by `toJSONValue` and `parseJSONValue`.
47 enum defaultMaxDepth = 512;
48 
49 /**
50  * Parses a JSON string or token range and returns the result as a
51  * `JSONValue`.
52  *
53  * The input string must be a valid JSON document. In particular, it must not
54  * contain any additional text other than whitespace after the end of the
55  * JSON document.
56  *
57  * See_also: `parseJSONValue`
58  */
59 JSONValue toJSONValue(LexOptions options = LexOptions.init, Input)(Input input, string filename = "", int maxDepth = defaultMaxDepth)
60     if (isInputRange!Input && (isSomeChar!(ElementType!Input) || isIntegral!(ElementType!Input)))
61 {
62     auto tokens = lexJSON!options(input, filename);
63     return toJSONValue(tokens, maxDepth);
64 }
65 /// ditto
66 JSONValue toJSONValue(Input)(Input tokens, int maxDepth = defaultMaxDepth)
67     if (isJSONTokenInputRange!Input)
68 {
69     import funkwerk.stdx.data.json.foundation;
70     auto ret = parseJSONValue(tokens, maxDepth);
71     enforceJson(tokens.empty, "Unexpected characters following JSON", tokens.location);
72     return ret;
73 }
74 
75 ///
76 @safe unittest
77 {
78     // parse a simple number
79     JSONValue a = toJSONValue(`1.0`);
80     assert(a == 1.0);
81 
82     // parse an object
83     JSONValue b = toJSONValue(`{"a": true, "b": "test"}`);
84     auto bdoc = b.get!(JSONValue[string]);
85     assert(bdoc.length == 2);
86     assert(bdoc["a"] == true);
87     assert(bdoc["b"] == "test");
88 
89     // parse an array
90     JSONValue c = toJSONValue(`[1, 2, null]`);
91     auto cdoc = c.get!(JSONValue[]);
92     assert(cdoc.length == 3);
93     assert(cdoc[0] == 1.0);
94     assert(cdoc[1] == 2.0);
95     assert(cdoc[2] == null);
96 
97     import std.conv;
98     JSONValue jv = toJSONValue(`{ "a": 1234 }`);
99     assert(jv["a"].to!int == 1234);
100 }
101 
102 unittest { // issue #22
103     import std.conv;
104     JSONValue jv = toJSONValue(`{ "a": 1234 }`);
105     assert(jv["a"].to!int == 1234);
106 }
107 
108 /*unittest
109 {
110     import std.bigint;
111     auto v = toJSONValue!(LexOptions.useBigInt)(`{"big": 12345678901234567890}`);
112     assert(v["big"].value == BigInt("12345678901234567890"));
113 }*/
114 
115 @safe unittest
116 {
117     import std.exception;
118     assertNotThrown(toJSONValue("{} \t\r\n"));
119     assertThrown(toJSONValue(`{} {}`));
120 }
121 
122 
123 /**
124  * Consumes a single JSON value from the input range and returns the result as a
125  * `JSONValue`.
126  *
127  * The input string must start with a valid JSON document. Any characters
128  * occurring after this document will be left in the input range. Use
129  * `toJSONValue` instead if you wish to perform a normal string to `JSONValue`
130  * conversion.
131  *
132  * See_also: `toJSONValue`
133  */
134 JSONValue parseJSONValue(LexOptions options = LexOptions.init, Input)(ref Input input, string filename = "", int maxDepth = defaultMaxDepth)
135     if (isInputRange!Input && (isSomeChar!(ElementType!Input) || isIntegral!(ElementType!Input)))
136 {
137     import funkwerk.stdx.data.json.foundation;
138 
139     auto tokens = lexJSON!options(input, filename);
140     auto ret = parseJSONValue(tokens, maxDepth);
141     input = tokens.input;
142     return ret;
143 }
144 
145 /// Parse an object
146 @safe unittest
147 {
148     // parse an object
149     string str = `{"a": true, "b": "test"}`;
150     JSONValue v = parseJSONValue(str);
151     assert(!str.length); // the input has been consumed
152 
153     auto obj = v.get!(JSONValue[string]);
154     assert(obj.length == 2);
155     assert(obj["a"] == true);
156     assert(obj["b"] == "test");
157 }
158 
159 /// Parse multiple consecutive values
160 @safe unittest
161 {
162     string str = `1.0 2.0`;
163     JSONValue v1 = parseJSONValue(str);
164     assert(v1 == 1.0);
165     assert(str == `2.0`);
166     JSONValue v2 = parseJSONValue(str);
167     assert(v2 == 2.0);
168     assert(str == ``);
169 }
170 
171 
172 /**
173  * Parses a stream of JSON tokens and returns the result as a $(D JSONValue).
174  *
175  * All tokens belonging to the document will be consumed from the input range.
176  * Any tokens after the end of the first JSON document will be left in the
177  * input token range for possible later consumption.
178 */
179 @safe JSONValue parseJSONValue(Input)(ref Input tokens, int maxDepth = defaultMaxDepth)
180     if (isJSONTokenInputRange!Input)
181 {
182     import std.array;
183     import funkwerk.stdx.data.json.foundation;
184 
185     enforceJson(maxDepth > 0, "Too much nesting", tokens.location);
186 
187     enforceJson(!tokens.empty, "Missing JSON value before EOF", tokens.location);
188 
189     JSONValue ret;
190 
191     final switch (tokens.front.kind) with (JSONTokenKind)
192     {
193         case none: assert(false);
194         case error: enforceJson(false, "Invalid token encountered", tokens.front.location); assert(false);
195         case null_: ret = JSONValue(null); break;
196         case boolean: ret = JSONValue(tokens.front.boolean); break;
197         case number:
198             final switch (tokens.front.number.type)
199             {
200                 case JSONNumber.Type.double_: ret = tokens.front.number.doubleValue; break;
201                 case JSONNumber.Type.long_: ret = tokens.front.number.longValue; break;
202                 case JSONNumber.Type.bigInt: () @trusted { ret = WrappedBigInt(tokens.front.number.bigIntValue); } (); break;
203             }
204             break;
205         case string: ret = JSONValue(tokens.front..string); break;
206         case objectStart:
207             auto loc = tokens.front.location;
208             bool first = true;
209             JSONValue[.string] obj;
210             tokens.popFront();
211             while (true)
212             {
213                 enforceJson(!tokens.empty, "Missing closing '}'", loc);
214                 if (tokens.front.kind == objectEnd) break;
215 
216                 if (!first)
217                 {
218                     enforceJson(tokens.front.kind == comma, "Expected ',' or '}'", tokens.front.location);
219                     tokens.popFront();
220                     enforceJson(!tokens.empty, "Expected field name", tokens.location);
221                 }
222                 else first = false;
223 
224                 enforceJson(tokens.front.kind == string, "Expected field name string", tokens.front.location);
225                 auto key = tokens.front..string;
226                 tokens.popFront();
227                 enforceJson(!tokens.empty && tokens.front.kind == colon, "Expected ':'",
228                     tokens.empty ? tokens.location : tokens.front.location);
229                 tokens.popFront();
230                 obj[key] = parseJSONValue(tokens, maxDepth - 1);
231             }
232             ret = JSONValue(obj, loc);
233             break;
234         case arrayStart:
235             auto loc = tokens.front.location;
236             bool first = true;
237             auto array = appender!(JSONValue[]);
238             tokens.popFront();
239             while (true)
240             {
241                 enforceJson(!tokens.empty, "Missing closing ']'", loc);
242                 if (tokens.front.kind == arrayEnd) break;
243 
244                 if (!first)
245                 {
246                     enforceJson(tokens.front.kind == comma, "Expected ',' or ']'", tokens.front.location);
247                     tokens.popFront();
248                 }
249                 else first = false;
250 
251                 () @trusted { array ~= parseJSONValue(tokens, maxDepth - 1); }();
252             }
253             ret = JSONValue(array.data, loc);
254             break;
255         case objectEnd, arrayEnd, colon, comma:
256             enforceJson(false, "Expected JSON value", tokens.front.location);
257             assert(false);
258     }
259 
260     tokens.popFront();
261     return ret;
262 }
263 
264 ///
265 @safe unittest
266 {
267     // lex
268     auto tokens = lexJSON(`[1, 2, 3]`);
269 
270     // parse
271     auto doc = parseJSONValue(tokens);
272 
273     auto arr = doc.get!(JSONValue[]);
274     assert(arr.length == 3);
275     assert(arr[0] == 1.0);
276     assert(arr[1] == 2.0);
277     assert(arr[2] == 3.0);
278 }
279 
280 @safe unittest
281 {
282     import std.exception;
283 
284     assertThrown(toJSONValue(`]`));
285     assertThrown(toJSONValue(`}`));
286     assertThrown(toJSONValue(`,`));
287     assertThrown(toJSONValue(`:`));
288     assertThrown(toJSONValue(`{`));
289     assertThrown(toJSONValue(`[`));
290     assertThrown(toJSONValue(`[1,]`));
291     assertThrown(toJSONValue(`[1,,]`));
292     assertThrown(toJSONValue(`[1,`));
293     assertThrown(toJSONValue(`[1 2]`));
294     assertThrown(toJSONValue(`{1: 1}`));
295     assertThrown(toJSONValue(`{"a": 1,}`));
296     assertThrown(toJSONValue(`{"a" 1}`));
297     assertThrown(toJSONValue(`{"a": 1 "b": 2}`));
298 }
299 
300 @safe unittest
301 {
302     import std.exception;
303 
304     // Test depth/nesting limitation
305     assertNotThrown(toJSONValue(`[]`, "", 1));
306     // values inside objects/arrays count as a level of nesting
307     assertThrown(toJSONValue(`[1, 2, 3]`, "", 1));
308     assertNotThrown(toJSONValue(`[1, 2, 3]`, "", 2));
309     assertThrown(toJSONValue(`[[]]`, "", 1));
310     assertNotThrown(toJSONValue(`{}`, "", 1));
311     assertThrown(toJSONValue(`{"a": {}}`, "", 1));
312 }
313 
314 /**
315  * Parses a JSON document using a lazy parser node range.
316  *
317  * This mode parsing mode is similar to a streaming XML (StAX) parser. It can
318  * be used to parse JSON documents of unlimited size. The memory consumption
319  * grows linearly with the nesting level (about 4 bytes per level), but is
320  * independent of the number of values in the JSON document.
321  *
322  * The resulting range of nodes is guaranteed to be ordered according to the
323  * following grammar, where uppercase terminals correspond to the node kind
324  * (See $(D JSONParserNodeKind)).
325  *
326  * $(UL
327  *   $(LI list → value*)
328  *   $(LI value → LITERAL | array | object)
329  *   $(LI array → ARRAYSTART (value)* ARRAYEND)
330  *   $(LI object → OBJECTSTART (KEY value)* OBJECTEND)
331  * )
332  */
333 JSONParserRange!(JSONLexerRange!(Input, options, String))
334     parseJSONStream(LexOptions options = LexOptions.init, String = string, Input)
335         (Input input, string filename = null)
336     if (isInputRange!Input && (isSomeChar!(ElementType!Input) || isIntegral!(ElementType!Input)))
337 {
338     return parseJSONStream(lexJSON!(options, String)(input, filename));
339 }
340 /// ditto
341 JSONParserRange!Input parseJSONStream(Input)(Input tokens)
342     if (isJSONTokenInputRange!Input)
343 {
344     return JSONParserRange!Input(tokens);
345 }
346 
347 ///
348 @safe unittest
349 {
350     import std.algorithm;
351 
352     auto rng1 = parseJSONStream(`{ "a": 1, "b": [null] }`);
353     with (JSONParserNodeKind)
354     {
355         assert(rng1.map!(n => n.kind).equal(
356             [objectStart, key, literal, key, arrayStart, literal, arrayEnd,
357             objectEnd]));
358     }
359 
360     auto rng2 = parseJSONStream(`1 {"a": 2} null`);
361     with (JSONParserNodeKind)
362     {
363         assert(rng2.map!(n => n.kind).equal(
364             [literal, objectStart, key, literal, objectEnd, literal]));
365     }
366 }
367 
368 @safe unittest
369 {
370     auto rng = parseJSONStream(`{"a": 1, "b": [null, true], "c": {"d": {}}}`);
371     with (JSONParserNodeKind)
372     {
373         rng.popFront();
374         assert(rng.front.kind == key && rng.front.key == "a"); rng.popFront();
375         assert(rng.front.kind == literal && rng.front.literal.number == 1.0); rng.popFront();
376         assert(rng.front.kind == key && rng.front.key == "b"); rng.popFront();
377         assert(rng.front.kind == arrayStart); rng.popFront();
378         assert(rng.front.kind == literal && rng.front.literal.kind == JSONTokenKind.null_); rng.popFront();
379         assert(rng.front.kind == literal && rng.front.literal.boolean == true); rng.popFront();
380         assert(rng.front.kind == arrayEnd); rng.popFront();
381         assert(rng.front.kind == key && rng.front.key == "c"); rng.popFront();
382         assert(rng.front.kind == objectStart); rng.popFront();
383         assert(rng.front.kind == key && rng.front.key == "d"); rng.popFront();
384         assert(rng.front.kind == objectStart); rng.popFront();
385         assert(rng.front.kind == objectEnd); rng.popFront();
386         assert(rng.front.kind == objectEnd); rng.popFront();
387         assert(rng.front.kind == objectEnd); rng.popFront();
388         assert(rng.empty);
389     }
390 }
391 
392 @safe unittest
393 {
394     auto rng = parseJSONStream(`[]`);
395     with (JSONParserNodeKind)
396     {
397         import std.algorithm;
398         assert(rng.map!(n => n.kind).equal([arrayStart, arrayEnd]));
399     }
400 }
401 
402 @safe unittest
403 {
404     import std.array;
405     import std.exception;
406 
407     assertThrown(parseJSONStream(`]`).array);
408     assertThrown(parseJSONStream(`}`).array);
409     assertThrown(parseJSONStream(`,`).array);
410     assertThrown(parseJSONStream(`:`).array);
411     assertThrown(parseJSONStream(`{`).array);
412     assertThrown(parseJSONStream(`[`).array);
413     assertThrown(parseJSONStream(`[1,]`).array);
414     assertThrown(parseJSONStream(`[1,,]`).array);
415     assertThrown(parseJSONStream(`[1,`).array);
416     assertThrown(parseJSONStream(`[1 2]`).array);
417     assertThrown(parseJSONStream(`{1: 1}`).array);
418     assertThrown(parseJSONStream(`{"a": 1,}`).array);
419     assertThrown(parseJSONStream(`{"a" 1}`).array);
420     assertThrown(parseJSONStream(`{"a": 1 "b": 2}`).array);
421     assertThrown(parseJSONStream(`{"a": 1, "b": [null, true], "c": {"d": {}}}}`).array);
422 }
423 
424 // Not possible to test anymore with the new String customization scheme
425 /*@safe unittest { // test for @nogc interface
426    static struct MyAppender
427    {
428         @nogc:
429         void put(string s) { }
430         void put(dchar ch) {}
431         void put(char ch) {}
432         @property string data() { return null; }
433     }
434     static MyAppender createAppender() @nogc { return MyAppender.init; }
435 
436     static struct EmptyStream
437     {
438         @nogc:
439         @property bool empty() { return true; }
440         @property dchar front() { return ' '; }
441         void popFront() { assert(false); }
442         @property EmptyStream save() { return this; }
443     }
444 
445     void test(T)()
446     {
447         T t;
448         auto str = parseJSONStream!(LexOptions.noThrow, createAppender)(t);
449         while (!str.empty) {
450             auto f = str.front;
451             str.popFront();
452         }
453     }
454     // just instantiate, don't run
455     auto t1 = &test!string;
456     auto t2 = &test!wstring;
457     auto t3 = &test!dstring;
458     auto t4 = &test!EmptyStream;
459 }*/
460 
461 
462 /**
463  * Lazy input range of JSON parser nodes.
464  *
465  * See $(D parseJSONStream) for more information.
466  */
467 struct JSONParserRange(Input)
468     if (isJSONTokenInputRange!Input)
469 {
470     import funkwerk.stdx.data.json.foundation;
471 
472     alias String = typeof(Input.front).String;
473 
474     private {
475         Input _input;
476         JSONTokenKind[] _containerStack;
477         size_t _containerStackFill = 0;
478         // see doc/why-we-dont-need-save.md
479         JSONTokenKind _currentKind;
480         JSONParserNodeKind _prevKind;
481         JSONParserNode!String _node;
482     }
483 
484     /**
485      * Constructs a new parser range.
486      */
487     this(Input input)
488     {
489         _input = input;
490     }
491 
492     // For debugging.
493     string toString()
494     {
495         import std.format : format;
496 
497         return format!"JSONParserRange(%s, %s, %s, %s)"(
498             _containerStack[0 .. _containerStackFill], _currentKind, _prevKind, _node);
499     }
500 
501     /**
502      * Determines of the range has been exhausted.
503      */
504     @property bool empty() { return _containerStackFill == 0 && _input.empty && _node.kind == JSONParserNodeKind.none; }
505 
506     /**
507      * Returns the current node from the stream.
508      */
509     @property ref const(JSONParserNode!String) front()
510     {
511         ensureFrontValid();
512         return _node;
513     }
514 
515     /**
516      * Skips to the next node in the stream.
517      */
518     void popFront()
519     {
520         assert(!empty);
521         ensureFrontValid();
522         _prevKind = _node.kind;
523         _node.kind = JSONParserNodeKind.none;
524     }
525 
526     private void ensureFrontValid()
527     {
528         if (_node.kind == JSONParserNodeKind.none)
529         {
530             readNext();
531             assert(_node.kind != JSONParserNodeKind.none);
532         }
533     }
534 
535     private void readNext()
536     {
537         if (_containerStackFill)
538         {
539             if (_currentKind == JSONTokenKind.objectStart)
540                 readNextInObject();
541             else readNextInArray();
542         }
543         else readNextValue();
544     }
545 
546     private void readNextInObject() @trusted
547     {
548         enforceJson(!_input.empty, "Missing closing '}'", _input.location);
549         switch (_prevKind)
550         {
551             default: assert(false);
552             case JSONParserNodeKind.objectStart:
553                 if (_input.front.kind == JSONTokenKind.objectEnd)
554                 {
555                     _node.kind = JSONParserNodeKind.objectEnd;
556                     popContainer;
557                 }
558                 else
559                 {
560                     enforceJson(_input.front.kind == JSONTokenKind..string,
561                         "Expected field name", _input.front.location);
562                     _node.key = _input.front..string;
563                 }
564                 _input.popFront();
565                 break;
566             case JSONParserNodeKind.key:
567                 enforceJson(_input.front.kind == JSONTokenKind.colon,
568                     "Expected ':'", _input.front.location);
569                 _input.popFront();
570                 readNextValue();
571                 break;
572             case JSONParserNodeKind.literal, JSONParserNodeKind.objectEnd, JSONParserNodeKind.arrayEnd:
573                 if (_input.front.kind == JSONTokenKind.objectEnd)
574                 {
575                     _node.kind = JSONParserNodeKind.objectEnd;
576                     popContainer;
577                 }
578                 else
579                 {
580                     enforceJson(_input.front.kind == JSONTokenKind.comma,
581                         "Expected ',' or '}'", _input.front.location);
582                     _input.popFront();
583                     enforceJson(!_input.empty && _input.front.kind == JSONTokenKind..string,
584                         "Expected field name", _input.front.location);
585                     _node.key = _input.front..string;
586                 }
587                 _input.popFront();
588                 break;
589         }
590     }
591 
592     private void readNextInArray()
593     {
594         enforceJson(!_input.empty, "Missing closing ']'", _input.location);
595         switch (_prevKind)
596         {
597             default: assert(false);
598             case JSONParserNodeKind.arrayStart:
599                 if (_input.front.kind == JSONTokenKind.arrayEnd)
600                 {
601                     _node.kind = JSONParserNodeKind.arrayEnd;
602                     popContainer;
603                     _input.popFront();
604                 }
605                 else
606                 {
607                     readNextValue();
608                 }
609                 break;
610             case JSONParserNodeKind.literal, JSONParserNodeKind.objectEnd, JSONParserNodeKind.arrayEnd:
611                 if (_input.front.kind == JSONTokenKind.arrayEnd)
612                 {
613                     _node.kind = JSONParserNodeKind.arrayEnd;
614                     popContainer;
615                     _input.popFront();
616                 }
617                 else
618                 {
619                     enforceJson(_input.front.kind == JSONTokenKind.comma,
620                         "Expected ',' or ']'", _input.front.location);
621                     _input.popFront();
622                     enforceJson(!_input.empty, "Missing closing ']'", _input.location);
623                     readNextValue();
624                 }
625                 break;
626         }
627     }
628 
629     private void readNextValue()
630     {
631         switch (_input.front.kind)
632         {
633             default:
634                 throw new JSONException("Expected JSON value", _input.location);
635             case JSONTokenKind.none: assert(false);
636             case JSONTokenKind.null_, JSONTokenKind.boolean,
637                     JSONTokenKind.number, JSONTokenKind..string:
638                 _node.literal = _input.front;
639                 _input.popFront();
640                 break;
641             case JSONTokenKind.objectStart:
642                 _node.kind = JSONParserNodeKind.objectStart;
643                 pushContainer(JSONTokenKind.objectStart);
644                 _input.popFront();
645                 break;
646             case JSONTokenKind.arrayStart:
647                 _node.kind = JSONParserNodeKind.arrayStart;
648                 pushContainer(JSONTokenKind.arrayStart);
649                 _input.popFront();
650                 break;
651         }
652     }
653 
654     private void popContainer()
655     {
656         _containerStackFill--;
657         if (_containerStackFill)
658             _currentKind = _containerStack[_containerStackFill - 1];
659     }
660 
661     private void pushContainer(JSONTokenKind kind)
662     {
663         import std.algorithm : max;
664         if (_containerStackFill)
665             _containerStack[_containerStackFill - 1] = _currentKind;
666         if (_containerStackFill >= _containerStack.length)
667             _containerStack.length = max(32, _containerStack.length*3/2);
668         _containerStack[_containerStackFill++] = kind;
669         _currentKind = kind;
670     }
671 }
672 
673 
674 /**
675  * Represents a single node of a JSON parse tree.
676  *
677  * See $(D parseJSONStream) and $(D JSONParserRange) more information.
678  */
679 struct JSONParserNode(String)
680 {
681     @safe:
682     import std.algorithm : among;
683     import funkwerk.stdx.data.json.foundation : Location;
684 
685     private alias Kind = JSONParserNodeKind; // compatibility alias
686 
687     private
688     {
689         Kind _kind = Kind.none;
690         union
691         {
692             String _key;
693             JSONToken!String _literal;
694         }
695     }
696 
697     /**
698      * The kind of this node.
699      */
700     @property Kind kind() const nothrow { return _kind; }
701     /// ditto
702     @property Kind kind(Kind value) nothrow
703         in (!value.among(Kind.key, Kind.literal))
704         { return _kind = value; }
705 
706     /**
707      * The key identifier for $(D Kind.key) nodes.
708      *
709      * Setting the key will automatically switch the node kind.
710      */
711     @property String key() const @trusted nothrow
712     {
713         assert(_kind == Kind.key);
714         return _key;
715     }
716     /// ditto
717     @property String key(String value) nothrow
718     {
719         _kind = Kind.key;
720         return () @trusted { return _key = value; } ();
721     }
722 
723     /**
724      * The literal token for $(D Kind.literal) nodes.
725      *
726      * Setting the literal will automatically switch the node kind.
727      */
728     @property ref inout(JSONToken!String) literal() inout @trusted nothrow
729     {
730         assert(_kind == Kind.literal);
731         return _literal;
732     }
733     /// ditto
734     @property ref JSONToken!String literal(JSONToken!String literal) return nothrow
735     {
736         _kind = Kind.literal;
737         return *() @trusted { return &(_literal = literal); } ();
738     }
739 
740     @property Location location()
741     const @trusted nothrow {
742         if (_kind == Kind.literal) return _literal.location;
743         return Location.init;
744     }
745 
746     /**
747      * Enables equality comparisons.
748      *
749      * Note that the location is considered part of the token and thus is
750      * included in the comparison.
751      */
752     bool opEquals(in ref JSONParserNode other)
753     const nothrow
754     {
755         if (this.kind != other.kind) return false;
756 
757         switch (this.kind)
758         {
759             default: return true;
760             case Kind.literal: return this.literal == other.literal;
761             case Kind.key: return this.key == other.key;
762         }
763     }
764     /// ditto
765     bool opEquals(JSONParserNode other) const nothrow { return opEquals(other); }
766 
767     unittest
768     {
769         JSONToken!string t1, t2, t3;
770         t1..string = "test";
771         t2..string = "test".idup;
772         t3..string = "other";
773 
774         JSONParserNode!string n1, n2;
775         n2.literal = t1; assert(n1 != n2);
776         n1.literal = t1; assert(n1 == n2);
777         n1.literal = t3; assert(n1 != n2);
778         n1.literal = t2; assert(n1 == n2);
779         n1.kind = Kind.objectStart; assert(n1 != n2);
780         n1.key = "test"; assert(n1 != n2);
781         n2.key = "other"; assert(n1 != n2);
782         n2.key = "test".idup; assert(n1 == n2);
783     }
784 
785     /**
786      * Enables usage of $(D JSONToken) as an associative array key.
787      */
788     size_t toHash() const nothrow @trusted
789     {
790         hash_t ret = 723125331 + cast(int)_kind * 3962627;
791 
792         switch (_kind)
793         {
794             default: return ret;
795             case Kind.literal: return ret + _literal.toHash();
796             case Kind.key: return ret + typeid(.string).getHash(&_key);
797         }
798     }
799 
800     /**
801      * Converts the node to a string representation.
802      *
803      * Note that this representation is NOT the JSON representation, but rather
804      * a representation suitable for printing out a node.
805      */
806     string toString() const
807     {
808         import std..string;
809         switch (this.kind)
810         {
811             default: return format("%s", this.kind);
812             case Kind.key: return format("[key \"%s\"]", this.key);
813             case Kind.literal: return literal.toString();
814         }
815     }
816 }
817 
818 
819 /**
820  * Identifies the kind of a parser node.
821  */
822 enum JSONParserNodeKind
823 {
824     none,        /// Used internally, never occurs in a node stream
825     key,         /// An object key
826     literal,     /// A literal value ($(D null), $(D boolean), $(D number) or $(D string))
827     objectStart, /// The start of an object value
828     objectEnd,   /// The end of an object value
829     arrayStart,  /// The start of an array value
830     arrayEnd,    /// The end of an array value
831 }
832 
833 
834 /// Tests if a given type is an input range of $(D JSONToken).
835 enum isJSONTokenInputRange(R) = isInputRange!R && is(typeof(R.init.front) : JSONToken!String, String);
836 
837 static assert(isJSONTokenInputRange!(JSONLexerRange!string));
838 
839 /// Tests if a given type is an input range of $(D JSONParserNode).
840 enum isJSONParserNodeInputRange(R) = isInputRange!R && is(typeof(R.init.front) : JSONParserNode!String, String);
841 
842 static assert(isJSONParserNodeInputRange!(JSONParserRange!(JSONLexerRange!string)));
843 
844 // Workaround for https://issues.dlang.org/show_bug.cgi?id=14425
845 private alias Workaround_14425 = JSONParserRange!(JSONLexerRange!string);
846 
847 
848 /**
849  * Skips a single JSON value in a parser stream.
850  *
851  * The value pointed to by `nodes.front` will be skipped. All JSON types will
852  * be skipped, which means in particular that arrays and objects will be
853  * skipped recursively.
854  *
855  * Params:
856  *   nodes = An input range of JSON parser nodes
857  */
858 void skipValue(R)(ref R nodes) if (isJSONParserNodeInputRange!R)
859 {
860     import funkwerk.stdx.data.json.foundation;
861     enforceJson(!nodes.empty, "Unexpected end of input", nodes.front.literal.location);
862 
863     auto k = nodes.front.kind;
864     nodes.popFront();
865 
866     with (JSONParserNodeKind) {
867         if (k != arrayStart && k != objectStart) return;
868 
869         int depth = 1;
870         while (!nodes.empty) {
871             k = nodes.front.kind;
872             nodes.popFront();
873             if (k == arrayStart || k == objectStart) depth++;
874             else if (k == arrayEnd || k == objectEnd) {
875                 if (--depth == 0) break;
876             }
877         }
878     }
879 }
880 
881 ///
882 @safe unittest
883 {
884     auto j = parseJSONStream(q{
885             [
886                 [1, 2, 3],
887                 "foo"
888             ]
889         });
890 
891     assert(j.front.kind == JSONParserNodeKind.arrayStart);
892     j.popFront();
893     
894     // skips the whole [1, 2, 3] array
895     j.skipValue();
896 
897     string value = j.readString;
898     assert(value == "foo");
899 
900     assert(j.front.kind == JSONParserNodeKind.arrayEnd);
901     j.popFront();
902 
903     assert(j.empty);
904 }
905 
906 
907 /**
908  * Skips all entries in an object until a certain key is reached.
909  *
910  * The node range must either point to the start of an object
911  * (`JSONParserNodeKind.objectStart`), or to a key within an object
912  * (`JSONParserNodeKind.key`).
913  *
914  * Params:
915  *   nodes = An input range of JSON parser nodes
916  *   key = Name of the key to find
917  *
918  * Returns:
919  *   `true` is returned if and only if the specified key has been found.
920  *
921  * Params:
922  *   nodes = An input range of JSON parser nodes
923  */
924 bool skipToKey(R)(ref R nodes, string key) if (isJSONParserNodeInputRange!R)
925 {
926     import std.algorithm/*.comparison*/ : among;
927     import funkwerk.stdx.data.json.foundation;
928 
929     enforceJson(!nodes.empty, "Unexpected end of input", Location.init);
930     enforceJson(nodes.front.kind.among!(JSONParserNodeKind.objectStart, JSONParserNodeKind.key) > 0,
931         "Expected object or object key", nodes.front.location);
932 
933     if (nodes.front.kind == JSONParserNodeKind.objectStart)
934         nodes.popFront();
935 
936     while (true) {
937         auto k = nodes.front.kind;
938         if (k == JSONParserNodeKind.objectEnd) {
939             nodes.popFront();
940             return false;
941         }
942 
943         assert(k == JSONParserNodeKind.key);
944         if (nodes.front.key == key) {
945             nodes.popFront();
946             return true;
947         }
948 
949         nodes.popFront();
950 
951         nodes.skipValue();
952     }
953 }
954 
955 ///
956 @safe unittest
957 {
958     auto j = parseJSONStream(q{
959             {
960                 "foo": 2,
961                 "bar": 3,
962                 "baz": false,
963                 "qux": "str"
964             }
965         });
966 
967     j.skipToKey("bar");
968     double v1 = j.readDouble;
969     assert(v1 == 3);
970 
971     j.skipToKey("qux");
972     string v2 = j.readString;
973     assert(v2 == "str");
974 
975     assert(j.front.kind == JSONParserNodeKind.objectEnd);
976     j.popFront();
977 
978     assert(j.empty);
979 }
980 
981 
982 /**
983  * Reads an array and issues a callback for each entry.
984  *
985  * Params:
986  *   nodes = An input range of JSON parser nodes
987  *   del = The callback to invoke for each array entry
988  */
989 void readArray(R)(ref R nodes, scope void delegate() @safe del) if (isJSONParserNodeInputRange!R)
990 {
991     import funkwerk.stdx.data.json.foundation;
992     enforceJson(!nodes.empty, "Unexpected end of input", Location.init);
993     enforceJson(nodes.front.kind == JSONParserNodeKind.arrayStart,
994         "Expected array", nodes.front.location);
995     nodes.popFront();
996 
997     while (true) {
998         auto k = nodes.front.kind;
999         if (k == JSONParserNodeKind.arrayEnd) {
1000           nodes.popFront();
1001           return;
1002         }
1003         del();
1004     }
1005 }
1006 
1007 ///
1008 @safe unittest
1009 {
1010     auto j = parseJSONStream(q{
1011             [
1012                 "foo",
1013                 "bar"
1014             ]
1015         });
1016 
1017     size_t i = 0;
1018     j.readArray({
1019         auto value = j.readString();
1020         switch (i++) {
1021             default: assert(false);
1022             case 0: assert(value == "foo"); break;
1023             case 1: assert(value == "bar"); break;
1024         }
1025     });
1026 
1027     assert(j.empty);
1028 }
1029 
1030 /**
1031  * Reads an object and issues a callback for each field.
1032  *
1033  * Params:
1034  *   nodes = An input range of JSON parser nodes
1035  *   del = The callback to invoke for each object field
1036  */
1037 void readObject(R)(ref R nodes, scope void delegate(string key) @safe del) if (isJSONParserNodeInputRange!R)
1038 {
1039     import funkwerk.stdx.data.json.foundation;
1040     enforceJson(!nodes.empty, "Unexpected end of input", Location.init);
1041     enforceJson(nodes.front.kind == JSONParserNodeKind.objectStart,
1042         "Expected object", nodes.front.literal.location);
1043     nodes.popFront();
1044 
1045     while (true) {
1046         auto k = nodes.front.kind;
1047         if (k == JSONParserNodeKind.objectEnd) {
1048           nodes.popFront();
1049           return;
1050         }
1051         auto key = nodes.front.key;
1052         nodes.popFront();
1053         del(key);
1054     }
1055 }
1056 
1057 ///
1058 @safe unittest
1059 {
1060     auto j = parseJSONStream(q{
1061             {
1062                 "foo": 1,
1063                 "bar": 2
1064             }
1065         });
1066 
1067     j.readObject((key) {
1068         auto value = j.readDouble;
1069         switch (key) {
1070             default: assert(false);
1071             case "foo": assert(value == 1); break;
1072             case "bar": assert(value == 2); break;
1073         }
1074     });
1075 
1076     assert(j.empty);
1077 }
1078 
1079 
1080 /**
1081  * Reads a single double value.
1082  *
1083  * Params:
1084  *   nodes = An input range of JSON parser nodes
1085  *
1086  * Throws: Throws a `JSONException` is the node range is empty or `nodes.front` is not a number.
1087  */
1088 double readDouble(R)(ref R nodes) if (isJSONParserNodeInputRange!R)
1089 {
1090     import funkwerk.stdx.data.json.foundation;
1091     enforceJson(!nodes.empty, "Unexpected end of input", Location.init);
1092     enforceJson(nodes.front.kind == JSONParserNodeKind.literal
1093         && nodes.front.literal.kind == JSONTokenKind.number,
1094         "Expected numeric value", nodes.front.literal.location);
1095     double ret = nodes.front.literal.number;
1096     nodes.popFront();
1097     return ret;
1098 }
1099 
1100 ///
1101 @safe unittest
1102 {
1103     auto j = parseJSONStream(`1.0`);
1104     double value = j.readDouble;
1105     assert(value == 1.0);
1106     assert(j.empty);
1107 }
1108 
1109 
1110 /**
1111  * Reads a single double value.
1112  *
1113  * Params:
1114  *   nodes = An input range of JSON parser nodes
1115  *
1116  * Throws: Throws a `JSONException` is the node range is empty or `nodes.front` is not a string.
1117  */
1118 string readString(R)(ref R nodes) if (isJSONParserNodeInputRange!R)
1119 {
1120     import funkwerk.stdx.data.json.foundation;
1121     enforceJson(!nodes.empty, "Unexpected end of input", Location.init);
1122     enforceJson(nodes.front.kind == JSONParserNodeKind.literal
1123         && nodes.front.literal.kind == JSONTokenKind..string,
1124         "Expected string value", nodes.front.literal.location);
1125     string ret = nodes.front.literal..string;
1126     nodes.popFront();
1127     return ret;
1128 }
1129 
1130 ///
1131 @safe unittest
1132 {
1133     auto j = parseJSONStream(`"foo"`);
1134     string value = j.readString;
1135     assert(value == "foo");
1136     assert(j.empty);
1137 }
1138 
1139 
1140 /**
1141  * Reads a single double value.
1142  *
1143  * Params:
1144  *   nodes = An input range of JSON parser nodes
1145  *
1146  * Throws: Throws a `JSONException` is the node range is empty or `nodes.front` is not a boolean.
1147  */
1148 bool readBool(R)(ref R nodes) if (isJSONParserNodeInputRange!R)
1149 {
1150     import funkwerk.stdx.data.json.foundation;
1151     enforceJson(!nodes.empty, "Unexpected end of input", Location.init);
1152     enforceJson(nodes.front.kind == JSONParserNodeKind.literal
1153         && nodes.front.literal.kind == JSONTokenKind.boolean,
1154         "Expected boolean value", nodes.front.literal.location);
1155     bool ret = nodes.front.literal.boolean;
1156     nodes.popFront();
1157     return ret;
1158 }
1159 
1160 ///
1161 @safe unittest
1162 {
1163     auto j = parseJSONStream(`true`);
1164     bool value = j.readBool;
1165     assert(value == true);
1166     assert(j.empty);
1167 }