Home | History | Annotate | Download | only in zzjson
      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 
     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 
     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 
    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 
    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 
    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 
    256 static ZZJSON *parse_true(ZZJSON_CONFIG *config) {
    257     return parse_literal(config, (char *)"true", ZZJSON_TRUE);
    258 }
    259 
    260 static ZZJSON *parse_false(ZZJSON_CONFIG *config) {
    261     return parse_literal(config, (char *)"false", ZZJSON_FALSE);
    262 }
    263 
    264 static ZZJSON *parse_null(ZZJSON_CONFIG *config) {
    265     return parse_literal(config, (char *)"null", ZZJSON_NULL);
    266 }
    267 
    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 
    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 
    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 
    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