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
28static ZZJSON *parse_array(ZZJSON_CONFIG *config);
29static ZZJSON *parse_object(ZZJSON_CONFIG *config);
30
31static void skipws(ZZJSON_CONFIG *config) {
32    int d, c = GETC();
33morews:
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();
40morecomments:
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;
49endws:
50    UNGETC(c);
51}
52
53static 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
118errout:
119    config->free(str);
120    return NULL;
121}
122
123static 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
141static 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
169skip:
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
186skipfrac:
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
210skipexpo:
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
235static 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
256static ZZJSON *parse_true(ZZJSON_CONFIG *config) {
257    return parse_literal(config, (char *)"true", ZZJSON_TRUE);
258}
259
260static ZZJSON *parse_false(ZZJSON_CONFIG *config) {
261    return parse_literal(config, (char *)"false", ZZJSON_FALSE);
262}
263
264static ZZJSON *parse_null(ZZJSON_CONFIG *config) {
265    return parse_literal(config, (char *)"null", ZZJSON_NULL);
266}
267
268static 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
295static 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 ']'");
324errout_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
368errout:
369    zzjson_free(config, retval);
370    return NULL;
371}
372
373static 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");
396errout_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 '}'");
419errout_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
463errout:
464    zzjson_free(config, retval);
465    return NULL;
466}
467
468ZZJSON *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