From 8a9e6b1bd3be1041792311a86d8aac68b659e891 Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Fri, 31 Jan 2025 08:02:09 +0100 Subject: feat: First version, failing json parse string --- .gitignore | 6 +++ CMakeLists.txt | 12 ++++++ json.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ json.h | 13 +++++++ log.c | 11 ++++++ log.h | 7 ++++ main.c | 47 ++++++++++++++++++++++++ memory.c | 27 ++++++++++++++ memory.h | 15 ++++++++ string.c | 33 +++++++++++++++++ string.h | 17 +++++++++ vec.c | 34 +++++++++++++++++ vec.h | 20 ++++++++++ 13 files changed, 355 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 json.c create mode 100644 json.h create mode 100644 log.c create mode 100644 log.h create mode 100644 main.c create mode 100644 memory.c create mode 100644 memory.h create mode 100644 string.c create mode 100644 string.h create mode 100644 vec.c create mode 100644 vec.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e7ea508 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +main + +cmake_install.cmake +Makefile +CMakeFiles/ +CMakeCache.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c94bd65 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.30.5) +project(kagamic VERSION 0.1 LANGUAGES C) +add_executable(main + main.c + string.h string.c + memory.h memory.c + vec.h vec.c + json.h json.c + log.h log.c +) +target_link_libraries(main m) +target_compile_options(main PRIVATE -Wall) diff --git a/json.c b/json.c new file mode 100644 index 0000000..e68d617 --- /dev/null +++ b/json.c @@ -0,0 +1,113 @@ +#include "json.h" + +#include +#include +#include +#include +#include +#include + +#include "string.h" + +typedef struct { + int value; + uintptr_t pos; +} parse_integer_result; + +parse_integer_result parse_integer(Arena *a, Str src, uintptr_t pos) { + int *ints = new (a, int, 16); // TODO: reuse in parsing + int i = 0; + while (isdigit(src.data[pos + i])) { + int val = (src.data[pos + i] - '0'); + ints[i] = val; + i++; + } + int result = 0; + for (int j = 0; j < i; j++) { + result += ints[j] * pow(10, (i - j - 1)); + } + printf("json.parse_integer: parsed int=%d at pos=%lu\n", result, pos); + return (parse_integer_result){result, pos + i}; +} + +uintptr_t absorb_whitespaces(Str src, uintptr_t pos) { + int i = 0; + while (isspace(src.data[pos + i])) { + i++; + } + return pos + i; +} + +typedef struct { + Vec *items; + uintptr_t pos; +} parse_array_result; + +parse_array_result parse_array(Arena *a, Str src, uintptr_t start_pos) { + printf("json.parse_array: parsing array %s\n", src.data + start_pos); + assert(src.data[start_pos] == '['); + uintptr_t pos = start_pos + 1; + Vec *items = Vec_new(a, JSON, 0); + for (;;) { + pos = absorb_whitespaces(src, pos); + parse_integer_result result = parse_integer(a, src, pos); + assert(result.pos > pos); + items = Vec_push(a, items, &result.value); + pos = result.pos; + pos = absorb_whitespaces(src, pos); + if (src.data[pos] == ',') { + pos++; + } else { + break; + } + }; + assert(src.data[pos] == ']'); + + return (parse_array_result){items, pos + 1}; +} + +typedef struct { + Str string; + uintptr_t pos; +} parse_string_result; + +parse_string_result parse_string(Arena *a, Str src, uintptr_t pos) { + printf("json.parse_string: %s", src.data + pos); + assert(src.data[pos] == '"'); + int i = 1; + + while (src.data[pos + i] != '"' && src.data[pos + i - 1] != '\\') { + i++; + assert(i + pos <= src.len); + } + assert(src.data[pos + i]); + char *s; + memcpy(s, src.data + pos + 1, i - 1); + + printf("%s", s); + + return (parse_string_result){Str_copy(a, src.data + pos + 1, i - 1), pos + i}; +} + +JSON JSON_parse(Arena *a, Str src) { + JSON json = {0}; + int pos = 0; + // printf("json.parse: src=%s", src.data); + if (isdigit(src.data[pos])) { + parse_integer_result result = parse_integer(a, src, 0); + json.integer = &result.value; + pos = result.pos; + } + if (src.data[pos] == '[') { + parse_array_result result = parse_array(a, src, pos); + json.array = result.items; + pos = result.pos; + } + if (src.data[pos] == '"') { + parse_string_result result = parse_string(a, src, pos); + json.string = &result.string; + pos = result.pos; + } + + return json; +} diff --git a/json.h b/json.h new file mode 100644 index 0000000..588cb6b --- /dev/null +++ b/json.h @@ -0,0 +1,13 @@ +#ifndef JSON_H +#define JSON_H +#include "memory.h" +#include "string.h" +#include "vec.h" + +typedef struct { + int *integer; + Vec *array; + Str *string; +} JSON; +JSON JSON_parse(Arena *a, Str src); +#endif diff --git a/log.c b/log.c new file mode 100644 index 0000000..9a3211f --- /dev/null +++ b/log.c @@ -0,0 +1,11 @@ +#include "log.h" + +#include + +void info(Logger l, const char *name, const char *str, ...) { + if (l.level >= INFO) { + printf("[%s] ", name); + printf(str); + printf("\n"); + } +}; diff --git a/log.h b/log.h new file mode 100644 index 0000000..51ce333 --- /dev/null +++ b/log.h @@ -0,0 +1,7 @@ +typedef enum { INFO } LogLevel; + +typedef struct { + LogLevel level; +} Logger; + +void info(Logger l, const char *name, const char *str, ...); diff --git a/main.c b/main.c new file mode 100644 index 0000000..0910b3d --- /dev/null +++ b/main.c @@ -0,0 +1,47 @@ +/* +Inspirations: +- https://nullprogram.com/ +*/ +#include +#include +#include +#include + +#include "json.h" +#include "memory.h" +#include "string.h" +#include "vec.h" + +#define sizeof(x) (ptrdiff_t)sizeof(x) +#define countof(a) (sizeof(a) / sizeof(*(a))) +#define lengthof(s) (countof(s) - 1) + +typedef struct { + int age; +} User; + +int main() { + Arena perm = Arena_new(128e3); + assert(Str_equals(S("Bonjour"), S("Bonjour"))); + // Testing JSON + printf("Arena: mem=%d B\n", Arena_get_used(perm)); + JSON json_int = JSON_parse(&perm, S("123")); + assert(*json_int.integer == 123); + printf("Arena: mem=%d B\n", Arena_get_used(perm)); + JSON json_int_array = JSON_parse(&perm, S("[1,2,3]")); + assert(json_int_array.integer == NULL); + assert(json_int_array.array->len == 3); + int sum = 0; + for (int i = 0; i < json_int_array.array->len; i++) { + sum += *Vec_get(json_int_array.array, int, i); + } + assert(sum == 6); + printf("Arena: mem=%d B\n", Arena_get_used(perm)); + assert(*Vec_get(JSON_parse(&perm, S("[1, 2, 3]")).array, int, 1) == 2); + printf("Arena: mem=%d B\n", Arena_get_used(perm)); + Str a = S("\"abc\""); + JSON s = JSON_parse(&perm, S("\"abc\"")); + // printf("%s (%d)\n", s.string->data, s.string->len); + // assert(Str_equals(*JSON_parse(&perm, S("\"abc\"")).string, S("abc")) == 1); + return 0; +} diff --git a/memory.c b/memory.c new file mode 100644 index 0000000..cfb5dd4 --- /dev/null +++ b/memory.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include + +#include "memory.h" + +Arena Arena_new(ptrdiff_t 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, ptrdiff_t count, int size, int align) { + int padding = -(uintptr_t)a->beg & (align - 1); + assert(count < (a->end - a->beg - padding) / size); + void *cursor = a->beg + padding; + a->beg += padding + count * size; + return memset(cursor, 0, count * size); +} + +int Arena_get_used(Arena a) { return a.cap - (a.end - a.beg); } diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..e1c5fdd --- /dev/null +++ b/memory.h @@ -0,0 +1,15 @@ +#include + +#ifndef MEMORY_H +#define MEMORY_H +typedef struct { + ptrdiff_t cap; + char *beg; + char *end; +} Arena; + +Arena Arena_new(ptrdiff_t cap); +#define new(a, T, n) (T *)alloc(a, n, sizeof(T), _Alignof(T)) +void *alloc(Arena *a, ptrdiff_t count, int size, int align); +int Arena_get_used(Arena a); +#endif diff --git a/string.c b/string.c new file mode 100644 index 0000000..509163f --- /dev/null +++ b/string.c @@ -0,0 +1,33 @@ +#include "string.h" + +#include +#include + +#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; +} + +int Str_equals(Str a, Str b) { + return (a.len == b.len) && (!a.len || !memcmp(a.data, b.data, a.len)); +} + +uint64_t Str_hash64(Str s) { + uint64_t h = 0x100; + for (ptrdiff_t i = 0; i < s.len; i++) { + h ^= s.data[i] & 255; + h *= 1111111111111111111; + } + return h; +} + +Str Str_copy(Arena *a, char *data, uintptr_t len) { + Str str = {0}; + str.data = new (a, char, len); + if (str.len) + memcpy(str.data, data, len); + return str; +} diff --git a/string.h b/string.h new file mode 100644 index 0000000..c441373 --- /dev/null +++ b/string.h @@ -0,0 +1,17 @@ +#include +#include + +#include "memory.h" + +#ifndef STRING_H +#define STRING_H +#define S(s) (Str){s, sizeof(s) - 1} +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); +#endif diff --git a/vec.c b/vec.c new file mode 100644 index 0000000..6962d03 --- /dev/null +++ b/vec.c @@ -0,0 +1,34 @@ +#include +#include + +#include "vec.h" + +Vec *_Vec_new(Arena *a, int size, ptrdiff_t cap, int align) { + Vec *v = new (a, Vec, 1); + v->data = alloc(a, cap, size, align); + v->size = size; + v->align = align; + v->cap = cap; + v->len = 0; + return v; +} + +Vec *Vec_push(Arena *a, Vec *v, void *data) { + // printf("push: dest=%p, src=%p, size=%d\n", v->data + v->len * v->size, + // data, + // v->size); + if (v->len == v->cap) { + void *cpy = alloc(a, v->cap * 2, v->size, v->align); + memcpy(cpy, v->data, v->len * v->size); + v->cap *= 2; + } + memcpy(v->data + v->len * v->size, data, v->size); + v->len++; + return v; +} + +void *_Vec_get(Vec *v, int pos) { + // printf("get: length=%lu, cap=%lu, pos=%d\n", v->len, v->cap, pos); + // printf("get: start=%p, asked=%p\n", v->data, v->data + v->size * pos); + return v->data + v->size * pos; +} diff --git a/vec.h b/vec.h new file mode 100644 index 0000000..9fb9590 --- /dev/null +++ b/vec.h @@ -0,0 +1,20 @@ +#include +#include + +#include "memory.h" + +#ifndef VEC_H +#define VEC_H +typedef struct { + void *data; + ptrdiff_t len; + ptrdiff_t cap; + uintptr_t size; + int align; +} Vec; +#define Vec_new(a, T, cap) _Vec_new(a, sizeof(T), cap, _Alignof(T)) +Vec *_Vec_new(Arena *a, int size, ptrdiff_t cap, int align); +Vec *Vec_push(Arena *a, Vec *v, void *data); +#define Vec_get(v, T, p) (T *)_Vec_get(v, p) +void *_Vec_get(Vec *v, int pos); +#endif -- cgit v1.3