1 /**
2  * JSON decoding.
3  *
4  * License:
5  *   This Source Code Form is subject to the terms of
6  *   the Mozilla Public License, v. 2.0. If a copy of
7  *   the MPL was not distributed with this file, You
8  *   can obtain one at http://mozilla.org/MPL/2.0/.
9  *
10  * Authors:
11  *   Vladimir Panteleev <vladimir@thecybershadow.net>
12  */
13 
14 module ae.utils.sd.json.decoder;
15 
16 import ae.utils.text : fromHex;
17 
18 import std.exception : enforce;
19 import std.format;
20 import std.utf : encode;
21 
22 struct JSONDecoder(C)
23 {
24 private:
25 	C[] s;
26 	size_t p;
27 
28 	C next()
29 	{
30 		enforce(p < s.length, "Unexpected end of JSON data");
31 		return s[p++];
32 	}
33 
34 	void skip()
35 	{
36 		p++;
37 	}
38 
39 	C[] readN(size_t n)
40 	{
41 		auto end = p + n;
42 		enforce(end <= s.length);
43 		C[] result = s[p .. end];
44 		p = end;
45 		return result;
46 	}
47 
48 	C peek()
49 	{
50 		enforce(p < s.length);
51 		return s[p];
52 	}
53 
54 	size_t mark()
55 	{
56 		return p;
57 	}
58 
59 	C[] slice(size_t a, size_t b)
60 	{
61 		return s[a..b];
62 	}
63 
64 	@property bool eof() { return p == s.length; }
65 
66 	// *******************************************************************
67 
68 	static bool isWhite(C c)
69 	{
70 		return c == ' ' || c == '\t';
71 	}
72 
73 	void skipWhitespace()
74 	{
75 		while (isWhite(peek()))
76 			skip();
77 	}
78 
79 	void expect(C c)
80 	{
81 		auto n = next();
82 		enforce(n==c, "Expected %s, got %s".format(c, n));
83 	}
84 
85 	// *******************************************************************
86 
87 	// The default reader
88 	public @property ValueReader reader() { return ValueReader(&this); }
89 	alias reader this;
90 
91 	struct ValueReader
92 	{
93 		JSONDecoder* json;
94 
95 		auto read(Handler)(Handler handler)
96 		{
97 			json.skipWhitespace();
98 			switch (json.peek())
99 			{
100 				case '[':
101 					static if (__traits(hasMember, Handler, q{handleArray}))
102 						return handler.handleArray(ArrayReader(json));
103 					else
104 						goto default;
105 				case '"':
106 					static if (__traits(hasMember, Handler, q{canHandleTypeHint}) && Handler.canHandleTypeHint!(C[]))
107 						return handler.handleTypeHint!(C[])(this);
108 					else
109 					static if (__traits(hasMember, Handler, q{handleArray}))
110 						return handler.handleArray(StringReader(json));
111 					else
112 						goto default;
113 				case 't':
114 					static if (__traits(hasMember, Handler, q{canHandleValue}) && Handler.canHandleValue!bool)
115 					{
116 						json.skip();
117 						json.expect('r');
118 						json.expect('u');
119 						json.expect('e');
120 						return handler.handleValue!bool(true);
121 					}
122 					else
123 						goto default;
124 				case 'f':
125 					static if (__traits(hasMember, Handler, q{canHandleValue}) && Handler.canHandleValue!bool)
126 					{
127 						json.skip();
128 						json.expect('a');
129 						json.expect('l');
130 						json.expect('s');
131 						json.expect('e');
132 						return handler.handleValue!bool(false);
133 					}
134 					else
135 						goto default;
136 				case 'n':
137 					static if (__traits(hasMember, Handler, q{canHandleValue}) && Handler.canHandleValue!(typeof(null)))
138 					{
139 						json.skip();
140 						json.expect('u');
141 						json.expect('l');
142 						json.expect('l');
143 						return handler.handleValue!(typeof(null))(null);
144 					}
145 					else
146 						goto default;
147 				case '-':
148 				case '0':
149 					..
150 				case '9':
151 					static if (__traits(hasMember, Handler, q{handleNumeric}))
152 						return handler.handleNumeric(NumericReader(json));
153 					else
154 						goto default;
155 				case '{':
156 					static if (__traits(hasMember, Handler, q{handleMap}))
157 						return handler.handleMap(ObjectReader(json));
158 					else
159 						goto default;
160 				default:
161 					throw new Exception("Unexpected JSON character: %s".format(json.peek()));
162 			}
163 		}
164 	}
165 
166 	struct ArrayReader
167 	{
168 		JSONDecoder* json;
169 
170 		auto read(Handler)(Handler handler)
171 		{
172 			json.skip(); // '['
173 			if (json.peek() == ']')
174 			{
175 				json.skip();
176 				return handler.handleEnd();
177 			}
178 			while (true)
179 			{
180 				handler.handleElement(ValueReader(json));
181 				json.skipWhitespace();
182 				if (json.peek()==']')
183 				{
184 					json.skip();
185 					return handler.handleEnd();
186 				}
187 				else
188 					json.expect(',');
189 			}
190 		}
191 
192 	}
193 
194 	struct ObjectReader
195 	{
196 		JSONDecoder* json;
197 
198 		void read(Handler)(Handler handler)
199 		{
200 			json.skip(); // '{'
201 			json.skipWhitespace();
202 			if (json.peek()=='}')
203 			{
204 				json.skip();
205 				return handler.handleEnd();
206 			}
207 
208 			while (true)
209 			{
210 				handler.handlePair(ObjectPairReader(json));
211 
212 				json.skipWhitespace();
213 				if (json.peek()=='}')
214 				{
215 					json.skip();
216 					return handler.handleEnd();
217 				}
218 				else
219 					json.expect(',');
220 			}
221 		}
222 	}
223 
224 	struct ObjectPairReader
225 	{
226 		JSONDecoder* json;
227 
228 		auto read(Handler)(Handler handler)
229 		{
230 			handler.handlePairKey(ValueReader(json));
231 			json.skipWhitespace();
232 			json.expect(':');
233 			handler.handlePairValue(ValueReader(json));
234 			return handler.handleEnd();
235 		}
236 	}
237 
238 	struct VarReader(V)
239 	{
240 		V v;
241 
242 		auto read(Handler)(Handler handler)
243 		{
244 			static assert(__traits(hasMember, Handler, q{canHandleValue}),
245 				Handler.stringof ~ " can't accept values");
246 			static assert(Handler.canHandleValue!V,
247 				Handler.stringof ~ " can't accept values of type " ~ V.stringof);
248 			return handler.handleValue!V(v);
249 		}
250 	}
251 
252 	struct StringReader
253 	{
254 		JSONDecoder* json;
255 
256 		void read(Handler)(Handler handler)
257 		{
258 			json.skip(); // '"'
259 
260 			auto start = json.mark();
261 
262 			void sendSlice(K)(K[] slice)
263 			{
264 				static if (__traits(hasMember, Handler, q{canHandleSlice}) && Handler.canHandleSlice!(K[]))
265 					handler.handleSlice(slice);
266 				else
267 					foreach (c; slice)
268 						handler.handleElement(VarReader!K(c));
269 			}
270 
271 			void flush()
272 			{
273 				auto end = json.mark();
274 				if (start != end)
275 					sendSlice(json.slice(start, end));
276 			}
277 
278 			void oneConst(C c)()
279 			{
280 				static C[1] arr = [c];
281 				sendSlice(arr[]);
282 			}
283 
284 			while (true)
285 			{
286 				C c = json.peek();
287 				if (c=='"')
288 				{
289 					flush();
290 					json.skip();
291 					return;
292 				}
293 				else
294 				if (c=='\\')
295 				{
296 					flush();
297 					json.skip();
298 					switch (json.next())
299 					{
300 						case '"':  oneConst!('"'); break;
301 						case '/':  oneConst!('/'); break;
302 						case '\\': oneConst!('\\'); break;
303 						case 'b':  oneConst!('\b'); break;
304 						case 'f':  oneConst!('\f'); break;
305 						case 'n':  oneConst!('\n'); break;
306 						case 'r':  oneConst!('\r'); break;
307 						case 't':  oneConst!('\t'); break;
308 						case 'u':
309 						{
310 							auto w = cast(wchar)fromHex!ushort(json.readN(4));
311 							static if (C.sizeof == 1)
312 							{
313 								char[4] buf;
314 								sendSlice(buf[0 .. encode(buf, w)]);
315 							}
316 							else
317 							{
318 								Unqual!C[1] buf;
319 								buf[0] = w;
320 								sendSlice(buf[]);
321 							}
322 							break;
323 						}
324 						default: enforce(false, "Unknown escape");
325 					}
326 					start = json.mark();
327 				}
328 				else
329 					json.skip();
330 			}
331 		}
332 	}
333 
334 	struct NumericReader
335 	{
336 		JSONDecoder* json;
337 
338 		auto read(Handler)(Handler handler)
339 		{
340 			auto p = json.mark();
341 
342 			static immutable bool[256] numeric =
343 			[
344 				'0':true,
345 				'1':true,
346 				'2':true,
347 				'3':true,
348 				'4':true,
349 				'5':true,
350 				'6':true,
351 				'7':true,
352 				'8':true,
353 				'9':true,
354 				'.':true,
355 				'-':true,
356 				'+':true,
357 				'e':true,
358 				'E':true,
359 			];
360 
361 			while (!json.eof() && numeric[json.peek()]) // TODO wchar/dchar OOB
362 				json.skip();
363 			handler.handleSlice!C(json.slice(p, json.mark()));
364 			return handler.handleEnd();
365 		}
366 	}
367 }
368 
369 auto decodeJSON(C)(C[] s)
370 {
371 	return JSONDecoder!C(s);
372 }
373 
374 unittest
375 {
376 	import ae.utils.sd.serialization.deserializer : deserializeInto;
377 
378 	{
379 		struct S { string str; int i; }
380 		S s;
381 		`{"str":"Hello","i":42}`
382 			.decodeJSON()
383 			.deserializeInto(s);
384 		assert(s.str == "Hello");
385 		assert(s.i == 42);
386 	}
387 
388 	{
389 		string s;
390 		`"Hello\nworld"`
391 			.decodeJSON()
392 			.deserializeInto(s);
393 		assert(s == "Hello\nworld");
394 	}
395 }