diff --git a/src/dged/buffer.c b/src/dged/buffer.c index c537fb3..84d2f75 100644 --- a/src/dged/buffer.c +++ b/src/dged/buffer.c @@ -788,8 +788,9 @@ struct location buffer_end(struct buffer *buffer) { if (buffer->lazy_row_add) { return (struct location){.line = nlines, .col = 0}; } else { - return (struct location){.line = nlines - 1, - .col = buffer_line_length(buffer, nlines - 1)}; + nlines = nlines == 0 ? 0 : nlines - 1; + return (struct location){.line = nlines, + .col = buffer_line_length(buffer, nlines)}; } } diff --git a/src/dged/json.c b/src/dged/json.c index 24d5c15..70d406b 100644 --- a/src/dged/json.c +++ b/src/dged/json.c @@ -18,22 +18,35 @@ 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 struct json_value create_array(struct json_value *parent) { + struct json_value val = {0}; + val.type = Json_Array; + val.parent = parent; + val.value.array = calloc(1, sizeof(struct json_array)); + VEC_INIT(&val.value.array->values, 10); + + return val; } -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 struct json_value create_object(struct json_value *parent) { + struct json_value val = {0}; + val.type = Json_Object; + val.parent = parent; + val.value.object = calloc(1, sizeof(struct json_object)); + HASHMAP_INIT(&val.value.object->members, 10, hash_name); + + return val; } -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 struct json_value create_string(const uint8_t *start, uint32_t len, + struct json_value *parent) { + struct json_value val = {0}; + val.type = Json_String; + val.parent = parent; + val.value.string.s = (uint8_t *)start; + val.value.string.l = len; + + return val; } static bool is_number(uint8_t byte) { return byte >= '0' && byte <= '9'; } @@ -43,133 +56,230 @@ enum object_parse_state { 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 parser_state { + const uint8_t *buf; + uint64_t pos; + uint64_t len; + uint32_t line; + uint32_t col; +}; + +static struct json_result parse_string(struct parser_state *state, + struct json_value *parent) { + uint64_t start_pos = ++state->pos; /* ++ to skip start of string (") */ + while (state->pos < state->len && state->buf[state->pos] != '"') { + ++state->pos; + ++state->col; + } + + if (state->pos < state->len) { + uint64_t len = state->pos - start_pos; + + // skip over " + ++state->pos; + ++state->col; + + return (struct json_result){ + .ok = true, + .result.document = create_string(&state->buf[start_pos], len, parent), + }; + } + + return (struct json_result){ + .ok = false, + .result.error = "expected end of string, found EOF", }; +} - 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(¤t->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(¤t->value.object->members, struct json_object_member, - k, tmp_val, hash); - (void)hash; - free(k); +static struct json_result parse_number(struct parser_state *state, + struct json_value *parent) { + uint64_t start_pos = state->pos; + while (state->pos < state->len && + (is_number(state->buf[state->pos]) || state->buf[state->pos] == '-' || + state->buf[state->pos] == '.')) { + ++state->pos; + ++state->col; + } - // start looking for next key - obj_parse_state = ObjectParseState_Key; - parent = current; + if (state->pos < state->len) { + uint64_t len = state->pos - start_pos; + ++state->pos; + ++state->col; + char *nmbr = + s8tocstr((struct s8){.s = (uint8_t *)&state->buf[start_pos], .l = len}); + struct json_result res = { + .ok = true, + .result.document.type = Json_Number, + .result.document.value.number = atof(nmbr), + .result.document.parent = parent, + }; + free(nmbr); + return res; + } + + return (struct json_result){ + .ok = false, + .result.error = "expected end of number, found EOF", + }; +} - tmp_key.type = Json_Null; - current = &tmp_key; +static struct json_result parse_value(struct parser_state *state, + struct json_value *parent) { + uint8_t byte = state->buf[state->pos]; + switch (byte) { + case '"': + return parse_string(state, parent); + case 't': + state->pos += 4; + state->col += 4; + return (struct json_result){ + .ok = true, + .result.document.type = Json_Bool, + .result.document.value.boolean = true, + .result.document.parent = parent, + }; + case 'f': + state->pos += 5; + state->col += 5; + return (struct json_result){ + .ok = true, + .result.document.type = Json_Bool, + .result.document.value.boolean = false, + .result.document.parent = parent, + }; + case 'n': + state->pos += 4; + state->col += 4; + return (struct json_result){ + .ok = true, + .result.document.type = Json_Null, + .result.document.parent = parent, + }; + default: + if (is_number(byte) || byte == '-' || byte == '.') { + return parse_number(state, parent); } + break; + } - switch (byte) { - case '[': - setarray(current); - parent = current; + return (struct json_result){ + .ok = false, + .result.error = "expected value", + }; +} + +struct json_result json_parse(const uint8_t *buf, uint64_t size) { + + enum object_parse_state expected = ObjectParseState_Value; + struct parser_state state = { + .buf = buf, + .pos = 0, + .len = size, + .line = 1, + .col = 0, + }; + + struct json_value root = {0}, key = {0}, value = {0}; + struct json_value *container = &root; + + while (state.pos < state.len) { + switch (state.buf[state.pos]) { + case ',': + case ' ': + case ':': + case '\r': + case '\t': + ++state.col; + ++state.pos; + continue; + + case '\n': + ++state.line; + ++state.pos; + state.col = 0; + continue; - tmp_val.type = Json_Null; - current = &tmp_val; - break; case ']': - current = parent; - break; - case '{': - setobject(current); - obj_parse_state = ObjectParseState_Key; - parent = current; + case '}': + container = container->parent; + ++state.pos; + ++state.col; + continue; - tmp_key.type = Json_Null; - current = &tmp_key; + case '[': + value = create_array(container); + ++state.pos; + ++state.col; break; - case '}': - current = parent; + case '{': + value = create_object(container); + expected = ObjectParseState_Key; + ++state.pos; + ++state.col; break; - case '"': - if (current->type == Json_String) { - // finish off the string - current->value.string.l = (buf + bufi) - current->value.string.s; - current = parent; + default: + if (expected == ObjectParseState_Key) { + struct json_result res = parse_string(&state, container); + + if (!res.ok) { + return res; + } + + key = res.result.document; + expected = ObjectParseState_Value; + + // dont insert anything now, we still need a value + continue; } else { - setstring(current, buf + bufi + 1 /* skip " */); + struct json_result res = parse_value(&state, container); + + if (!res.ok) { + return res; + } + + value = res.result.document; } - 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; + } + + struct json_value *inserted = NULL; + // where to put value? + if (container->type == Json_Object) { + // TODO: remove this alloc, should not be needed + char *k = s8tocstr(key.value.string); + HASHMAP_APPEND(&container->value.object->members, + struct json_object_member, k, + struct json_object_member * val); + + // TODO: duplicate key + if (val != NULL) { + inserted = &val->value; + val->value = value; + // start looking for next key + expected = ObjectParseState_Key; } - break; + + free(k); + } else if (container->type == Json_Array) { + VEC_APPEND(&container->value.array->values, struct json_value * val); + inserted = val; + *val = value; + } else { // root + *container = value; + inserted = container; } - // TODO: not entirely correct - ++col; + // did we insert a container? + // In this case, this is the current container + if (inserted != NULL && + (value.type == Json_Object || value.type == Json_Array)) { + container = inserted; + } } - return res; + + return (struct json_result){ + .ok = true, + .result.document = root, + }; } void json_destroy(struct json_value *value) { diff --git a/src/dged/json.h b/src/dged/json.h index c0428b9..fdabae3 100644 --- a/src/dged/json.h +++ b/src/dged/json.h @@ -7,9 +7,9 @@ #include "s8.h" enum json_type { + Json_Null = 0, Json_Array, Json_Object, - Json_Null, Json_Number, Json_String, Json_Bool, @@ -24,6 +24,7 @@ struct json_value { double number; bool boolean; } value; + struct json_value *parent; }; struct json_result { @@ -34,21 +35,91 @@ struct json_result { } result; }; -struct json_writer; +/** + * Parse a json document from a string. + * + * @returns Structure describing the result of the parse + * operation. The member @ref ok, if true represents a + * successful parse, with the result in @ref result.document. + * If @ref ok is false, the parse operation has an error, + * and @ref result.error contains a descriptive error message. + */ +struct json_result json_parse(const uint8_t *buf, uint64_t size); -struct json_result json_parse(uint8_t *buf, uint64_t size); +/** + * Destroy a json value, returning all memory + * allocated for the structure. + * + * @param [in] value The json value to destroy. + */ void json_destroy(struct json_value *value); +/** + * Return the number of members in a JSON object. + * + * @param [in] obj The JSON object to get number of members for. + * + * @returns The number of members in @ref obj. + */ uint64_t json_len(struct json_object *obj); + +/** + * Test if the JSON object contains the specified key. + * + * @param [in] obj The JSON object to look for @ref key in. + * @param [in] key The key to search for. + * + * @returns True if @ref key exists in @ref obj, false otherwise. + */ bool json_contains(struct json_object *obj, struct s8 key); + +/** + * Get a value from a JSON object. + * + * @param [in] obj The JSON object to get from. + * @param [in] key The key of the value to get. + * + * @returns A pointer to the json value distinguished by @ref key, + * if it exists, NULL otherwise. + */ struct json_value *json_get(struct json_object *obj, struct s8 key); +/** + * Get the length of a JSON array. + * + * @param [in] arr The array to get the length of + * + * @returns The length of @ref arr. + */ uint64_t json_array_len(struct json_array *arr); + +/** + * Iterate a JSON array. + * + * @param [in] arr The array to iterate. + * @param [in] cb The callback to invoke for each member in @ref arr. + */ void json_array_foreach(struct json_array *arr, void (*cb)(uint64_t, struct json_value)); + +/** + * Get a member from a JSON array by index. + * + * @param [in] arr The array to get from. + * @param [in] idx The index to get the value at. + * + * @returns A pointer to the value at @ref idx in @ref arr. If @ref idx + * is outside the array length, this returns NULL. + */ 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); +/** + * Render a JSON value to a string. + * + * @param [in] val The json value to render to a string. + * + * @returns The JSON object rendered as a string. + */ +struct s8 json_value_to_string(const struct json_value *val); #endif diff --git a/src/dged/lsp.c b/src/dged/lsp.c index 3c699f4..e73a41f 100644 --- a/src/dged/lsp.c +++ b/src/dged/lsp.c @@ -75,9 +75,9 @@ void lsp_destroy(struct lsp *lsp) { free(lsp->process); } if (lsp->command != NULL) { - char *command = lsp->command[0]; + char *const *command = &lsp->command[0]; while (command != NULL) { - free(command); + free(*command); ++command; }