diff options
| author | Martin Kagamino Lehoux <martin@lehoux.net> | 2025-01-31 08:02:09 +0100 |
|---|---|---|
| committer | Martin Kagamino Lehoux <martin@lehoux.net> | 2025-01-31 08:02:09 +0100 |
| commit | 8a9e6b1bd3be1041792311a86d8aac68b659e891 (patch) | |
| tree | 2a62729da17b2e6669c3d71a162525ff37e42c6a | |
feat: First version, failing json parse string
| -rw-r--r-- | .gitignore | 6 | ||||
| -rw-r--r-- | CMakeLists.txt | 12 | ||||
| -rw-r--r-- | json.c | 113 | ||||
| -rw-r--r-- | json.h | 13 | ||||
| -rw-r--r-- | log.c | 11 | ||||
| -rw-r--r-- | log.h | 7 | ||||
| -rw-r--r-- | main.c | 47 | ||||
| -rw-r--r-- | memory.c | 27 | ||||
| -rw-r--r-- | memory.h | 15 | ||||
| -rw-r--r-- | string.c | 33 | ||||
| -rw-r--r-- | string.h | 17 | ||||
| -rw-r--r-- | vec.c | 34 | ||||
| -rw-r--r-- | vec.h | 20 |
13 files changed, 355 insertions, 0 deletions
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) @@ -0,0 +1,113 @@ +#include "json.h" + +#include <assert.h> +#include <ctype.h> +#include <math.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#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; +} @@ -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 @@ -0,0 +1,11 @@ +#include "log.h" + +#include <stdio.h> + +void info(Logger l, const char *name, const char *str, ...) { + if (l.level >= INFO) { + printf("[%s] ", name); + printf(str); + printf("\n"); + } +}; @@ -0,0 +1,7 @@ +typedef enum { INFO } LogLevel; + +typedef struct { + LogLevel level; +} Logger; + +void info(Logger l, const char *name, const char *str, ...); @@ -0,0 +1,47 @@ +/* +Inspirations: +- https://nullprogram.com/ +*/ +#include <assert.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> + +#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 <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#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 <stddef.h> + +#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 <stdint.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; +} + +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 <stddef.h> +#include <stdint.h> + +#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 @@ -0,0 +1,34 @@ +#include <stdio.h> +#include <string.h> + +#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; +} @@ -0,0 +1,20 @@ +#include <stddef.h> +#include <stdint.h> + +#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 |
