aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Kagamino Lehoux <martin@lehoux.net>2025-07-21 14:26:06 +0200
committerMartin Kagamino Lehoux <martin@lehoux.net>2025-07-21 14:26:06 +0200
commit88eb8c969f6abe119994c7ab307b9be58ff076a5 (patch)
tree3a8fed52b29bdf12f5a6ac65393df8abb0c8f437
parenta378ff1b173f7a67272e574fb1a2c275ecb9661b (diff)
feat: JSON object parsing
-rw-r--r--json.c51
-rw-r--r--json.h9
-rw-r--r--string.c15
-rw-r--r--string.h2
-rw-r--r--test_json.c36
-rw-r--r--testing.h6
-rw-r--r--vec.c6
7 files changed, 103 insertions, 22 deletions
diff --git a/json.c b/json.c
index 2aa3013..0ef03a2 100644
--- a/json.c
+++ b/json.c
@@ -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;
+}
diff --git a/json.h b/json.h
index 44b064e..5f2f4c0 100644
--- a/json.h
+++ b/json.h
@@ -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
diff --git a/string.c b/string.c
index cc78719..191059e 100644
--- a/string.c
+++ b/string.c
@@ -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);
+}
diff --git a/string.h b/string.h
index c441373..d8835f4 100644
--- a/string.h
+++ b/string.h
@@ -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;
+}
diff --git a/testing.h b/testing.h
index cb40294..0b479c9 100644
--- a/testing.h
+++ b/testing.h
@@ -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; \
+ }
diff --git a/vec.c b/vec.c
index a8fd57a..af5f338 100644
--- a/vec.c
+++ b/vec.c
@@ -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;
}