• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* JSON Parser
2  * ZZJSON - Copyright (C) 2008-2009 by Ivo van Poorten
3  * License: GNU Lesser General Public License version 2.1
4  */
5 
6 #include "zzjson.h"
7 #include <ctype.h>
8 #include <string.h>
9 #include <math.h>
10 #include <stdio.h>
11 
12 #define GETC()          config->getchar(config->ihandle)
13 #define UNGETC(c)       config->ungetchar(c, config->ihandle)
14 #define SKIPWS()        skipws(config)
15 #ifdef CONFIG_NO_ERROR_MESSAGES
16 #define ERROR(x...)
17 #else
18 #define ERROR(x...)     config->error(config->ehandle, ##x)
19 #endif
20 #define MEMERROR()      ERROR("out of memory")
21 
22 #define ALLOW_EXTRA_COMMA    (config->strictness & ZZJSON_ALLOW_EXTRA_COMMA)
23 #define ALLOW_ILLEGAL_ESCAPE (config->strictness & ZZJSON_ALLOW_ILLEGAL_ESCAPE)
24 #define ALLOW_CONTROL_CHARS  (config->strictness & ZZJSON_ALLOW_CONTROL_CHARS)
25 #define ALLOW_GARBAGE_AT_END (config->strictness & ZZJSON_ALLOW_GARBAGE_AT_END)
26 #define ALLOW_COMMENTS       (config->strictness & ZZJSON_ALLOW_COMMENTS)
27 
28 static ZZJSON *parse_array(ZZJSON_CONFIG *config);
29 static ZZJSON *parse_object(ZZJSON_CONFIG *config);
30 
skipws(ZZJSON_CONFIG * config)31 static void skipws(ZZJSON_CONFIG *config) {
32     int d, c = GETC();
33 morews:
34     while (isspace(c)) c = GETC();
35     if (!ALLOW_COMMENTS) goto endws;
36     if (c != '/') goto endws;
37     d = GETC();
38     if (d != '*') goto endws; /* pushing back c will generate a parse error */
39     c = GETC();
40 morecomments:
41     while (c != '*') {
42         if (c == EOF) goto endws;
43         c = GETC();
44     }
45     c = GETC();
46     if (c != '/') goto morecomments;
47     c = GETC();
48     if (isspace(c) || c == '/') goto morews;
49 endws:
50     UNGETC(c);
51 }
52 
parse_string(ZZJSON_CONFIG * config)53 static char *parse_string(ZZJSON_CONFIG *config) {
54     unsigned int len = 16, pos = 0;
55     int c;
56     char *str = NULL;
57 
58     SKIPWS();
59     c = GETC();
60     if (c != '"') {
61         ERROR("string: expected \" at the start");
62         return NULL;
63     }
64 
65     str = config->malloc(len);
66     if (!str) {
67         MEMERROR();
68         return NULL;
69     }
70     c = GETC();
71     while (c > 0 && c != '"') {
72         if (!ALLOW_CONTROL_CHARS && c >= 0 && c <= 31) {
73             ERROR("string: control characters not allowed");
74             goto errout;
75         }
76         if (c == '\\') {
77             c = GETC();
78             switch (c) {
79                 case 'b': c = '\b'; break;
80                 case 'f': c = '\f'; break;
81                 case 'n': c = '\n'; break;
82                 case 'r': c = '\r'; break;
83                 case 't': c = '\t'; break;
84                 case 'u': {
85                     UNGETC(c);    /* ignore \uHHHH, copy verbatim */
86                     c = '\\';
87                     break;
88                 }
89                 case '\\': case '/': case '"':
90                           break;
91                 default:
92                     if (!ALLOW_ILLEGAL_ESCAPE) {
93                         ERROR("string: illegal escape character");
94                         goto errout;
95                     }
96             }
97         }
98         str[pos++] = c;
99         if (pos == len-1) {
100             void *tmp = str;
101             len *= 2;
102             str = config->realloc(str, len);
103             if (!str) {
104                 MEMERROR();
105                 str = tmp;
106                 goto errout;
107             }
108         }
109         c = GETC();
110     }
111     if (c != '"') {
112         ERROR("string: expected \" at the end");
113         goto errout;
114     }
115     str[pos] = 0;
116     return str;
117 
118 errout:
119     config->free(str);
120     return NULL;
121 }
122 
parse_string2(ZZJSON_CONFIG * config)123 static ZZJSON *parse_string2(ZZJSON_CONFIG *config) {
124     ZZJSON *zzjson = NULL;
125     char *str;
126 
127     str = parse_string(config);
128     if (str) {
129         zzjson = config->calloc(1, sizeof(ZZJSON));
130         if (!zzjson) {
131             MEMERROR();
132             config->free(str);
133             return NULL;
134         }
135         zzjson->type = ZZJSON_STRING;
136         zzjson->value.string.string = str;
137     }
138     return zzjson;
139 }
140 
parse_number(ZZJSON_CONFIG * config)141 static ZZJSON *parse_number(ZZJSON_CONFIG *config) {
142     ZZJSON *zzjson;
143     unsigned long long ival = 0, expo = 0;
144     double dval = 0.0, frac = 0.0, fracshft = 10.0;
145     int c, dbl = 0, sign = 1, signexpo = 1;
146 
147     SKIPWS();
148     c = GETC();
149     if (c == '-') {
150         sign = -1;
151         c = GETC();
152     }
153     if (c == '0') {
154         c = GETC();
155         goto skip;
156     }
157 
158     if (!isdigit(c)) {
159         ERROR("number: digit expected");
160         return NULL;
161     }
162 
163     while (isdigit(c)) {
164         ival *= 10;
165         ival += c - '0';
166         c = GETC();
167     }
168 
169 skip:
170     if (c != '.') goto skipfrac;
171 
172     dbl = 1;
173 
174     c = GETC();
175     if (!isdigit(c)) {
176         ERROR("number: digit expected");
177         return NULL;
178     }
179 
180     while (isdigit(c)) {
181         frac += (double)(c - '0') / fracshft;
182         fracshft *= 10.0;
183         c = GETC();
184     }
185 
186 skipfrac:
187     if (c != 'e' && c != 'E') goto skipexpo;
188 
189     dbl = 1;
190 
191     c = GETC();
192     if (c == '+')
193         c = GETC();
194     else if (c == '-') {
195         signexpo = -1;
196         c = GETC();
197     }
198 
199     if (!isdigit(c)) {
200         ERROR("number: digit expected");
201         return NULL;
202     }
203 
204     while (isdigit(c)) {
205         expo *= 10;
206         expo += c - '0';
207         c = GETC();
208     }
209 
210 skipexpo:
211     UNGETC(c);
212 
213     if (dbl) {
214         dval = sign * (long long) ival;
215         dval += sign * frac;
216         dval *= pow(10.0, (double) signexpo * expo);
217     }
218 
219     zzjson = config->calloc(1, sizeof(ZZJSON));
220     if (!zzjson) {
221         MEMERROR();
222         return NULL;
223     }
224     if (dbl) {
225         zzjson->type = ZZJSON_NUMBER_DOUBLE;
226         zzjson->value.number.val.dval = dval;
227     } else {
228         zzjson->type = sign < 0 ? ZZJSON_NUMBER_NEGINT : ZZJSON_NUMBER_POSINT;
229         zzjson->value.number.val.ival = ival;
230     }
231 
232     return zzjson;
233 }
234 
parse_literal(ZZJSON_CONFIG * config,char * s,ZZJSON_TYPE t)235 static ZZJSON *parse_literal(ZZJSON_CONFIG *config, char *s, ZZJSON_TYPE t) {
236     char b[strlen(s)+1];
237     unsigned int i;
238 
239     for (i=0; i<strlen(s); i++) b[i] = GETC();
240     b[i] = 0;
241 
242     if (!strcmp(b,s)) {
243         ZZJSON *zzjson;
244         zzjson = config->calloc(1, sizeof(ZZJSON));
245         if (!zzjson) {
246             MEMERROR();
247             return NULL;
248         }
249         zzjson->type = t;
250         return zzjson;
251     }
252     ERROR("literal: expected %s", s);
253     return NULL;
254 }
255 
parse_true(ZZJSON_CONFIG * config)256 static ZZJSON *parse_true(ZZJSON_CONFIG *config) {
257     return parse_literal(config, (char *)"true", ZZJSON_TRUE);
258 }
259 
parse_false(ZZJSON_CONFIG * config)260 static ZZJSON *parse_false(ZZJSON_CONFIG *config) {
261     return parse_literal(config, (char *)"false", ZZJSON_FALSE);
262 }
263 
parse_null(ZZJSON_CONFIG * config)264 static ZZJSON *parse_null(ZZJSON_CONFIG *config) {
265     return parse_literal(config, (char *)"null", ZZJSON_NULL);
266 }
267 
parse_value(ZZJSON_CONFIG * config)268 static ZZJSON *parse_value(ZZJSON_CONFIG *config) {
269     ZZJSON *retval = NULL;
270     int c;
271 
272     SKIPWS();
273     c = GETC();
274     UNGETC(c);
275     switch (c) {
276         case '"':   retval = parse_string2(config); break;
277         case '0': case '1': case '2': case '3': case '4': case '5':
278         case '6': case '7': case '8': case '9': case '-':
279                     retval = parse_number(config); break;
280         case '{':   retval = parse_object(config); break;
281         case '[':   retval = parse_array(config); break;
282         case 't':   retval = parse_true(config); break;
283         case 'f':   retval = parse_false(config); break;
284         case 'n':   retval = parse_null(config); break;
285     }
286 
287     if (!retval) {
288         ERROR("value: invalid value");
289         return retval;
290     }
291 
292     return retval;
293 }
294 
parse_array(ZZJSON_CONFIG * config)295 static ZZJSON *parse_array(ZZJSON_CONFIG *config) {
296     ZZJSON *retval = NULL, **next = &retval;
297     int c;
298 
299     SKIPWS();
300     c = GETC();
301     if (c != '[') {
302         ERROR("array: expected '['");
303         return NULL;
304     }
305 
306     SKIPWS();
307     c = GETC();
308     while (c > 0 && c != ']') {
309         ZZJSON *zzjson = NULL, *val = NULL;
310 
311         UNGETC(c);
312 
313         SKIPWS();
314         val = parse_value(config);
315         if (!val) {
316             ERROR("array: value expected");
317             goto errout;
318         }
319 
320         SKIPWS();
321         c = GETC();
322         if (c != ',' && c != ']') {
323             ERROR("array: expected ',' or ']'");
324 errout_with_val:
325             zzjson_free(config, val);
326             goto errout;
327         }
328         if (c == ',') {
329             SKIPWS();
330             c = GETC();
331             if (c == ']' && !ALLOW_EXTRA_COMMA) {
332                 ERROR("array: expected value after ','");
333                 goto errout_with_val;
334             }
335         }
336         UNGETC(c);
337 
338         zzjson = config->calloc(1, sizeof(ZZJSON));
339         if (!zzjson) {
340             MEMERROR();
341             zzjson_free(config, val);
342             goto errout_with_val;
343         }
344         zzjson->type            = ZZJSON_ARRAY;
345         zzjson->value.array.val = val;
346         *next = zzjson;
347         next = &zzjson->next;
348 
349         c = GETC();
350     }
351 
352     if (c != ']') {
353         ERROR("array: expected ']'");
354         goto errout;
355     }
356 
357     if (!retval) {  /* empty array, [ ] */
358         retval = config->calloc(1, sizeof(ZZJSON));
359         if (!retval) {
360             MEMERROR();
361             return NULL;
362         }
363         retval->type = ZZJSON_ARRAY;
364     }
365 
366     return retval;
367 
368 errout:
369     zzjson_free(config, retval);
370     return NULL;
371 }
372 
parse_object(ZZJSON_CONFIG * config)373 static ZZJSON *parse_object(ZZJSON_CONFIG *config) {
374     ZZJSON *retval = NULL;
375     int c;
376     ZZJSON **next = &retval;
377 
378     SKIPWS();
379     c = GETC();
380     if (c != '{') {
381         ERROR("object: expected '{'");
382         return NULL;
383     }
384 
385     SKIPWS();
386     c = GETC();
387     while (c > 0 && c != '}') {
388         ZZJSON *zzjson = NULL, *val = NULL;
389         char *str;
390 
391         UNGETC(c);
392 
393         str = parse_string(config);
394         if (!str) {
395             ERROR("object: expected string");
396 errout_with_str:
397             config->free(str);
398             goto errout;
399         }
400 
401         SKIPWS();
402         c = GETC();
403         if (c != ':') {
404             ERROR("object: expected ':'");
405             goto errout_with_str;
406         }
407 
408         SKIPWS();
409         val = parse_value(config);
410         if (!val) {
411             ERROR("object: value expected");
412             goto errout_with_str;
413         }
414 
415         SKIPWS();
416         c = GETC();
417         if (c != ',' && c != '}') {
418             ERROR("object: expected ',' or '}'");
419 errout_with_str_and_val:
420             zzjson_free(config, val);
421             goto errout_with_str;
422         }
423         if (c == ',') {
424             SKIPWS();
425             c = GETC();
426             if (c == '}' && !ALLOW_EXTRA_COMMA) {
427                 ERROR("object: expected pair after ','");
428                 goto errout_with_str_and_val;
429             }
430         }
431         UNGETC(c);
432 
433         zzjson = config->calloc(1, sizeof(ZZJSON));
434         if (!zzjson) {
435             MEMERROR();
436             goto errout_with_str_and_val;
437         }
438         zzjson->type                = ZZJSON_OBJECT;
439         zzjson->value.object.label  = str;
440         zzjson->value.object.val    = val;
441         *next = zzjson;
442         next = &zzjson->next;
443 
444         c = GETC();
445     }
446 
447     if (c != '}') {
448         ERROR("object: expected '}'");
449         goto errout;
450     }
451 
452     if (!retval) {  /* empty object, { } */
453         retval = config->calloc(1, sizeof(ZZJSON));
454         if (!retval) {
455             MEMERROR();
456             return NULL;
457         }
458         retval->type = ZZJSON_OBJECT;
459     }
460 
461     return retval;
462 
463 errout:
464     zzjson_free(config, retval);
465     return NULL;
466 }
467 
zzjson_parse(ZZJSON_CONFIG * config)468 ZZJSON *zzjson_parse(ZZJSON_CONFIG *config) {
469     ZZJSON *retval;
470     int c;
471 
472     SKIPWS();
473     c = GETC();
474     UNGETC(c);
475     if (c == '[')       retval = parse_array(config);
476     else if (c == '{')  retval = parse_object(config);
477     else                { ERROR("expected '[' or '{'"); return NULL; }
478 
479     if (!retval) return NULL;
480 
481     SKIPWS();
482     c = GETC();
483     if (c >= 0 && !ALLOW_GARBAGE_AT_END) {
484         ERROR("parse: garbage at end of file");
485         zzjson_free(config, retval);
486         return NULL;
487     }
488 
489     return retval;
490 }
491