Skip to content

Commit

Permalink
WIP json parser
Browse files Browse the repository at this point in the history
  • Loading branch information
abbec committed Aug 22, 2024
1 parent 21f87f1 commit e4ee02d
Show file tree
Hide file tree
Showing 9 changed files with 409 additions and 6 deletions.
6 changes: 4 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,11 @@ ASAN ?= false
.endif

.if $(LSP_ENABLE) == true
HEADERS += src/dged/lsp.h src/main/lsp.h
SOURCES += src/dged/lsp.c
HEADERS += src/dged/lsp.h src/main/lsp.h src/dged/json.h
SOURCES += src/dged/lsp.c src/dged/json.c
MAIN_SOURCES += src/main/lsp.c
TEST_SOURCES += test/json.c
CFLAGS += -DLSP_ENABLED
.endif

UNAME_S != uname -s | tr '[:upper:]' '[:lower:]'
Expand Down
9 changes: 9 additions & 0 deletions src/dged/hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,12 @@ uint32_t hash_name(const char *s) {

return hash;
}

uint32_t hash_name_s8(struct s8 s) {
unsigned long hash = 5381;

for (uint64_t i = 0; i < s.l; ++i)
hash = ((hash << 5) + hash) + s.s[i]; /* hash * 33 + c */

return hash;
}
3 changes: 3 additions & 0 deletions src/dged/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

#include <stdint.h>

#include "s8.h"

uint32_t hash_name(const char *s);
uint32_t hash_name_s8(struct s8 s);

#endif
8 changes: 4 additions & 4 deletions src/dged/hashmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,16 @@
} \
var = res != NULL ? &(res->value) : NULL;

