summaryrefslogtreecommitdiff
path: root/src/dged/json.c
diff options
context:
space:
mode:
authorAlbert Cervin <albert@acervin.com>2024-05-22 00:00:29 +0200
committerAlbert Cervin <albert@acervin.com>2024-09-12 20:17:56 +0200
commit405da5f84b072ea97b69359454899f45d92d24b6 (patch)
tree20525b4bc44a5d8cbab4d62abe8413e174731db6 /src/dged/json.c
parent4ab7e453e26afc6e9f4938c65f89463fbba9e267 (diff)
downloaddged-405da5f84b072ea97b69359454899f45d92d24b6.tar.gz
dged-405da5f84b072ea97b69359454899f45d92d24b6.tar.xz
dged-405da5f84b072ea97b69359454899f45d92d24b6.zip
WIP LSP client
This contains the start of an LSP client. Nothing (except starting the LSP server) works at the moment and the feature is disabled by default.
Diffstat (limited to 'src/dged/json.c')
-rw-r--r--src/dged/json.c234
1 files changed, 234 insertions, 0 deletions
diff --git a/src/dged/json.c b/src/dged/json.c
new file mode 100644
index 0000000..24d5c15
--- /dev/null
+++ b/src/dged/json.c
@@ -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;
+}