diff options
Diffstat (limited to 'mons_json/src/json.c')
-rw-r--r-- | mons_json/src/json.c | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/mons_json/src/json.c b/mons_json/src/json.c new file mode 100644 index 0000000..7b1cc64 --- /dev/null +++ b/mons_json/src/json.c @@ -0,0 +1,468 @@ +#include "json.h" +#include <ctype.h> +#include <string.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdio.h> + +int try_parse_string(char **current, mons_json_value *out); +int try_parse_number(char **current, mons_json_value *out); +int try_parse_object(char **current, mons_json_value *out); +int try_parse_value(char **current, mons_json_value *out); +int try_parse_array(char **current, mons_json_value *out); +int try_parse_literal(char **current, mons_json_value *out); + +int mons_json_parse(char *json, mons_json_value *out) { + char *current = json; + if (try_parse_value(¤t, out) == EXIT_SUCCESS) { + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} + +int try_parse_value(char **current, mons_json_value *out) { + // Trim space + while (isspace(**current)) + (*current)++; + if (**current == 0) { + return EXIT_FAILURE; + } + + if (try_parse_literal(current, out) == EXIT_SUCCESS) { + return EXIT_SUCCESS; + } + + if (try_parse_array(current, out) == EXIT_SUCCESS) { + return EXIT_SUCCESS; + } + + if (try_parse_object(current, out) == EXIT_SUCCESS) { + return EXIT_SUCCESS; + } + + if (try_parse_number(current, out) == EXIT_SUCCESS) { + return EXIT_SUCCESS; + } + + if (try_parse_string(current, out) == EXIT_SUCCESS) { + return EXIT_SUCCESS; + } + + return EXIT_FAILURE; +} + +int try_parse_string(char **current, mons_json_value *out) { + if (**current != '"') { + return EXIT_FAILURE; + } + + (*current)++; + char *end_of_string = strchr(*current, '"'); + int string_length = end_of_string - *current; + char *buffer = calloc(string_length + 1, sizeof(char)); + // TODO: Unescape Characters + memcpy(buffer, *current, string_length); + out->type = MONS_JSON_STRING; + out->data.string = buffer; + *current += string_length + 1; + return EXIT_SUCCESS; +} + +int try_parse_number(char **current, mons_json_value *out) { + char *end_of_value = *current; + while (!isspace(*end_of_value) && *end_of_value != ',' && + *end_of_value != 0 && *end_of_value != ']' && *end_of_value != '}') { + end_of_value++; + } + int value_length = end_of_value - *current; + if (value_length == 0) { + return EXIT_FAILURE; + } + bool has_decimal = false; + bool has_exponent = false; + for (int i = 0; i < value_length; i++) { + if (i == 0 && **current == '-') { + continue; + } + if (!isdigit(*(*current + i))) { + switch (*(*current + i)) { + case 'e': + case 'E': + if (has_exponent) { + return EXIT_FAILURE; + } else { + has_exponent = true; + } + break; + case '+': + case '-': + if (*(*current + i - 1) != 'e' && *(*current +i - 1) != 'E') { + return EXIT_FAILURE; + } + break; + case '.': + if (has_decimal) { + return EXIT_FAILURE; + } else { + has_decimal = true; + } + break; + default: + return EXIT_FAILURE; + } + } + } + // Value is a valid number + out->type = MONS_JSON_NUMBER; + out->data.number = atof(*current); + *current += value_length; + return EXIT_SUCCESS; +} + +int try_parse_object(char **current, mons_json_value *out) { + if (**current != '{') { + return EXIT_FAILURE; + } + (*current)++; + + mons_hashmap map = mons_hashmap_new(sizeof(mons_json_value), 10); + + while (true) { + // Trim space + while (isspace(**current)) + (*current)++; + if (**current == 0) { + printf("ALL WHITESPACE\n"); + return EXIT_FAILURE; + } + + mons_json_value key; + if (try_parse_string(current, &key) == EXIT_SUCCESS) { + // Trim space + while (isspace(**current)) + (*current)++; + if (*current == 0) { + return EXIT_FAILURE; + } + + if (**current != ':') { + return EXIT_FAILURE; + } + (*current)++; + mons_json_value value; + if (try_parse_value(current, &value) == EXIT_FAILURE) { + return EXIT_FAILURE; + } + mons_hashmap_insert(&map, key.data.string, &value); + continue; + } else { + // Trim space + while (isspace(**current)) + (*current)++; + if (*current == 0) { + return EXIT_FAILURE; + } + switch (**current) { + case ',': + (*current)++; + continue; + case '}': + (*current)++; + out->type = MONS_JSON_OBJECT; + out->data.object = map; + return EXIT_SUCCESS; + default: + return EXIT_FAILURE; + } + } + } + return EXIT_FAILURE; +} + +int try_parse_array(char **current, mons_json_value *out) { + if (**current != '[') { + return EXIT_FAILURE; + } + (*current)++; + + mons_json_array array = { + .values = NULL, + .len = 0, + }; + + while (true) { + // Trim space + while (isspace(**current)) + (*current)++; + if (**current == 0) { + return EXIT_FAILURE; + } + + mons_json_value value; + if (try_parse_value(current, &value) == EXIT_SUCCESS) { + array.len++; + array.values = + realloc(array.values, sizeof(mons_json_value) * array.len); + array.values[array.len - 1] = value; + continue; + } else { + // Trim space + while (isspace(**current)) + (*current)++; + if (*current == 0) { + return EXIT_FAILURE; + } + switch (**current) { + case ',': + (*current)++; + continue; + case ']': + (*current)++; + out->type = MONS_JSON_ARRAY; + out->data.array = array; + return EXIT_SUCCESS; + default: + return EXIT_FAILURE; + } + } + } + + return EXIT_FAILURE; +} + +int try_parse_literal(char **current, mons_json_value *out) { + // Trim space + while (isspace(**current)) + (*current)++; + if (**current == 0) { + return EXIT_FAILURE; + } + + if (strncmp(*current, "true", 4) == 0) { + *current += 4; + out->type = MONS_JSON_BOOL; + out->data.boolean = true; + return EXIT_SUCCESS; + } else if (strncmp(*current, "false", 5) == 0) { + *current += 5; + out->type = MONS_JSON_BOOL; + out->data.boolean = false; + return EXIT_SUCCESS; + } else if (strncmp(*current, "null", 4) == 0) { + *current += 4; + out->type = MONS_JSON_NULL; + out->data.null = NULL; + return EXIT_SUCCESS; + } + + return EXIT_FAILURE; +} + +void json_value_to_string(mons_json_value value, char **buffer, + unsigned int *current_len) { + switch (value.type) { + case MONS_JSON_NULL: + *buffer = realloc(*buffer, *current_len + 4); + strcpy(*buffer + *current_len, "null"); + *current_len += 4; + break; + case MONS_JSON_BOOL: + if (value.data.boolean) { + *buffer = realloc(*buffer, *current_len + 4); + memcpy(*buffer + *current_len, "true", 4); + *current_len += 4; + break; + } else { + *buffer = realloc(*buffer, *current_len + 5); + memcpy(*buffer + *current_len, "false", 5); + *current_len += 5; + break; + } + case MONS_JSON_ARRAY: { + *buffer = realloc(*buffer, *current_len + 1); + (*buffer)[*current_len] = '['; + *current_len += 1; + for (int i = 0; i < value.data.array.len; i++) { + mons_json_value member = value.data.array.values[i]; + char *buff = NULL; + unsigned int len = 0; + json_value_to_string(member, &buff, &len); + *buffer = realloc(*buffer, *current_len + len + 1); + memcpy(*buffer + *current_len, buff, len); + free(buff); + *current_len += len; + *buffer = realloc(*buffer, *current_len + 1); + if (i + 1 < value.data.array.len) { + (*buffer)[*current_len] = ','; + } else { + (*buffer)[*current_len] = ']'; + } + *current_len += 1; + } + break; + } + case MONS_JSON_OBJECT: + *buffer = realloc(*buffer, *current_len + 1); + (*buffer)[*current_len] = '{'; + *current_len += 1; + for (int i = 0; i < value.data.object.len; i++) { + mons_hashmap_pair pair; + mons_hashmap_at(value.data.object, i, &pair); + char *key = pair.key; + mons_json_value *entry_value = pair.value; + // key + *buffer = realloc(*buffer, *current_len + strlen(key) + 3); + sprintf(*buffer + *current_len, "\"%s\":", key); + *current_len += strlen(key) + 3; + char *buff = NULL; + unsigned int len = 0; + json_value_to_string(*entry_value, &buff, &len); + *buffer = realloc(*buffer, *current_len + len + 1); + memcpy(*buffer + *current_len, buff, len); + free(buff); + *current_len += len; + *buffer = realloc(*buffer, *current_len + 1); + if (i + 1 < value.data.object.len) { + (*buffer)[*current_len] = ','; + } else { + (*buffer)[*current_len] = '}'; + } + *current_len += 1; + } + break; + case MONS_JSON_NUMBER: { + unsigned int len = snprintf(0, 0, "%e", value.data.number); + *buffer = realloc(*buffer, *current_len + len); + sprintf(*buffer + *current_len, "%e", value.data.number); + *current_len += len; + break; + } + case MONS_JSON_STRING: { + unsigned int len = snprintf(0, 0, "\"%s\"", value.data.string); + *buffer = realloc(*buffer, *current_len + len); + sprintf(*buffer + *current_len, "\"%s\"", value.data.string); + *current_len += len; + break; + } + default: + break; + } +} + +char *mons_json_to_string(mons_json_value json) { + char *buffer = NULL; + unsigned int len = 0; + json_value_to_string(json, &buffer, &len); + buffer = realloc(buffer, len + 1); + buffer[len] = 0; + return buffer; +} + +void mons_json_free(mons_json_value json) { + switch(json.type) { + case MONS_JSON_OBJECT: + mons_hashmap_free(&json.data.object); + break; + case MONS_JSON_ARRAY: + for(int i = 0; i < json.data.array.len; i++) { + mons_json_free(json.data.array.values[i]); + } + json.data.array.len = 0; + free(json.data.array.values); + json.data.array.values = NULL; + break; + case MONS_JSON_STRING: + free(json.data.string); + json.data.string = NULL; + break; + default: + break; + } +} + +int mons_json_get_value(mons_json_value json, char *name, mons_json_value *out) { + int name_len = strlen(name); + char *name_copy = calloc(name_len + 1, 1); + strcpy(name_copy, name); + char *field = strtok(name_copy, "."); + mons_json_value member = json; + while(field != NULL) { + char *subscript = strchr(field, '['); + int array_index = 0; + if(subscript != NULL) { + array_index = atoi(subscript + 1); + *subscript = 0; + } + + if (member.type != MONS_JSON_OBJECT) { + return EXIT_FAILURE; + } + mons_json_value value; + if (mons_hashmap_get(member.data.object, field, &value) == EXIT_SUCCESS) { + member = value; + if (subscript != NULL) { + member = member.data.array.values[array_index]; + } + } else { + return EXIT_FAILURE; + } + field = strtok(NULL, "."); + } + *out = member; + return EXIT_SUCCESS; +} + +int mons_json_get_int(mons_json_value json, char *name, int *out) { + mons_json_value value = {0}; + if(mons_json_get_value(json, name, &value) == EXIT_SUCCESS && value.type == MONS_JSON_NUMBER) { + *out = value.data.number; + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} + +int mons_json_get_float(mons_json_value json, char *name, float *out) { + mons_json_value value = {0}; + if(mons_json_get_value(json, name, &value) == EXIT_SUCCESS && value.type == MONS_JSON_NUMBER) { + *out = value.data.number; + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} + +int mons_json_get_bool(mons_json_value json, char *name, bool *out) { + mons_json_value value = {0}; + if(mons_json_get_value(json, name, &value) == EXIT_SUCCESS && value.type == MONS_JSON_BOOL) { + *out = value.data.boolean; + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} + +int mons_json_get_array(mons_json_value json, char *name, mons_json_array *out) { + mons_json_value value = {0}; + if(mons_json_get_value(json, name, &value) == EXIT_SUCCESS && value.type == MONS_JSON_ARRAY) { + *out = value.data.array; + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} + +int mons_json_get_object(mons_json_value json, char *name, mons_hashmap *out) { + mons_json_value value = {0}; + if(mons_json_get_value(json, name, &value) == EXIT_SUCCESS && value.type == MONS_JSON_OBJECT) { + *out = value.data.object; + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} + +int mons_json_get_string(mons_json_value json, char *name, char **out) { + mons_json_value value = {0}; + if(mons_json_get_value(json, name, &value) == EXIT_SUCCESS && value.type == MONS_JSON_STRING) { + *out = value.data.string; + return EXIT_SUCCESS; + } + return EXIT_FAILURE; +} + |