diff --git a/examples/03_parsing_database.c b/examples/03_parsing_database.c index ccc9c01..94afbd3 100644 --- a/examples/03_parsing_database.c +++ b/examples/03_parsing_database.c @@ -24,13 +24,13 @@ bool parse_person(Jimp *jimp, Person *p) if (!jimp_object_begin(jimp)) return false; while (jimp_object_member(jimp)) { if (strcmp(jimp->member, "name") == 0) { - if (!jimp_string(jimp, &p->name)) return false; + if (!jimp_string(jimp, &p->name, NULL)) return false; } else if (strcmp(jimp->member, "age") == 0) { - if (!jimp_number(jimp, &p->age)) return false; + if (!jimp_number(jimp, &p->age, 0)) return false; } else if (strcmp(jimp->member, "location") == 0) { - if (!jimp_string(jimp, &p->location)) return false; + if (!jimp_string(jimp, &p->location, NULL)) return false; } else if (strcmp(jimp->member, "body_count") == 0) { - if (!jimp_number(jimp, &p->body_count)) return false; + if (!jimp_number(jimp, &p->body_count, 0)) return false; } else { jimp_unknown_member(jimp); return false; @@ -93,7 +93,7 @@ int main() if (!jimp_array_begin(&jimp)) return 1; while (jimp_array_item(&jimp)) { double x = 0; - if (!jimp_number(&jimp, &x)) return 1; + if (!jimp_number(&jimp, &x, 0)) return 1; da_append(&xs, x); } if (!jimp_array_end(&jimp)) return 1; diff --git a/examples/04_parsing_defaults.c b/examples/04_parsing_defaults.c new file mode 100644 index 0000000..3a44783 --- /dev/null +++ b/examples/04_parsing_defaults.c @@ -0,0 +1,132 @@ +#include +#include +#define NOB_IMPLEMENTATION +#define NOB_STRIP_PREFIX +#include "../thirdparty/nob.h" +#define JIMP_IMPLEMENTATION +#include "../jimp.h" + +typedef struct { + const char *name; + double age; + const char *location; + double body_count; +} Person; + +typedef struct { + Person *items; + size_t count; + size_t capacity; +} People; + +bool parse_person(Jimp *jimp, Person *p) +{ + if (!jimp_object_begin(jimp)) return false; + while (jimp_object_member(jimp)) { + if (strcmp(jimp->member, "name") == 0) { + if (!jimp_string(jimp, &p->name, strdup("Default Name"))) return false; + } else if (strcmp(jimp->member, "age") == 0) { + if (!jimp_number(jimp, &p->age, 18)) return false; + } else if (strcmp(jimp->member, "location") == 0) { + if (!jimp_string(jimp, &p->location, strdup("UNKNOWN"))) return false; + } else if (strcmp(jimp->member, "body_count") == 0) { + if (!jimp_number(jimp, &p->body_count, 69)) return false; + } else { + jimp_unknown_member(jimp); + return false; + } + } + return jimp_object_end(jimp); +} + +bool parse_people(Jimp *jimp, People *ps) +{ + if (!jimp_array_begin(jimp)) return false; + while (jimp_array_item(jimp)) { + Person p = {0}; // jimp_object_* uses this value as its default + if (!parse_person(jimp, &p)) return false; + da_append(ps, p); + } + if (!jimp_array_end(jimp)) return false; + + return true; +} + +void print_person(const Person *p) +{ + printf("name = %s\n", p->name); + printf("age = %lf\n", p->age); + printf("location = %s\n", p->location); + printf("body_count = %lf\n", p->body_count); +} + +typedef struct { + long *items; + size_t count; + size_t capacity; +} Numbers; + +int main() +{ + const char *file_path = "database_default.json"; + String_Builder sb = {0}; + if (!read_entire_file(file_path, &sb)) return 1; + Jimp jimp = { + .file_path = file_path, + .start = sb.items, + .end = sb.items + sb.count, + .point = sb.items, + }; + + People ps = {0}; + Numbers xs = {0}; + Numbers xs_null = {0}; + if (!jimp_object_begin(&jimp)) return 1; + while (jimp_object_member(&jimp)) { + if (strcmp(jimp.member, "profile") == 0) { + if (!parse_people(&jimp, &ps)) return 1; + } else if (strcmp(jimp.member, "number") == 0) { + if (!jimp_array_begin(&jimp)) return 1; + while (jimp_array_item(&jimp)) { + double x; + if (!jimp_number(&jimp, &x, 0)) return 1; + da_append(&xs, x); + } + if (!jimp_array_end(&jimp)) return 1; + } else if (strcmp(jimp.member, "array_null") == 0) { + // The default for array is an empty array + if (!jimp_array_begin(&jimp)) return 1; + while (jimp_array_item(&jimp)) { + double x; + if (!jimp_number(&jimp, &x, 0)) return 1; + da_append(&xs_null, x); + } + if (!jimp_array_end(&jimp)) return 1; + } else { + jimp_unknown_member(&jimp); + return 1; + } + } + if (!jimp_object_end(&jimp)) return 1; + + da_foreach(Person, p, &ps) { + print_person(p); + printf("\n"); + } + printf("------------------------------\n"); + da_foreach(long, x, &xs) { + printf("%ld ", *x); + } + printf("\n"); + printf("------------------------------\n"); + if(xs_null.count == 0) { + printf("[]\n"); + } else { + da_foreach(long, x, &xs_null) { + printf("%ld ", *x); + } + } + printf("\n"); + + return 0; +} diff --git a/examples/Makefile b/examples/Makefile index c2874fc..90856c4 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,7 +1,7 @@ CFLAGS=-Wall -Wextra -Wswitch-enum -ggdb .PHONY: all -all: 01_from_readme 02_binary_tree 03_parsing_database +all: 01_from_readme 02_binary_tree 03_parsing_database 04_parsing_defaults 01_from_readme: 01_from_readme.c ../jim.h $(CC) $(CFLAGS) -o 01_from_readme 01_from_readme.c @@ -11,3 +11,6 @@ all: 01_from_readme 02_binary_tree 03_parsing_database 03_parsing_database: 03_parsing_database.c ../jimp.h ../thirdparty/nob.h $(CC) $(CFLAGS) -o 03_parsing_database 03_parsing_database.c + +04_parsing_defaults: 04_parsing_defaults.c ../jimp.h ../thirdparty/nob.h + $(CC) $(CFLAGS) -o 04_parsing_defaults 04_parsing_defaults.c diff --git a/examples/database_default.json b/examples/database_default.json new file mode 100644 index 0000000..1d4918e --- /dev/null +++ b/examples/database_default.json @@ -0,0 +1,26 @@ +{ + "number": [69, 420, null, 80085], + "array_null": null, + "profile": [ + { + "location": "Wonderland", + "location": "Wonderland", + "body_count": 150, + "name": "Alice Smith", + "age": 34 + }, + { + "name": null, + "age": 45, + "location": "Atlantis", + "body_count": null + }, + { + "name": "Ian Malcolm", + "age": 60, + "location": null, + "body_count": 400 + }, + null + ] +} diff --git a/jimp.h b/jimp.h index a46e7c2..54a30af 100644 --- a/jimp.h +++ b/jimp.h @@ -50,12 +50,12 @@ typedef struct { double number; const char *member; + bool value_is_null; } Jimp; -// TODO: how do null-s fit into this entire system? -bool jimp_bool(Jimp *jimp, bool *boolean); -bool jimp_number(Jimp *jimp, double *number); -bool jimp_string(Jimp *jimp, const char **string); +bool jimp_bool(Jimp *jimp, bool *boolean, bool defaultt); +bool jimp_number(Jimp *jimp, double *number, double defaultt); +bool jimp_string(Jimp *jimp, const char **string, const char*defaultt); bool jimp_object_begin(Jimp *jimp); bool jimp_object_member(Jimp *jimp); bool jimp_object_end(Jimp *jimp); @@ -200,37 +200,50 @@ void jimp_diagf(Jimp *jimp, const char *fmt, ...) static const char *jimp__token_kind(Jimp_Token token) { - switch (token) { - case JIMP_EOF: return "end of input"; - case JIMP_INVALID: return "invalid"; - case JIMP_OCURLY: return "{"; - case JIMP_CCURLY: return "}"; - case JIMP_OBRACKET: return "["; - case JIMP_CBRACKET: return "]"; - case JIMP_COMMA: return ","; - case JIMP_COLON: return ":"; - case JIMP_TRUE: return "true"; - case JIMP_FALSE: return "false"; - case JIMP_NULL: return "null"; - case JIMP_STRING: return "string"; - case JIMP_NUMBER: return "number"; - } - assert(0 && "unreachable"); - return NULL; + switch (token) { + case JIMP_EOF: return "end of input"; + case JIMP_INVALID: return "invalid"; + case JIMP_OCURLY: return "{"; + case JIMP_CCURLY: return "}"; + case JIMP_OBRACKET: return "["; + case JIMP_CBRACKET: return "]"; + case JIMP_COMMA: return ","; + case JIMP_COLON: return ":"; + case JIMP_TRUE: return "true"; + case JIMP_FALSE: return "false"; + case JIMP_NULL: return "null"; + case JIMP_STRING: return "string"; + case JIMP_NUMBER: return "number"; + } + assert(0 && "unreachable"); + return NULL; } bool jimp_array_begin(Jimp *jimp) { - return jimp__get_and_expect_token(jimp, JIMP_OBRACKET); + if (!jimp__get_token(jimp)) return false; + if(jimp->token == JIMP_NULL) { + jimp->value_is_null = true; + return true; + } else if(jimp->token != JIMP_OBRACKET) { + jimp_diagf(jimp, "ERROR: expected `%s` or `%s`, but got `%s`\n", jimp__token_kind(JIMP_OBRACKET), jimp__token_kind(JIMP_NULL), jimp__token_kind(jimp->token)); + return false; + } + return true; } bool jimp_array_end(Jimp *jimp) { + if(jimp->value_is_null) { + jimp->value_is_null = false; + return true; + } return jimp__get_and_expect_token(jimp, JIMP_CBRACKET); } bool jimp_array_item(Jimp *jimp) { + if(jimp->value_is_null) return false; const char *point = jimp->point; if (!jimp__get_token(jimp)) return false; if (jimp->token == JIMP_COMMA) return true; @@ -249,11 +262,19 @@ void jimp_unknown_member(Jimp *jimp) bool jimp_object_begin(Jimp *jimp) { - return jimp__get_and_expect_token(jimp, JIMP_OCURLY); + if (!jimp__get_token(jimp)) return false; + if(jimp->token == JIMP_NULL) { + jimp->value_is_null = true; + } else if(jimp->token != JIMP_OCURLY) { + jimp_diagf(jimp, "ERROR: expected `%s` or `%s`, but got `%s`\n", jimp__token_kind(JIMP_OCURLY), jimp__token_kind(JIMP_NULL), jimp__token_kind(jimp->token)); + return false; + } + return true; } bool jimp_object_member(Jimp *jimp) { + if(jimp->value_is_null) return false; const char *point = jimp->point; if (!jimp__get_token(jimp)) return false; if (jimp->token == JIMP_COMMA) { @@ -274,34 +295,54 @@ bool jimp_object_member(Jimp *jimp) bool jimp_object_end(Jimp *jimp) { + if(jimp->value_is_null) { + jimp->value_is_null = false; + return true; + } return jimp__get_and_expect_token(jimp, JIMP_CCURLY); } -bool jimp_string(Jimp *jimp, const char **string) +bool jimp_string(Jimp *jimp, const char **string, const char*defaultt) { - if (!jimp__get_and_expect_token(jimp, JIMP_STRING)) return false; - *string = strdup(jimp->string); + if (!jimp__get_token(jimp)) return false; + if(jimp->token == JIMP_NULL) { + *string = defaultt; + } else if(jimp->token == JIMP_STRING) { + *string = strdup(jimp->string); + } else { + jimp_diagf(jimp, "ERROR: expected `%s` or `%s`, but got `%s`\n", jimp__token_kind(JIMP_STRING), jimp__token_kind(JIMP_NULL), jimp__token_kind(jimp->token)); + return false; + } return true; } -bool jimp_bool(Jimp *jimp, bool *boolean) +bool jimp_bool(Jimp *jimp, bool *boolean, bool defaultt) { - jimp__get_token(jimp); - if (jimp->token == JIMP_TRUE) { + if (!jimp__get_token(jimp)) return false; + if(jimp->token == JIMP_NULL) { + *boolean = defaultt; + } else if (jimp->token == JIMP_TRUE) { *boolean = true; } else if (jimp->token == JIMP_FALSE) { *boolean = false; } else { - jimp_diagf(jimp, "ERROR: expected boolean, but got `%s`\n", jimp__token_kind(jimp->token)); + jimp_diagf(jimp, "ERROR: expected boolean or `%s`, but got `%s`\n", jimp__token_kind(JIMP_NULL), jimp__token_kind(jimp->token)); return false; } return true; } -bool jimp_number(Jimp *jimp, double *number) +bool jimp_number(Jimp *jimp, double *number, double defaultt) { - if (!jimp__get_and_expect_token(jimp, JIMP_NUMBER)) return false; - *number = jimp->number; + if (!jimp__get_token(jimp)) return false; + if(jimp->token == JIMP_NULL) { + *number = defaultt; + } else if(jimp->token == JIMP_NUMBER) { + *number = jimp->number; + } else { + jimp_diagf(jimp, "ERROR: expected `%s` or `%s`, but got `%s`\n", jimp__token_kind(JIMP_NUMBER), jimp__token_kind(JIMP_NULL), jimp__token_kind(jimp->token)); + return false; + } return true; }