337 lines
6.4 KiB
C
337 lines
6.4 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "json.h"
|
|
#include "sortedmap.h"
|
|
#include "arraylist.h"
|
|
|
|
Json * parse_null(FILE *stream) {
|
|
Json * json;
|
|
if (getc(stream) == 'u' && getc(stream) == 'l' && getc(stream) == 'l') {
|
|
json = malloc(sizeof(Json));
|
|
json->type = NIL;
|
|
return json;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
Json * parse_true(FILE *stream) {
|
|
Json * json;
|
|
if (getc(stream) == 'r' && getc(stream) == 'u' && getc(stream) == 'e') {
|
|
json = malloc(sizeof(Json));
|
|
json->type = BOOLEAN;
|
|
json->value.boolean = 1;
|
|
return json;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
Json * parse_false(FILE *stream) {
|
|
Json * json;
|
|
if (getc(stream) == 'a' && getc(stream) == 'l' && getc(stream) == 's' && getc(stream) == 'e') {
|
|
json = malloc(sizeof(Json));
|
|
json->type = BOOLEAN;
|
|
json->value.boolean = 0;
|
|
return json;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* TODO: scientific notation */
|
|
Json * parse_number(FILE *stream, int sign) {
|
|
Json * json;
|
|
long num = 0, den = 0;
|
|
char c;
|
|
while (1) {
|
|
c = getc(stream);
|
|
switch(c) {
|
|
case '0':
|
|
num = num * 10;
|
|
den *= 10;
|
|
break;
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
num = num * 10 + (c - '1' + 1);
|
|
den *= 10;
|
|
break;
|
|
case '.':
|
|
den = 1;
|
|
break;
|
|
default:
|
|
if (c != EOF) ungetc(c, stream);
|
|
if (den == 0) den = 1;
|
|
json = malloc(sizeof(Json));
|
|
json->type = NUMBER;
|
|
json->value.number = sign * num * 1.0 / den;
|
|
return json;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* TODO: more escaped characters, unicode? */
|
|
Json * parse_string(FILE *stream) {
|
|
int i = 0, cap = 10;
|
|
char * buffer = calloc(cap, sizeof(char));
|
|
char c;
|
|
int escaped = 0;
|
|
Json * json;
|
|
while ((c = getc(stream)) != EOF) {
|
|
switch (c) {
|
|
case '\\':
|
|
escaped = 1;
|
|
break;
|
|
case '"':
|
|
if (!escaped) {
|
|
json = malloc(sizeof(Json));
|
|
json->type = STRING;
|
|
json->value.string = calloc(i + 1, sizeof(char));
|
|
memcpy(json->value.string, buffer, i);
|
|
free(buffer);
|
|
return json;
|
|
}
|
|
default:
|
|
if (i == cap) {
|
|
cap *= 2;
|
|
buffer = realloc(buffer, cap * sizeof(char));
|
|
}
|
|
buffer[i++] = c;
|
|
}
|
|
}
|
|
free(buffer);
|
|
return NULL;
|
|
}
|
|
|
|
Json * parse_list(FILE *stream) {
|
|
char c;
|
|
Json * json;
|
|
ArrayList * values = list();
|
|
int expect_comma = 0;
|
|
while ((c = getc(stream)) != EOF) {
|
|
switch (c) {
|
|
case ']':
|
|
json = malloc(sizeof(Json));
|
|
json->type = LIST;
|
|
json->value.list = values;
|
|
return json;
|
|
case ' ':
|
|
case '\n':
|
|
case '\t':
|
|
break;
|
|
case ',':
|
|
if (expect_comma) {
|
|
expect_comma = 0;
|
|
break;
|
|
}
|
|
default:
|
|
ungetc(c, stream);
|
|
json = json_parse(stream);
|
|
add_list(values, (void *) json);
|
|
expect_comma = 1;
|
|
}
|
|
}
|
|
fold_list(values, NULL, free_listitem);
|
|
free_list(values);
|
|
return NULL;
|
|
}
|
|
|
|
enum map_state { MKEY, MCOLON, MVALUE, MCOMMA };
|
|
|
|
Json * parse_map(FILE *stream) {
|
|
char c;
|
|
Json * json = NULL;
|
|
char * key = NULL;
|
|
SortedMap * values = map();
|
|
enum map_state s = MKEY;
|
|
while ((c = getc(stream)) != EOF) {
|
|
switch (s) {
|
|
case MKEY:
|
|
switch (c) {
|
|
case ' ': case '\n': case '\t': break;
|
|
case '}':
|
|
json = malloc(sizeof(Json));
|
|
json->type = MAP;
|
|
json->value.map = values;
|
|
return json;
|
|
case '"':
|
|
json = parse_string(stream);
|
|
key = json->value.string;
|
|
free(json);
|
|
json = NULL;
|
|
s = MCOLON;
|
|
}
|
|
break;
|
|
case MCOLON:
|
|
switch (c) {
|
|
case ' ': case '\n': case '\t': break;
|
|
case ':':
|
|
s = MVALUE;
|
|
}
|
|
break;
|
|
case MVALUE:
|
|
switch (c) {
|
|
case ' ': case '\n': case '\t': break;
|
|
default:
|
|
ungetc(c, stream);
|
|
json = json_parse(stream);
|
|
values = put_map(values, key, (void *) json);
|
|
json = NULL;
|
|
s = MCOMMA;
|
|
}
|
|
break;
|
|
case MCOMMA:
|
|
switch (c) {
|
|
case ' ': case '\n': case '\t': break;
|
|
case ',':
|
|
s = MKEY;
|
|
break;
|
|
case '}':
|
|
json = malloc(sizeof(Json));
|
|
json->type = MAP;
|
|
json->value.map = values;
|
|
return json;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
fold_map(values, NULL, free_mappair);
|
|
free_map(values);
|
|
return NULL;
|
|
}
|
|
|
|
Json * json_parse(FILE *stream) {
|
|
char c;
|
|
while ((c = getc(stream)) != EOF) {
|
|
switch (c) {
|
|
case '{':
|
|
return parse_map(stream);
|
|
case '[':
|
|
return parse_list(stream);
|
|
case '"':
|
|
return parse_string(stream);
|
|
case 't':
|
|
return parse_true(stream);
|
|
case 'f':
|
|
return parse_false(stream);
|
|
case 'n':
|
|
return parse_null(stream);
|
|
case '-':
|
|
return parse_number(stream, -1);
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
case '8':
|
|
case '9':
|
|
ungetc(c, stream);
|
|
return parse_number(stream, 1);
|
|
case ' ':
|
|
case '\n':
|
|
case '\t':
|
|
break; /* whitespace */
|
|
default:
|
|
fprintf(stderr, "encountered char: '%c'", c);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void print_string(FILE *stream, char *string) {
|
|
fprintf(stream, "\"%s\"", string);
|
|
}
|
|
|
|
struct printacc {
|
|
FILE *stream;
|
|
int index;
|
|
};
|
|
|
|
void * json_print_map(void * voidacc, char *key, void *value) {
|
|
struct printacc *acc = (struct printacc *) voidacc;
|
|
if (acc->index++ > 0) {
|
|
fprintf(acc->stream, ", ");
|
|
}
|
|
print_string(acc->stream, key);
|
|
fprintf(acc->stream, ": ");
|
|
json_print((Json *) value, acc->stream);
|
|
return acc;
|
|
}
|
|
|
|
void json_print(Json *this, FILE *stream) {
|
|
int i;
|
|
struct printacc acc = { NULL, 0 };
|
|
switch (this->type) {
|
|
case NIL:
|
|
fprintf(stream, "null");
|
|
break;
|
|
case BOOLEAN:
|
|
fprintf(stream, this->value.boolean == 0 ? "false" : "true");
|
|
break;
|
|
case STRING:
|
|
print_string(stream, this->value.string);
|
|
break;
|
|
case NUMBER:
|
|
fprintf(stream, "%f", this->value.number);
|
|
break;
|
|
case LIST:
|
|
fprintf(stream, "[");
|
|
if (this->value.list->size > 0) {
|
|
json_print((Json *) get_list(this->value.list, 0), stream);
|
|
for(i = 1; i < this->value.list->size; i++) {
|
|
fprintf(stream, ", ");
|
|
json_print((Json *) get_list(this->value.list, i), stream);
|
|
}
|
|
}
|
|
fprintf(stream, "]");
|
|
break;
|
|
case MAP:
|
|
fprintf(stream, "{");
|
|
acc.stream = stream;
|
|
fold_map(this->value.map, (void *) &acc, json_print_map);
|
|
fprintf(stream, "}");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void * json_freelist(void *acc, int index, void *item) {
|
|
json_free((Json *) item);
|
|
return NULL;
|
|
}
|
|
|
|
void * json_freemap(void *acc, char *key, void *value) {
|
|
free(key);
|
|
json_free((Json *) value);
|
|
return NULL;
|
|
}
|
|
|
|
void json_free(Json *this) {
|
|
switch (this->type) {
|
|
case NIL:
|
|
case BOOLEAN:
|
|
case NUMBER:
|
|
break;
|
|
case STRING:
|
|
free(this->value.string);
|
|
break;
|
|
case LIST:
|
|
fold_list(this->value.list, NULL, json_freelist);
|
|
free_list(this->value.list);
|
|
break;
|
|
case MAP:
|
|
fold_map(this->value.map, NULL, json_freemap);
|
|
free_map(this->value.map);
|
|
break;
|
|
}
|
|
free(this);
|
|
}
|