123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 |
- #include <stdio.h>
- #include <stdint.h>
- #include <ctype.h>
- #include <math.h>
- #include <toaru/hashmap.h>
- #include <toaru/list.h>
- #include <toaru/json.h>
- typedef struct JSON_Value Value;
- /* Internal usage */
- struct JSON_Context {
- const char * string;
- int c;
- const char * error;
- };
- void json_free(Value * v) {
- if (v->type == JSON_TYPE_STRING) {
- free(v->string);
- }
- if (v->type == JSON_TYPE_OBJECT) {
- hashmap_free(v->object);
- free(v->object);
- }
- if (v->type == JSON_TYPE_ARRAY) {
- foreach(node, v->array) {
- json_free(node->value);
- }
- list_free(v->array);
- free(v->array);
- }
- free(v);
- }
- static Value * value(struct JSON_Context * ctx);
- static int peek(struct JSON_Context * ctx) {
- return ctx->string[ctx->c];
- }
- static void advance(struct JSON_Context * ctx) {
- ctx->c++;
- }
- static void whitespace(struct JSON_Context * ctx) {
- while (1) {
- int ch = peek(ctx);
- if (ch == ' ' || ch == '\r' || ch == '\n' || ch == '\t') {
- advance(ctx);
- } else {
- break;
- }
- }
- }
- static Value * string(struct JSON_Context * ctx) {
- if (peek(ctx) != '"') return NULL;
- advance(ctx);
- int size = 4;
- char * tmp = malloc(4);
- tmp[0] = 0;
- int used = 0;
- #define add(c) do { \
- if (used + 1 == size) { \
- size *= 2; \
- tmp = realloc(tmp, size); \
- } \
- tmp[used] = c; \
- tmp[used+1] = 0; \
- used++; \
- } while (0)
- while (1) {
- int ch = peek(ctx);
- if (ch == 0) goto string_error;
- if (ch == '"') {
- break;
- } else if (ch == '\\') {
- advance(ctx);
- ch = peek(ctx);
- if (ch == '"') add('"');
- else if (ch == '\\') add('\\');
- else if (ch == '/') add('/');
- else if (ch == 'b') add('\b');
- else if (ch == 'f') add('\f');
- else if (ch == 'n') add('\n');
- else if (ch == 'r') add('\r');
- else if (ch == 't') add('\t');
- else if (ch == 'u') {
- /* Parse hex */
- advance(ctx);
- char hex_digits[5];
- if (!isxdigit(peek(ctx))) goto string_error;
- hex_digits[0] = peek(ctx); advance(ctx);
- if (!isxdigit(peek(ctx))) goto string_error;
- hex_digits[1] = peek(ctx); advance(ctx);
- if (!isxdigit(peek(ctx))) goto string_error;
- hex_digits[2] = peek(ctx); advance(ctx);
- if (!isxdigit(peek(ctx))) goto string_error;
- hex_digits[3] = peek(ctx); /* will be advanced later */
- hex_digits[4] = 0;
- uint32_t val = strtoul(hex_digits, NULL, 16);
- if (val < 0x0080) {
- add(val);
- } else if (val < 0x0800) {
- add(0xC0 | (val >> 6));
- add(0x80 | (val & 0x3F));
- } else {
- add(0xE0 | (val >> 12));
- add(0x80 | ((val >> 6) & 0x3F));
- add(0x80 | (val & 0x3F));
- }
- } else {
- goto string_error;
- }
- advance(ctx);
- } else {
- add(ch);
- advance(ctx);
- }
- }
- if (peek(ctx) != '"') {
- ctx->error = "Unexpected EOF?";
- goto string_error;
- }
- advance(ctx);
- Value * out = malloc(sizeof(Value));
- out->type = JSON_TYPE_STRING;
- out->string = strdup(tmp);
- free(tmp);
- return out;
- string_error:
- free(tmp);
- return NULL;
- }
- static Value * object(struct JSON_Context * ctx) {
- if (peek(ctx) != '{') {
- ctx->error = "Expected { (internal error)";
- return NULL;
- }
- advance(ctx);
- Value * out;
- hashmap_t * output = hashmap_create(10);
- output->hash_val_free = (void (*)(void *))json_free;
- whitespace(ctx);
- if (peek(ctx) == '}') {
- advance(ctx);
- goto _object_done;
- }
- while (1) {
- whitespace(ctx);
- Value * s = string(ctx);
- if (!s) {
- ctx->error = "Expected string";
- break;
- }
- whitespace(ctx);
- if (peek(ctx) != ':') {
- ctx->error = "Expected :";
- break;
- }
- advance(ctx);
- Value * v = value(ctx);
- hashmap_set(output, s->string, v);
- json_free(s);
- if (peek(ctx) == '}') {
- advance(ctx);
- goto _object_done;
- }
- if (peek(ctx) != ',') {
- ctx->error = "Expected , or {";
- break;
- }
- advance(ctx);
- }
- hashmap_free(output);
- return NULL;
- _object_done:
- out = malloc(sizeof(Value));
- out->type = JSON_TYPE_OBJECT;
- out->object = output;
- return out;
- }
- static Value * boolean(struct JSON_Context * ctx) {
- int value = -1;
- if (peek(ctx) == 't') {
- advance(ctx);
- if (peek(ctx) != 'r') { ctx->error = "Invalid literal while parsing bool"; return NULL; }
- advance(ctx);
- if (peek(ctx) != 'u') { ctx->error = "Invalid literal while parsing bool"; return NULL; }
- advance(ctx);
- if (peek(ctx) != 'e') { ctx->error = "Invalid literal while parsing bool"; return NULL; }
- advance(ctx);
- value = 1;
- } else if (peek(ctx) == 'f') {
- advance(ctx);
- if (peek(ctx) != 'a') { ctx->error = "Invalid literal while parsing bool"; return NULL; }
- advance(ctx);
- if (peek(ctx) != 'l') { ctx->error = "Invalid literal while parsing bool"; return NULL; }
- advance(ctx);
- if (peek(ctx) != 's') { ctx->error = "Invalid literal while parsing bool"; return NULL; }
- advance(ctx);
- if (peek(ctx) != 'e') { ctx->error = "Invalid literal while parsing bool"; return NULL; }
- advance(ctx);
- value = 0;
- } else { ctx->error = "Invalid literal while parsing bool"; return NULL; }
- Value * out = malloc(sizeof(Value));
- out->type = JSON_TYPE_BOOL;
- out->boolean = value;
- return out;
- }
- static Value * null(struct JSON_Context * ctx) {
- if (peek(ctx) != 'n') { ctx->error = "Invalid literal while parsing null"; return NULL; }
- advance(ctx);
- if (peek(ctx) != 'u') { ctx->error = "Invalid literal while parsing null"; return NULL; }
- advance(ctx);
- if (peek(ctx) != 'l') { ctx->error = "Invalid literal while parsing null"; return NULL; }
- advance(ctx);
- if (peek(ctx) != 'l') { ctx->error = "Invalid literal while parsing null"; return NULL; }
- advance(ctx);
- Value * out = malloc(sizeof(Value));
- out->type = JSON_TYPE_NULL;
- return out;
- }
- static Value * number(struct JSON_Context * ctx) {
- double value = 0;
- int sign = 1;
- if (peek(ctx) == '-') {
- /* Negative */
- sign = -1;
- advance(ctx);
- }
- if (peek(ctx) == '0') {
- advance(ctx);
- } else if (isdigit(peek(ctx))) {
- /* Read any digit */
- value = peek(ctx) - '0';
- advance(ctx);
- while (isdigit(peek(ctx))) {
- value *= 10;
- value += peek(ctx) - '0';
- advance(ctx);
- }
- } else {
- ctx->error = "Expected digit";
- return NULL;
- }
- if (peek(ctx) == '.') {
- /* Read fractional part */
- advance(ctx);
- double multiplier = 0.1;
- /* read at least one digit */
- if (!isdigit(peek(ctx))) {
- ctx->error = "Expected digit";
- return NULL;
- }
- while (isdigit(peek(ctx))) {
- value += multiplier * (peek(ctx) - '0');
- multiplier *= 0.1;
- advance(ctx);
- }
- }
- if (peek(ctx) == 'e' || peek(ctx) == 'E') {
- /* Read exponent */
- int exp_sign = 1;
- advance(ctx);
- if (peek(ctx) == '+') advance(ctx);
- else if (peek(ctx) == '-') {
- exp_sign = -1;
- advance(ctx);
- }
- /* read digits */
- if (!isdigit(peek(ctx))) {
- ctx->error = "Expected digit";
- return NULL;
- }
- double exp = peek(ctx) - '0';
- advance(ctx);
- while (isdigit(peek(ctx))) {
- exp *= 10;
- exp += peek(ctx) - '0';
- advance(ctx);
- }
- value = value * pow(10.0,exp * exp_sign);
- }
- Value * out = malloc(sizeof(Value));
- out->type = JSON_TYPE_NUMBER;
- out->number = value * sign;
- return out;
- }
- static Value * array(struct JSON_Context * ctx) {
- if (peek(ctx) != '[') return NULL;
- advance(ctx);
- whitespace(ctx);
- list_t * output = list_create();
- Value * out;
- if (peek(ctx) == ']') {
- advance(ctx);
- goto _array_done;
- }
- while (1) {
- Value * next = value(ctx);
- if (!next) break;
- list_insert(output, next);
- if (peek(ctx) == ']') {
- advance(ctx);
- goto _array_done;
- }
- if (peek(ctx) != ',') {
- ctx->error = "Expected ,";
- break;
- }
- advance(ctx);
- }
- /* uh oh */
- foreach(node, output) {
- json_free(node->value);
- }
- list_free(output);
- free(output);
- return NULL;
- _array_done:
- out = malloc(sizeof(Value));
- out->type = JSON_TYPE_ARRAY;
- out->array = output;
- return out;
- }
- #define WHITE(c) { \
- Value * out = c; \
- whitespace(ctx); \
- return out; \
- }
- static Value * value(struct JSON_Context * ctx) {
- whitespace(ctx);
- if (peek(ctx) == '"') WHITE(string(ctx))
- else if (peek(ctx) == '{') WHITE(object(ctx))
- else if (peek(ctx) == '[') WHITE(array(ctx))
- else if (peek(ctx) == '-' || isdigit(peek(ctx))) WHITE(number(ctx))
- else if (peek(ctx) == 't') WHITE(boolean(ctx))
- else if (peek(ctx) == 'f') WHITE(boolean(ctx))
- else if (peek(ctx) == 'n') WHITE(null(ctx))
- ctx->error = "Unexpected value";
- return NULL;
- }
- Value * json_parse(const char * str) {
- struct JSON_Context ctx;
- ctx.string = str;
- ctx.c = 0;
- ctx.error = NULL;
- Value * out = value(&ctx);
- #if 0
- if (!out) {
- fprintf(stderr, "JSON parse error at %d (%c)\n", ctx.c, ctx.string[ctx.c]);
- fprintf(stderr, "%s\n", ctx.error);
- fprintf(stderr, "%s\n", ctx.string);
- for (int i = 0; i < ctx.c; ++i) { fprintf(stderr, " "); }
- fprintf(stderr, "^\n");
- }
- #endif
- return out;
- }
- Value * json_parse_file(const char * filename) {
- FILE * f = fopen(filename, "r");
- if (!f) return NULL;
- fseek(f, 0, SEEK_END);
- size_t size = ftell(f);
- fseek(f, 0, SEEK_SET);
- char * tmp = malloc(size + 1);
- fread(tmp, size, 1, f);
- tmp[size] = 0;
- fclose(f);
- Value * out = json_parse(tmp);
- free(tmp);
- return out;
- }
|