diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2025-07-21 14:26:06 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2025-07-21 14:26:06 +0200 |
| commit | 88eb8c969f6abe119994c7ab307b9be58ff076a5 (patch) | |
| tree | 3a8fed52b29bdf12f5a6ac65393df8abb0c8f437 | |
| parent | a378ff1b173f7a67272e574fb1a2c275ecb9661b (diff) | |
feat: JSON object parsing
| -rw-r--r-- | json.c | 51 | ||||
| -rw-r--r-- | json.h | 9 | ||||
| -rw-r--r-- | string.c | 15 | ||||
| -rw-r--r-- | string.h | 2 | ||||
| -rw-r--r-- | test_json.c | 36 | ||||
| -rw-r--r-- | testing.h | 6 | ||||
| -rw-r--r-- | vec.c | 6 |
7 files changed, 103 insertions, 22 deletions
@@ -3,8 +3,8 @@ #include <assert.h> #include <ctype.h> #include <math.h> +#include <stddef.h> #include <stdint.h> -#include <stdio.h> #include "string.h" @@ -43,7 +43,7 @@ uintptr_t absorb_whitespaces(char *src, uintptr_t pos) { } typedef struct { - Vec *items; + Vec *items; // Vec<JSON> uintptr_t pos; } parse_array_result; @@ -86,6 +86,40 @@ parse_string_result parse_string(Arena *a, char *src, uintptr_t pos) { return (parse_string_result){s, pos + i + 1}; } +typedef struct { + JSONObject *object; + uintptr_t pos; +} parse_object_result; + +parse_object_result parse_object(Arena *a, char *src, uintptr_t pos) { + assert(src[pos] == '{'); + JSONObject *object = new(a, JSONObject, 1); + object->len = 0; + object->keys = Vec_new(a, Str, 2); + object->values = Vec_new(a, JSON, 2); + pos = absorb_whitespaces(src, pos + 1); + if (src[pos] == '}') { + return (parse_object_result){object, pos + 1}; + } + for (;;) { + assert(src[pos] == '"'); + parse_string_result str_result = parse_string(a, src, pos); + Str key = str_result.string; + pos = absorb_whitespaces(src, str_result.pos); + assert(src[pos] == ':'); + pos = absorb_whitespaces(src, pos+1); + parse_any_result any_result = parse_any(a, src, pos); + object->len++; + Vec_push(a, &object->keys, &key); + Vec_push(a, &object->values, any_result.value); + pos = absorb_whitespaces(src, any_result.pos); + if (src[pos] != ',') {break;} + pos = absorb_whitespaces(src, pos +1); + } + assert(src[pos] == '}'); + return (parse_object_result){object, pos+1}; +} + parse_any_result parse_any(Arena *a, char *src, uintptr_t pos) { JSON *value = new (a, JSON, 1); if (isdigit(src[pos])) { @@ -100,6 +134,10 @@ parse_any_result parse_any(Arena *a, char *src, uintptr_t pos) { parse_string_result result = parse_string(a, src, pos); value->string = &result.string; pos = result.pos; + } else if (src[pos] == '{') { + parse_object_result result = parse_object(a, src, pos); + value->object = result.object; + pos = result.pos; } return (parse_any_result){value, pos}; @@ -116,3 +154,12 @@ JSON JSON_Int(Arena *a, int val) { *json.integer = val; return json; } + +JSON* JSONObject_get(JSONObject *obj, Str key) { + for (ptrdiff_t i = 0; i < obj->len; i++) { + if (Str_equals(*Vec_get(&obj->keys, Str, i), key)) { + return Vec_get(&obj->values, JSON, i); + } + } + return NULL; +} @@ -5,10 +5,17 @@ #include "vec.h" typedef struct { + Vec keys; // Vec<Str> + Vec values; // Vec<JSON> + ptrdiff_t len; +} JSONObject; +typedef struct { int *integer; - Vec *array; + Vec *array; // Vec<JSON> Str *string; + JSONObject *object; } JSON; +JSON* JSONObject_get(JSONObject *obj, Str key); JSON JSON_parse(Arena *a, char *src); JSON JSON_Int(Arena *a, int val); #endif @@ -1,16 +1,11 @@ #include "string.h" #include <stdint.h> +#include <stdio.h> #include <string.h> #include "memory.h" -Str *Str_new(Arena *a, uintptr_t len) { - Str *s = new (a, Str, 1); - s->data = new (a, char, len); - return s; -} - // Returns 1 if strings are equal, 0 otherwise int Str_equals(Str a, Str b) { return (a.len == b.len) && (!a.len || !memcmp(a.data, b.data, a.len)); @@ -26,9 +21,13 @@ uint64_t Str_hash64(Str s) { } Str Str_copy(Arena *a, char *data, uintptr_t len) { - Str str = {0, len}; - str.data = new (a, char, len); + Str str = {.data = NULL, .len = len}; + str.data = new (a, char, len); if (str.len) memcpy(str.data, data, len); return str; } + +void Str_print(Str s) { + printf("%.*s", (int)s.len, s.data); +} @@ -10,8 +10,8 @@ typedef struct { char *data; ptrdiff_t len; } Str; -Str *Str_new(Arena *a, uintptr_t len); int Str_equals(Str a, Str b); uint64_t Str_hash64(Str s); Str Str_copy(Arena *a, char *data, uintptr_t len); +void Str_print(Str s); #endif diff --git a/test_json.c b/test_json.c index 751b5bd..e187b62 100644 --- a/test_json.c +++ b/test_json.c @@ -1,5 +1,6 @@ #include "json.h" #include "testing.h" +#include <stdio.h> char test_json_integer() { Arena perm = Arena_new(128e3); @@ -7,7 +8,7 @@ char test_json_integer() { JSON json_int = JSON_parse(&perm, "123"); t_assert(*json_int.integer == 123); - t_assert(Arena_get_used(perm) == 28); + t_assert_equal(Arena_get_used(perm), 36); return 0; } @@ -17,7 +18,7 @@ char test_json_int_array() { JSON json_int_array = JSON_parse(&perm, "[1,2,3]"); t_assert(json_int_array.integer == NULL); - t_assert(json_int_array.array->len == 3); + t_assert_equal(json_int_array.array->len, 3); int ints[3]; int sum = 0; for (int i = 0; i < json_int_array.array->len; i++) { @@ -29,7 +30,7 @@ char test_json_int_array() { t_assert(ints[1] == 2); t_assert(ints[2] == 3); t_assert(sum == 6); - t_assert(Arena_get_used(perm) == 264); + t_assert_equal(Arena_get_used(perm), 344); return 0; } @@ -46,7 +47,7 @@ char test_json_nested_array() { Str str = *val_2.string; Str expected = S("abc"); t_assert(Str_equals(str, expected)); - t_assert(Arena_get_used(perm) == 203); + t_assert_equal(Arena_get_used(perm), 267); return 0; } @@ -57,7 +58,32 @@ char test_json_string() { JSON js = JSON_parse(&perm, "\"abc\""); t_assert(Str_equals(*js.string, S("abc"))); - t_assert(Arena_get_used(perm) == 27); + t_assert_equal(Arena_get_used(perm), 35); return 0; } + +char test_json_empty_object() { + Arena perm = Arena_new(128e3); + + JSON doc = JSON_parse(&perm, "{}"); + + t_assert_equal(doc.object->len, 0); + t_assert_equal(Arena_get_used(perm), 216); + + return 0; +} + +char test_json_object() { + Arena perm = Arena_new(128e3); + + JSON doc = JSON_parse(&perm, "{\"key\": \"value\"}"); + + t_assert_equal(Arena_get_used(perm), 261); + t_assert_equal(doc.object->len, 1); + t_assert(Str_equals(*Vec_get(&doc.object->keys, Str, 0), S("key"))); + t_assert(Str_equals(*JSONObject_get(doc.object, S("key"))->string, S("value"))) + t_assert_equal(Arena_get_used(perm), 261); + + return 0; +} @@ -5,3 +5,9 @@ fprintf(stderr, "Assertion failed: %s at %s:%d in %s()\n", #fact, __FILE__, __LINE__, __func__); \ return 1; \ } + + # define t_assert_equal(a, b) \ + if (!(a == b)) { \ + fprintf(stderr, "Assertion failed: %d (%s) != %d (%s) at %s:%d in %s()\n", a, #a, b, #b, __FILE__, __LINE__, __func__); \ + return 1; \ + } @@ -5,12 +5,8 @@ Vec _Vec_new(Arena *a, int size, ptrdiff_t cap, int align) { assert(cap > 0); - Vec v = {0}; + Vec v = {.size = size, .cap = cap, .len = 0, .align = align,.data = NULL}; v.data = alloc(a, cap, size, align); - v.size = size; - v.align = align; - v.cap = cap; - v.len = 0; return v; } |