#define HASHMAP_CONTAINS_KEY(map, key) \
uint32_t needle = (map)->hash_fn(key); \
#define HASHMAP_CONTAINS_KEY(map, type, k, var) \
uint32_t needle = (map)->hash_fn(k); \
bool exists = false; \
VEC_FOR_EACH((map)->entries, struct pair *pair) { \
VEC_FOR_EACH(&(map)->entries, type *pair) { \
if (needle == pair->key) { \
exists = true; \
break; \
} \
} \
exists
var = exists;

#define HASHMAP_FOR_EACH(map, var) VEC_FOR_EACH_INDEXED(&(map)->entries, var, i)

Expand Down
234 changes: 234 additions & 0 deletions src/dged/json.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
#include "json.h"

#include "hash.h"
#include "hashmap.h"
#include "utf8.h"
#include "vec.h"

#include <stddef.h>
#include <stdio.h>

HASHMAP_ENTRY_TYPE(json_object_member, struct json_value);

struct json_object {
HASHMAP(struct json_object_member) members;
};

struct json_array {
VEC(struct json_value) values;
};

static void setarray(struct json_value *val) {
val->type = Json_Array;
val->value.array = calloc(1, sizeof(struct json_array));
VEC_INIT(&val->value.array->values, 10);
}

static void setobject(struct json_value *val) {
val->type = Json_Object;
val->value.object = calloc(1, sizeof(struct json_object));
HASHMAP_INIT(&val->value.object->members, 10, hash_name);
}

static void setstring(struct json_value *val, uint8_t *current) {
val->type = Json_String;
val->value.string.s = current;
val->value.string.l = 0;
}

static bool is_number(uint8_t byte) { return byte >= '0' && byte <= '9'; }

enum object_parse_state {
ObjectParseState_Key,
ObjectParseState_Value,
};

struct json_result json_parse(uint8_t *buf, uint64_t size) {
struct json_result res = {
.ok = true,
.result.document.type = Json_Null,
};

struct json_value *parent = NULL;
struct json_value *current = &res.result.document;
struct json_value tmp_key = {0};
struct json_value tmp_val = {0};
uint32_t line = 1, col = 0;

enum object_parse_state obj_parse_state = ObjectParseState_Key;
for (uint64_t bufi = 0; bufi < size; ++bufi) {
uint8_t byte = buf[bufi];

// handle appends to the current scope
if (current->type == Json_Array) {
VEC_PUSH(&current->value.array->values, tmp_val);
parent = current;

// start looking for next value
tmp_val.type = Json_Null;
current = &tmp_val;
} else if (current->type == Json_Object &&
obj_parse_state == ObjectParseState_Key) {
// key is in tmp_key, start looking for value
obj_parse_state = ObjectParseState_Value;
parent = current;

tmp_val.type = Json_Null;
current = &tmp_val;
} else if (current->type == Json_Object &&
obj_parse_state == ObjectParseState_Value) {
// value is in tmp_val
// TODO: remove this alloc, should not be needed
char *k = s8tocstr(tmp_key.value.string);
uint32_t hash = 0;
HASHMAP_INSERT(&current->value.object->members, struct json_object_member,
k, tmp_val, hash);
(void)hash;
free(k);

// start looking for next key
obj_parse_state = ObjectParseState_Key;
parent = current;

tmp_key.type = Json_Null;
current = &tmp_key;
}

switch (byte) {
case '[':
setarray(current);
parent = current;

tmp_val.type = Json_Null;
current = &tmp_val;
break;
case ']':
current = parent;
break;
case '{':
setobject(current);
obj_parse_state = ObjectParseState_Key;
parent = current;

tmp_key.type = Json_Null;
current = &tmp_key;
break;
case '}':
current = parent;
break;
case '"':
if (current->type == Json_String) {
// finish off the string
current->value.string.l = (buf + bufi) - current->value.string.s;
current = parent;
} else {
setstring(current, buf + bufi + 1 /* skip " */);
}
break;
case '\n':
++line;
col = 0;
break;
default:
if (current->type == Json_String) {
// append to string
} else if (current->type == Json_Number &&
!(is_number(byte) || byte == '-' || byte == '.')) {
// end of number
current->value.string.l = (buf + bufi) - current->value.string.s;
char *nmbr = s8tocstr(current->value.string);
current->value.number = atof(nmbr);
free(nmbr);

current = parent;

} else if (current->type == Json_Null &&
(is_number(byte) || byte == '-' || byte == '.')) {
// borrow string storage in the value for storing number
// as a string
setstring(current, buf + bufi);
current->type = Json_Number;
} else if (byte == 't') {
current->type = Json_Bool;
current->value.boolean = true;

current = parent;
} else if (byte == 'f') {
current->type = Json_Bool;
current->value.boolean = false;

current = parent;
} else if (byte == 'n') {
current->type = Json_Null;

current = parent;
}
break;
}

// TODO: not entirely correct
++col;
}
return res;
}

void json_destroy(struct json_value *value) {
switch (value->type) {
case Json_Array:
struct json_array *arr = value->value.array;
VEC_FOR_EACH(&arr->values, struct json_value * val) { json_destroy(val); }
VEC_DESTROY(&arr->values);
break;
case Json_Object:
struct json_object *obj = value->value.object;
HASHMAP_FOR_EACH(&obj->members, struct json_object_member * memb) {
json_destroy(&memb->value);
}

HASHMAP_DESTROY(&obj->members);
case Json_Null:
case Json_Number:
case Json_String:
case Json_Bool:
break;
}
}

uint64_t json_array_len(struct json_array *arr) {
return VEC_SIZE(&arr->values);
}

struct json_value *json_array_get(struct json_array *arr, uint64_t idx) {
struct json_value *ret = NULL;

if (idx <= VEC_SIZE(&arr->values)) {
ret = &VEC_ENTRIES(&arr->values)[idx];
}

return ret;
}

uint64_t json_len(struct json_object *obj) {
return HASHMAP_SIZE(&obj->members);
}

bool json_contains(struct json_object *obj, struct s8 key) {
// TODO: get rid of alloc
char *k = s8tocstr(key);
HASHMAP_CONTAINS_KEY(&obj->members, struct json_object_member, k, bool res);

free(k);

return res;
}

struct json_value *json_get(struct json_object *obj, struct s8 key) {
// TODO: get rid of alloc
char *k = s8tocstr(key);
HASHMAP_GET(&obj->members, struct json_object_member, k,
struct json_value * result);

free(k);

return result;
}
54 changes: 54 additions & 0 deletions src/dged/json.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#ifndef _JSON_H
#define _JSON_H

#include <stdbool.h>
#include <stdint.h>

#include "s8.h"

enum json_type {
Json_Array,
Json_Object,
Json_Null,
Json_Number,
Json_String,
Json_Bool,
};

struct json_value {
enum json_type type;
union {
struct s8 string;
struct json_object *object;
struct json_array *array;
double number;
bool boolean;
} value;
};

struct json_result {
bool ok;
union {
const char *error;
struct json_value document;
} result;
};

struct json_writer;

struct json_result json_parse(uint8_t *buf, uint64_t size);
void json_destroy(struct json_value *value);

uint64_t json_len(struct json_object *obj);
bool json_contains(struct json_object *obj, struct s8 key);
struct json_value *json_get(struct json_object *obj, struct s8 key);

uint64_t json_array_len(struct json_array *arr);
void json_array_foreach(struct json_array *arr,
void (*cb)(uint64_t, struct json_value));
struct json_value *json_array_get(struct json_array *arr, uint64_t idx);

struct json_writer *json_writer_create();
struct s8 json_writer_done(struct json_writer *writer);

#endif
Loading

0 comments on commit e4ee02d

Please sign in to comment.