Use arena with JSON parser. Slightly altered output for json printing.

This commit is contained in:
Christoffer Lerno
2024-09-20 00:39:10 +02:00
parent 8a9834cac0
commit 9447913de6
8 changed files with 104 additions and 189 deletions

View File

@@ -7,9 +7,8 @@ void check_json_keys(const char* valid_keys[][2], size_t key_count, const char*
{
static bool failed_shown = false;
bool failed = false;
for (size_t i = 0; i < json->member_len; i++)
FOREACH(const char *, key, json->keys)
{
const char *key = json->keys[i];
for (size_t j = 0; j < key_count; j++)
{
if (str_eq(key, valid_keys[j][0])) goto OK;
@@ -36,7 +35,7 @@ void check_json_keys(const char* valid_keys[][2], size_t key_count, const char*
const char *get_optional_string(const char *file, const char *category, JSONObject *table, const char *key)
{
JSONObject *value = json_obj_get(table, key);
JSONObject *value = json_map_get(table, key);
if (!value) return NULL;
if (value->type != J_STRING)
{
@@ -67,7 +66,7 @@ const char *get_string(const char *file, const char *category, JSONObject *table
int get_valid_bool(const char *file, const char *target, JSONObject *json, const char *key, int default_val)
{
JSONObject *value = json_obj_get(json, key);
JSONObject *value = json_map_get(json, key);
if (!value) return default_val;
if (value->type != J_BOOL)
{
@@ -79,7 +78,7 @@ int get_valid_bool(const char *file, const char *target, JSONObject *json, const
const char **get_string_array(const char *file, const char *category, JSONObject *table, const char *key, bool mandatory)
{
JSONObject *value = json_obj_get(table, key);
JSONObject *value = json_map_get(table, key);
if (!value)
{
if (mandatory)
@@ -91,9 +90,8 @@ const char **get_string_array(const char *file, const char *category, JSONObject
}
if (value->type != J_ARRAY) goto NOT_ARRAY;
const char **values = NULL;
for (unsigned i = 0; i < value->array_len; i++)
FOREACH(JSONObject *, val, value->elements)
{
JSONObject *val = value->elements[i];
if (val->type != J_STRING) goto NOT_ARRAY;
vec_add(values, val->str);
}
@@ -163,7 +161,7 @@ void get_list_append_strings(const char *file, const char *target, JSONObject *j
int get_valid_string_setting(const char *file, const char *target, JSONObject *json, const char *key, const char** values, int first_result, int count, const char *expected)
{
JSONObject *value = json_obj_get(json, key);
JSONObject *value = json_map_get(json, key);
if (!value)
{
return -1;
@@ -193,7 +191,7 @@ int get_valid_enum_from_string(const char *str, const char *target, const char *
long get_valid_integer(JSONObject *table, const char *key, const char *category, bool mandatory)
{
JSONObject *value = json_obj_get(table, key);
JSONObject *value = json_map_get(table, key);
if (!value)
{
if (mandatory)

View File

@@ -49,9 +49,8 @@ static inline void parse_library_type(Library *library, LibraryTarget ***target_
{
if (!object) return;
if (object->type != J_OBJECT) error_exit("Expected a set of targets in %s.", library->dir);
for (size_t i = 0; i < object->member_len; i++)
FOREACH_IDX(i, JSONObject *, member, object->members)
{
JSONObject *member = object->members[i];
const char *key = object->keys[i];
if (member->type != J_OBJECT) error_exit("Expected a list of properties for a target in %s.", library->dir);
check_json_keys(manifest_target_keys, manifest_target_keys_count, manifest_deprecated_target_keys, manifest_deprecated_target_key_count, member, key, "--list-manifest-properties");
@@ -115,7 +114,7 @@ static Library *add_library(JSONObject *object, const char *dir)
library->win_crt = (WinCrtLinking)get_valid_string_setting(library->dir, NULL, object, "wincrt", wincrt_linking, 0, 3, "'none', 'static' or 'dynamic'.");
get_list_append_strings(library->dir, NULL, object, &library->csource_dirs, "c-sources", "c-sources-override", "c-sources-add");
get_list_append_strings(library->dir, NULL, object, &library->cinclude_dirs, "c-include-dirs", "c-include-dirs-override", "c-include-dirs-add");
parse_library_type(library, &library->targets, json_obj_get(object, "targets"));
parse_library_type(library, &library->targets, json_map_get(object, "targets"));
return library;
}
@@ -166,7 +165,7 @@ INLINE void zip_check_err(const char *lib, const char *error)
INLINE JSONObject* read_manifest(const char *lib, const char *manifest_data)
{
JsonParser parser;
json_init_string(&parser, manifest_data, &malloc_arena);
json_init_string(&parser, manifest_data);
JSONObject *json = json_parse(&parser);
if (parser.error_message)
{

View File

@@ -448,7 +448,7 @@ static void project_add_targets(const char *filename, Project *project, JSONObje
BuildTarget default_target = default_build_target;
load_into_build_target(filename, project_data, NULL, &default_target);
JSONObject *targets_json = json_obj_get(project_data, "targets");
JSONObject *targets_json = json_map_get(project_data, "targets");
if (!targets_json)
{
error_exit("No targets found in project.");
@@ -457,9 +457,8 @@ static void project_add_targets(const char *filename, Project *project, JSONObje
{
error_exit("'targets' did not contain map of targets.");
}
for (unsigned i = 0; i < targets_json->member_len; i++)
FOREACH_IDX(i, JSONObject *, object, targets_json->members)
{
JSONObject *object = targets_json->members[i];
const char *key = targets_json->keys[i];
if (object->type != J_OBJECT)
{
@@ -519,7 +518,7 @@ Project *project_load(const char **filename_ref)
const char *filename = *filename_ref = file_exists(PROJECT_JSON5) ? PROJECT_JSON5 : PROJECT_JSON;
char *read = file_read_all(filename, &size);
JsonParser parser;
json_init_string(&parser, read, &malloc_arena);
json_init_string(&parser, read);
JSONObject *json = json_parse(&parser);
if (parser.error_message)
{

View File

@@ -10,7 +10,7 @@ static JSONObject *read_project(const char **file_used)
*file_used = project_filename;
char *read = file_read_all(project_filename, &size);
JsonParser parser;
json_init_string(&parser, read, &malloc_arena);
json_init_string(&parser, read);
JSONObject *json = json_parse(&parser);
if (parser.error_message)
{
@@ -259,41 +259,31 @@ void add_libraries_to_project_file(const char** libs, const char* target_name) {
const char *filename;
JSONObject *project_json = read_project(&filename);
// TODO! check if target is specified and exists (NULL at the moment)
JSONObject *libraries_json = json_obj_get(project_json, "dependencies");
JSONObject *libraries_json = json_map_get(project_json, "dependencies");
const char** dependencies = NULL;
for(int i = 0; i < libraries_json->array_len; i++)
FOREACH(JSONObject *, element, libraries_json->elements)
{
vec_add(dependencies, libraries_json->elements[i]->str);
vec_add(dependencies, element->str);
}
// check if libraries are already present
FOREACH(const char*, lib, libs)
{
if (str_findlist(lib, vec_size(dependencies), dependencies)!=-1) continue;
vec_add(dependencies, lib);
}
JSONObject** elements = NULL;
FOREACH(const char*, dep, dependencies)
{
JSONObject* obj = json_new_object(&malloc_arena, J_STRING);
obj->str = dep;
vec_add(elements, obj);
vec_add(elements, json_new_string(dep));
}
// TODO fancier functions for altering JSON file (quite cumbersome at the moment)
// TODO! check if "dependency" entry exists in the project.json file.
// Apply changes to JSON object
libraries_json->elements = elements;
libraries_json->array_len = vec_size(dependencies);
// write to project json file
FILE *file = fopen(filename, "w");
@@ -306,36 +296,19 @@ void add_target_project(BuildOptions *build_options)
{
const char *filename;
JSONObject *project_json = read_project(&filename);
JSONObject *targets_json = json_obj_get(project_json, "targets");
JSONObject *targets_json = json_map_get(project_json, "targets");
for (unsigned i = 0; i < targets_json->member_len; i++)
if (json_map_get(targets_json, build_options->project_options.target_name) != NULL)
{
JSONObject *object = targets_json->members[i];
const char *key = targets_json->keys[i];
if (str_eq(key, build_options->project_options.target_name))
{
error_exit("Target with name '%s' already exists", key);
}
error_exit("Target with name '%s' already exists", build_options->project_options.target_name);
}
JSONObject *target_type_obj = json_new_object(&malloc_arena, J_STRING);
target_type_obj->str = targets[build_options->project_options.target_type];
JSONObject *target_type_obj = json_new_string(targets[build_options->project_options.target_type]);
JSONObject *new_target = json_new_map();
json_map_set(new_target, "type", target_type_obj);
JSONObject *new_target = json_new_object(&malloc_arena, J_OBJECT);
new_target->members = malloc_arena(sizeof(JSONObject) * 16);
new_target->keys = malloc_arena(sizeof(JSONObject) * 16);
new_target->keys[0] = "type";
new_target->members[0] = target_type_obj;
new_target->member_len = 1;
size_t index = targets_json->member_len;
targets_json->members[index] = new_target;
targets_json->keys[index] = build_options->project_options.target_name;
targets_json->member_len++;
json_map_set(targets_json, build_options->project_options.target_name, new_target);
FILE *file = fopen(filename, "w");
print_json_to_file(project_json, file);
@@ -400,7 +373,7 @@ void view_project(BuildOptions *build_options)
/* Target information */
PRINTFN("Targets: ");
JSONObject *targets_json = json_obj_get(project_json, "targets");
JSONObject *targets_json = json_map_get(project_json, "targets");
if (!targets_json)
{
error_exit("No targets found in project.");
@@ -410,11 +383,9 @@ void view_project(BuildOptions *build_options)
error_exit("'targets' did not contain map of targets.");
}
for (unsigned i = 0; i < targets_json->member_len; i++)
FOREACH_IDX(i, JSONObject *, object, targets_json->members)
{
JSONObject *object = targets_json->members[i];
const char *key = targets_json->keys[i];
if (object->type != J_OBJECT)
{
error_exit("Invalid data in target '%s'", key);

View File

@@ -30,16 +30,16 @@ MacSDK *macos_sysroot_sdk_information(const char *sdk_path)
const char *settings_json_path = scratch_buffer_to_string();
if (!file_exists(settings_json_path)) error_exit("Invalid MacOS SDK path: '%s'.", sdk_path);
const char *file = file_read_all(settings_json_path, &len);
json_init_string(&parser, file, &malloc_arena);
json_init_string(&parser, file);
MacSDK *sdk = CALLOCS(MacSDK);
JSONObject *top_object = json_parse(&parser);
JSONObject *supported_targets = json_obj_get(top_object, "SupportedTargets");
JSONObject *macosx_target = json_obj_get(supported_targets, "macosx");
JSONObject *supported_targets = json_map_get(top_object, "SupportedTargets");
JSONObject *macosx_target = json_map_get(supported_targets, "macosx");
const char *default_deploy_target = json_obj_get(macosx_target, "DefaultDeploymentTarget")->str;
const char *default_deploy_target = json_map_get(macosx_target, "DefaultDeploymentTarget")->str;
parse_version(default_deploy_target, &sdk->macos_deploy_target);
const char *min_deploy_target = json_obj_get(macosx_target, "MinimumDeploymentTarget")->str;
const char *min_deploy_target = json_map_get(macosx_target, "MinimumDeploymentTarget")->str;
parse_version(min_deploy_target, &sdk->macos_min_deploy_target);
return sdk;

View File

@@ -169,20 +169,20 @@ static void test_json(void)
{
printf("Begin json testing.\n");
JsonParser parser;
json_init_string(&parser, "123", &malloc);
json_init_string(&parser, "123");
JSONObject *obj = json_parse(&parser);
TEST_ASSERT(obj->type == J_NUMBER, "Expected number");
TEST_ASSERT(obj->f == 123.0, "Expected number match");
json_init_string(&parser, "[123, 23.123]", &malloc);
json_init_string(&parser, "[123, 23.123]");
JSONObject *array = json_parse(&parser);
TEST_ASSERT(array->type == J_ARRAY, "Expected array");
TEST_ASSERT(array->array_len == 2, "Expected 2 elements");
TEST_ASSERT(vec_size(array->elements) == 2, "Expected 2 elements");
TEST_ASSERT(array->elements[0]->f == 123.0, "Matching element 1");
TEST_ASSERT(array->elements[1]->f == 23.123, "Matching element 1");
json_init_string(&parser, "[\"hello\\nworld\\t.\", 123]", &malloc);
json_init_string(&parser, "[\"hello\\nworld\\t.\", 123]");
array = json_parse(&parser);
TEST_ASSERT(array->type == J_ARRAY, "Expected array");
TEST_ASSERT(array->array_len == 2, "Expected 2 elements");
TEST_ASSERT(vec_size(array->elements) == 2, "Expected 2 elements");
TEST_ASSERT(array->elements[1]->f == 123.0, "Matching element 1");
TEST_ASSERT(array->elements[0]->type == J_STRING, "Matching element 0");
TEST_ASSERT(strcmp(array->elements[0]->str, "hello\nworld\t.") == 0, "Mismatching string");

View File

@@ -1,14 +1,12 @@
#include "lib.h"
#include "json.h"
#define PRINTF(file, string, ...) fprintf(file, string, ##__VA_ARGS__) /* NOLINT */
JSONObject error = { .type = J_ERROR };
JSONObject true_val = { .type = J_BOOL, .b = true };
JSONObject false_val = { .type = J_BOOL, .b = false };
JSONObject zero_val = { .type = J_NUMBER, .f = 0.0 };
JSONObject empty_obj_val = { .type = J_OBJECT, .member_len = 0 };
#define CONSUME(token_) do { if (!consume(parser, token_)) { json_error(parser, "Unexpected character encountered."); return &error; } } while(0)
@@ -111,7 +109,7 @@ static void json_parse_string(JsonParser *parser)
if (c == '\\' && current[0] != '\0') current++;
}
size_t max_size = current - parser->current;
char *str = parser->allocator(max_size + 1);
char *str = malloc_arena(max_size + 1);
char *str_current = str;
while (1)
{
@@ -274,55 +272,37 @@ static inline bool consume(JsonParser *parser, JSONTokenType token)
JSONObject *json_parse_array(JsonParser *parser)
{
CONSUME(T_LBRACKET);
JSONObject *array = json_new_object(J_ARRAY);
if (consume(parser, T_RBRACKET))
{
JSONObject *array = json_new_object(parser->allocator, J_ARRAY);
array->array_len = 0;
return array;
}
size_t capacity = 16;
JSONObject *array = json_new_object(parser->allocator, J_ARRAY);
JSONObject **elements = parser->allocator(sizeof(JSONObject *) * capacity);
size_t index = 0;
while (1)
{
JSONObject *parsed = json_parse(parser);
if (parser->error_message) return &error;
if (index >= capacity)
{
JSONObject **elements_old = elements;
size_t copy_size = capacity * sizeof(JSONObject *);
capacity *= 2;
elements = parser->allocator(sizeof(JSONObject *) * capacity);
memcpy(elements, elements_old, copy_size);
}
elements[index++] = parsed;
vec_add(array->elements, parsed);
if (consume(parser, T_RBRACKET)) break;
CONSUME(T_COMMA);
// Allow trailing comma
if (consume(parser, T_RBRACKET)) break;
}
array->elements = elements;
array->array_len = index;
return array;
}
JSONObject *json_parse_object(JsonParser *parser)
{
CONSUME(T_LBRACE);
JSONObject *obj = json_new_map();
if (consume(parser, T_RBRACE))
{
return &empty_obj_val;
return obj;
}
size_t capacity = 16;
JSONObject *obj = json_new_object(parser->allocator, J_OBJECT);
JSONObject **elements = parser->allocator(sizeof(JSONObject *) * capacity);
const char **keys = parser->allocator(sizeof(JSONObject *) * capacity);
size_t index = 0;
while (1)
{
@@ -334,20 +314,8 @@ JSONObject *json_parse_object(JsonParser *parser)
JSONObject *value = json_parse(parser);
if (parser->error_message) return NULL;
if (index >= capacity)
{
JSONObject **elements_old = elements;
const char **keys_old = keys;
size_t copy_size = capacity * sizeof(void *);
capacity *= 2;
elements = parser->allocator(sizeof(JSONObject *) * capacity);
keys = parser->allocator(sizeof(JSONObject *) * capacity);
memcpy(elements, elements_old, copy_size);
memcpy(keys, keys_old, copy_size);
}
keys[index] = key;
elements[index++] = value;
vec_add(obj->keys, key);
vec_add(obj->members, value);
if (consume(parser, T_RBRACE)) break;
if (!consume(parser, T_COMMA))
@@ -358,19 +326,29 @@ JSONObject *json_parse_object(JsonParser *parser)
// Allow trailing comma
if (consume(parser, T_RBRACE)) break;
}
obj->members = elements;
obj->keys = keys;
obj->member_len = index;
return obj;
}
JSONObject *json_obj_get(JSONObject *obj, const char *key)
void json_map_set(JSONObject *obj, const char *key, JSONObject *value)
{
FOREACH_IDX(i, const char *, a_key, obj->keys)
{
if (str_eq(a_key, key))
{
obj->members[i] = value;
}
}
vec_add(obj->members, value);
vec_add(obj->keys, key);
}
JSONObject *json_map_get(JSONObject *obj, const char *key)
{
assert(obj->type == J_OBJECT);
for (unsigned i = 0; i < obj->member_len; i++)
FOREACH_IDX(i, const char *, a_key, obj->keys)
{
if (strcmp(obj->keys[i], key) == 0) return obj->elements[i];
if (str_eq(a_key, key)) return obj->members[i];
}
return NULL;
}
@@ -396,9 +374,7 @@ JSONObject *json_parse(JsonParser *parser)
return NULL;
case T_STRING:
{
JSONObject *obj = json_new_object(parser->allocator, J_STRING);
obj->type = J_STRING;
obj->str = parser->last_string;
JSONObject *obj = json_new_string(parser->last_string);
json_lexer_advance(parser);
return obj;
}
@@ -410,7 +386,7 @@ JSONObject *json_parse(JsonParser *parser)
json_lexer_advance(parser);
return &zero_val;
}
obj = json_new_object(parser->allocator, J_NUMBER);
obj = json_new_object(J_NUMBER);
obj->type = J_NUMBER;
obj->f = parser->last_number;
json_lexer_advance(parser);
@@ -429,10 +405,9 @@ JSONObject *json_parse(JsonParser *parser)
UNREACHABLE
}
void json_init_string(JsonParser *parser, const char *str, JsonAllocator *allocator)
void json_init_string(JsonParser *parser, const char *str)
{
parser->current = str;
parser->allocator = allocator;
parser->error_message = NULL;
parser->line = 1;
json_lexer_advance(parser);
@@ -444,44 +419,9 @@ bool is_freable(JSONObject *obj)
if (obj == &true_val) return false;
if (obj == &false_val) return false;
if (obj == &zero_val) return false;
if (obj == &empty_obj_val) return false;
return true;
}
void json_free(JsonDeallocator *deallocator, JSONObject **ptr)
{
JSONObject *obj = *ptr;
if (!is_freable(obj)) return;
switch (obj->type)
{
case J_OBJECT:
for (size_t i = 0; i < obj->member_len; i++)
{
json_free(deallocator, &obj->members[i]);
deallocator((char *)obj->keys[i]);
}
deallocator(obj->keys);
deallocator(obj->members);
break;
case J_ARRAY:
for (size_t i = 0; i < obj->array_len; i++)
{
json_free(deallocator, &obj->elements[i]);
}
deallocator(obj->elements);
break;
case J_STRING:
deallocator((char *)obj->str);
break;
default:
break;
}
deallocator(*ptr);
*ptr = NULL;
}
static inline void print_indent(int indent_level, FILE *file)
{
for (int i = 0; i < indent_level; i++)
@@ -510,57 +450,58 @@ static inline void print_json(JSONObject *obj, int indent_level, FILE *file)
break;
case J_ARRAY:
fputs(" [ ", file);
if (obj->array_len == 0)
if (!vec_size(obj->elements))
{
fputs(" ]", file);
break;
}
bool should_print_item_per_line = false;
for (size_t i = 0; i < obj->array_len; i++)
FOREACH(JSONObject *, object, obj->elements)
{
if (obj->elements[i]->type == J_OBJECT)
if (object->type == J_OBJECT)
{
should_print_item_per_line = true;
break;
}
}
if (!should_print_item_per_line && obj->array_len < 5)
if (!should_print_item_per_line && vec_size(obj->elements) < 5)
{
for (size_t i = 0; i < obj->array_len; i++)
FOREACH_IDX(i, JSONObject *, object, obj->elements)
{
if (i != 0) fputs(", ", file);
print_json(obj->elements[i], indent_level, file);
print_json(object, indent_level, file);
}
fputs(" ]", file);
break;
}
fputs("\n", file);
for (size_t i = 0; i < obj->array_len; i++)
{
print_indent(indent_level + 1, file);
print_json(obj->elements[i], indent_level + 1, file);
fputs(",\n", file);
fputs("\n", file);
FOREACH_IDX(i, JSONObject *, object, obj->elements)
{
if (i != 0) fputs(",\n", file);
print_indent(indent_level + 1, file);
print_json(object, indent_level + 1, file);
}
fputs("\n ]", file);
}
fputs(" ]", file);
break;
case J_OBJECT:
{
fputs("{\n", file);
for (size_t i = 0; i < obj->member_len; i++)
FOREACH_IDX(i, JSONObject *, object, obj->members)
{
if (i != 0) fputs(",\n", file);
print_indent(indent_level + 1, file);
PRINTF(file, "\"%s\": ", obj->keys[i]);
print_json(obj->members[i], indent_level + 1, file);
fputs(",\n", file);
print_json(object, indent_level + 1, file);
}
fputs("\n", file);
print_indent(indent_level, file);
fputs("}", file);
break;
}
default:
break;
}

View File

@@ -21,13 +21,11 @@ typedef struct JSONObject_
struct
{
struct JSONObject_ **elements;
size_t array_len;
};
struct
{
struct JSONObject_ **members;
const char **keys;
size_t member_len;
};
};
} JSONObject;
@@ -49,31 +47,40 @@ typedef enum JSONTokenType_
T_EOF
} JSONTokenType;
typedef void *(JsonAllocator)(size_t);
typedef void *(JsonDeallocator)(void *);
typedef struct
{
unsigned line;
const char *current;
JSONTokenType current_token_type;
const char *error_message;
JsonAllocator *allocator;
const char *last_string;
double last_number;
} JsonParser;
void json_init_string(JsonParser *parser, const char *str, JsonAllocator *allocator);
void json_init_string(JsonParser *parser, const char *str);
JSONObject *json_parse(JsonParser *parser);
JSONObject *json_obj_get(JSONObject *obj, const char *key);
INLINE JSONObject *json_new_object(JsonAllocator *allocator, JSONType type);
void json_free(JsonDeallocator *deallocator, JSONObject **ptr);
JSONObject *json_map_get(JSONObject *obj, const char *key);
void json_map_set(JSONObject *obj, const char *key, JSONObject *value);
INLINE JSONObject *json_new_object(JSONType type);
void print_json_to_file(JSONObject *obj, FILE *file);
INLINE JSONObject *json_new_object(JsonAllocator *allocator, JSONType type)
INLINE JSONObject *json_new_string(const char *str)
{
JSONObject *obj = allocator(sizeof(JSONObject));
obj->type = type;
JSONObject *obj = malloc_arena(sizeof(JSONObject));
*obj = (JSONObject) { .type = J_STRING, .str = str };
return obj;
}
INLINE JSONObject *json_new_map(void)
{
JSONObject *obj = malloc_arena(sizeof(JSONObject));
*obj = (JSONObject) { .type = J_OBJECT };
return obj;
}
INLINE JSONObject *json_new_object(JSONType type)
{
JSONObject *obj = malloc_arena(sizeof(JSONObject));
*obj = (JSONObject) { .type = type };
return obj;
}