From 800ad9e898fc93f828e79955f5ca749eddff010a Mon Sep 17 00:00:00 2001 From: kostyavechkanov Date: Tue, 13 Aug 2024 21:34:53 +1000 Subject: [PATCH] Feature/add-target (#1) (#1350) Feature/add target (#1) project add-target command --- src/build/build.h | 87 +++++++++++++------- src/build/build_internal.h | 9 +- src/build/build_options.c | 30 +++++-- src/build/common_build.c | 11 +++ src/build/project.c | 16 ---- src/build/project_manipulation.c | 92 +++++++++++++++------ src/main.c | 21 ++--- src/utils/json.c | 136 +++++++++++++++++++++++++++---- src/utils/json.h | 9 +- 9 files changed, 295 insertions(+), 116 deletions(-) diff --git a/src/build/build.h b/src/build/build.h index 5c6629825..4a605dd37 100644 --- a/src/build/build.h +++ b/src/build/build.h @@ -3,8 +3,8 @@ // Use of this source code is governed by the GNU LGPLv3.0 license // a copy of which can be found in the LICENSE file. -#include "../version.h" #include "../utils/lib.h" +#include "../version.h" #include #define MAX_BUILD_LIB_DIRS 1024 @@ -48,16 +48,16 @@ typedef enum COMMAND_PROJECT, } CompilerCommand; - typedef enum { SUBCOMMAND_MISSING = 0, - SUBCOMMAND_VIEW + SUBCOMMAND_VIEW, + SUBCOMMAND_ADD } ProjectSubcommand; typedef enum { - DIAG_NONE = 0, // Don't use! + DIAG_NONE = 0, // Don't use! DIAG_WARNING_TYPE, // Don't use! DIAG_UNUSED, DIAG_UNUSED_PARAMETER, @@ -133,10 +133,10 @@ typedef enum typedef enum { OPTIMIZATION_NOT_SET = -1, - OPTIMIZATION_NONE = 0, // -O0 - OPTIMIZATION_LESS = 1, // -O1 - OPTIMIZATION_MORE = 2, // -O2 - OPTIMIZATION_AGGRESSIVE = 3, // -O3 + OPTIMIZATION_NONE = 0, // -O0 + OPTIMIZATION_LESS = 1, // -O1 + OPTIMIZATION_MORE = 2, // -O2 + OPTIMIZATION_AGGRESSIVE = 3, // -O3 } OptimizationLevel; typedef enum @@ -149,7 +149,7 @@ typedef enum typedef enum { SINGLE_MODULE_NOT_SET = -1, - SINGLE_MODULE_OFF = 0, // NOLINT + SINGLE_MODULE_OFF = 0, // NOLINT SINGLE_MODULE_ON = 1 } SingleModule; @@ -211,9 +211,9 @@ typedef enum typedef enum { SIZE_OPTIMIZATION_NOT_SET = -1, - SIZE_OPTIMIZATION_NONE = 0, // None - SIZE_OPTIMIZATION_SMALL = 1, // -Os - SIZE_OPTIMIZATION_TINY = 2, // -Oz + SIZE_OPTIMIZATION_NONE = 0, // None + SIZE_OPTIMIZATION_SMALL = 1, // -Os + SIZE_OPTIMIZATION_TINY = 2, // -Oz } SizeOptimizationLevel; typedef enum @@ -233,7 +233,7 @@ typedef enum typedef enum { STRUCT_RETURN_DEFAULT = -1, - STRUCT_RETURN_STACK = 0, // NOLINT + STRUCT_RETURN_STACK = 0, // NOLINT STRUCT_RETURN_REG = 1 } StructReturn; @@ -351,6 +351,34 @@ typedef enum #define ANY_WINDOWS_ARCH_OS WINDOWS_AARCH64: case WINDOWS_X64: case MINGW_X64 +typedef enum +{ + TARGET_TYPE_EXECUTABLE, + TARGET_TYPE_STATIC_LIB, + TARGET_TYPE_DYNAMIC_LIB, + TARGET_TYPE_OBJECT_FILES, + TARGET_TYPE_BENCHMARK, + TARGET_TYPE_TEST, +} TargetType; + +static const char *targets[6] = { + [TARGET_TYPE_EXECUTABLE] = "executable", + [TARGET_TYPE_STATIC_LIB] = "static-lib", + [TARGET_TYPE_DYNAMIC_LIB] = "dynamic-lib", + [TARGET_TYPE_BENCHMARK] = "benchmark", + [TARGET_TYPE_TEST] = "test", + [TARGET_TYPE_OBJECT_FILES] = "object-files" +}; +static const char *target_desc[6] = { + [TARGET_TYPE_EXECUTABLE] = "Executable", + [TARGET_TYPE_STATIC_LIB] = "Static library", + [TARGET_TYPE_DYNAMIC_LIB] = "Dynamic library", + [TARGET_TYPE_BENCHMARK] = "benchmark suite", + [TARGET_TYPE_TEST] = "test suite", + [TARGET_TYPE_OBJECT_FILES] = "object files" +}; + + typedef struct BuildOptions_ { const char *lib_dir[MAX_BUILD_LIB_DIRS]; @@ -365,17 +393,20 @@ typedef struct BuildOptions_ size_t linker_lib_count; const char* std_lib_dir; VectorConv vector_conv; - struct { + struct + { const char *sdk; const char *def; WinCrtLinking crt_linking; } win; - struct { + struct + { const char *sysroot; const char *min_version; const char *sdk_version; } macos; - struct { + struct + { const char *crt; const char *crtbegin; } linuxpaths; @@ -397,7 +428,12 @@ typedef struct BuildOptions_ bool silence_deprecation; CompilerBackend backend; CompilerCommand command; - ProjectSubcommand subcommand; + struct + { + ProjectSubcommand command; + const char *target_name; + TargetType target_type; + } project_options; CompileOption compile_option; TrustLevel trust_level; DiagnosticsSeverity severity[DIAG_END_SENTINEL]; @@ -459,16 +495,6 @@ typedef struct BuildOptions_ bool testing; } BuildOptions; -typedef enum -{ - TARGET_TYPE_EXECUTABLE, - TARGET_TYPE_STATIC_LIB, - TARGET_TYPE_DYNAMIC_LIB, - TARGET_TYPE_OBJECT_FILES, - TARGET_TYPE_BENCHMARK, - TARGET_TYPE_TEST, -} TargetType; - typedef struct { struct Library__ *parent; @@ -500,7 +526,6 @@ typedef struct Library__ LibraryTarget **targets; } Library; - typedef struct { TargetType type; @@ -681,8 +706,10 @@ BuildOptions parse_arguments(int argc, const char *argv[]); ArchOsTarget arch_os_target_from_string(const char *target); bool command_accepts_files(CompilerCommand command); bool command_passes_args(CompilerCommand command); -void update_build_target_with_opt_level(BuildTarget *target, OptimizationSetting level); +void update_build_target_with_opt_level(BuildTarget *target, + OptimizationSetting level); void create_project(BuildOptions *build_options); void create_library(BuildOptions *build_options); void resolve_libraries(BuildTarget *build_target); -void view_project(BuildOptions *build_options); \ No newline at end of file +void view_project(BuildOptions *build_options); +void add_target_project(BuildOptions *build_options); diff --git a/src/build/build_internal.h b/src/build/build_internal.h index 1c45f8c6a..959a5e2c4 100644 --- a/src/build/build_internal.h +++ b/src/build/build_internal.h @@ -104,7 +104,7 @@ BuildTarget *project_select_target(Project *project, const char *optional_target void update_feature_flags(const char ***flags, const char ***removed_flag, const char *arg, bool add); const char *get_string(const char *file, const char *category, JSONObject *table, const char *key, - const char *default_value); + const char *default_value); int get_valid_bool(const char *file, const char *target, JSONObject *json, const char *key, int default_val); const char *get_optional_string(const char *file, const char *category, JSONObject *table, const char *key); const char *get_mandatory_string(const char *file, const char *category, JSONObject *object, const char *key); @@ -112,7 +112,8 @@ const char **get_string_array(const char *file, const char *category, JSONObject const char **get_optional_string_array(const char *file, const char *target, JSONObject *table, const char *key); const char *get_cflags(const char *file, const char *target, JSONObject *json, const char *original_flags); void get_list_append_strings(const char *file, const char *target, JSONObject *json, const char ***list_ptr, - const char *base, const char *override, const char *add); -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); -void check_json_keys(const char* valid_keys[][2], size_t key_count, const char* deprecated_keys[], size_t deprecated_key_count, JSONObject *json, const char *target_name, const char *option); + const char *base, const char *override, const char *add); +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); +int get_valid_enum_from_string(const char *str, const char *target, const char **values, int count, const char *expected); +void check_json_keys(const char *valid_keys[][2], size_t key_count, const char *deprecated_keys[], size_t deprecated_key_count, JSONObject *json, const char *target_name, const char *option); long get_valid_integer(JSONObject *table, const char *key, const char *category, bool mandatory); diff --git a/src/build/build_options.c b/src/build/build_options.c index 2e34ebc2d..1774f1013 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -2,8 +2,9 @@ // Use of this source code is governed by the GNU LGPLv3.0 license // a copy of which can be found in the LICENSE file. -#include "build_internal.h" #include "../utils/whereami.h" +#include "build.h" +#include "build_internal.h" extern int llvm_version_major; bool silence_deprecation; @@ -216,7 +217,6 @@ static inline const char *next_arg() return current_arg; } - static inline bool next_is_opt() { return args[arg_index + 1][0] == '-'; @@ -283,26 +283,43 @@ static void project_usage() PRINTF("Usage: %s [] project []", args[0]); PRINTF(""); PRINTF("Project Subcommands:"); - PRINTF(" view view the current projects structure"); + PRINTF(" view view the current projects structure"); + PRINTF(" add-target add a new target to the project"); } static void parse_project_subcommand(BuildOptions *options) { if (arg_match("view")) { - options->subcommand = SUBCOMMAND_VIEW; + options->project_options.command = SUBCOMMAND_VIEW; return; } - PROJECT_FAIL_WITH_ERR("Cannot process the unknown subcommand \"%s\".", current_arg); + if (arg_match("add-target")) + { + options->project_options.command = SUBCOMMAND_ADD; + + if (at_end() || next_is_opt()) error_exit("Expected a target name"); + options->project_options.target_name = next_arg(); + + if (at_end() || next_is_opt()) error_exit("Expected a target type like 'executable' or 'static-lib'"); + options->project_options.target_type = (TargetType)get_valid_enum_from_string(next_arg(), "type", targets, ELEMENTLEN(targets), "a target type like 'executable' or 'static-lib'"); + + return; + } + + PROJECT_FAIL_WITH_ERR("Cannot process the unknown subcommand \"%s\".", + current_arg); } static void parse_project_options(BuildOptions *options) { + options->project_options.command = SUBCOMMAND_MISSING; if (at_end()) { project_usage(); return; } + next_arg(); parse_project_subcommand(options); } @@ -437,7 +454,6 @@ static void parse_command(BuildOptions *options) if (arg_match("project")) { options->command = COMMAND_PROJECT; - options->subcommand = SUBCOMMAND_MISSING; parse_project_options(options); return; } @@ -1185,7 +1201,6 @@ static void parse_option(BuildOptions *options) FAIL_WITH_ERR("Cannot process the unknown option \"%s\".", current_arg); } - BuildOptions parse_arguments(int argc, const char *argv[]) { arg_count = argc; @@ -1284,7 +1299,6 @@ BuildOptions parse_arguments(int argc, const char *argv[]) return build_options; } - ArchOsTarget arch_os_target_from_string(const char *target) { for (unsigned i = 1; i <= ARCH_OS_TARGET_LAST; i++) diff --git a/src/build/common_build.c b/src/build/common_build.c index 8188fcdb8..a263ff150 100644 --- a/src/build/common_build.c +++ b/src/build/common_build.c @@ -180,6 +180,17 @@ int get_valid_string_setting(const char *file, const char *target, JSONObject *j error_exit("In file '%s': Invalid value for '%s', expected %s", file, key, expected); } +int get_valid_enum_from_string(const char *str, const char *target, const char **values, int count, const char *expected) +{ + int res = str_findlist(str, count, values); + if (res >= 0) return res; + if (target) + { + error_exit("'%s' had an invalid value, expected %s", target, expected); + } + error_exit("Invalid value, expected %s", expected); +} + long get_valid_integer(JSONObject *table, const char *key, const char *category, bool mandatory) { JSONObject *value = json_obj_get(table, key); diff --git a/src/build/project.c b/src/build/project.c index fd7442336..c186a9502 100644 --- a/src/build/project.c +++ b/src/build/project.c @@ -423,22 +423,6 @@ static void project_add_target(Project *project, BuildTarget *default_target, J static void project_add_targets(Project *project, JSONObject *project_data) { assert(project_data->type == J_OBJECT); - static const char* targets[6] = { - [TARGET_TYPE_EXECUTABLE] = "executable", - [TARGET_TYPE_STATIC_LIB] = "static-lib", - [TARGET_TYPE_DYNAMIC_LIB] = "dynamic-lib", - [TARGET_TYPE_BENCHMARK] = "benchmark", - [TARGET_TYPE_TEST] = "test", - [TARGET_TYPE_OBJECT_FILES] = "object-files" - }; - static const char *target_desc[6] = { - [TARGET_TYPE_EXECUTABLE] = "Executable", - [TARGET_TYPE_STATIC_LIB] = "Static library", - [TARGET_TYPE_DYNAMIC_LIB] = "Dynamic library", - [TARGET_TYPE_BENCHMARK] = "benchmark suite", - [TARGET_TYPE_TEST] = "test suite", - [TARGET_TYPE_OBJECT_FILES] = "object files" - }; BuildTarget default_target = default_build_target; load_into_build_target(project_data, NULL, &default_target); diff --git a/src/build/project_manipulation.c b/src/build/project_manipulation.c index 8f3a7789d..ea7ea2e32 100644 --- a/src/build/project_manipulation.c +++ b/src/build/project_manipulation.c @@ -1,7 +1,7 @@ #include "build_internal.h" #define PRINTFN(string, ...) fprintf(stdout, string "\n", ##__VA_ARGS__) // NOLINT #define PRINTF(string, ...) fprintf(stdout, string, ##__VA_ARGS__) // NOLINT -static JSONObject* read_project() +static JSONObject *read_project() { size_t size; char *read = file_read_all(PROJECT_JSON, &size); @@ -19,52 +19,52 @@ static JSONObject* read_project() return json; } -static void print_vec(const char* header, const char** vec, bool opt) +static void print_vec(const char *header, const char **vec, bool opt) { if (opt && !vec) return; PRINTF("%s: ", header); - if (!vec_size(vec)) + if (!vec_size(vec)) { PRINTFN("*none*"); return; - } - FOREACH_IDX(i, const char *, item, vec) + } + FOREACH_IDX(i, const char *, item, vec) { if (i > 0) PRINTF(", "); - PRINTF("%s",item); + PRINTF("%s", item); } PRINTFN(""); } -static void print_opt_str(const char* header, const char* str) +static void print_opt_str(const char *header, const char *str) { if (!str) return; PRINTFN("%s: %s", header, str); } -static void print_opt_setting(const char* header, int setting, const char** values) +static void print_opt_setting(const char *header, int setting, const char **values) { if (setting < 0) return; PRINTFN("%s: %s", header, values[setting]); } -static void print_opt_bool(const char* header, int b) +static void print_opt_bool(const char *header, int b) { if (b == -1) return; PRINTF("%s: ", header); PRINTFN(b ? "true" : "false"); } -static void print_opt_int(const char* header, long v) +static void print_opt_int(const char *header, long v) { if (v < 0) return; PRINTFN("%s: %ld", header, v); } -static const char* generate_expected(const char** options, size_t n) +static const char *generate_expected(const char **options, size_t n) { scratch_buffer_clear(); - for (size_t i = 0; i < n; i++) + for (size_t i = 0; i < n; i++) { if (i > 0) scratch_buffer_append(", "); if (i == n - 1) scratch_buffer_append(" or "); @@ -74,7 +74,7 @@ static const char* generate_expected(const char** options, size_t n) } -const char* optimization_levels[] = { +const char *optimization_levels[] = { [OPT_SETTING_O0] = "O0", [OPT_SETTING_O1] = "O1", [OPT_SETTING_O2] = "O2", @@ -82,10 +82,10 @@ const char* optimization_levels[] = { [OPT_SETTING_O4] = "O4", [OPT_SETTING_O5] = "O5", [OPT_SETTING_OSMALL] = "Os", - [OPT_SETTING_OTINY] = "Oz" + [OPT_SETTING_OTINY] = "Oz" }; -const char* debug_levels[] = { +const char *debug_levels[] = { [DEBUG_INFO_NONE] = "none", [DEBUG_INFO_LINE_TABLES] = "line-tables", [DEBUG_INFO_FULL] = "full" @@ -171,12 +171,12 @@ do {\ print_opt_int("\t" header, v);\ } while(0); -static void view_target(const char* name, JSONObject* target) +static void view_target(const char *name, JSONObject *target) { /* General target information */ - PRINTFN("- %s",name); + PRINTFN("- %s", name); print_opt_str("\tName", name); - TARGET_VIEW_MANDATORY_STRING("Type","type"); + TARGET_VIEW_MANDATORY_STRING("Type", "type"); TARGET_VIEW_STRING("Target language target", "langrev"); TARGET_VIEW_STRING_ARRAY("Warnings used", "warnings"); TARGET_VIEW_STRING_ARRAY("Additional c3l library search paths", "dependency-search-paths"); @@ -204,7 +204,7 @@ static void view_target(const char* name, JSONObject* target) TARGET_VIEW_SETTING("Floating point behaviour", "fp-math", fp_math); TARGET_VIEW_STRING_ARRAY("Additional linked libraries", "linked-libraries"); TARGET_VIEW_STRING_ARRAY("Linked libraries (override)", "linked-libraries-override"); - TARGET_VIEW_STRING("Linker","linker"); + TARGET_VIEW_STRING("Linker", "linker"); TARGET_VIEW_STRING_ARRAY("Additional linker search paths", "linker-search-paths"); TARGET_VIEW_STRING_ARRAY("Linker search paths (override)", "linker-search-paths-override"); TARGET_VIEW_STRING_ARRAY("Additional linker arguments", "link-args"); @@ -236,13 +236,52 @@ static void view_target(const char* name, JSONObject* target) TARGET_VIEW_BOOL("Return structs on the stack", "x86-stack-struct-return"); } -void view_project(BuildOptions *build_options) +void add_target_project(BuildOptions *build_options) { - JSONObject* project_json = read_project(); + JSONObject *project_json = read_project(); + JSONObject *targets_json = json_obj_get(project_json, "targets"); + + for (unsigned i = 0; i < targets_json->member_len; i++) + { + 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); + } + } + + JSONObject *target_type_obj = json_new_object(&malloc_arena, J_STRING); + target_type_obj->str = targets[build_options->project_options.target_type]; + + + 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++; + + FILE *file = fopen(PROJECT_JSON, "w"); + print_json_to_file(project_json, file); + fclose(file); +} + +void view_project(BuildOptions *build_options) +{ + JSONObject *project_json = read_project(); /* General information */ - VIEW_MANDATORY_STRING_ARRAY("Authors","authors"); - VIEW_MANDATORY_STRING("Version","version"); + VIEW_MANDATORY_STRING_ARRAY("Authors", "authors"); + VIEW_MANDATORY_STRING("Version", "version"); VIEW_MANDATORY_STRING("Project language target", "langrev"); VIEW_MANDATORY_STRING_ARRAY("Warnings used", "warnings"); VIEW_MANDATORY_STRING_ARRAY("c3l library search paths", "dependency-search-paths"); @@ -262,7 +301,7 @@ void view_project(BuildOptions *build_options) VIEW_STRING_ARRAY("Enabled features", "features"); VIEW_SETTING("Floating point behaviour", "fp-math", fp_math); VIEW_STRING_ARRAY("Linked libraries", "linked-libraries"); - VIEW_STRING("Linker","linker"); + VIEW_STRING("Linker", "linker"); VIEW_STRING_ARRAY("Linker search paths", "linker-search-paths"); VIEW_STRING_ARRAY("Linker arguments", "link-args"); VIEW_BOOL("Link libc", "link-libc"); @@ -303,14 +342,15 @@ 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++) + for (unsigned i = 0; i < targets_json->member_len; i++) { 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_target(key,object); + view_target(key, object); } } diff --git a/src/main.c b/src/main.c index 750177b55..e9cb06d21 100644 --- a/src/main.c +++ b/src/main.c @@ -1,8 +1,9 @@ -#include -#include "compiler/compiler.h" #include "build/build.h" +#include "compiler/compiler.h" #include "compiler_tests/tests.h" #include "utils/lib.h" +#include + bool debug_log = false; bool debug_stats = false; @@ -99,13 +100,16 @@ int main_real(int argc, const char *argv[]) compile_file_list(&build_options); break; case COMMAND_PROJECT: - switch (build_options.subcommand) + switch (build_options.project_options.command) { case SUBCOMMAND_VIEW: view_project(&build_options); break; + case SUBCOMMAND_ADD: + add_target_project(&build_options); + break; case SUBCOMMAND_MISSING: - UNREACHABLE + break; } break; case COMMAND_MISSING: @@ -121,7 +125,7 @@ int main_real(int argc, const char *argv[]) int wmain(int argc, const uint16_t *argv[]) { - char** args = malloc(sizeof(void*) * (unsigned)argc); + char **args = malloc(sizeof(void *) * (unsigned)argc); for (unsigned i = 0; i < (unsigned)argc; i++) { args[i] = win_utf16to8(argv[i]); @@ -131,9 +135,6 @@ int wmain(int argc, const uint16_t *argv[]) #else -int main(int argc, const char *argv[]) -{ - return main_real(argc, argv); -} +int main(int argc, const char *argv[]) { return main_real(argc, argv); } -#endif \ No newline at end of file +#endif diff --git a/src/utils/json.c b/src/utils/json.c index cf6f0f4ee..8b08a94d2 100644 --- a/src/utils/json.c +++ b/src/utils/json.c @@ -2,6 +2,8 @@ #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 }; @@ -16,7 +18,7 @@ static inline void json_skip_whitespace(JsonParser *parser) char c; while (1) { - RETRY: + RETRY: switch (parser->current[0]) { case '/': @@ -24,7 +26,7 @@ static inline void json_skip_whitespace(JsonParser *parser) if (c == '/') { parser->current++; - while ((c = (++parser->current)[0]) && c != '\n') {} + while ((c = (++parser->current)[0]) && c != '\n') { } goto RETRY; } if (c == '*') @@ -259,6 +261,7 @@ static inline void json_lexer_advance(JsonParser *parser) } UNREACHABLE } + static inline bool consume(JsonParser *parser, JSONTokenType token) { if (parser->current_token_type == token) @@ -272,27 +275,27 @@ static inline bool consume(JsonParser *parser, JSONTokenType token) JSONObject *json_parse_array(JsonParser *parser) { CONSUME(T_LBRACKET); - if (consume(parser, T_RBRACKET)) - { - return &empty_array_val; - } + if (consume(parser, T_RBRACKET)) return &empty_array_val; + size_t capacity = 16; JSONObject *array = json_new_object(parser->allocator, J_ARRAY); - JSONObject** elements = parser->allocator(sizeof(JSONObject*) * capacity); + 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*); + size_t copy_size = capacity * sizeof(JSONObject *); capacity *= 2; - elements = parser->allocator(sizeof(JSONObject*) * capacity); + elements = parser->allocator(sizeof(JSONObject *) * capacity); memcpy(elements, elements_old, copy_size); } elements[index++] = parsed; + if (consume(parser, T_RBRACKET)) break; CONSUME(T_COMMA); // Allow trailing comma @@ -306,35 +309,42 @@ JSONObject *json_parse_array(JsonParser *parser) JSONObject *json_parse_object(JsonParser *parser) { CONSUME(T_LBRACE); + if (consume(parser, T_RBRACE)) { return &empty_obj_val; } + 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); + JSONObject **elements = parser->allocator(sizeof(JSONObject *) * capacity); + const char **keys = parser->allocator(sizeof(JSONObject *) * capacity); size_t index = 0; while (1) { const char *key = parser->last_string; + CONSUME(T_STRING); CONSUME(T_COLON); + 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*); + size_t copy_size = capacity * sizeof(void *); capacity *= 2; - elements = parser->allocator(sizeof(JSONObject*) * capacity); - keys = parser->allocator(sizeof(JSONObject*) * capacity); + 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; + if (consume(parser, T_RBRACE)) break; if (!consume(parser, T_COMMA)) { @@ -391,7 +401,7 @@ JSONObject *json_parse(JsonParser *parser) case T_NUMBER: { JSONObject *obj = NULL; - if (parser->last_number == 0) + if (parser->last_number == 0) { json_lexer_advance(parser); return &zero_val; @@ -441,13 +451,13 @@ void json_free(JsonDeallocator *deallocator, JSONObject **ptr) if (!is_freable(obj)) return; - switch(obj->type) + 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((char *)obj->keys[i]); } deallocator(obj->keys); deallocator(obj->members); @@ -460,7 +470,7 @@ void json_free(JsonDeallocator *deallocator, JSONObject **ptr) deallocator(obj->elements); break; case J_STRING: - deallocator((char*)obj->str); + deallocator((char *)obj->str); break; default: break; @@ -468,3 +478,93 @@ void json_free(JsonDeallocator *deallocator, JSONObject **ptr) deallocator(*ptr); *ptr = NULL; } + +static inline void print_indent(int indent_level, FILE *file) +{ + for (int i = 0; i < indent_level; i++) + { + fputs(" ", file); + } +} + +static inline void print_json(JSONObject *obj, int indent_level, FILE *file) +{ + if (obj == NULL) + { + return; + } + + switch (obj->type) + { + case J_BOOL: + PRINTF(file, "%s", obj->b ? "true" : "false"); + break; + case J_STRING: + PRINTF(file, "\"%s\"", obj->str); + break; + case J_NUMBER: + PRINTF(file, "%f", obj->f); + break; + case J_ARRAY: + fputs(" [ ", file); + + if (obj->array_len == 0) + { + fputs(" ]", file); + break; + } + + bool should_print_item_per_line = false; + + for (size_t i = 0; i < obj->array_len; i++) + { + if (obj->elements[i]->type == J_OBJECT) + { + should_print_item_per_line = true; + break; + } + } + + if (!should_print_item_per_line && obj->array_len < 5) + { + for (size_t i = 0; i < obj->array_len; i++) + { + if (i != 0) fputs(", ", file); + print_json(obj->elements[i], 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(" ]", file); + break; + case J_OBJECT: + fputs("{\n", file); + for (size_t i = 0; i < obj->member_len; i++) + { + 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_indent(indent_level, file); + fputs("}", file); + break; + default: + break; + } +} + +void print_json_to_file(JSONObject *obj, FILE *file) +{ + print_json(obj, 0, file); +} + diff --git a/src/utils/json.h b/src/utils/json.h index 128577252..b446f863b 100644 --- a/src/utils/json.h +++ b/src/utils/json.h @@ -6,7 +6,7 @@ typedef enum J_ARRAY, J_NUMBER, J_BOOL, - J_ERROR, + J_ERROR } JSONType; @@ -46,11 +46,11 @@ typedef enum JSONTokenType_ T_TRUE, T_FALSE, T_NULL, - T_EOF, + T_EOF } JSONTokenType; -typedef void*(JsonAllocator)(size_t); -typedef void*(JsonDeallocator)(void*); +typedef void *(JsonAllocator)(size_t); +typedef void *(JsonDeallocator)(void *); typedef struct { @@ -68,6 +68,7 @@ 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); +void print_json_to_file(JSONObject *obj, FILE *file); INLINE JSONObject *json_new_object(JsonAllocator *allocator, JSONType type)