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         JSONParserNodeKind _prevKind;
479         JSONParserNode!String _node;
480     }
481 
482     /**
483      * Constructs a new parser range.
484      */
485     this(Input input)
486     {
487         _input = input;
488     }
489 
490     /**
491      * Determines of the range has been exhausted.
492      */
493     @property bool empty() { return _containerStackFill == 0 && _input.empty && _node.kind == JSONParserNodeKind.none; }
494 
495     /**
496      * Returns the current node from the stream.
497      */
498     @property ref const(JSONParserNode!String) front()
499     {
500         ensureFrontValid();
501         return _node;
502     }
503 
504     /**
505      * Skips to the next node in the stream.
506      */
507     void popFront()
508     {
509         assert(!empty);
510         ensureFrontValid();
511         _prevKind = _node.kind;
512         _node.kind = JSONParserNodeKind.none;
513     }
514 
515     private void ensureFrontValid()
516     {
517         if (_node.kind == JSONParserNodeKind.none)
518         {
519             readNext();
520             assert(_node.kind != JSONParserNodeKind.none);
521         }
522     }
523 
524     private void readNext()
525     {
526         if (_containerStackFill)
527         {
528             if (_containerStack[_containerStackFill-1] == JSONTokenKind.objectStart)
529                 readNextInObject();
530             else readNextInArray();
531         }
532         else readNextValue();
533     }
534 
535     private void readNextInObject() @trusted
536     {
537         enforceJson(!_input.empty, "Missing closing '}'", _input.location);
538         switch (_prevKind)
539         {
540             default: assert(false);
541             case JSONParserNodeKind.objectStart:
542                 if (_input.front.kind == JSONTokenKind.objectEnd)
543                 {
544                     _node.kind = JSONParserNodeKind.objectEnd;
545                     _containerStackFill--;
546                 }
547                 else
548                 {
549                     enforceJson(_input.front.kind == JSONTokenKind..string,
550                         "Expected field name", _input.front.location);
551                     _node.key = _input.front..string;
552                 }
553                 _input.popFront();
554                 break;
555             case JSONParserNodeKind.key:
556                 enforceJson(_input.front.kind == JSONTokenKind.colon,
557                     "Expected ':'", _input.front.location);
558                 _input.popFront();
559                 readNextValue();
560                 break;
561             case JSONParserNodeKind.literal, JSONParserNodeKind.objectEnd, JSONParserNodeKind.arrayEnd:
562                 if (_input.front.kind == JSONTokenKind.objectEnd)
563                 {
564                     _node.kind = JSONParserNodeKind.objectEnd;
565                     _containerStackFill--;
566                 }
567                 else
568                 {
569                     enforceJson(_input.front.kind == JSONTokenKind.comma,
570                         "Expected ',' or '}'", _input.front.location);
571                     _input.popFront();
572                     enforceJson(!_input.empty && _input.front.kind == JSONTokenKind..string,
573                         "Expected field name", _input.front.location);
574                     _node.key = _input.front..string;
575                 }
576                 _input.popFront();
577                 break;
578         }
579     }
580 
581     private void readNextInArray()
582     {
583         enforceJson(!_input.empty, "Missing closing ']'", _input.location);
584         switch (_prevKind)
585         {
586             default: assert(false);
587             case JSONParserNodeKind.arrayStart:
588                 if (_input.front.kind == JSONTokenKind.arrayEnd)
589                 {
590                     _node.kind = JSONParserNodeKind.arrayEnd;
591                     _containerStackFill--;
592                     _input.popFront();
593                 }
594                 else
595                 {
596                     readNextValue();
597                 }
598                 break;
599             case JSONParserNodeKind.literal, JSONParserNodeKind.objectEnd, JSONParserNodeKind.arrayEnd:
600                 if (_input.front.kind == JSONTokenKind.arrayEnd)
601                 {
602                     _node.kind = JSONParserNodeKind.arrayEnd;
603                     _containerStackFill--;
604                     _input.popFront();
605                 }
606                 else
607                 {
608                     enforceJson(_input.front.kind == JSONTokenKind.comma,
609                         "Expected ',' or ']'", _input.front.location);
610                     _input.popFront();
611                     enforceJson(!_input.empty, "Missing closing ']'", _input.location);
612                     readNextValue();
613                 }
614                 break;
615         }
616     }
617 
618     private void readNextValue()
619     {
620         switch (_input.front.kind)
621         {
622             default:
623                 throw new JSONException("Expected JSON value", _input.location);
624             case JSONTokenKind.none: assert(false);
625             case JSONTokenKind.null_, JSONTokenKind.boolean,
626                     JSONTokenKind.number, JSONTokenKind..string:
627                 _node.literal = _input.front;
628                 _input.popFront();
629                 break;
630             case JSONTokenKind.objectStart:
631                 _node.kind = JSONParserNodeKind.objectStart;
632                 pushContainer(JSONTokenKind.objectStart);
633                 _input.popFront();
634                 break;
635             case JSONTokenKind.arrayStart:
636                 _node.kind = JSONParserNodeKind.arrayStart;
637                 pushContainer(JSONTokenKind.arrayStart);
638                 _input.popFront();
639                 break;
640         }
641     }
642 
643     private void pushContainer(JSONTokenKind kind)
644     {
645         import std.algorithm/*.comparison*/ : max;
646         if (_containerStackFill >= _containerStack.length)
647             _containerStack.length = max(32, _containerStack.length*3/2);
648         _containerStack[_containerStackFill++] = kind;
649     }
650 }
651 
652 
653 /**
654  * Represents a single node of a JSON parse tree.
655  *
656  * See $(D parseJSONStream) and $(D JSONParserRange) more information.
657  */
658 struct JSONParserNode(String)
659 {
660     @safe:
661     import std.algorithm/*.comparison*/ : among;
662     import funkwerk.stdx.data.json.foundation : Location;
663 
664     private alias Kind = JSONParserNodeKind; // compatibility alias
665 
666     private
667     {
668         Kind _kind = Kind.none;
669         union
670         {
671             String _key;
672             JSONToken!String _literal;
673         }
674     }
675 
676     /**
677      * The kind of this node.
678      */
679     @property Kind kind() const nothrow { return _kind; }
680     /// ditto
681     @property Kind kind(Kind value) nothrow
682         in { assert(!value.among(Kind.key, Kind.literal)); }
683         body { return _kind = value; }
684 
685     /**
686      * The key identifier for $(D Kind.key) nodes.
687      *
688      * Setting the key will automatically switch the node kind.
689      */
690     @property String key() const @trusted nothrow
691     {
692         assert(_kind == Kind.key);
693         return _key;
694     }
695     /// ditto
696     @property String key(String value) nothrow
697     {
698         _kind = Kind.key;
699         return () @trusted { return _key = value; } ();
700     }
701 
702     /**
703      * The literal token for $(D Kind.literal) nodes.
704      *
705      * Setting the literal will automatically switch the node kind.
706      */
707     @property ref inout(JSONToken!String) literal() inout @trusted nothrow
708     {
709         assert(_kind == Kind.literal);
710         return _literal;
711     }
712     /// ditto
713     @property ref JSONToken!String literal(JSONToken!String literal) return nothrow
714     {
715         _kind = Kind.literal;
716         return *() @trusted { return &(_literal = literal); } ();
717     }
718 
719     @property Location location()
720     const @trusted nothrow {
721         if (_kind == Kind.literal) return _literal.location;
722         return Location.init;
723     }
724 
725     /**
726      * Enables equality comparisons.
727      *
728      * Note that the location is considered part of the token and thus is
729      * included in the comparison.
730      */
731     bool opEquals(in ref JSONParserNode other)
732     const nothrow
733     {
734         if (this.kind != other.kind) return false;
735 
736         switch (this.kind)
737         {
738             default: return true;
739             case Kind.literal: return this.literal == other.literal;
740             case Kind.key: return this.key == other.key;
741         }
742     }
743     /// ditto
744     bool opEquals(JSONParserNode other) const nothrow { return opEquals(other); }
745 
746     unittest
747     {
748         JSONToken!string t1, t2, t3;
749         t1..string = "test";
750         t2..string = "test".idup;
751         t3..string = "other";
752 
753         JSONParserNode!string n1, n2;
754         n2.literal = t1; assert(n1 != n2);
755         n1.literal = t1; assert(n1 == n2);
756         n1.literal = t3; assert(n1 != n2);
757         n1.literal = t2; assert(n1 == n2);
758         n1.kind = Kind.objectStart; assert(n1 != n2);
759         n1.key = "test"; assert(n1 != n2);
760         n2.key = "other"; assert(n1 != n2);
761         n2.key = "test".idup; assert(n1 == n2);
762     }
763 
764     /**
765      * Enables usage of $(D JSONToken) as an associative array key.
766      */
767     size_t toHash() const nothrow @trusted
768     {
769         hash_t ret = 723125331 + cast(int)_kind * 3962627;
770 
771         switch (_kind)
772         {
773             default: return ret;
774             case Kind.literal: return ret + _literal.toHash();
775             case Kind.key: return ret + typeid(.string).getHash(&_key);
776         }
777     }
778 
779     /**
780      * Converts the node to a string representation.
781      *
782      * Note that this representation is NOT the JSON representation, but rather
783      * a representation suitable for printing out a node.
784      */
785     string toString() const
786     {
787         import std.string;
788         switch (this.kind)
789         {
790             default: return format("%s", this.kind);
791             case Kind.key: return format("[key \"%s\"]", this.key);
792             case Kind.literal: return literal.toString();
793         }
794     }
795 }
796 
797 
798 /**
799  * Identifies the kind of a parser node.
800  */
801 enum JSONParserNodeKind
802 {
803     none,        /// Used internally, never occurs in a node stream
804     key,         /// An object key
805     literal,     /// A literal value ($(D null), $(D boolean), $(D number) or $(D string))
806     objectStart, /// The start of an object value
807     objectEnd,   /// The end of an object value
808     arrayStart,  /// The start of an array value
809     arrayEnd,    /// The end of an array value
810 }
811 
812 
813 /// Tests if a given type is an input range of $(D JSONToken).
814 enum isJSONTokenInputRange(R) = isInputRange!R && is(typeof(R.init.front) : JSONToken!String, String);
815 
816 static assert(isJSONTokenInputRange!(JSONLexerRange!string));
817 
818 /// Tests if a given type is an input range of $(D JSONParserNode).
819 enum isJSONParserNodeInputRange(R) = isInputRange!R && is(typeof(R.init.front) : JSONParserNode!String, String);
820 
821 static assert(isJSONParserNodeInputRange!(JSONParserRange!(JSONLexerRange!string)));
822 
823 // Workaround for https://issues.dlang.org/show_bug.cgi?id=14425
824 private alias Workaround_14425 = JSONParserRange!(JSONLexerRange!string);
825 
826 
827 /**
828  * Skips a single JSON value in a parser stream.
829  *
830  * The value pointed to by `nodes.front` will be skipped. All JSON types will
831  * be skipped, which means in particular that arrays and objects will be
832  * skipped recursively.
833  *
834  * Params:
835  *   nodes = An input range of JSON parser nodes
836  */
837 void skipValue(R)(ref R nodes) if (isJSONParserNodeInputRange!R)
838 {
839     import funkwerk.stdx.data.json.foundation;
840     enforceJson(!nodes.empty, "Unexpected end of input", nodes.front.literal.location);
841 
842     auto k = nodes.front.kind;
843     nodes.popFront();
844 
845     with (JSONParserNodeKind) {
846         if (k != arrayStart && k != objectStart) return;
847 
848         int depth = 1;
849         while (!nodes.empty) {
850             k = nodes.front.kind;
851             nodes.popFront();
852             if (k == arrayStart || k == objectStart) depth++;
853             else if (k == arrayEnd || k == objectEnd) {
854                 if (--depth == 0) break;
855             }
856         }
857     }
858 }
859 
860 ///
861 @safe unittest
862 {
863     auto j = parseJSONStream(q{
864             [
865                 [1, 2, 3],
866                 "foo"
867             ]
868         });
869 
870     assert(j.front.kind == JSONParserNodeKind.arrayStart);
871     j.popFront();
872     
873     // skips the whole [1, 2, 3] array
874     j.skipValue();
875 
876     string value = j.readString;
877     assert(value == "foo");
878 
879     assert(j.front.kind == JSONParserNodeKind.arrayEnd);
880     j.popFront();
881 
882     assert(j.empty);
883 }
884 
885 
886 /**
887  * Skips all entries in an object until a certain key is reached.
888  *
889  * The node range must either point to the start of an object
890  * (`JSONParserNodeKind.objectStart`), or to a key within an object
891  * (`JSONParserNodeKind.key`).
892  *
893  * Params:
894  *   nodes = An input range of JSON parser nodes
895  *   key = Name of the key to find
896  *
897  * Returns:
898  *   `true` is returned if and only if the specified key has been found.
899  *
900  * Params:
901  *   nodes = An input range of JSON parser nodes
902  */
903 bool skipToKey(R)(ref R nodes, string key) if (isJSONParserNodeInputRange!R)
904 {
905     import std.algorithm/*.comparison*/ : among;
906     import funkwerk.stdx.data.json.foundation;
907 
908     enforceJson(!nodes.empty, "Unexpected end of input", Location.init);
909     enforceJson(nodes.front.kind.among!(JSONParserNodeKind.objectStart, JSONParserNodeKind.key) > 0,
910         "Expected object or object key", nodes.front.location);
911 
912     if (nodes.front.kind == JSONParserNodeKind.objectStart)
913         nodes.popFront();
914 
915     while (true) {
916         auto k = nodes.front.kind;
917         if (k == JSONParserNodeKind.objectEnd) {
918             nodes.popFront();
919             return false;
920         }
921 
922         assert(k == JSONParserNodeKind.key);
923         if (nodes.front.key == key) {
924             nodes.popFront();
925             return true;
926         }
927 
928         nodes.popFront();
929 
930         nodes.skipValue();
931     }
932 }
933 
934 ///
935 @safe unittest
936 {
937     auto j = parseJSONStream(q{
938             {
939                 "foo": 2,
940                 "bar": 3,
941                 "baz": false,
942                 "qux": "str"
943             }
944         });
945 
946     j.skipToKey("bar");
947     double v1 = j.readDouble;
948     assert(v1 == 3);
949 
950     j.skipToKey("qux");
951     string v2 = j.readString;
952     assert(v2 == "str");
953 
954     assert(j.front.kind == JSONParserNodeKind.objectEnd);
955     j.popFront();
956 
957     assert(j.empty);
958 }
959 
960 
961 /**
962  * Reads an array and issues a callback for each entry.
963  *
964  * Params:
965  *   nodes = An input range of JSON parser nodes
966  *   del = The callback to invoke for each array entry
967  */
968 void readArray(R)(ref R nodes, scope void delegate() @safe del) if (isJSONParserNodeInputRange!R)
969 {
970     import funkwerk.stdx.data.json.foundation;
971     enforceJson(!nodes.empty, "Unexpected end of input", Location.init);
972     enforceJson(nodes.front.kind == JSONParserNodeKind.arrayStart,
973         "Expected array", nodes.front.location);
974     nodes.popFront();
975 
976     while (true) {
977         auto k = nodes.front.kind;
978         if (k == JSONParserNodeKind.arrayEnd) {
979           nodes.popFront();
980           return;
981         }
982         del();
983     }
984 }
985 
986 ///
987 @safe unittest
988 {
989     auto j = parseJSONStream(q{
990             [
991                 "foo",
992                 "bar"
993             ]
994         });
995 
996     size_t i = 0;
997     j.readArray({
998         auto value = j.readString();
999         switch (i++) {
1000             default: assert(false);
1001             case 0: assert(value == "foo"); break;
1002             case 1: assert(value == "bar"); break;
1003         }
1004     });
1005 
1006     assert(j.empty);
1007 }
1008 
1009 /** Reads an array and returns a lazy range of parser node ranges.
1010   *
1011   * The given parser node range must point to a node of kind
1012   * `JSONParserNodeKind.arrayStart`. Each of the returned sub ranges
1013   * corresponds to the contents of a single array entry.
1014   *
1015   * Params:
1016   *   nodes = An input range of JSON parser nodes
1017   *
1018   * Throws:
1019   *   A `JSONException` is thrown if the input range does not point to the
1020   *   start of an array.
1021 */
1022 auto readArray(R)(ref R nodes) @system if (isJSONParserNodeInputRange!R)
1023 {
1024     static struct VR {
1025         R* nodes;
1026         size_t depth = 0;
1027 
1028         @property bool empty() { return !nodes || nodes.empty; }
1029 
1030         @property ref const(typeof(nodes.front)) front() { return nodes.front; }
1031 
1032         void popFront()
1033         {
1034             switch (nodes.front.kind) with (JSONParserNodeKind)
1035             {
1036                 default: break;
1037                 case objectStart, arrayStart: depth++; break;
1038                 case objectEnd, arrayEnd: depth--; break;
1039             }
1040 
1041             nodes.popFront();
1042 
1043             if (depth == 0) nodes = null;
1044         }
1045     }
1046 
1047     static struct ARR {
1048         R* nodes;
1049         VR value;
1050 
1051         @property bool empty() { return !nodes || nodes.empty; }
1052 
1053         @property ref inout(VR) front() inout { return value; }
1054 
1055         void popFront()
1056         {
1057             while (!value.empty) value.popFront();
1058             if (nodes.front.kind == JSONParserNodeKind.arrayEnd) {
1059                 nodes.popFront();
1060                 nodes = null;
1061             } else {
1062                 value = VR(nodes);
1063             }
1064         }
1065     }
1066 
1067     import funkwerk.stdx.data.json.foundation;
1068 
1069     enforceJson(!nodes.empty, "Unexpected end of input", Location.init);
1070     enforceJson(nodes.front.kind == JSONParserNodeKind.arrayStart,
1071         "Expected array", nodes.front.location);
1072     nodes.popFront();
1073 
1074     ARR ret;
1075 
1076     if (nodes.front.kind != JSONParserNodeKind.arrayEnd) {
1077         ret.nodes = &nodes;
1078         ret.value = VR(&nodes);
1079     } else nodes.popFront();
1080 
1081     return ret;
1082 }
1083 
1084 ///
1085 unittest {
1086     auto j = parseJSONStream(q{
1087             [
1088                 "foo",
1089                 "bar"
1090             ]
1091         });
1092 
1093     size_t i = 0;
1094     foreach (ref entry; j.readArray) {
1095         auto value = entry.readString;
1096         assert(entry.empty);
1097         switch (i++) {
1098             default: assert(false);
1099             case 0: assert(value == "foo"); break;
1100             case 1: assert(value == "bar"); break;
1101         }
1102     }
1103     assert(i == 2);
1104 }
1105 
1106 
1107 /**
1108  * Reads an object and issues a callback for each field.
1109  *
1110  * Params:
1111  *   nodes = An input range of JSON parser nodes
1112  *   del = The callback to invoke for each object field
1113  */
1114 void readObject(R)(ref R nodes, scope void delegate(string key) @safe del) if (isJSONParserNodeInputRange!R)
1115 {
1116     import funkwerk.stdx.data.json.foundation;
1117     enforceJson(!nodes.empty, "Unexpected end of input", Location.init);
1118     enforceJson(nodes.front.kind == JSONParserNodeKind.objectStart,
1119         "Expected object", nodes.front.literal.location);
1120     nodes.popFront();
1121 
1122     while (true) {
1123         auto k = nodes.front.kind;
1124         if (k == JSONParserNodeKind.objectEnd) {
1125           nodes.popFront();
1126           return;
1127         }
1128         auto key = nodes.front.key;
1129         nodes.popFront();
1130         del(key);
1131     }
1132 }
1133 
1134 ///
1135 @safe unittest
1136 {
1137     auto j = parseJSONStream(q{
1138             {
1139                 "foo": 1,
1140                 "bar": 2
1141             }
1142         });
1143 
1144     j.readObject((key) {
1145         auto value = j.readDouble;
1146         switch (key) {
1147             default: assert(false);
1148             case "foo": assert(value == 1); break;
1149             case "bar": assert(value == 2); break;
1150         }
1151     });
1152 
1153     assert(j.empty);
1154 }
1155 
1156 
1157 /**
1158  * Reads a single double value.
1159  *
1160  * Params:
1161  *   nodes = An input range of JSON parser nodes
1162  *
1163  * Throws: Throws a `JSONException` is the node range is empty or `nodes.front` is not a number.
1164  */
1165 double readDouble(R)(ref R nodes) if (isJSONParserNodeInputRange!R)
1166 {
1167     import funkwerk.stdx.data.json.foundation;
1168     enforceJson(!nodes.empty, "Unexpected end of input", Location.init);
1169     enforceJson(nodes.front.kind == JSONParserNodeKind.literal
1170         && nodes.front.literal.kind == JSONTokenKind.number,
1171         "Expected numeric value", nodes.front.literal.location);
1172     double ret = nodes.front.literal.number;
1173     nodes.popFront();
1174     return ret;
1175 }
1176 
1177 ///
1178 @safe unittest
1179 {
1180     auto j = parseJSONStream(`1.0`);
1181     double value = j.readDouble;
1182     assert(value == 1.0);
1183     assert(j.empty);
1184 }
1185 
1186 
1187 /**
1188  * Reads a single double value.
1189  *
1190  * Params:
1191  *   nodes = An input range of JSON parser nodes
1192  *
1193  * Throws: Throws a `JSONException` is the node range is empty or `nodes.front` is not a string.
1194  */
1195 string readString(R)(ref R nodes) if (isJSONParserNodeInputRange!R)
1196 {
1197     import funkwerk.stdx.data.json.foundation;
1198     enforceJson(!nodes.empty, "Unexpected end of input", Location.init);
1199     enforceJson(nodes.front.kind == JSONParserNodeKind.literal
1200         && nodes.front.literal.kind == JSONTokenKind..string,
1201         "Expected string value", nodes.front.literal.location);
1202     string ret = nodes.front.literal..string;
1203     nodes.popFront();
1204     return ret;
1205 }
1206 
1207 ///
1208 @safe unittest
1209 {
1210     auto j = parseJSONStream(`"foo"`);
1211     string value = j.readString;
1212     assert(value == "foo");
1213     assert(j.empty);
1214 }
1215 
1216 
1217 /**
1218  * Reads a single double value.
1219  *
1220  * Params:
1221  *   nodes = An input range of JSON parser nodes
1222  *
1223  * Throws: Throws a `JSONException` is the node range is empty or `nodes.front` is not a boolean.
1224  */
1225 bool readBool(R)(ref R nodes) if (isJSONParserNodeInputRange!R)
1226 {
1227     import funkwerk.stdx.data.json.foundation;
1228     enforceJson(!nodes.empty, "Unexpected end of input", Location.init);
1229     enforceJson(nodes.front.kind == JSONParserNodeKind.literal
1230         && nodes.front.literal.kind == JSONTokenKind.boolean,
1231         "Expected boolean value", nodes.front.literal.location);
1232     bool ret = nodes.front.literal.boolean;
1233     nodes.popFront();
1234     return ret;
1235 }
1236 
1237 ///
1238 @safe unittest
1239 {
1240     auto j = parseJSONStream(`true`);
1241     bool value = j.readBool;
1242     assert(value == true);
1243     assert(j.empty);
1244 }