#include "json.h" #include #define UNREACHABLE() assert(false) Json json_init(FILE* stream, Whitespace whitespace) { return (Json){ .stream = stream, .whitespace = whitespace, .indent_level = 0, .next_punctuation = BEGINNING, }; } void json_free(Json* json) { } void begin_value(Json* json); void end_value(Json* json); void push_indentation(Json* json); void pop_indentation(Json* json); void indent(Json* json); void json_begin_array(Json* json) { begin_value(json); putc('[', json->stream); push_indentation(json); json->next_punctuation = NONE; } void json_end_array(Json* json) { pop_indentation(json); switch (json->next_punctuation) { case NONE: break; case COMMA: indent(json); break; case BEGINNING: case COLON: UNREACHABLE(); } putc(']', json->stream); end_value(json); } void json_begin_object(Json* json) { begin_value(json); putc('{', json->stream); push_indentation(json); json->next_punctuation = NONE; } void json_end_object(Json* json) { pop_indentation(json); switch (json->next_punctuation) { case NONE: break; case COMMA: indent(json); break; case BEGINNING: case COLON: UNREACHABLE(); } putc('}', json->stream); end_value(json); } void json_add_object_field(Json* json, const char* key) { begin_value(json); fprintf(json->stream, "\"%s\"", key); json->next_punctuation = COLON; } void json_add_string(Json* json, const char* value) { begin_value(json); fprintf(json->stream, "\"%s\"", value); end_value(json); } void json_add_long(Json* json, long value) { begin_value(json); fprintf(json->stream, "%ld", value); end_value(json); } void json_add_double(Json* json, double value) { begin_value(json); fprintf(json->stream, "%f", value); end_value(json); } void json_add_bool(Json* json, bool value) { begin_value(json); const char* s; if (value) { s = "true"; } else { s = "false"; } fputs(s, json->stream); end_value(json); } void json_add_null(Json* json) { begin_value(json); fputs("null", json->stream); end_value(json); } void begin_value(Json* json) { switch (json->next_punctuation) { case BEGINNING: break; case NONE: indent(json); break; case COMMA: fputc(',', json->stream); indent(json); break; case COLON: putc(':', json->stream); if (json->whitespace != MINIFIED) { fputc(' ', json->stream); } break; } } void end_value(Json* json) { json->next_punctuation = COMMA; } void push_indentation(Json* json) { json->indent_level += 1; } void pop_indentation(Json* json) { json->indent_level -= 1; } void indent(Json* json) { char indent_char = ' '; int n; switch (json->whitespace) { case MINIFIED: return; case INDENT_1: n = 1 * json->indent_level; break; case INDENT_2: n = 2 * json->indent_level; break; case INDENT_3: n = 3 * json->indent_level; break; case INDENT_4: n = 4 * json->indent_level; break; case INDENT_8: n = 8 * json->indent_level; break; case INDENT_TAB: indent_char = '\t'; n = json->indent_level; break; } putc('\n', json->stream); for (int i = 0; i < n; i++) { putc(indent_char, json->stream); } }