aboutsummaryrefslogtreecommitdiff
path: root/mons_json/src/json.c
diff options
context:
space:
mode:
Diffstat (limited to 'mons_json/src/json.c')
-rw-r--r--mons_json/src/json.c468
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(&current, 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;
+}
+