#include "json.h" #include #include #include #include #include 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; }