From 025c8125fad9e87ab7962e10d8bbffc72f4a3f18 Mon Sep 17 00:00:00 2001 From: Felix Van der Jeugt Date: Tue, 8 Nov 2022 23:31:00 +0100 Subject: [PATCH] add json parsing of lists and maps --- .gitignore | 1 + arraylist.c | 14 ++-- arraylist.h | 3 +- echojson.c | 12 ++++ json.c | 135 ++++++++++++++++++++++++++++++++++--- json.h | 4 ++ jsontest/complex.json | 9 +++ jsontest/float.json | 1 + jsontest/list.json | 1 + jsontest/null.json | 1 + jsontest/number.json | 1 + jsontest/simplestring.json | 1 + makefile | 7 ++ planarbot.c | 10 ++- sortedmap.c | 27 +++++++- sortedmap.h | 3 +- 16 files changed, 210 insertions(+), 20 deletions(-) create mode 100644 echojson.c create mode 100644 jsontest/complex.json create mode 100644 jsontest/float.json create mode 100644 jsontest/list.json create mode 100644 jsontest/null.json create mode 100644 jsontest/number.json create mode 100644 jsontest/simplestring.json diff --git a/.gitignore b/.gitignore index 1a5cdd7..8d365fe 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.o planarbot +echojson diff --git a/arraylist.c b/arraylist.c index da84d5e..551e267 100644 --- a/arraylist.c +++ b/arraylist.c @@ -26,18 +26,20 @@ void add_list(ArrayList *this, void *item) { this->items[this->size++] = item; } -void free_listitems(ArrayList *this) { +void * fold_list(ArrayList *this, void *init, void * (*f)(void *acc, int index, void *item)) { int i; for(i = 0; i < this->size; i++) { - free(this->items[i]); + init = f(init, i, this->items[i]); } + return init; +} + +void * free_listitem(void *acc, int index, void *item) { + free(item); + return NULL; } void free_list(ArrayList *this) { - int i; - for (i = 0; i < this->size; i++) { - free(this->items[i]); - } free(this->items); free(this); } diff --git a/arraylist.h b/arraylist.h index f0ff6a3..377c994 100644 --- a/arraylist.h +++ b/arraylist.h @@ -9,6 +9,7 @@ typedef struct arraylist { ArrayList * list(); void * get_list(ArrayList *this, int index); void add_list(ArrayList *this, void *item); -void free_listitems(ArrayList *this); +void * fold_list(ArrayList *this, void *init, void * (*f)(void *acc, int index, void *item)); +void * free_listitem(void *acc, int index, void *item); void free_list(ArrayList *this); #endif diff --git a/echojson.c b/echojson.c new file mode 100644 index 0000000..cf611dc --- /dev/null +++ b/echojson.c @@ -0,0 +1,12 @@ +#include + +#include "json.h" + +int main(int argc, char **argv) { + FILE *fp = fopen(argv[1], "r"); + Json * json = json_parse(fp); + json_print(json, stdout); + json_free(json); + fclose(fp); + return 0; +} diff --git a/json.c b/json.c index 530a725..fd729a7 100644 --- a/json.c +++ b/json.c @@ -67,6 +67,7 @@ Json * parse_number(FILE *stream, int sign) { 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; @@ -104,15 +105,109 @@ Json * parse_string(FILE *stream) { buffer[i++] = c; } } + free(buffer); return NULL; } -Json * parse_list(FILE *stream) { return NULL; /* TODO */ } -Json * parse_map(FILE *stream) { return NULL; /* TODO */ } +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; - Json * json = NULL; while ((c = getc(stream)) != EOF) { switch (c) { case '{': @@ -149,7 +244,11 @@ Json * json_parse(FILE *stream) { fprintf(stderr, "encountered char: '%c'", c); } } - return json; + return NULL; +} + +void print_string(FILE *stream, char *string) { + fprintf(stream, "\"%s\"", string); } struct printacc { @@ -159,7 +258,12 @@ struct printacc { 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; } @@ -174,7 +278,7 @@ void json_print(Json *this, FILE *stream) { fprintf(stream, this->value.boolean == 0 ? "false" : "true"); break; case STRING: - fprintf(stream, "\"%s\"", this->value.string); + print_string(stream, this->value.string); break; case NUMBER: fprintf(stream, "%f", this->value.number); @@ -193,12 +297,23 @@ void json_print(Json *this, FILE *stream) { case MAP: fprintf(stream, "{"); acc.stream = stream; - fold_map((void *) &acc, this->value.map, json_print_map); + 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: @@ -209,10 +324,12 @@ void json_free(Json *this) { free(this->value.string); break; case LIST: - /* TODO */ + fold_list(this->value.list, NULL, json_freelist); + free_list(this->value.list); break; case MAP: - /* TODO */ + fold_map(this->value.map, NULL, json_freemap); + free_map(this->value.map); break; } free(this); diff --git a/json.h b/json.h index 5bf3e28..d7408d7 100644 --- a/json.h +++ b/json.h @@ -1,7 +1,11 @@ #ifndef JSON_HEADER #define JSON_HEADER + +#include + #include "sortedmap.h" #include "arraylist.h" + enum jsontype { NIL, BOOLEAN, STRING, NUMBER, LIST, MAP }; typedef struct json { diff --git a/jsontest/complex.json b/jsontest/complex.json new file mode 100644 index 0000000..e85418f --- /dev/null +++ b/jsontest/complex.json @@ -0,0 +1,9 @@ +{ + "planets": [ + { "ship_count": 2, "x": -2.0, "y": 0.0, "owner": 1, "name": "your planet" }, + { "ship_count": 4, "x": 2.0, "y": 0.0, "owner": 2, "name": "enemy planet" }, + { "ship_count": 2, "x": 0.0, "y": 2.0, "owner": null, "name": "neutral planet" } + ], "expeditions": [ + { "id": 169, "ship_count": 8, "origin": "your planet", "destination": "enemy planet", "owner": 1, "turns_remaining": 2 } + ] +} diff --git a/jsontest/float.json b/jsontest/float.json new file mode 100644 index 0000000..f2fd4a8 --- /dev/null +++ b/jsontest/float.json @@ -0,0 +1 @@ +1.231 \ No newline at end of file diff --git a/jsontest/list.json b/jsontest/list.json new file mode 100644 index 0000000..6aaed8d --- /dev/null +++ b/jsontest/list.json @@ -0,0 +1 @@ +["asdf", 1, 2] \ No newline at end of file diff --git a/jsontest/null.json b/jsontest/null.json new file mode 100644 index 0000000..ec747fa --- /dev/null +++ b/jsontest/null.json @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/jsontest/number.json b/jsontest/number.json new file mode 100644 index 0000000..d8263ee --- /dev/null +++ b/jsontest/number.json @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/jsontest/simplestring.json b/jsontest/simplestring.json new file mode 100644 index 0000000..01da0da --- /dev/null +++ b/jsontest/simplestring.json @@ -0,0 +1 @@ +"asdf" \ No newline at end of file diff --git a/makefile b/makefile index c5b5bbf..59f0d92 100644 --- a/makefile +++ b/makefile @@ -3,11 +3,18 @@ CFLAGS += -g -Wall -Werror -pedantic -ansi all: planarbot inputfile valgrind ./planarbot < inputfile +test: echojson + find jsontest -type f -exec valgrind ./echojson \{\} \; + planarbot: planarbot.o arraylist.o sortedmap.o json.o planarbot.o: arraylist.h arraylist.o: arraylist.h sortedmap.o: sortedmap.h json.o: json.h arraylist.h sortedmap.h +echojson: echojson.o json.o arraylist.o sortedmap.o +echojson.o: json.h clean: rm -f *.o planarbot + +.PHONY: all clean diff --git a/planarbot.c b/planarbot.c index abd2bbc..c1ec3b4 100644 --- a/planarbot.c +++ b/planarbot.c @@ -104,14 +104,16 @@ Gamestate *read_gamestate() { } void free_gamestate(Gamestate *this) { + fold_list(this->planets, NULL, free_listitem); free_list(this->planets); + fold_list(this->expeditions, NULL, free_listitem); free_list(this->expeditions); free(this); } int main(int argc, char **argv) { Json * json = json_parse(stdin); - Gamestate *gamestate; + /*Gamestate *gamestate; SortedMap *test; gamestate = read_gamestate(); @@ -124,7 +126,11 @@ int main(int argc, char **argv) { test = put_map(test, "awer", "qwer"); printf("%s\n", (char *) get_map(test, "awer")); print_map(test); - free_map(test); + free_map(test); */ + json_print(json, stdout); + json_free(json); + + json = json_parse(stdin); json_print(json, stdout); json_free(json); return 0; diff --git a/sortedmap.c b/sortedmap.c index 2e80a6d..415e317 100644 --- a/sortedmap.c +++ b/sortedmap.c @@ -98,7 +98,7 @@ void * fold_map(SortedMap *this, void * init, void * (*f)(void * acc, char *key, case LEAF: return f(init, this->key, this->content.value); case INTERN: - return fold_map(fold_map(init, this->content.children.l, f), this->content.children.r, f); + return fold_map(this->content.children.r, fold_map(this->content.children.l, init, f), f); default: return NULL; } @@ -118,6 +118,31 @@ void free_mapvalues(SortedMap *this) { } } +void free_mapkeys(SortedMap *this) { + switch (this->type) { + case EMPTY: + break; + case LEAF: + free(this->key); + break; + case INTERN: + free_mapkeys(this->content.children.l); + free_mapkeys(this->content.children.r); + break; + } +} + +void * free_mappair(void *acc, char *key, void *value) { + free(key); + free(value); + return NULL; +} + +void * free_mapitem(void *acc, char *key, void *value) { + free(value); + return NULL; +} + void free_map(SortedMap *this) { switch (this->type) { case EMPTY: diff --git a/sortedmap.h b/sortedmap.h index ec457a0..9bab76e 100644 --- a/sortedmap.h +++ b/sortedmap.h @@ -19,6 +19,7 @@ void * get_map(SortedMap *this, char *key); SortedMap * put_map(SortedMap *this, char *key, void *value); void print_map(SortedMap *this); void * fold_map(SortedMap *this, void * init, void * (*f)(void * acc, char *key, void *value)); -void free_mapvalues(SortedMap *this); +void * free_mappair(void *acc, char *key, void *value); +void * free_mapitem(void *acc, char *key, void *value); void free_map(SortedMap *this); #endif