diff options
Diffstat (limited to 'common/unqlite/jx9_json.c')
-rw-r--r-- | common/unqlite/jx9_json.c | 730 |
1 files changed, 730 insertions, 0 deletions
diff --git a/common/unqlite/jx9_json.c b/common/unqlite/jx9_json.c new file mode 100644 index 0000000..d92f786 --- /dev/null +++ b/common/unqlite/jx9_json.c | |||
@@ -0,0 +1,730 @@ | |||
1 | /* | ||
2 | * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. | ||
3 | * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ | ||
4 | * Version 1.7.2 | ||
5 | * For information on licensing, redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES | ||
6 | * please contact Symisc Systems via: | ||
7 | * legal@symisc.net | ||
8 | * licensing@symisc.net | ||
9 | * contact@symisc.net | ||
10 | * or visit: | ||
11 | * http://jx9.symisc.net/ | ||
12 | */ | ||
13 | /* $SymiscID: json.c v1.0 FreeBSD 2012-12-16 00:28 stable <chm@symisc.net> $ */ | ||
14 | #ifndef JX9_AMALGAMATION | ||
15 | #include "jx9Int.h" | ||
16 | #endif | ||
17 | /* This file deals with JSON serialization, decoding and stuff like that. */ | ||
18 | /* | ||
19 | * Section: | ||
20 | * JSON encoding/decoding routines. | ||
21 | * Authors: | ||
22 | * Symisc Systems, devel@symisc.net. | ||
23 | * Copyright (C) Symisc Systems, http://jx9.symisc.net | ||
24 | * Status: | ||
25 | * Devel. | ||
26 | */ | ||
27 | /* Forward reference */ | ||
28 | static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData); | ||
29 | static int VmJsonObjectEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData); | ||
30 | /* | ||
31 | * JSON encoder state is stored in an instance | ||
32 | * of the following structure. | ||
33 | */ | ||
34 | typedef struct json_private_data json_private_data; | ||
35 | struct json_private_data | ||
36 | { | ||
37 | SyBlob *pOut; /* Output consumer buffer */ | ||
38 | int isFirst; /* True if first encoded entry */ | ||
39 | int iFlags; /* JSON encoding flags */ | ||
40 | int nRecCount; /* Recursion count */ | ||
41 | }; | ||
42 | /* | ||
43 | * Returns the JSON representation of a value.In other word perform a JSON encoding operation. | ||
44 | * According to wikipedia | ||
45 | * JSON's basic types are: | ||
46 | * Number (double precision floating-point format in JavaScript, generally depends on implementation) | ||
47 | * String (double-quoted Unicode, with backslash escaping) | ||
48 | * Boolean (true or false) | ||
49 | * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values | ||
50 | * do not need to be of the same type) | ||
51 | * Object (an unordered collection of key:value pairs with the ':' character separating the key | ||
52 | * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should | ||
53 | * be distinct from each other) | ||
54 | * null (empty) | ||
55 | * Non-significant white space may be added freely around the "structural characters" | ||
56 | * (i.e. the brackets "[{]}", colon ":" and comma ","). | ||
57 | */ | ||
58 | static sxi32 VmJsonEncode( | ||
59 | jx9_value *pIn, /* Encode this value */ | ||
60 | json_private_data *pData /* Context data */ | ||
61 | ){ | ||
62 | SyBlob *pOut = pData->pOut; | ||
63 | int nByte; | ||
64 | if( jx9_value_is_null(pIn) || jx9_value_is_resource(pIn)){ | ||
65 | /* null */ | ||
66 | SyBlobAppend(pOut, "null", sizeof("null")-1); | ||
67 | }else if( jx9_value_is_bool(pIn) ){ | ||
68 | int iBool = jx9_value_to_bool(pIn); | ||
69 | sxu32 iLen; | ||
70 | /* true/false */ | ||
71 | iLen = iBool ? sizeof("true") : sizeof("false"); | ||
72 | SyBlobAppend(pOut, iBool ? "true" : "false", iLen-1); | ||
73 | }else if( jx9_value_is_numeric(pIn) && !jx9_value_is_string(pIn) ){ | ||
74 | const char *zNum; | ||
75 | /* Get a string representation of the number */ | ||
76 | zNum = jx9_value_to_string(pIn, &nByte); | ||
77 | SyBlobAppend(pOut,zNum,nByte); | ||
78 | }else if( jx9_value_is_string(pIn) ){ | ||
79 | const char *zIn, *zEnd; | ||
80 | int c; | ||
81 | /* Encode the string */ | ||
82 | zIn = jx9_value_to_string(pIn, &nByte); | ||
83 | zEnd = &zIn[nByte]; | ||
84 | /* Append the double quote */ | ||
85 | SyBlobAppend(pOut,"\"", sizeof(char)); | ||
86 | for(;;){ | ||
87 | if( zIn >= zEnd ){ | ||
88 | /* No more input to process */ | ||
89 | break; | ||
90 | } | ||
91 | c = zIn[0]; | ||
92 | /* Advance the stream cursor */ | ||
93 | zIn++; | ||
94 | if( c == '"' || c == '\\' ){ | ||
95 | /* Unescape the character */ | ||
96 | SyBlobAppend(pOut,"\\", sizeof(char)); | ||
97 | } | ||
98 | /* Append character verbatim */ | ||
99 | SyBlobAppend(pOut,(const char *)&c,sizeof(char)); | ||
100 | } | ||
101 | /* Append the double quote */ | ||
102 | SyBlobAppend(pOut,"\"",sizeof(char)); | ||
103 | }else if( jx9_value_is_json_array(pIn) ){ | ||
104 | /* Encode the array/object */ | ||
105 | pData->isFirst = 1; | ||
106 | if( jx9_value_is_json_object(pIn) ){ | ||
107 | /* Encode the object instance */ | ||
108 | pData->isFirst = 1; | ||
109 | /* Append the curly braces */ | ||
110 | SyBlobAppend(pOut, "{",sizeof(char)); | ||
111 | /* Iterate throw object attribute */ | ||
112 | jx9_array_walk(pIn,VmJsonObjectEncode, pData); | ||
113 | /* Append the closing curly braces */ | ||
114 | SyBlobAppend(pOut, "}",sizeof(char)); | ||
115 | }else{ | ||
116 | /* Append the square bracket or curly braces */ | ||
117 | SyBlobAppend(pOut,"[",sizeof(char)); | ||
118 | /* Iterate throw array entries */ | ||
119 | jx9_array_walk(pIn, VmJsonArrayEncode, pData); | ||
120 | /* Append the closing square bracket or curly braces */ | ||
121 | SyBlobAppend(pOut,"]",sizeof(char)); | ||
122 | } | ||
123 | }else{ | ||
124 | /* Can't happen */ | ||
125 | SyBlobAppend(pOut,"null",sizeof("null")-1); | ||
126 | } | ||
127 | /* All done */ | ||
128 | return JX9_OK; | ||
129 | } | ||
130 | /* | ||
131 | * The following walker callback is invoked each time we need | ||
132 | * to encode an array to JSON. | ||
133 | */ | ||
134 | static int VmJsonArrayEncode(jx9_value *pKey, jx9_value *pValue, void *pUserData) | ||
135 | { | ||
136 | json_private_data *pJson = (json_private_data *)pUserData; | ||
137 | if( pJson->nRecCount > 31 ){ | ||
138 | /* Recursion limit reached, return immediately */ | ||
139 | SXUNUSED(pKey); /* cc warning */ | ||
140 | return JX9_OK; | ||
141 | } | ||
142 | if( !pJson->isFirst ){ | ||
143 | /* Append the colon first */ | ||
144 | SyBlobAppend(pJson->pOut,",",(int)sizeof(char)); | ||
145 | } | ||
146 | /* Encode the value */ | ||
147 | pJson->nRecCount++; | ||
148 | VmJsonEncode(pValue, pJson); | ||
149 | pJson->nRecCount--; | ||
150 | pJson->isFirst = 0; | ||
151 | return JX9_OK; | ||
152 | } | ||
153 | /* | ||
154 | * The following walker callback is invoked each time we need to encode | ||
155 | * a object instance [i.e: Object in the JX9 jargon] to JSON. | ||
156 | */ | ||
157 | static int VmJsonObjectEncode(jx9_value *pKey,jx9_value *pValue,void *pUserData) | ||
158 | { | ||
159 | json_private_data *pJson = (json_private_data *)pUserData; | ||
160 | const char *zKey; | ||
161 | int nByte; | ||
162 | if( pJson->nRecCount > 31 ){ | ||
163 | /* Recursion limit reached, return immediately */ | ||
164 | return JX9_OK; | ||
165 | } | ||
166 | if( !pJson->isFirst ){ | ||
167 | /* Append the colon first */ | ||
168 | SyBlobAppend(pJson->pOut,",",sizeof(char)); | ||
169 | } | ||
170 | /* Extract a string representation of the key */ | ||
171 | zKey = jx9_value_to_string(pKey, &nByte); | ||
172 | /* Append the key and the double colon */ | ||
173 | if( nByte > 0 ){ | ||
174 | SyBlobAppend(pJson->pOut,"\"",sizeof(char)); | ||
175 | SyBlobAppend(pJson->pOut,zKey,(sxu32)nByte); | ||
176 | SyBlobAppend(pJson->pOut,"\"",sizeof(char)); | ||
177 | }else{ | ||
178 | /* Can't happen */ | ||
179 | SyBlobAppend(pJson->pOut,"null",sizeof("null")-1); | ||
180 | } | ||
181 | SyBlobAppend(pJson->pOut,":",sizeof(char)); | ||
182 | /* Encode the value */ | ||
183 | pJson->nRecCount++; | ||
184 | VmJsonEncode(pValue, pJson); | ||
185 | pJson->nRecCount--; | ||
186 | pJson->isFirst = 0; | ||
187 | return JX9_OK; | ||
188 | } | ||
189 | /* | ||
190 | * Returns a string containing the JSON representation of value. | ||
191 | * In other words, perform the serialization of the given JSON object. | ||
192 | */ | ||
193 | JX9_PRIVATE int jx9JsonSerialize(jx9_value *pValue,SyBlob *pOut) | ||
194 | { | ||
195 | json_private_data sJson; | ||
196 | /* Prepare the JSON data */ | ||
197 | sJson.nRecCount = 0; | ||
198 | sJson.pOut = pOut; | ||
199 | sJson.isFirst = 1; | ||
200 | sJson.iFlags = 0; | ||
201 | /* Perform the encoding operation */ | ||
202 | VmJsonEncode(pValue, &sJson); | ||
203 | /* All done */ | ||
204 | return JX9_OK; | ||
205 | } | ||
206 | /* Possible tokens from the JSON tokenization process */ | ||
207 | #define JSON_TK_TRUE 0x001 /* Boolean true */ | ||
208 | #define JSON_TK_FALSE 0x002 /* Boolean false */ | ||
209 | #define JSON_TK_STR 0x004 /* String enclosed in double quotes */ | ||
210 | #define JSON_TK_NULL 0x008 /* null */ | ||
211 | #define JSON_TK_NUM 0x010 /* Numeric */ | ||
212 | #define JSON_TK_OCB 0x020 /* Open curly braces '{' */ | ||
213 | #define JSON_TK_CCB 0x040 /* Closing curly braces '}' */ | ||
214 | #define JSON_TK_OSB 0x080 /* Open square bracke '[' */ | ||
215 | #define JSON_TK_CSB 0x100 /* Closing square bracket ']' */ | ||
216 | #define JSON_TK_COLON 0x200 /* Single colon ':' */ | ||
217 | #define JSON_TK_COMMA 0x400 /* Single comma ',' */ | ||
218 | #define JSON_TK_ID 0x800 /* ID */ | ||
219 | #define JSON_TK_INVALID 0x1000 /* Unexpected token */ | ||
220 | /* | ||
221 | * Tokenize an entire JSON input. | ||
222 | * Get a single low-level token from the input file. | ||
223 | * Update the stream pointer so that it points to the first | ||
224 | * character beyond the extracted token. | ||
225 | */ | ||
226 | static sxi32 VmJsonTokenize(SyStream *pStream, SyToken *pToken, void *pUserData, void *pCtxData) | ||
227 | { | ||
228 | int *pJsonErr = (int *)pUserData; | ||
229 | SyString *pStr; | ||
230 | int c; | ||
231 | /* Ignore leading white spaces */ | ||
232 | while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisSpace(pStream->zText[0]) ){ | ||
233 | /* Advance the stream cursor */ | ||
234 | if( pStream->zText[0] == '\n' ){ | ||
235 | /* Update line counter */ | ||
236 | pStream->nLine++; | ||
237 | } | ||
238 | pStream->zText++; | ||
239 | } | ||
240 | if( pStream->zText >= pStream->zEnd ){ | ||
241 | /* End of input reached */ | ||
242 | SXUNUSED(pCtxData); /* cc warning */ | ||
243 | return SXERR_EOF; | ||
244 | } | ||
245 | /* Record token starting position and line */ | ||
246 | pToken->nLine = pStream->nLine; | ||
247 | pToken->pUserData = 0; | ||
248 | pStr = &pToken->sData; | ||
249 | SyStringInitFromBuf(pStr, pStream->zText, 0); | ||
250 | if( pStream->zText[0] >= 0xc0 || SyisAlpha(pStream->zText[0]) || pStream->zText[0] == '_' ){ | ||
251 | /* The following code fragment is taken verbatim from the xPP source tree. | ||
252 | * xPP is a modern embeddable macro processor with advanced features useful for | ||
253 | * application seeking for a production quality, ready to use macro processor. | ||
254 | * xPP is a widely used library developed and maintened by Symisc Systems. | ||
255 | * You can reach the xPP home page by following this link: | ||
256 | * http://xpp.symisc.net/ | ||
257 | */ | ||
258 | const unsigned char *zIn; | ||
259 | /* Isolate UTF-8 or alphanumeric stream */ | ||
260 | if( pStream->zText[0] < 0xc0 ){ | ||
261 | pStream->zText++; | ||
262 | } | ||
263 | for(;;){ | ||
264 | zIn = pStream->zText; | ||
265 | if( zIn[0] >= 0xc0 ){ | ||
266 | zIn++; | ||
267 | /* UTF-8 stream */ | ||
268 | while( zIn < pStream->zEnd && ((zIn[0] & 0xc0) == 0x80) ){ | ||
269 | zIn++; | ||
270 | } | ||
271 | } | ||
272 | /* Skip alphanumeric stream */ | ||
273 | while( zIn < pStream->zEnd && zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_') ){ | ||
274 | zIn++; | ||
275 | } | ||
276 | if( zIn == pStream->zText ){ | ||
277 | /* Not an UTF-8 or alphanumeric stream */ | ||
278 | break; | ||
279 | } | ||
280 | /* Synchronize pointers */ | ||
281 | pStream->zText = zIn; | ||
282 | } | ||
283 | /* Record token length */ | ||
284 | pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); | ||
285 | /* A simple identifier */ | ||
286 | pToken->nType = JSON_TK_ID; | ||
287 | if( pStr->nByte == sizeof("true") -1 && SyStrnicmp(pStr->zString, "true", sizeof("true")-1) == 0 ){ | ||
288 | /* boolean true */ | ||
289 | pToken->nType = JSON_TK_TRUE; | ||
290 | }else if( pStr->nByte == sizeof("false") -1 && SyStrnicmp(pStr->zString,"false", sizeof("false")-1) == 0 ){ | ||
291 | /* boolean false */ | ||
292 | pToken->nType = JSON_TK_FALSE; | ||
293 | }else if( pStr->nByte == sizeof("null") -1 && SyStrnicmp(pStr->zString,"null", sizeof("null")-1) == 0 ){ | ||
294 | /* NULL */ | ||
295 | pToken->nType = JSON_TK_NULL; | ||
296 | } | ||
297 | return SXRET_OK; | ||
298 | } | ||
299 | if( pStream->zText[0] == '{' || pStream->zText[0] == '[' || pStream->zText[0] == '}' || pStream->zText[0] == ']' | ||
300 | || pStream->zText[0] == ':' || pStream->zText[0] == ',' ){ | ||
301 | /* Single character */ | ||
302 | c = pStream->zText[0]; | ||
303 | /* Set token type */ | ||
304 | switch(c){ | ||
305 | case '[': pToken->nType = JSON_TK_OSB; break; | ||
306 | case '{': pToken->nType = JSON_TK_OCB; break; | ||
307 | case '}': pToken->nType = JSON_TK_CCB; break; | ||
308 | case ']': pToken->nType = JSON_TK_CSB; break; | ||
309 | case ':': pToken->nType = JSON_TK_COLON; break; | ||
310 | case ',': pToken->nType = JSON_TK_COMMA; break; | ||
311 | default: | ||
312 | break; | ||
313 | } | ||
314 | /* Advance the stream cursor */ | ||
315 | pStream->zText++; | ||
316 | }else if( pStream->zText[0] == '"') { | ||
317 | /* JSON string */ | ||
318 | pStream->zText++; | ||
319 | pStr->zString++; | ||
320 | /* Delimit the string */ | ||
321 | while( pStream->zText < pStream->zEnd ){ | ||
322 | if( pStream->zText[0] == '"' && pStream->zText[-1] != '\\' ){ | ||
323 | break; | ||
324 | } | ||
325 | if( pStream->zText[0] == '\n' ){ | ||
326 | /* Update line counter */ | ||
327 | pStream->nLine++; | ||
328 | } | ||
329 | pStream->zText++; | ||
330 | } | ||
331 | if( pStream->zText >= pStream->zEnd ){ | ||
332 | /* Missing closing '"' */ | ||
333 | pToken->nType = JSON_TK_INVALID; | ||
334 | *pJsonErr = SXERR_SYNTAX; | ||
335 | }else{ | ||
336 | pToken->nType = JSON_TK_STR; | ||
337 | pStream->zText++; /* Jump the closing double quotes */ | ||
338 | } | ||
339 | }else if( pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ | ||
340 | /* Number */ | ||
341 | pStream->zText++; | ||
342 | pToken->nType = JSON_TK_NUM; | ||
343 | while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ | ||
344 | pStream->zText++; | ||
345 | } | ||
346 | if( pStream->zText < pStream->zEnd ){ | ||
347 | c = pStream->zText[0]; | ||
348 | if( c == '.' ){ | ||
349 | /* Real number */ | ||
350 | pStream->zText++; | ||
351 | while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ | ||
352 | pStream->zText++; | ||
353 | } | ||
354 | if( pStream->zText < pStream->zEnd ){ | ||
355 | c = pStream->zText[0]; | ||
356 | if( c=='e' || c=='E' ){ | ||
357 | pStream->zText++; | ||
358 | if( pStream->zText < pStream->zEnd ){ | ||
359 | c = pStream->zText[0]; | ||
360 | if( c =='+' || c=='-' ){ | ||
361 | pStream->zText++; | ||
362 | } | ||
363 | while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ | ||
364 | pStream->zText++; | ||
365 | } | ||
366 | } | ||
367 | } | ||
368 | } | ||
369 | }else if( c=='e' || c=='E' ){ | ||
370 | /* Real number */ | ||
371 | pStream->zText++; | ||
372 | if( pStream->zText < pStream->zEnd ){ | ||
373 | c = pStream->zText[0]; | ||
374 | if( c =='+' || c=='-' ){ | ||
375 | pStream->zText++; | ||
376 | } | ||
377 | while( pStream->zText < pStream->zEnd && pStream->zText[0] < 0xc0 && SyisDigit(pStream->zText[0]) ){ | ||
378 | pStream->zText++; | ||
379 | } | ||
380 | } | ||
381 | } | ||
382 | } | ||
383 | }else{ | ||
384 | /* Unexpected token */ | ||
385 | pToken->nType = JSON_TK_INVALID; | ||
386 | /* Advance the stream cursor */ | ||
387 | pStream->zText++; | ||
388 | *pJsonErr = SXERR_SYNTAX; | ||
389 | /* Abort processing immediatley */ | ||
390 | return SXERR_ABORT; | ||
391 | } | ||
392 | /* record token length */ | ||
393 | pStr->nByte = (sxu32)((const char *)pStream->zText-pStr->zString); | ||
394 | if( pToken->nType == JSON_TK_STR ){ | ||
395 | pStr->nByte--; | ||
396 | } | ||
397 | /* Return to the lexer */ | ||
398 | return SXRET_OK; | ||
399 | } | ||
400 | /* | ||
401 | * JSON decoded input consumer callback signature. | ||
402 | */ | ||
403 | typedef int (*ProcJSONConsumer)(jx9_context *, jx9_value *, jx9_value *, void *); | ||
404 | /* | ||
405 | * JSON decoder state is kept in the following structure. | ||
406 | */ | ||
407 | typedef struct json_decoder json_decoder; | ||
408 | struct json_decoder | ||
409 | { | ||
410 | jx9_context *pCtx; /* Call context */ | ||
411 | ProcJSONConsumer xConsumer; /* Consumer callback */ | ||
412 | void *pUserData; /* Last argument to xConsumer() */ | ||
413 | int iFlags; /* Configuration flags */ | ||
414 | SyToken *pIn; /* Token stream */ | ||
415 | SyToken *pEnd; /* End of the token stream */ | ||
416 | int rec_count; /* Current nesting level */ | ||
417 | int *pErr; /* JSON decoding error if any */ | ||
418 | }; | ||
419 | /* Forward declaration */ | ||
420 | static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData); | ||
421 | /* | ||
422 | * Dequote [i.e: Resolve all backslash escapes ] a JSON string and store | ||
423 | * the result in the given jx9_value. | ||
424 | */ | ||
425 | static void VmJsonDequoteString(const SyString *pStr, jx9_value *pWorker) | ||
426 | { | ||
427 | const char *zIn = pStr->zString; | ||
428 | const char *zEnd = &pStr->zString[pStr->nByte]; | ||
429 | const char *zCur; | ||
430 | int c; | ||
431 | /* Mark the value as a string */ | ||
432 | jx9_value_string(pWorker, "", 0); /* Empty string */ | ||
433 | for(;;){ | ||
434 | zCur = zIn; | ||
435 | while( zIn < zEnd && zIn[0] != '\\' ){ | ||
436 | zIn++; | ||
437 | } | ||
438 | if( zIn > zCur ){ | ||
439 | /* Append chunk verbatim */ | ||
440 | jx9_value_string(pWorker, zCur, (int)(zIn-zCur)); | ||
441 | } | ||
442 | zIn++; | ||
443 | if( zIn >= zEnd ){ | ||
444 | /* End of the input reached */ | ||
445 | break; | ||
446 | } | ||
447 | c = zIn[0]; | ||
448 | /* Unescape the character */ | ||
449 | switch(c){ | ||
450 | case '"': jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break; | ||
451 | case '\\': jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); break; | ||
452 | case 'n': jx9_value_string(pWorker, "\n", (int)sizeof(char)); break; | ||
453 | case 'r': jx9_value_string(pWorker, "\r", (int)sizeof(char)); break; | ||
454 | case 't': jx9_value_string(pWorker, "\t", (int)sizeof(char)); break; | ||
455 | case 'f': jx9_value_string(pWorker, "\f", (int)sizeof(char)); break; | ||
456 | default: | ||
457 | jx9_value_string(pWorker, (const char *)&c, (int)sizeof(char)); | ||
458 | break; | ||
459 | } | ||
460 | /* Advance the stream cursor */ | ||
461 | zIn++; | ||
462 | } | ||
463 | } | ||
464 | /* | ||
465 | * Returns a jx9_value holding the image of a JSON string. In other word perform a JSON decoding operation. | ||
466 | * According to wikipedia | ||
467 | * JSON's basic types are: | ||
468 | * Number (double precision floating-point format in JavaScript, generally depends on implementation) | ||
469 | * String (double-quoted Unicode, with backslash escaping) | ||
470 | * Boolean (true or false) | ||
471 | * Array (an ordered sequence of values, comma-separated and enclosed in square brackets; the values | ||
472 | * do not need to be of the same type) | ||
473 | * Object (an unordered collection of key:value pairs with the ':' character separating the key | ||
474 | * and the value, comma-separated and enclosed in curly braces; the keys must be strings and should | ||
475 | * be distinct from each other) | ||
476 | * null (empty) | ||
477 | * Non-significant white space may be added freely around the "structural characters" (i.e. the brackets "[{]}", colon ":" and comma ", "). | ||
478 | */ | ||
479 | static sxi32 VmJsonDecode( | ||
480 | json_decoder *pDecoder, /* JSON decoder */ | ||
481 | jx9_value *pArrayKey /* Key for the decoded array */ | ||
482 | ){ | ||
483 | jx9_value *pWorker; /* Worker variable */ | ||
484 | sxi32 rc; | ||
485 | /* Check if we do not nest to much */ | ||
486 | if( pDecoder->rec_count > 31 ){ | ||
487 | /* Nesting limit reached, abort decoding immediately */ | ||
488 | return SXERR_ABORT; | ||
489 | } | ||
490 | if( pDecoder->pIn->nType & (JSON_TK_STR|JSON_TK_ID|JSON_TK_TRUE|JSON_TK_FALSE|JSON_TK_NULL|JSON_TK_NUM) ){ | ||
491 | /* Scalar value */ | ||
492 | pWorker = jx9_context_new_scalar(pDecoder->pCtx); | ||
493 | if( pWorker == 0 ){ | ||
494 | jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); | ||
495 | /* Abort the decoding operation immediately */ | ||
496 | return SXERR_ABORT; | ||
497 | } | ||
498 | /* Reflect the JSON image */ | ||
499 | if( pDecoder->pIn->nType & JSON_TK_NULL ){ | ||
500 | /* Nullify the value.*/ | ||
501 | jx9_value_null(pWorker); | ||
502 | }else if( pDecoder->pIn->nType & (JSON_TK_TRUE|JSON_TK_FALSE) ){ | ||
503 | /* Boolean value */ | ||
504 | jx9_value_bool(pWorker, (pDecoder->pIn->nType & JSON_TK_TRUE) ? 1 : 0 ); | ||
505 | }else if( pDecoder->pIn->nType & JSON_TK_NUM ){ | ||
506 | SyString *pStr = &pDecoder->pIn->sData; | ||
507 | /* | ||
508 | * Numeric value. | ||
509 | * Get a string representation first then try to get a numeric | ||
510 | * value. | ||
511 | */ | ||
512 | jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte); | ||
513 | /* Obtain a numeric representation */ | ||
514 | jx9MemObjToNumeric(pWorker); | ||
515 | }else if( pDecoder->pIn->nType & JSON_TK_ID ){ | ||
516 | SyString *pStr = &pDecoder->pIn->sData; | ||
517 | jx9_value_string(pWorker, pStr->zString, (int)pStr->nByte); | ||
518 | }else{ | ||
519 | /* Dequote the string */ | ||
520 | VmJsonDequoteString(&pDecoder->pIn->sData, pWorker); | ||
521 | } | ||
522 | /* Invoke the consumer callback */ | ||
523 | rc = pDecoder->xConsumer(pDecoder->pCtx, pArrayKey, pWorker, pDecoder->pUserData); | ||
524 | if( rc == SXERR_ABORT ){ | ||
525 | return SXERR_ABORT; | ||
526 | } | ||
527 | /* All done, advance the stream cursor */ | ||
528 | pDecoder->pIn++; | ||
529 | }else if( pDecoder->pIn->nType & JSON_TK_OSB /*'[' */) { | ||
530 | ProcJSONConsumer xOld; | ||
531 | void *pOld; | ||
532 | /* Array representation*/ | ||
533 | pDecoder->pIn++; | ||
534 | /* Create a working array */ | ||
535 | pWorker = jx9_context_new_array(pDecoder->pCtx); | ||
536 | if( pWorker == 0 ){ | ||
537 | jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); | ||
538 | /* Abort the decoding operation immediately */ | ||
539 | return SXERR_ABORT; | ||
540 | } | ||
541 | /* Save the old consumer */ | ||
542 | xOld = pDecoder->xConsumer; | ||
543 | pOld = pDecoder->pUserData; | ||
544 | /* Set the new consumer */ | ||
545 | pDecoder->xConsumer = VmJsonArrayDecoder; | ||
546 | pDecoder->pUserData = pWorker; | ||
547 | /* Decode the array */ | ||
548 | for(;;){ | ||
549 | /* Jump trailing comma. Note that the standard JX9 engine will not let you | ||
550 | * do this. | ||
551 | */ | ||
552 | while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){ | ||
553 | pDecoder->pIn++; | ||
554 | } | ||
555 | if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CSB) /*']'*/ ){ | ||
556 | if( pDecoder->pIn < pDecoder->pEnd ){ | ||
557 | pDecoder->pIn++; /* Jump the trailing ']' */ | ||
558 | } | ||
559 | break; | ||
560 | } | ||
561 | /* Recurse and decode the entry */ | ||
562 | pDecoder->rec_count++; | ||
563 | rc = VmJsonDecode(pDecoder, 0); | ||
564 | pDecoder->rec_count--; | ||
565 | if( rc == SXERR_ABORT ){ | ||
566 | /* Abort processing immediately */ | ||
567 | return SXERR_ABORT; | ||
568 | } | ||
569 | /*The cursor is automatically advanced by the VmJsonDecode() function */ | ||
570 | if( (pDecoder->pIn < pDecoder->pEnd) && | ||
571 | ((pDecoder->pIn->nType & (JSON_TK_CSB/*']'*/|JSON_TK_COMMA/*','*/))==0) ){ | ||
572 | /* Unexpected token, abort immediatley */ | ||
573 | *pDecoder->pErr = SXERR_SYNTAX; | ||
574 | return SXERR_ABORT; | ||
575 | } | ||
576 | } | ||
577 | /* Restore the old consumer */ | ||
578 | pDecoder->xConsumer = xOld; | ||
579 | pDecoder->pUserData = pOld; | ||
580 | /* Invoke the old consumer on the decoded array */ | ||
581 | xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld); | ||
582 | }else if( pDecoder->pIn->nType & JSON_TK_OCB /*'{' */) { | ||
583 | ProcJSONConsumer xOld; | ||
584 | jx9_value *pKey; | ||
585 | void *pOld; | ||
586 | /* Object representation*/ | ||
587 | pDecoder->pIn++; | ||
588 | /* Create a working array */ | ||
589 | pWorker = jx9_context_new_array(pDecoder->pCtx); | ||
590 | pKey = jx9_context_new_scalar(pDecoder->pCtx); | ||
591 | if( pWorker == 0 || pKey == 0){ | ||
592 | jx9_context_throw_error(pDecoder->pCtx, JX9_CTX_ERR, "JX9 is running out of memory"); | ||
593 | /* Abort the decoding operation immediately */ | ||
594 | return SXERR_ABORT; | ||
595 | } | ||
596 | /* Save the old consumer */ | ||
597 | xOld = pDecoder->xConsumer; | ||
598 | pOld = pDecoder->pUserData; | ||
599 | /* Set the new consumer */ | ||
600 | pDecoder->xConsumer = VmJsonArrayDecoder; | ||
601 | pDecoder->pUserData = pWorker; | ||
602 | /* Decode the object */ | ||
603 | for(;;){ | ||
604 | /* Jump trailing comma. Note that the standard JX9 engine will not let you | ||
605 | * do this. | ||
606 | */ | ||
607 | while( (pDecoder->pIn < pDecoder->pEnd) && (pDecoder->pIn->nType & JSON_TK_COMMA) ){ | ||
608 | pDecoder->pIn++; | ||
609 | } | ||
610 | if( pDecoder->pIn >= pDecoder->pEnd || (pDecoder->pIn->nType & JSON_TK_CCB) /*'}'*/ ){ | ||
611 | if( pDecoder->pIn < pDecoder->pEnd ){ | ||
612 | pDecoder->pIn++; /* Jump the trailing ']' */ | ||
613 | } | ||
614 | break; | ||
615 | } | ||
616 | if( (pDecoder->pIn->nType & (JSON_TK_ID|JSON_TK_STR)) == 0 || &pDecoder->pIn[1] >= pDecoder->pEnd | ||
617 | || (pDecoder->pIn[1].nType & JSON_TK_COLON) == 0){ | ||
618 | /* Syntax error, return immediately */ | ||
619 | *pDecoder->pErr = SXERR_SYNTAX; | ||
620 | return SXERR_ABORT; | ||
621 | } | ||
622 | if( pDecoder->pIn->nType & JSON_TK_ID ){ | ||
623 | SyString *pStr = &pDecoder->pIn->sData; | ||
624 | jx9_value_string(pKey, pStr->zString, (int)pStr->nByte); | ||
625 | }else{ | ||
626 | /* Dequote the key */ | ||
627 | VmJsonDequoteString(&pDecoder->pIn->sData, pKey); | ||
628 | } | ||
629 | /* Jump the key and the colon */ | ||
630 | pDecoder->pIn += 2; | ||
631 | /* Recurse and decode the value */ | ||
632 | pDecoder->rec_count++; | ||
633 | rc = VmJsonDecode(pDecoder, pKey); | ||
634 | pDecoder->rec_count--; | ||
635 | if( rc == SXERR_ABORT ){ | ||
636 | /* Abort processing immediately */ | ||
637 | return SXERR_ABORT; | ||
638 | } | ||
639 | /* Reset the internal buffer of the key */ | ||
640 | jx9_value_reset_string_cursor(pKey); | ||
641 | /*The cursor is automatically advanced by the VmJsonDecode() function */ | ||
642 | } | ||
643 | /* Restore the old consumer */ | ||
644 | pDecoder->xConsumer = xOld; | ||
645 | pDecoder->pUserData = pOld; | ||
646 | /* Invoke the old consumer on the decoded object*/ | ||
647 | xOld(pDecoder->pCtx, pArrayKey, pWorker, pOld); | ||
648 | /* Release the key */ | ||
649 | jx9_context_release_value(pDecoder->pCtx, pKey); | ||
650 | }else{ | ||
651 | /* Unexpected token */ | ||
652 | return SXERR_ABORT; /* Abort immediately */ | ||
653 | } | ||
654 | /* Release the worker variable */ | ||
655 | jx9_context_release_value(pDecoder->pCtx, pWorker); | ||
656 | return SXRET_OK; | ||
657 | } | ||
658 | /* | ||
659 | * The following JSON decoder callback is invoked each time | ||
660 | * a JSON array representation [i.e: [15, "hello", FALSE] ] | ||
661 | * is being decoded. | ||
662 | */ | ||
663 | static int VmJsonArrayDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData) | ||
664 | { | ||
665 | jx9_value *pArray = (jx9_value *)pUserData; | ||
666 | /* Insert the entry */ | ||
667 | jx9_array_add_elem(pArray, pKey, pWorker); /* Will make it's own copy */ | ||
668 | SXUNUSED(pCtx); /* cc warning */ | ||
669 | /* All done */ | ||
670 | return SXRET_OK; | ||
671 | } | ||
672 | /* | ||
673 | * Standard JSON decoder callback. | ||
674 | */ | ||
675 | static int VmJsonDefaultDecoder(jx9_context *pCtx, jx9_value *pKey, jx9_value *pWorker, void *pUserData) | ||
676 | { | ||
677 | /* Return the value directly */ | ||
678 | jx9_result_value(pCtx, pWorker); /* Will make it's own copy */ | ||
679 | SXUNUSED(pKey); /* cc warning */ | ||
680 | SXUNUSED(pUserData); | ||
681 | /* All done */ | ||
682 | return SXRET_OK; | ||
683 | } | ||
684 | /* | ||
685 | * Exported JSON decoding interface | ||
686 | */ | ||
687 | JX9_PRIVATE int jx9JsonDecode(jx9_context *pCtx,const char *zJSON,int nByte) | ||
688 | { | ||
689 | jx9_vm *pVm = pCtx->pVm; | ||
690 | json_decoder sDecoder; | ||
691 | SySet sToken; | ||
692 | SyLex sLex; | ||
693 | sxi32 rc; | ||
694 | /* Tokenize the input */ | ||
695 | SySetInit(&sToken, &pVm->sAllocator, sizeof(SyToken)); | ||
696 | rc = SXRET_OK; | ||
697 | SyLexInit(&sLex, &sToken, VmJsonTokenize, &rc); | ||
698 | SyLexTokenizeInput(&sLex,zJSON,(sxu32)nByte, 0, 0, 0); | ||
699 | if( rc != SXRET_OK ){ | ||
700 | /* Something goes wrong while tokenizing input. [i.e: Unexpected token] */ | ||
701 | SyLexRelease(&sLex); | ||
702 | SySetRelease(&sToken); | ||
703 | /* return NULL */ | ||
704 | jx9_result_null(pCtx); | ||
705 | return JX9_OK; | ||
706 | } | ||
707 | /* Fill the decoder */ | ||
708 | sDecoder.pCtx = pCtx; | ||
709 | sDecoder.pErr = &rc; | ||
710 | sDecoder.pIn = (SyToken *)SySetBasePtr(&sToken); | ||
711 | sDecoder.pEnd = &sDecoder.pIn[SySetUsed(&sToken)]; | ||
712 | sDecoder.iFlags = 0; | ||
713 | sDecoder.rec_count = 0; | ||
714 | /* Set a default consumer */ | ||
715 | sDecoder.xConsumer = VmJsonDefaultDecoder; | ||
716 | sDecoder.pUserData = 0; | ||
717 | /* Decode the raw JSON input */ | ||
718 | rc = VmJsonDecode(&sDecoder, 0); | ||
719 | if( rc == SXERR_ABORT ){ | ||
720 | /* | ||
721 | * Something goes wrong while decoding JSON input.Return NULL. | ||
722 | */ | ||
723 | jx9_result_null(pCtx); | ||
724 | } | ||
725 | /* Clean-up the mess left behind */ | ||
726 | SyLexRelease(&sLex); | ||
727 | SySetRelease(&sToken); | ||
728 | /* All done */ | ||
729 | return JX9_OK; | ||
730 | } | ||