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 //