diff --git a/json.c b/json.c index b1123e8..3ac84bb 100644 --- a/json.c +++ b/json.c @@ -6,13 +6,6 @@ #include "s8slice.h" -struct json { - struct json_value* root; - struct json_value** ctx; - S8Slice pending_key; - bool has_pending_key; -}; - struct json_value { S8Slice key; struct json_value* next; @@ -30,12 +23,20 @@ struct json_value { double number; S8Slice string; bool boolean; - struct json_value* object_head; - struct json_value* array_head; + struct json_value* child; }; }; -static struct json_value* new_value(struct json* j) +struct json_builder { + struct json_value root; + struct json_value** ctx; + + struct json_value* ancestry_stack[1024]; + size_t ancestry_stack_len; +}; + + +static struct json_value* new_value(struct json_builder* j) { struct json_value* new = calloc(1, sizeof(struct json_value)); if (!new) { @@ -45,85 +46,189 @@ static struct json_value* new_value(struct json* j) return new; } -void json_init(struct json* j) +static inline void push(struct json_builder* j, struct json_value* v) { - j->ctx = &j->root; + if (j->ancestry_stack_len > sizeof j->ancestry_stack / sizeof *j->ancestry_stack) { + fprintf(stderr, "depth too deep\n"); + abort(); + } + j->ancestry_stack[j->ancestry_stack_len++] = v; } -void json_add_key(struct json* j, S8Slice key) +static inline void pop(struct json_builder* j) { - j->has_pending_key = true; - j->pending_key = key; + if (j->ancestry_stack_len <= 1) { + fprintf(stderr, "popping root from stack\n"); + abort(); + } + j->ctx = &j->ancestry_stack[--j->ancestry_stack_len]->next; } -void json_add_value(struct json* j, struct json_value* new) +static inline bool parent_is_object(struct json_builder* j) +{ + return j->ancestry_stack[j->ancestry_stack_len - 1]->kind == JSON_OBJECT; +} + +static void json_add_value(struct json_builder* j, S8Slice* key, struct json_value* new) { *j->ctx = new; j->ctx = &new->next; - /* - if (j->has_pending_key) { - new->key = j->pending_key; - j->has_pending_key = false; - } - */ + if (parent_is_object(j)) { + if (!key) { + fprintf(stderr, "adding value with no key to object\n"); + abort(); + } + new->key = *key; + } } -void json_add_object(struct json* j) +void json_init(struct json_builder* j) { + j->root.kind = JSON_OBJECT; + j->ctx = &j->root.child; + push(j, &j->root); +} + +#define json_add_object(j, key) for (int _ = (json_add_object_(j, key), 1); _; _ = (pop(j), 0)) +void json_add_object_(struct json_builder* j, S8Slice* key) +{ + struct json_value* new = new_value(j); new->kind = JSON_OBJECT; - json_add_value(j, new); - j->ctx = &new->object_head; + json_add_value(j, key, new); + j->ctx = &new->child; + + push(j, new); } -void json_add_number(struct json* j, double n) +#define json_add_array(j, key) for (int _ = (json_add_array_(j, key), 1); _; _ = (pop(j), 0)) +void json_add_array_(struct json_builder* j, S8Slice* key) +{ + struct json_value* new = new_value(j); + new->kind = JSON_ARRAY; + json_add_value(j, key, new); + j->ctx = &new->child; + + push(j, new); +} + +void json_add_number(struct json_builder* j, S8Slice* key, double n) { struct json_value* new = new_value(j); new->kind = JSON_NUMBER; new->number = n; - json_add_value(j, new); + json_add_value(j, key, new); } -void json_print_value(struct json_value* val, int depth) +void json_add_string(struct json_builder* j, S8Slice* key, S8Slice str) { + struct json_value* new = new_value(j); + new->kind = JSON_STRING; + new->string = str; + json_add_value(j, key, new); +} + +void json_add_bool(struct json_builder* j, S8Slice* key, bool x) +{ + struct json_value* new = new_value(j); + new->kind = JSON_BOOL; + new->boolean = x; + json_add_value(j, key, new); +} + +void json_add_null(struct json_builder* j, S8Slice* key) +{ + struct json_value* new = new_value(j); + new->kind = JSON_NULL; + json_add_value(j, key, new); +} + +void json_print_value(struct json_value* val, int depth, bool print_key) +{ +#define print_indentation() do {for (int i = 0; i < depth; i++) {printf(" ");}} while(0) if (!val) { return; } - for (int i = 0; i < depth; i++) { - printf(" "); + printf("\n"); + print_indentation(); + if (print_key) { + printf("\"%.*s\": ", val->key.len, val->key.data); } switch (val->kind) { case JSON_NUMBER: { - printf("%lf,\n", val->number); - json_print_value(val->next, depth); + printf("%lf", val->number); + } break; + + case JSON_STRING: { + printf("\"%.*s\"", val->string.len, val->string.data); + } break; + + case JSON_BOOL: { + const S8Slice bool_name[2] = { + [false] = S("false"), + [true] = S("true"), + }; + S8Slice bs = bool_name[val->boolean]; + printf("%.*s", bs.len, bs.data); + } break; + + case JSON_NULL: { + printf("null"); } break; case JSON_OBJECT: { - json_print_value(val->object_head, depth+1); + printf("{"); + json_print_value(val->child, depth+1, true); + printf("\n"); + print_indentation(); + printf("}"); + } break; + + case JSON_ARRAY: { + printf("["); + json_print_value(val->child, depth+1, false); + printf("\n"); + print_indentation(); + printf("]"); } break; default: { - fprintf(stderr, "not implemented"); + fprintf(stderr, "not implemented\n"); abort(); } break; } + + printf(","); + + json_print_value(val->next, depth, print_key); +#undef print_indentation } -void json_print(struct json* j) +void json_print(struct json_builder* j) { - json_print_value(j->root, 0); + json_print_value(&j->root, 0, false); } int main() { - struct json j = {0}; + struct json_builder j = {0}; json_init(&j); - json_add_number(&j, 53); + json_add_number(&j, &S("foo"), 53); - json_add_object(&j); - - json_add_number(&j, 12); + json_add_object(&j, &S("bar")) { + json_add_number(&j, &S("a"), 1); + json_add_null(&j, &S("b")); + json_add_bool(&j, &S("c"), true); + json_add_array(&j, &S("d")) { + json_add_number(&j, &S("e"), 4); + json_add_number(&j, &S("f"), 5); + json_add_number(&j, &S("g"), 6); + } + json_add_number(&j, &S("h"), 7); + json_add_number(&j, &S("i"), 8); + json_add_number(&j, &S("j"), 9); + }; json_print(&j); diff --git a/s8slice.h b/s8slice.h index 2f6847f..41d2477 100644 --- a/s8slice.h +++ b/s8slice.h @@ -8,7 +8,7 @@ typedef struct s8_slice { int64_t len; } S8Slice; -#define S8(str) (S8Slice) { \ +#define S(str) (S8Slice) { \ .data = (uint8_t*)("" str), \ .len = sizeof(str)-1, \ }