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