#include #include #include #include #include "s8slice.h" struct json_value { S8Slice key; struct json_value* next; enum { JSON_OBJECT, JSON_ARRAY, JSON_NUMBER, JSON_STRING, JSON_BOOL, JSON_NULL, } kind; union { double number; S8Slice string; bool boolean; struct json_value* child; }; }; 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) { perror("calloc"); abort(); } return new; } static inline void push(struct json_builder* j, struct json_value* v) { 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; } static inline void pop(struct json_builder* j) { if (j->ancestry_stack_len <= 1) { fprintf(stderr, "popping root from stack\n"); abort(); } j->ctx = &j->ancestry_stack[--j->ancestry_stack_len]->next; } 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 (parent_is_object(j)) { if (!key) { fprintf(stderr, "adding value with no key to object\n"); abort(); } new->key = *key; } } 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, key, new); j->ctx = &new->child; push(j, new); } #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, key, new); } 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; } printf("\n"); print_indentation(); if (print_key) { printf("\"%.*s\": ", val->key.len, val->key.data); } switch (val->kind) { case JSON_NUMBER: { 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: { 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\n"); abort(); } break; } printf(","); json_print_value(val->next, depth, print_key); #undef print_indentation } void json_print(struct json_builder* j) { json_print_value(&j->root, 0, false); } int main() { struct json_builder j = {0}; json_init(&j); json_add_number(&j, &S("foo"), 53); 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); return EXIT_SUCCESS; }