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