237 lines
5.5 KiB
C
237 lines
5.5 KiB
C
|
|
#include <stdlib.h>
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
|
|
#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;
|
|
}
|