diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2025-07-23 19:39:13 +0200 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2025-07-23 19:39:26 +0200 |
| commit | 421300c27123ca647772dc6d9bd0cabf7f5f9c99 (patch) | |
| tree | d916b62056b2440aef374b21373d16272bb709ee /src | |
| parent | 53e9e371a16ae2d167f1fc2577dff2c2426eed68 (diff) | |
refactor: src & pragma once
Diffstat (limited to 'src')
| -rw-r--r-- | src/json.c | 263 | ||||
| -rw-r--r-- | src/log.c | 12 | ||||
| -rw-r--r-- | src/memory.c | 30 | ||||
| -rw-r--r-- | src/str.c | 32 | ||||
| -rw-r--r-- | src/vec.c | 29 |
5 files changed, 366 insertions, 0 deletions
diff --git a/src/json.c b/src/json.c new file mode 100644 index 0000000..6f00da8 --- /dev/null +++ b/src/json.c @@ -0,0 +1,263 @@ +#include <assert.h> +#include <ctype.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "../include/json.h" +#include "../include/str.h" +#include "../include/types.h" + +JSONObject *JSONObject_new(Arena *a) { + JSONObject *object = new(a, JSONObject, 1); + object->len = 0; + object->keys = Vec_new(a, Str, 2); + object->values = Vec_new(a, JSON, 2); + return object; +} + +JSON JSON_integer(i32 value) { + JSON json = {0}; + json.type = JSON_INTEGER; + json.as.integer = value; + return json; +} + +JSON JSON_float(f32 value) { + JSON json = {0}; + json.type = JSON_FLOAT; + json.as.floating = value; + return json; +} + +JSON JSON_array(Vec items) { + JSON json = {0}; + json.type = JSON_ARRAY; + json.as.array = items; + return json; +} + +JSON JSON_string(Str value) { + JSON json = {0}; + json.type = JSON_STRING; + json.as.string = value; + return json; +} + +JSON JSON_object(size len, Vec keys, Vec values) { + assert(len == keys.len && len == values.len); + JSON json = {0}; + json.type = JSON_OBJECT; + json.as.object.len = len; + json.as.object.keys = keys; + json.as.object.values = values; + return json; +} + +JSON JSON_boolean(bool value) { + JSON json = {0}; + json.type = JSON_BOOL; + json.as.boolean = value; + return json; +} + +JSON JSON_null() { + JSON json = {0}; + json.type = JSON_NULL; + return json; +} + +i32 JSON_fprint(FILE *w, JSON json) { + switch (json.type) { + case JSON_STRING: + fprintf(w, "\""); + Str_fprint(w, json.as.string); + return fprintf(w, "\""); + case JSON_INTEGER: + return fprintf(w, "%d", json.as.integer); + case JSON_FLOAT: + return fprintf(w, "%f", json.as.floating); + case JSON_NULL: + return fprintf(w, "null"); + case JSON_BOOL: + return fprintf(w, "%s", json.as.boolean ? "true" : "false"); + case JSON_ARRAY: + fprintf(w, "["); + for (size i = 0; i < json.as.array.len; i++) { + JSON value = *Vec_get(json.as.array, JSON, i); + JSON_fprint(w, value); + if (i < json.as.array.len - 1) fprintf(w, ", "); + } + fprintf(w, "]"); + return 0; + case JSON_OBJECT: + fprintf(w, "{"); + for (size i = 0; i < json.as.object.len; i++) { + Str key = *Vec_get(json.as.object.keys, Str, i); + fprintf(w, "\""); + Str_fprint(w, key); + fprintf(w, "\": "); + JSON value = *Vec_get(json.as.object.values, JSON, i); + JSON_fprint(w, value); + if (i < json.as.object.len - 1) fprintf(w, ", "); + } + fprintf(w, "}"); + return 0; + } + assert(false); +} + +typedef struct { + JSON value; + uptr pos; +} parse_result; + +parse_result parse_any(Arena *a, byte *src, uptr pos); + +parse_result parse_number(byte *src, uptr start_pos) { + parse_result result = {0}; + result.pos = start_pos; + i32 integer = 0; + while (isdigit(src[result.pos])) { + integer = integer * 10 + (src[result.pos] - '0'); + result.pos++; + } + if (src[result.pos] != '.') { + result.value = JSON_integer(integer); + return result; + } + result.pos++; + f32 fractional_part = 0.0; + f32 decimal_place = 0.1; + while (isdigit(src[result.pos])) { + fractional_part += (src[result.pos] - '0') * decimal_place; + decimal_place *= 0.1; + result.pos++; + } + result.value = JSON_float((f32)integer + fractional_part); + return result; +} + +uptr absorb_whitespaces(byte *src, uptr start_pos) { + uptr pos = start_pos; + while (isspace(src[pos])) { + pos++; + } + return pos; +} + +parse_result parse_array(Arena *a, byte *src, uptr start_pos) { + assert(src[start_pos] == '['); + parse_result result = {0}; + result.pos = start_pos + 1; + if (src[result.pos] == ']') { + result.pos++; + return result; + } + Vec items = Vec_new(a, JSON, 2); + for (;;) { + result.pos = absorb_whitespaces(src, result.pos); + parse_result item_result = parse_any(a, src, result.pos); + Vec_push(a, &items, &item_result.value); + result.pos = absorb_whitespaces(src, item_result.pos); + if (src[result.pos] == ',') { + result.pos++; + } else { + break; + } + }; + assert(src[result.pos] == ']'); + result.pos++; + result.value = JSON_array(items); + return result; +} + +parse_result parse_string(Arena *a, byte *src, uptr start_pos) { + assert(src[start_pos] == '"'); + parse_result result = {0}; + result.pos = start_pos + 1; + + while (src[result.pos] != '"' && src[result.pos - 1] != '\\') { + result.pos++; + } + assert(src[result.pos] == '"'); + result.value = JSON_string( + Str_copy(a, &src[start_pos + 1], result.pos - start_pos - 1)); + result.pos++; + + return result; +} + +parse_result parse_object(Arena *a, byte *src, uptr start_pos) { + assert(src[start_pos] == '{'); + parse_result result = {0}; + result.pos = absorb_whitespaces(src, start_pos + 1); + size len = 0; + Vec keys = Vec_new(a, Str, 2); + Vec values = Vec_new(a, JSON, 2); + if (src[result.pos] == '}') { + result.pos++; + result.value = JSON_object(len, keys, values); + return result; + } + for (;;) { + assert(src[result.pos] == '"'); + parse_result key_result = parse_string(a, src, result.pos); + result.pos = absorb_whitespaces(src, key_result.pos); + assert(src[result.pos] == ':'); + result.pos = absorb_whitespaces(src, result.pos + 1); + parse_result value_result = parse_any(a, src, result.pos); + len++; + assert(key_result.value.type == JSON_STRING); + Vec_push(a, &keys, &key_result.value.as.string); + Vec_push(a, &values, &value_result.value); + result.pos = absorb_whitespaces(src, value_result.pos); + if (src[result.pos] != ',') { + break; + } + result.pos = absorb_whitespaces(src, result.pos + 1); + } + assert(src[result.pos] == '}'); + result.pos++; + result.value = JSON_object(len, keys, values); + return result; +} + +parse_result parse_any(Arena *a, byte *src, uptr pos) { + pos = absorb_whitespaces(src, pos); + if (isdigit(src[pos])) return parse_number(src, pos); + if (src[pos] == '[') return parse_array(a, src, pos); + if (src[pos] == '"') return parse_string(a, src, pos); + if (src[pos] == '{') return parse_object(a, src, pos); + if (memcmp(src + pos, "true", 4) == 0) { + parse_result result = {0}; + result.value = JSON_boolean(true); + result.pos = pos + 4; + return result; + } + if (memcmp(src + pos, "false", 5) == 0) { + parse_result result = {0}; + result.value = JSON_boolean(false); + result.pos = pos + 5; + return result; + } + if (memcmp(src + pos, "null", 4) == 0) { + parse_result result = {0}; + result.value = JSON_null(); + result.pos = pos + 4; + return result; + } + assert(false); +} + +JSON JSON_parse(Arena *a, byte *src) { return parse_any(a, src, 0).value; } + +JSON *JSONObject_get(JSONObject obj, Str key) { + for (size i = 0; i < obj.len; i++) { + if (Str_equals(*Vec_get(obj.keys, Str, i), key)) { + return Vec_get(obj.values, JSON, i); + } + } + assert(false); +} diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..c1d1717 --- /dev/null +++ b/src/log.c @@ -0,0 +1,12 @@ +#include <stdio.h> + +#include "../include/log.h" +#include "../include/types.h" + +void info(Logger l, const byte *name, const byte *str, ...) { + if (l.level >= INFO) { + printf("[%s] ", name); + printf("%s", str); + printf("\n"); + } +} diff --git a/src/memory.c b/src/memory.c new file mode 100644 index 0000000..63acb39 --- /dev/null +++ b/src/memory.c @@ -0,0 +1,30 @@ +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include "../include/memory.h" +#include "../include/types.h" + +Arena Arena_new(size cap) { + Arena a = {0}; + a.cap = cap; + a.beg = malloc(cap); + a.end = a.beg ? a.beg + cap : 0; + return a; +} + +// Magic to allow optimisation __attribute((malloc, alloc_size(2, 4), +// alloc_align(3))) +void *alloc(Arena *a, size count, size tsize, size align) { + size padding = -(uptr)a->beg & (align - 1); + byte *beg = a->beg + padding; + size alloc_size = count * tsize; + assert(beg + alloc_size < a->end); + a->beg = beg + alloc_size; + return memset(beg, 0, count * tsize); +} + +void Arena_reset(Arena *a) { a->beg = a->end - a->cap; } + +size Arena_get_used(Arena a) { return a.cap - (a.end - a.beg); } diff --git a/src/str.c b/src/str.c new file mode 100644 index 0000000..eccacd8 --- /dev/null +++ b/src/str.c @@ -0,0 +1,32 @@ +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "../include/memory.h" +#include "../include/str.h" + +// Returns 1 if strings are equal, 0 otherwise +i32 Str_equals(Str a, Str b) { + return (a.len == b.len) && (!a.len || !memcmp(a.data, b.data, a.len)); +} + +u64 Str_hash64(Str s) { + u64 h = 0x100; + for (size i = 0; i < s.len; i++) { + h ^= s.data[i] & 255; + h *= 1111111111111111111; + } + return h; +} + +Str Str_copy(Arena *a, byte *data, size len) { + Str str = {0}; + str.len = len; + str.data = new(a, byte, len); + if (str.len) memcpy(str.data, data, len); + return str; +} + +i32 Str_fprint(FILE *w, Str s) { + return fprintf(w, "%.*s", (i32)s.len, s.data); +} diff --git a/src/vec.c b/src/vec.c new file mode 100644 index 0000000..a5c2fb4 --- /dev/null +++ b/src/vec.c @@ -0,0 +1,29 @@ +#include <string.h> + +#include "../include/assert.h" +#include "../include/types.h" +#include "../include/vec.h" + +Vec _Vec_new(Arena *a, size tsize, size cap, size align) { + assert(cap > 0); + Vec v = {0}; + v.tsize = tsize; + v.cap = cap; + v.align = align; + v.len = 0; + v.data = alloc(a, cap, tsize, align); + return v; +} + +void Vec_push(Arena *a, Vec *v, void *data) { + if (v->len == v->cap) { + void *cpy = alloc(a, v->cap * 2, v->tsize, v->align); + memcpy(cpy, v->data, v->len * v->tsize); + v->cap *= 2; + v->data = cpy; + } + memcpy(v->data + v->len * v->tsize, data, v->tsize); + v->len++; +} + +void *_Vec_get(Vec v, size pos) { return v.data + v.tsize * pos; } |
