From 20ef894f8825f4469c7248127e35d65d0bcd09bc Mon Sep 17 00:00:00 2001 From: Ur Mom Date: Tue, 14 May 2024 21:55:42 +0200 Subject: [PATCH 01/21] Initial commit --- src/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main.c b/src/main.c index 03ab6b9..70fc423 100644 --- a/src/main.c +++ b/src/main.c @@ -13,6 +13,14 @@ extern FILE *yyin; */ void notify_exit(void) { DEBUG("Exiting gemstone..."); } +size_t check_option(const char* name) { +if (0 != name){ + return 1; +} else { + return 0; +}; +}; + /** * @brief Closes File after compiling. * From 027b54d0872eeb69a039fecef4e75a9f0123923a Mon Sep 17 00:00:00 2001 From: Ur Mom Date: Tue, 14 May 2024 22:32:44 +0200 Subject: [PATCH 02/21] Second commit --- src/main.c | 50 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/src/main.c b/src/main.c index 70fc423..c9eda55 100644 --- a/src/main.c +++ b/src/main.c @@ -7,19 +7,44 @@ extern FILE *yyin; +// Global array to store options +char options[5][10]; +int num_options = 0; + /** * @brief Log a debug message to inform about beginning exit procedures * */ void notify_exit(void) { DEBUG("Exiting gemstone..."); } + +/** + * @brief add option to global option array + * + */ + +void add_option(const char* option) { + if (num_options < 5 ) { + strcpy(options[num_options], option); + num_options++; + } else { + PANIC("Too Many Options given"); + } +} + +/** + * @brief Check if Option is set + * + */ + size_t check_option(const char* name) { -if (0 != name){ - return 1; -} else { - return 0; -}; -}; + for (int i = 0; i < num_options; i++) { + if (strcmp(options[i], name) == 0) { + return 1; + } + } + return 0; +} /** * @brief Closes File after compiling. @@ -38,7 +63,6 @@ void close_file(void) { */ void setup(void) { // setup preample - log_init(); DEBUG("starting gemstone..."); @@ -54,6 +78,18 @@ void setup(void) { int main(int argc, char *argv[]) { + // Iteration through arguments + for (int i = 1; i < argc; i++) { + // Check if the argument starts with "--" + if (argv[i][0] == '-' && argv[i][1] == '-') { + // Extract option name + char option[10]; + strcpy(option, argv[i] + 2); + + // Add option to the global array + add_option(option); + } + } setup(); atexit(close_file); From 347a2f0088c2e9b29b402d973aff9259f97eb961 Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 12:05:22 +0200 Subject: [PATCH 03/21] added git submodule tomlc99 --- .gitmodules | 3 +++ dep/tomlc99 | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 dep/tomlc99 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..be66b92 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "dep/tomlc99"] + path = dep/tomlc99 + url = https://github.com/cktan/tomlc99.git diff --git a/dep/tomlc99 b/dep/tomlc99 new file mode 160000 index 0000000..5221b3d --- /dev/null +++ b/dep/tomlc99 @@ -0,0 +1 @@ +Subproject commit 5221b3d3d66c25a1dc6f0372b4f824f1202fe398 From e4c36f341b7a6f6110f79ce80c82a697f8e305fa Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 12:13:55 +0200 Subject: [PATCH 04/21] added target for tomlc99 --- CMakeLists.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index be613ed..09a66d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,22 @@ add_custom_command(OUTPUT ${YACC_GENERATED_SOURCE_FILE} COMMENT "generate C source file for parser" VERBATIM) +# ------------------------------------------------ # +# TOML-C99 # +# ------------------------------------------------ # + +add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c + COMMAND git + ARGS submodule update --init --recursive --checkout + COMMENT "checkout dependency TOML-C99" + VERBATIM) + +add_library(tomlc99 ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c) +set_target_properties(tomlc99 + PROPERTIES + OUTPUT_NAME "toml" + ARCHIVE_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/dep) + # ------------------------------------------------ # # Source # # ------------------------------------------------ # @@ -93,6 +109,8 @@ set_target_properties(release OUTPUT_NAME "gsc" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/release) +target_link_libraries(release tomlc99) + # FIXME: cannot compile with /O2 because of /RTC1 flag if (MSVC) set(RELEASE_FLAGS) @@ -125,6 +143,8 @@ set_target_properties(debug OUTPUT_NAME "gsc" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/debug) +target_link_libraries(debug tomlc99) + if (MSVC) set(DEBUG_FLAGS /DEBUG) else() @@ -154,6 +174,8 @@ set_target_properties(check OUTPUT_NAME "gsc" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/check) +target_link_libraries(check tomlc99) + if (MSVC) set(CHECK_FLAGS /DEBUG /WX) else() From e69af85e3770856910bcf7550f1212d3712366b6 Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 16:15:59 +0200 Subject: [PATCH 05/21] added options --- CMakeLists.txt | 2 + src/cfg/opt.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++ src/cfg/opt.h | 34 ++++++++++++++ src/main.c | 44 ------------------ 4 files changed, 154 insertions(+), 44 deletions(-) create mode 100644 src/cfg/opt.c create mode 100644 src/cfg/opt.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 09a66d9..4aa5707 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,8 @@ set_target_properties(tomlc99 OUTPUT_NAME "toml" ARCHIVE_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/dep) +include_directories(${PROJECT_SOURCE_DIR}/dep/tomlc99) + # ------------------------------------------------ # # Source # # ------------------------------------------------ # diff --git a/src/cfg/opt.c b/src/cfg/opt.c new file mode 100644 index 0000000..bb74e51 --- /dev/null +++ b/src/cfg/opt.c @@ -0,0 +1,118 @@ +// +// Created by servostar on 5/31/24. +// + +#include +#include +#include + +TargetConfig default_target_config() { + TargetConfig config; + + config.name = "debug"; + config.print_ast = false; + config.print_asm = false; + config.print_ir = false; + + return config; +} + +TargetConfig default_target_config_from_args(int argc, char *argv[]) { + TargetConfig config = default_target_config(); + + for (int i = 0; i < argc; i++) { + char *option = argv[i]; + + if (strcmp(option, "--print-ast") == 0) { + config.print_ast = true; + } else if (strcmp(option, "--print-asm") == 0) { + config.print_asm = true; + } else if (strcmp(option, "--print-ir") == 0) { + config.print_ir = true; + } + } + + return config; +} + +void print_help(void) { + const char *lines[] = { + "Gemstone Compiler (C) GPL-2.0\n", + "Compile file(s): gsc [files]", + "Build project (build.toml): gsc [directory] [target]|all", + "Options:", + " --print-ast print resulting abstract syntax tree to a file", + " --print-asm print resulting assembly language to a file", + " --print-ir print resulting LLVM-IR to a file" + }; + + for (unsigned int i = 0; i < sizeof(lines) / sizeof(const char *); i++) { + printf("%s\n", lines[i]); + } +} + +#define PROJECT_OK 0 +#define PROJECT_TOML_ERR 1 +#define PROJECT_SEMANTIC_ERR 2 + +static int parse_project_table(ProjectConfig *config, toml_table_t *project_table) { + // project name + toml_datum_t name = toml_string_in(project_table, "name"); + if (!name.ok) { + printf("Invalid project configuration: project must have a name\n\n"); + return PROJECT_SEMANTIC_ERR; + } + + config->name = name.u.s; + + // project version + toml_datum_t version = toml_string_in(project_table, "version"); + if (version.ok) { + config->name = version.u.s; + } + + // author names + toml_array_t *authors = toml_array_in(project_table, "authors"); + if (authors) { + unsigned int len = 0; + config->authors = malloc(sizeof(const char *) * 2); + + for (int i = 0;; i++) { + toml_datum_t author = toml_string_at(authors, i); + if (!author.ok) + break; + + char** new_authors = realloc(config->authors, sizeof(const char *) * ++len); + config->authors = new_authors; + config->authors[len] = author.u.s; + } + } +} + +int load_project_config(ProjectConfig *config) { + FILE *config_file = fopen("build.toml", "r"); + if (config_file == NULL) { + return PROJECT_TOML_ERR; + } + + char err_buf[200]; + + toml_table_t *conf = toml_parse_file(config_file, err_buf, sizeof(err_buf)); + fclose(config_file); + + if (!conf) { + printf("Invalid project configuration: %s\n\n", err_buf); + return PROJECT_SEMANTIC_ERR; + } + + toml_table_t *project = toml_table_in(conf, "project"); + if (project) { + parse_project_table(config, project); + + return PROJECT_OK; + } + printf("Invalid project configuration: missing project table\n\n"); + + toml_free(conf); + return PROJECT_SEMANTIC_ERR; +} diff --git a/src/cfg/opt.h b/src/cfg/opt.h new file mode 100644 index 0000000..cf89d4f --- /dev/null +++ b/src/cfg/opt.h @@ -0,0 +1,34 @@ +// +// Created by servostar on 5/31/24. +// + +#ifndef GEMSTONE_OPT_H +#define GEMSTONE_OPT_H + +#include + +typedef struct TargetConfig_t { + char* name; + bool print_ast; + bool print_asm; + bool print_ir; +} TargetConfig; + +typedef struct ProjectConfig_t { + // name of the project + char* name; + // description + char* desc; + // version + char* version; + // list of authors + char** authors; +} ProjectConfig; + +TargetConfig default_target_config(); + +TargetConfig default_target_config_from_args(int argc, char* argv[]); + +void print_help(void); + +#endif //GEMSTONE_OPT_H diff --git a/src/main.c b/src/main.c index e9914ef..83e9705 100644 --- a/src/main.c +++ b/src/main.c @@ -9,10 +9,6 @@ extern FILE *yyin; -// Global array to store options -char options[5][10]; -int num_options = 0; - /** * @brief Log a debug message to inform about beginning exit procedures * @@ -20,34 +16,6 @@ int num_options = 0; void notify_exit(void) { DEBUG("Exiting gemstone..."); } -/** - * @brief add option to global option array - * - */ - -void add_option(const char* option) { - if (num_options < 5 ) { - strcpy(options[num_options], option); - num_options++; - } else { - PANIC("Too Many Options given"); - } -} - -/** - * @brief Check if Option is set - * - */ - -size_t check_option(const char* name) { - for (int i = 0; i < num_options; i++) { - if (strcmp(options[i], name) == 0) { - return 1; - } - } - return 0; -} - /** * @brief Closes File after compiling. * @@ -84,18 +52,6 @@ void setup(void) { int main(int argc, char *argv[]) { - // Iteration through arguments - for (int i = 1; i < argc; i++) { - // Check if the argument starts with "--" - if (argv[i][0] == '-' && argv[i][1] == '-') { - // Extract option name - char option[10]; - strcpy(option, argv[i] + 2); - - // Add option to the global array - add_option(option); - } - } setup(); atexit(close_file); From c527c99392704f9c1a1a3e654c9d102283911250 Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 16:33:48 +0200 Subject: [PATCH 06/21] added license and description to project --- src/cfg/opt.c | 22 ++++++++++++++++------ src/cfg/opt.h | 5 ++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index bb74e51..10d1540 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -4,7 +4,6 @@ #include #include -#include TargetConfig default_target_config() { TargetConfig config; @@ -74,19 +73,30 @@ static int parse_project_table(ProjectConfig *config, toml_table_t *project_tabl // author names toml_array_t *authors = toml_array_in(project_table, "authors"); if (authors) { - unsigned int len = 0; - config->authors = malloc(sizeof(const char *) * 2); + config->authors = g_array_new(FALSE, FALSE, sizeof(char*)); for (int i = 0;; i++) { toml_datum_t author = toml_string_at(authors, i); if (!author.ok) break; - char** new_authors = realloc(config->authors, sizeof(const char *) * ++len); - config->authors = new_authors; - config->authors[len] = author.u.s; + g_array_append_val(config->authors, author.u.s); } } + + // project description + toml_datum_t description = toml_string_in(project_table, "description"); + if (description.ok) { + config->desc = description.u.s; + } + + // project license + toml_datum_t license = toml_string_in(project_table, "license"); + if (license.ok) { + config->license = license.u.s; + } + + return PROJECT_OK; } int load_project_config(ProjectConfig *config) { diff --git a/src/cfg/opt.h b/src/cfg/opt.h index cf89d4f..5c9e6b5 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -6,6 +6,7 @@ #define GEMSTONE_OPT_H #include +#include typedef struct TargetConfig_t { char* name; @@ -21,8 +22,10 @@ typedef struct ProjectConfig_t { char* desc; // version char* version; + // license + char* license; // list of authors - char** authors; + GArray* authors; } ProjectConfig; TargetConfig default_target_config(); From 5fb10bca8815de1fa9c786f976c580391c56d98a Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 16:55:43 +0200 Subject: [PATCH 07/21] added option to compile for app or lib --- src/cfg/opt.c | 11 ++++++++--- src/cfg/opt.h | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 10d1540..6a1862f 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -28,6 +28,10 @@ TargetConfig default_target_config_from_args(int argc, char *argv[]) { config.print_asm = true; } else if (strcmp(option, "--print-ir") == 0) { config.print_ir = true; + } else if (strcmp(option, "--mode=app") == 0) { + config.mode = Application; + } else if (strcmp(option, "--mode=lib") == 0) { + config.mode = Library; } } @@ -40,9 +44,10 @@ void print_help(void) { "Compile file(s): gsc [files]", "Build project (build.toml): gsc [directory] [target]|all", "Options:", - " --print-ast print resulting abstract syntax tree to a file", - " --print-asm print resulting assembly language to a file", - " --print-ir print resulting LLVM-IR to a file" + " --print-ast print resulting abstract syntax tree to a file", + " --print-asm print resulting assembly language to a file", + " --print-ir print resulting LLVM-IR to a file", + " --mode=[app|lib] set the compilation mode to either application or library" }; for (unsigned int i = 0; i < sizeof(lines) / sizeof(const char *); i++) { diff --git a/src/cfg/opt.h b/src/cfg/opt.h index 5c9e6b5..cd00b62 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -8,11 +8,26 @@ #include #include +typedef enum TargetCompilationMode_t { + Application, + Library +} TargetCompilationMode; + typedef struct TargetConfig_t { char* name; bool print_ast; bool print_asm; bool print_ir; + // root module file which imports all submodules + char* root_module; + // output directory for binaries + char* output_directory; + // output directory for intermediate representations (LLVM-IR, Assembly, ...) + char* archive_directory; + // mode of compilation + TargetCompilationMode mode; + // number between 1 and 3 + int optimization_level; } TargetConfig; typedef struct ProjectConfig_t { From 68ca76cb458dd037426c343c9b6d07c9514337d5 Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 16:57:25 +0200 Subject: [PATCH 08/21] added missing default target options --- src/cfg/opt.c | 5 +++++ src/cfg/opt.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 6a1862f..95ec3a1 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -12,6 +12,11 @@ TargetConfig default_target_config() { config.print_ast = false; config.print_asm = false; config.print_ir = false; + config.mode = Application; + config.archive_directory = "archive"; + config.archive_directory = "bin"; + config.optimization_level = 1; + config.root_module = NULL; return config; } diff --git a/src/cfg/opt.h b/src/cfg/opt.h index cd00b62..c407523 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -19,6 +19,7 @@ typedef struct TargetConfig_t { bool print_asm; bool print_ir; // root module file which imports all submodules + // if this is NULL use the first commandline argument as root module char* root_module; // output directory for binaries char* output_directory; From 76b011511ae7fbc041105af8a434fc25cb3e4678 Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 19:09:28 +0200 Subject: [PATCH 09/21] targets are now read in --- src/cfg/opt.c | 110 +++++++++++++++++++++++++++++++++++++++++--------- src/cfg/opt.h | 1 + 2 files changed, 91 insertions(+), 20 deletions(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 95ec3a1..feeafcb 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -5,6 +5,8 @@ #include #include +#define MAX_TARGETS_PER_PROJECT 100 + TargetConfig default_target_config() { TargetConfig config; @@ -64,26 +66,45 @@ void print_help(void) { #define PROJECT_TOML_ERR 1 #define PROJECT_SEMANTIC_ERR 2 +static void get_bool(bool* boolean, toml_table_t *table, const char* name) { + toml_datum_t datum = toml_bool_in(table, name); + + if (datum.ok) { + *boolean = datum.u.b; + } +} + +static void get_str(char** string, toml_table_t *table, const char* name) { + toml_datum_t datum = toml_string_in(table, name); + + if (datum.ok) { + *string = datum.u.s; + } +} + +static void get_int(int* integer, toml_table_t *table, const char* name) { + toml_datum_t datum = toml_int_in(table, name); + + if (datum.ok) { + *integer = (int) datum.u.i; + } +} + static int parse_project_table(ProjectConfig *config, toml_table_t *project_table) { // project name - toml_datum_t name = toml_string_in(project_table, "name"); - if (!name.ok) { + get_str(&config->name, project_table, "version"); + if (config->name == NULL) { printf("Invalid project configuration: project must have a name\n\n"); return PROJECT_SEMANTIC_ERR; } - config->name = name.u.s; - // project version - toml_datum_t version = toml_string_in(project_table, "version"); - if (version.ok) { - config->name = version.u.s; - } + get_str(&config->name, project_table, "version"); // author names toml_array_t *authors = toml_array_in(project_table, "authors"); if (authors) { - config->authors = g_array_new(FALSE, FALSE, sizeof(char*)); + config->authors = g_array_new(FALSE, FALSE, sizeof(char *)); for (int i = 0;; i++) { toml_datum_t author = toml_string_at(authors, i); @@ -95,15 +116,63 @@ static int parse_project_table(ProjectConfig *config, toml_table_t *project_tabl } // project description - toml_datum_t description = toml_string_in(project_table, "description"); - if (description.ok) { - config->desc = description.u.s; + get_str(&config->desc, project_table, "description"); + // project license + get_str(&config->license, project_table, "license"); + + return PROJECT_OK; +} + +static int get_mode_from_str(TargetCompilationMode* mode, const char* name) { + if (strcmp(name, "application") == 0) { + *mode = Application; + return PROJECT_OK; + } else if (strcmp(name, "library") == 0) { + *mode = Library; + return PROJECT_OK; + } + printf("Invalid project configuration, mode is invalid: %s\n\n", name); + return PROJECT_SEMANTIC_ERR; +} + +static int parse_target(ProjectConfig *config, toml_table_t *target_table, const char* name) { + TargetConfig target_config = default_target_config(); + + target_config.name = (char*) name; + + get_bool(&target_config.print_ast, target_table, "print_ast"); + get_bool(&target_config.print_asm, target_table, "print_asm"); + get_bool(&target_config.print_ir, target_table, "print_ir"); + + get_str(&target_config.root_module, target_table, "root"); + get_str(&target_config.output_directory, target_table, "output"); + get_str(&target_config.archive_directory, target_table, "archive"); + + get_int(&target_config.optimization_level, target_table, "opt"); + + char* mode = NULL; + get_str(&mode, target_table, "mode"); + int err = get_mode_from_str(&target_config.mode, mode); + if (err != PROJECT_OK) { + return err; } - // project license - toml_datum_t license = toml_string_in(project_table, "license"); - if (license.ok) { - config->license = license.u.s; + g_array_append_val(config->targets, target_config); + + return PROJECT_OK; +} + +static int parse_targets(ProjectConfig *config, toml_table_t *root) { + toml_table_t *targets = toml_table_in(root, "target"); + + for (int i = 0; i < MAX_TARGETS_PER_PROJECT; i++) { + const char *key = toml_key_in(targets, i); + + if (!key) + break; + + toml_table_t *target = toml_table_in(targets, key); + parse_target(config, target, key); } return PROJECT_OK; @@ -127,11 +196,12 @@ int load_project_config(ProjectConfig *config) { toml_table_t *project = toml_table_in(conf, "project"); if (project) { - parse_project_table(config, project); - - return PROJECT_OK; + if (parse_project_table(config, project) == PROJECT_OK) { + return parse_targets(config, project); + } + } else { + printf("Invalid project configuration: missing project table\n\n"); } - printf("Invalid project configuration: missing project table\n\n"); toml_free(conf); return PROJECT_SEMANTIC_ERR; diff --git a/src/cfg/opt.h b/src/cfg/opt.h index c407523..ec259e9 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -42,6 +42,7 @@ typedef struct ProjectConfig_t { char* license; // list of authors GArray* authors; + GArray* targets; } ProjectConfig; TargetConfig default_target_config(); From 8f24596779b8cf7fc50f1283bc2862ef9941151f Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 21:25:37 +0200 Subject: [PATCH 10/21] fixed implementation vaults --- CMakeLists.txt | 5 ++ src/cfg/opt.c | 169 +++++++++++++++++++++++++++++++++++++------------ src/cfg/opt.h | 21 +++++- src/main.c | 139 +++++++++++++++++++++++++++++++--------- 4 files changed, 260 insertions(+), 74 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ca09790..d3763ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,8 @@ set(CMAKE_C_STANDARD_REQUIRED TRUE) set(GEMSTONE_TEST_DIR ${PROJECT_SOURCE_DIR}/tests) set(GEMSTONE_BINARY_DIR ${PROJECT_SOURCE_DIR}/bin) +add_compile_definitions(GSC_VERSION="${PROJECT_VERSION}") + include(CTest) if(BUILD_TESTING) @@ -144,6 +146,9 @@ target_compile_options(release PUBLIC ${FLAGS} ${RELEASE_FLAGS}) # add src directory as include path target_include_directories(release PUBLIC src) +# disable assertions +target_compile_definitions(release PUBLIC NDEBUG="1") + # ------------------------------------------------ # # Target DEBUG # # ------------------------------------------------ # diff --git a/src/cfg/opt.c b/src/cfg/opt.c index feeafcb..ca22b6a 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -4,41 +4,47 @@ #include #include +#include -#define MAX_TARGETS_PER_PROJECT 100 +TargetConfig* default_target_config() { + DEBUG("generating default target config..."); -TargetConfig default_target_config() { - TargetConfig config; + TargetConfig* config = malloc(sizeof(TargetConfig)); - config.name = "debug"; - config.print_ast = false; - config.print_asm = false; - config.print_ir = false; - config.mode = Application; - config.archive_directory = "archive"; - config.archive_directory = "bin"; - config.optimization_level = 1; - config.root_module = NULL; + config->name = NULL; + config->print_ast = false; + config->print_asm = false; + config->print_ir = false; + config->mode = Application; + config->archive_directory = NULL; + config->archive_directory = NULL; + config->optimization_level = 1; + config->root_module = NULL; return config; } -TargetConfig default_target_config_from_args(int argc, char *argv[]) { - TargetConfig config = default_target_config(); +TargetConfig* default_target_config_from_args(int argc, char *argv[]) { + DEBUG("generating default target from command line..."); + + TargetConfig* config = default_target_config(); for (int i = 0; i < argc; i++) { + DEBUG("processing argument: %ld %s", i, argv[i]); char *option = argv[i]; if (strcmp(option, "--print-ast") == 0) { - config.print_ast = true; + config->print_ast = true; } else if (strcmp(option, "--print-asm") == 0) { - config.print_asm = true; + config->print_asm = true; } else if (strcmp(option, "--print-ir") == 0) { - config.print_ir = true; + config->print_ir = true; } else if (strcmp(option, "--mode=app") == 0) { - config.mode = Application; + config->mode = Application; } else if (strcmp(option, "--mode=lib") == 0) { - config.mode = Library; + config->mode = Library; + } else { + config->root_module = option; } } @@ -46,11 +52,14 @@ TargetConfig default_target_config_from_args(int argc, char *argv[]) { } void print_help(void) { + DEBUG("printing help dialog..."); + const char *lines[] = { - "Gemstone Compiler (C) GPL-2.0\n", - "Compile file(s): gsc [files]", - "Build project (build.toml): gsc [directory] [target]|all", + "Gemstone Compiler (C) GPL-2.0", + "Build a project target: gsc build [target]|all", + "Compile non-project file: gsc compile [file]", "Options:", + " --version print the version", " --print-ast print resulting abstract syntax tree to a file", " --print-asm print resulting assembly language to a file", " --print-ir print resulting LLVM-IR to a file", @@ -62,35 +71,42 @@ void print_help(void) { } } -#define PROJECT_OK 0 -#define PROJECT_TOML_ERR 1 -#define PROJECT_SEMANTIC_ERR 2 - static void get_bool(bool* boolean, toml_table_t *table, const char* name) { + DEBUG("retrieving boolean %s", name); + toml_datum_t datum = toml_bool_in(table, name); if (datum.ok) { *boolean = datum.u.b; + DEBUG("boolean has value: %d", datum.u.b); } } static void get_str(char** string, toml_table_t *table, const char* name) { + DEBUG("retrieving string %s", name); + toml_datum_t datum = toml_string_in(table, name); if (datum.ok) { *string = datum.u.s; + DEBUG("string has value: %s", datum.u.s); } } static void get_int(int* integer, toml_table_t *table, const char* name) { + DEBUG("retrieving integer %s", name); + toml_datum_t datum = toml_int_in(table, name); if (datum.ok) { *integer = (int) datum.u.i; + DEBUG("integer has value: %ld", datum.u.i); } } static int parse_project_table(ProjectConfig *config, toml_table_t *project_table) { + DEBUG("parsing project table..."); + // project name get_str(&config->name, project_table, "version"); if (config->name == NULL) { @@ -99,7 +115,7 @@ static int parse_project_table(ProjectConfig *config, toml_table_t *project_tabl } // project version - get_str(&config->name, project_table, "version"); + get_str(&config->name, project_table, "name"); // author names toml_array_t *authors = toml_array_in(project_table, "authors"); @@ -136,34 +152,44 @@ static int get_mode_from_str(TargetCompilationMode* mode, const char* name) { } static int parse_target(ProjectConfig *config, toml_table_t *target_table, const char* name) { - TargetConfig target_config = default_target_config(); + DEBUG("parsing target table..."); - target_config.name = (char*) name; + TargetConfig* target_config = default_target_config(); - get_bool(&target_config.print_ast, target_table, "print_ast"); - get_bool(&target_config.print_asm, target_table, "print_asm"); - get_bool(&target_config.print_ir, target_table, "print_ir"); + target_config->name = (char*) name; - get_str(&target_config.root_module, target_table, "root"); - get_str(&target_config.output_directory, target_table, "output"); - get_str(&target_config.archive_directory, target_table, "archive"); + get_bool(&target_config->print_ast, target_table, "print_ast"); + get_bool(&target_config->print_asm, target_table, "print_asm"); + get_bool(&target_config->print_ir, target_table, "print_ir"); - get_int(&target_config.optimization_level, target_table, "opt"); + get_str(&target_config->root_module, target_table, "root"); + get_str(&target_config->output_directory, target_table, "output"); + get_str(&target_config->archive_directory, target_table, "archive"); + + get_int(&target_config->optimization_level, target_table, "opt"); char* mode = NULL; get_str(&mode, target_table, "mode"); - int err = get_mode_from_str(&target_config.mode, mode); + int err = get_mode_from_str(&target_config->mode, mode); if (err != PROJECT_OK) { return err; } - g_array_append_val(config->targets, target_config); + g_hash_table_insert(config->targets, target_config->name, target_config); return PROJECT_OK; } static int parse_targets(ProjectConfig *config, toml_table_t *root) { + DEBUG("parsing targets of project \"%s\"", config->name); + toml_table_t *targets = toml_table_in(root, "target"); + if (targets == NULL) { + printf("Project has no targets\n"); + return PROJECT_SEMANTIC_ERR; + } + + config->targets = g_hash_table_new(g_str_hash, g_str_equal); for (int i = 0; i < MAX_TARGETS_PER_PROJECT; i++) { const char *key = toml_key_in(targets, i); @@ -179,8 +205,12 @@ static int parse_targets(ProjectConfig *config, toml_table_t *root) { } int load_project_config(ProjectConfig *config) { - FILE *config_file = fopen("build.toml", "r"); + DEBUG("loading project config..."); + + FILE *config_file = fopen(PROJECT_CONFIG_FILE, "r"); if (config_file == NULL) { + printf("Cannot open file %s: %s\n", PROJECT_CONFIG_FILE, strerror(errno)); + ERROR("project file not found"); return PROJECT_TOML_ERR; } @@ -197,7 +227,7 @@ int load_project_config(ProjectConfig *config) { toml_table_t *project = toml_table_in(conf, "project"); if (project) { if (parse_project_table(config, project) == PROJECT_OK) { - return parse_targets(config, project); + return parse_targets(config, conf); } } else { printf("Invalid project configuration: missing project table\n\n"); @@ -206,3 +236,62 @@ int load_project_config(ProjectConfig *config) { toml_free(conf); return PROJECT_SEMANTIC_ERR; } + +void delete_target_config(TargetConfig* config) { + if (config->root_module != NULL) { + free(config->root_module); + } + if (config->archive_directory != NULL) { + free(config->archive_directory); + } + if (config->name != NULL) { + free(config->name); + } + if (config->output_directory != NULL) { + free(config->output_directory); + } + free(config); +} + +void delete_project_config(ProjectConfig* config) { + if (config->name != NULL) { + free(config->name); + } + if (config->authors != NULL) { + g_array_free(config->authors, TRUE); + } + if (config->desc != NULL) { + free(config->desc); + } + if (config->license != NULL) { + free(config->license); + } + if (config->targets != NULL) { + GHashTableIter iter; + + g_hash_table_iter_init(&iter, config->targets); + + char* key; + TargetConfig* val; + while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) { + delete_target_config(val); + } + + g_hash_table_destroy(config->targets); + } + + free(config); +} + +ProjectConfig* default_project_config() { + ProjectConfig* config = malloc(sizeof(ProjectConfig)); + + config->authors = NULL; + config->name = NULL; + config->targets = NULL; + config->license = NULL; + config->version = NULL; + config->desc = NULL; + + return config; +} diff --git a/src/cfg/opt.h b/src/cfg/opt.h index ec259e9..c54bfb2 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -8,6 +8,13 @@ #include #include +#define MAX_TARGETS_PER_PROJECT 100 +#define PROJECT_CONFIG_FILE "build.toml" + +#define PROJECT_OK 0 +#define PROJECT_TOML_ERR 1 +#define PROJECT_SEMANTIC_ERR 2 + typedef enum TargetCompilationMode_t { Application, Library @@ -42,13 +49,21 @@ typedef struct ProjectConfig_t { char* license; // list of authors GArray* authors; - GArray* targets; + GHashTable* targets; } ProjectConfig; -TargetConfig default_target_config(); +TargetConfig* default_target_config(); -TargetConfig default_target_config_from_args(int argc, char* argv[]); +ProjectConfig* default_project_config(); + +TargetConfig* default_target_config_from_args(int argc, char* argv[]); + +int load_project_config(ProjectConfig *config); void print_help(void); +void delete_project_config(ProjectConfig* config); + +void delete_target_config(TargetConfig*); + #endif //GEMSTONE_OPT_H diff --git a/src/main.c b/src/main.c index 4888ee5..c0be30b 100644 --- a/src/main.c +++ b/src/main.c @@ -6,6 +6,7 @@ #include #include #include +#include extern void yyrestart(FILE *); @@ -22,6 +23,7 @@ ModuleFile *current_file; */ [[nodiscard("AST may be in invalid state")]] [[gnu::nonnull(1), gnu::nonnull(1)]] + static size_t compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) { assert(file->path != NULL); assert(ast != NULL); @@ -30,6 +32,7 @@ static size_t compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) { if (file->handle == NULL) { INFO("unable to open file: %s", file->path); + printf("Cannot open file %s: %s\n", file->path, strerror(errno)); return 1; } @@ -92,43 +95,117 @@ void setup(void) { DEBUG("finished starting up gemstone..."); } +void build_target(ModuleFileStack *unit, TargetConfig *target) { + printf("Compiling file: %s\n\n", target->root_module); + + TokenLocation location = { + .line_start = 0, + .line_end = 0, + .col_start = 0, + .col_end = 0 + }; + AST_NODE_PTR ast = AST_new_node(location, AST_Module, NULL); + ModuleFile *file = push_file(unit, target->root_module); + + if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) { + // TODO: parse AST to semantic values + // TODO: backend codegen + } + + AST_delete_node(ast); + + print_file_statistics(file); +} + +void compile_file(ModuleFileStack *unit, int argc, char *argv[]) { + INFO("compiling basic files..."); + + TargetConfig *target = default_target_config_from_args(argc, argv); + + if (target->root_module == NULL) { + printf("No input file specified\n"); + delete_target_config(target); + return; + } + + build_target(unit, target); + + delete_target_config(target); +} + +void build_project_targets(ModuleFileStack *unit, ProjectConfig *config, const char *filter) { + if (strcmp(filter, "all") == 0) { + GHashTableIter iter; + + g_hash_table_iter_init(&iter, config->targets); + + char* key; + TargetConfig* val; + while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) { + build_target(unit, val); + } + } else if (g_hash_table_contains(config->targets, filter)) { + build_target(unit, g_hash_table_lookup(config->targets, filter)); + } +} + +void build_project(ModuleFileStack *unit, int argc, char *argv[]) { + if (argc >= 1) { + ProjectConfig* config = default_project_config(); + int err = load_project_config(config); + + if (err == PROJECT_OK) { + if (argc == 1) { + build_project_targets(unit, config, "all"); + } else { + build_project_targets(unit, config, argv[0]); + } + } + + delete_project_config(config); + + } else { + printf("Expected 1 target to run\n"); + } +} + +void configure_run_mode(int argc, char *argv[]) { + if (argc > 1) { + + ModuleFileStack files; + files.files = NULL; + + if (strcmp(argv[1], "build") == 0) { + build_project(&files, argc - 2, &argv[2]); + } else if (strcmp(argv[1], "compile") == 0) { + compile_file(&files, argc - 2, &argv[2]); + } else { + printf("invalid mode of operation\n"); + } + + if (files.files == NULL) { + printf("No input files, nothing to do.\n\n"); + exit(1); + } + + print_unit_statistics(&files); + delete_files(&files); + + return; + } + INFO("no arguments provided"); + + print_help(); +} + int main(int argc, char *argv[]) { setup(); atexit(close_file); - ModuleFileStack files; - files.files = NULL; + printf("running GSC version %s\n", GSC_VERSION); - for (int i = 1; i < argc; i++) { - printf("Compiling file: %s\n\n", argv[i]); + configure_run_mode(argc, argv); - TokenLocation location = { - .line_start = 0, - .line_end = 0, - .col_start = 0, - .col_end = 0 - }; - AST_NODE_PTR ast = AST_new_node(location, AST_Module, NULL); - ModuleFile *file = push_file(&files, argv[i]); - - if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) { - // TODO: parse AST to semantic values - // TODO: backend codegen - } - - AST_delete_node(ast); - - print_file_statistics(file); - } - - if (files.files == NULL) { - printf("No input files, nothing to do.\n\n"); - exit(1); - } - - print_unit_statistics(&files); - - delete_files(&files); return 0; } From 3a8796a462373c044899ed3ce3bb185508cd76e8 Mon Sep 17 00:00:00 2001 From: servostar Date: Sat, 1 Jun 2024 01:00:22 +0200 Subject: [PATCH 11/21] fixed segfaults --- src/cfg/opt.c | 2 +- src/io/files.c | 34 ++++++++++++++- src/io/files.h | 3 ++ src/main.c | 113 +++++++++++++++++++++++++------------------------ 4 files changed, 94 insertions(+), 58 deletions(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index ca22b6a..3f5b829 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -44,7 +44,7 @@ TargetConfig* default_target_config_from_args(int argc, char *argv[]) { } else if (strcmp(option, "--mode=lib") == 0) { config->mode = Library; } else { - config->root_module = option; + config->root_module = strdup(option); } } diff --git a/src/io/files.c b/src/io/files.c index 2d4fda7..09ff395 100644 --- a/src/io/files.c +++ b/src/io/files.c @@ -217,7 +217,11 @@ void print_unit_statistics(ModuleFileStack *file_stack) { stats.error_count += file->statistics.error_count; } - printf("%d files generated ", file_stack->files->len); + if (stats.info_count + stats.warning_count + stats.error_count < 1) { + return; + } + + printf("%d file(s) generated ", file_stack->files->len); if (stats.info_count > 0) { printf("%ld notice(s) ", stats.info_count); @@ -233,3 +237,31 @@ void print_unit_statistics(ModuleFileStack *file_stack) { printf("\n\n"); } + +void print_message(Message kind, const char *fmt, ...) { + const char *accent_color = RESET; + const char *kind_text = "unknown"; + switch (kind) { + case Info: + kind_text = "info"; + accent_color = CYAN; + break; + case Warning: + kind_text = "warning"; + accent_color = YELLOW; + break; + case Error: + kind_text = "error"; + accent_color = RED; + break; + } + + va_list args; + va_start(args, fmt); + + printf("%s%s:%s ", accent_color, kind_text, RESET); + vprintf(fmt, args); + printf("\n\n"); + + va_end(args); +} diff --git a/src/io/files.h b/src/io/files.h index 076b8d0..25d5078 100644 --- a/src/io/files.h +++ b/src/io/files.h @@ -70,6 +70,9 @@ TokenLocation empty_location(void); [[gnu::nonnull(1), gnu::nonnull(2)]] void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, const char *message); +[[gnu::nonnull(2)]] +void print_message(Message kind, const char *fmt, ...); + [[gnu::nonnull(1)]] void print_file_statistics(ModuleFile *file); diff --git a/src/main.c b/src/main.c index c0be30b..a309dc9 100644 --- a/src/main.c +++ b/src/main.c @@ -32,7 +32,7 @@ static size_t compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) { if (file->handle == NULL) { INFO("unable to open file: %s", file->path); - printf("Cannot open file %s: %s\n", file->path, strerror(errno)); + print_message(Error, "Cannot open file %s: %s", file->path, strerror(errno)); return 1; } @@ -96,18 +96,20 @@ void setup(void) { } void build_target(ModuleFileStack *unit, TargetConfig *target) { - printf("Compiling file: %s\n\n", target->root_module); + print_message(Info, "Compiling file: %s", target->root_module); - TokenLocation location = { - .line_start = 0, - .line_end = 0, - .col_start = 0, - .col_end = 0 - }; + TokenLocation location = new_location(0,0,0,0); AST_NODE_PTR ast = AST_new_node(location, AST_Module, NULL); ModuleFile *file = push_file(unit, target->root_module); if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) { + + + + if (target->print_ast) { + + } + // TODO: parse AST to semantic values // TODO: backend codegen } @@ -123,7 +125,7 @@ void compile_file(ModuleFileStack *unit, int argc, char *argv[]) { TargetConfig *target = default_target_config_from_args(argc, argv); if (target->root_module == NULL) { - printf("No input file specified\n"); + print_message(Error, "No input file specified."); delete_target_config(target); return; } @@ -133,69 +135,68 @@ void compile_file(ModuleFileStack *unit, int argc, char *argv[]) { delete_target_config(target); } -void build_project_targets(ModuleFileStack *unit, ProjectConfig *config, const char *filter) { - if (strcmp(filter, "all") == 0) { +void build_project_targets(ModuleFileStack *unit, ProjectConfig *config, int argc, char *argv[]) { + if (argc == 1 && strcmp(argv[0], "all") == 0) { GHashTableIter iter; g_hash_table_iter_init(&iter, config->targets); - char* key; - TargetConfig* val; + char *key; + TargetConfig *val; while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) { build_target(unit, val); } - } else if (g_hash_table_contains(config->targets, filter)) { - build_target(unit, g_hash_table_lookup(config->targets, filter)); + } else { + for (int i = 0; i < argc; i++) { + char *target_name = argv[i]; + if (g_hash_table_contains(config->targets, target_name)) { + build_target(unit, g_hash_table_lookup(config->targets, target_name)); + } + } } } void build_project(ModuleFileStack *unit, int argc, char *argv[]) { - if (argc >= 1) { - ProjectConfig* config = default_project_config(); - int err = load_project_config(config); - - if (err == PROJECT_OK) { - if (argc == 1) { - build_project_targets(unit, config, "all"); - } else { - build_project_targets(unit, config, argv[0]); - } - } - - delete_project_config(config); - - } else { - printf("Expected 1 target to run\n"); + if (argc <= 0) { + print_message(Error, "No targets specified."); + return; } + + ProjectConfig *config = default_project_config(); + int err = load_project_config(config); + + if (err == PROJECT_OK) { + build_project_targets(unit, config, argc, argv); + } + + delete_project_config(config); } void configure_run_mode(int argc, char *argv[]) { - if (argc > 1) { - - ModuleFileStack files; - files.files = NULL; - - if (strcmp(argv[1], "build") == 0) { - build_project(&files, argc - 2, &argv[2]); - } else if (strcmp(argv[1], "compile") == 0) { - compile_file(&files, argc - 2, &argv[2]); - } else { - printf("invalid mode of operation\n"); - } - - if (files.files == NULL) { - printf("No input files, nothing to do.\n\n"); - exit(1); - } - - print_unit_statistics(&files); - delete_files(&files); - + if (argc <= 0) { + INFO("no arguments provided"); + print_help(); return; } - INFO("no arguments provided"); - print_help(); + ModuleFileStack files; + files.files = NULL; + + if (strcmp(argv[0], "build") == 0) { + build_project(&files, argc - 1, &argv[1]); + } else if (strcmp(argv[0], "compile") == 0) { + compile_file(&files, argc - 1, &argv[1]); + } else { + print_message(Error, "Invalid mode of operation. Rerun with --help."); + } + + if (files.files == NULL) { + print_message(Error, "No input files, nothing to do."); + exit(1); + } + + print_unit_statistics(&files); + delete_files(&files); } int main(int argc, char *argv[]) { @@ -203,9 +204,9 @@ int main(int argc, char *argv[]) { setup(); atexit(close_file); - printf("running GSC version %s\n", GSC_VERSION); + print_message(Info, "running GSC version %s", GSC_VERSION); - configure_run_mode(argc, argv); + configure_run_mode(argc - 1, &argv[1]); return 0; } From 01f5ef953d8f56546d95ae00ec0491989fef87f7 Mon Sep 17 00:00:00 2001 From: servostar Date: Sat, 1 Jun 2024 14:52:59 +0200 Subject: [PATCH 12/21] fixed devkit not cloning submodules --- CMakeLists.txt | 2 ++ Dockerfile | 2 ++ src/cfg/opt.c | 29 ++++++++++++++++------------- src/main.c | 9 +++++++-- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d3763ea..b5a44cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,8 @@ pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0) # TOML-C99 # # ------------------------------------------------ # +execute_process(COMMAND git submodule init -- dep/tomlc99) + add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c COMMAND git ARGS submodule update --init --recursive --checkout diff --git a/Dockerfile b/Dockerfile index 68265d8..bb287f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,5 +10,7 @@ COPY --chown=lorang CMakeLists.txt /home/lorang/ COPY --chown=lorang run-check-test.sh /home/lorang/ COPY --chown=lorang .env /home/lorang/ COPY --chown=lorang run-docker-build.sh /home/lorang/ +COPY --chown=lorang dep /home/lorang/dep +COPY --chown=lorang .git /home/lorang/.git RUN cmake . diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 3f5b829..92577d8 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -5,19 +5,20 @@ #include #include #include +#include TargetConfig* default_target_config() { DEBUG("generating default target config..."); TargetConfig* config = malloc(sizeof(TargetConfig)); - config->name = NULL; + config->name = strdup("out"); config->print_ast = false; config->print_asm = false; config->print_ir = false; config->mode = Application; - config->archive_directory = NULL; - config->archive_directory = NULL; + config->archive_directory = strdup("archive"); + config->output_directory = strdup("bin"); config->optimization_level = 1; config->root_module = NULL; @@ -43,8 +44,10 @@ TargetConfig* default_target_config_from_args(int argc, char *argv[]) { config->mode = Application; } else if (strcmp(option, "--mode=lib") == 0) { config->mode = Library; - } else { + } else if (config->root_module == NULL) { config->root_module = strdup(option); + } else { + print_message(Warning, "Got more than one file to compile, using first by ignoring others."); } } @@ -185,7 +188,7 @@ static int parse_targets(ProjectConfig *config, toml_table_t *root) { toml_table_t *targets = toml_table_in(root, "target"); if (targets == NULL) { - printf("Project has no targets\n"); + print_message(Warning, "Project has no targets"); return PROJECT_SEMANTIC_ERR; } @@ -209,7 +212,7 @@ int load_project_config(ProjectConfig *config) { FILE *config_file = fopen(PROJECT_CONFIG_FILE, "r"); if (config_file == NULL) { - printf("Cannot open file %s: %s\n", PROJECT_CONFIG_FILE, strerror(errno)); + print_message(Error, "Cannot open file %s: %s", PROJECT_CONFIG_FILE, strerror(errno)); ERROR("project file not found"); return PROJECT_TOML_ERR; } @@ -220,17 +223,17 @@ int load_project_config(ProjectConfig *config) { fclose(config_file); if (!conf) { - printf("Invalid project configuration: %s\n\n", err_buf); + print_message(Error, "Invalid project configuration: %s", err_buf); return PROJECT_SEMANTIC_ERR; } toml_table_t *project = toml_table_in(conf, "project"); - if (project) { - if (parse_project_table(config, project) == PROJECT_OK) { - return parse_targets(config, conf); - } - } else { - printf("Invalid project configuration: missing project table\n\n"); + if (!project) { + print_message(Error, "Invalid project configuration: missing project table."); + } + + if (parse_project_table(config, project) == PROJECT_OK) { + return parse_targets(config, conf); } toml_free(conf); diff --git a/src/main.c b/src/main.c index a309dc9..397c652 100644 --- a/src/main.c +++ b/src/main.c @@ -95,6 +95,12 @@ void setup(void) { DEBUG("finished starting up gemstone..."); } +static void setup_target_environment(const TargetConfig* target) { + if (target->output_directory) { + + } +} + void build_target(ModuleFileStack *unit, TargetConfig *target) { print_message(Info, "Compiling file: %s", target->root_module); @@ -103,8 +109,7 @@ void build_target(ModuleFileStack *unit, TargetConfig *target) { ModuleFile *file = push_file(unit, target->root_module); if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) { - - + setup_target_environment(target); if (target->print_ast) { From fcbfb548c335c14eac497fc8635e0019dbb09314 Mon Sep 17 00:00:00 2001 From: servostar Date: Sun, 2 Jun 2024 20:57:59 +0200 Subject: [PATCH 13/21] added doxygen and added new compiler module --- src/compiler.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++ src/compiler.h | 15 +++ src/io/files.c | 95 +++++++++++++++++- src/io/files.h | 74 ++++++++++++++ src/main.c | 176 +--------------------------------- 5 files changed, 439 insertions(+), 176 deletions(-) create mode 100644 src/compiler.c create mode 100644 src/compiler.h diff --git a/src/compiler.c b/src/compiler.c new file mode 100644 index 0000000..1889763 --- /dev/null +++ b/src/compiler.c @@ -0,0 +1,255 @@ +// +// Created by servostar on 6/2/24. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void yyrestart(FILE *); + +// Module AST node used by the parser for AST construction. +[[maybe_unused]] +AST_NODE_PTR root; +// Current file which gets compiled the parser. +// NOTE: due to global state no concurrent compilation is possible +// on parser level. +[[maybe_unused]] +ModuleFile *current_file; + +/** + * @brief Compile the specified file into AST + * @param ast Initialized AST module node to build program rules + * @param file The file to be processed + * @return EXIT_SUCCESS in case the parsing was success full anything lese if not + */ +[[nodiscard("AST may be in invalid state")]] +[[gnu::nonnull(1), gnu::nonnull(1)]] +static int compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) { + assert(file->path != NULL); + assert(ast != NULL); + + file->handle = fopen(file->path, "r"); + + if (file->handle == NULL) { + INFO("unable to open file: %s", file->path); + print_message(Error, "Cannot open file %s: %s", file->path, strerror(errno)); + return EXIT_FAILURE; + } + + DEBUG("parsing file: %s", file->path); + // setup global state + root = ast; + current_file = file; + yyin = file->handle; + yyrestart(yyin); + lex_reset(); + + yyparse(); + + // clean up global state + // current_file = NULL; + root = NULL; + yyin = NULL; + + return EXIT_SUCCESS; +} + +/** + * @brief Setup the environment of the target. + * @param target + * @return EXIT_SUCCESS if successful EXIT_FAILURE otherwise + */ +static int setup_target_environment(const TargetConfig *target) { + DEBUG("setting up environment for target: %s", target->name); + + assert(target->output_directory != NULL); + assert(target->archive_directory != NULL); + + int result; + + result = create_directory(target->archive_directory); + if (result != 0 && errno != EEXIST) { + const char *message = get_last_error(); + assert(message != NULL); + + print_message(Error, "Unable to create directory: %s: %s", target->archive_directory, message); + free((void *) message); + return EXIT_FAILURE; + } + + result = create_directory(target->output_directory); + if (result != 0 && errno != EEXIST) { + const char *message = get_last_error(); + assert(message != NULL); + + print_message(Error, "Unable to create directory: %s: %s", target->output_directory, message); + free((void *) message); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +/** + * @brief Print the supplied AST of the specified target to a graphviz ".gv" file + * @param ast + * @param target + */ +static void print_ast_to_file(AST_NODE_PTR ast, TargetConfig *target) { + assert(ast != NULL); + assert(target != NULL); + + if (!target->print_ast) + return; + + // create file path to write graphviz to + const char *path = make_file_path(1, target->name, ".gv", target->archive_directory); + + FILE *output = fopen((const char *) path, "w"); + if (output == NULL) { + const char *message = get_last_error(); + print_message(Error, "Unable to open file for syntax tree at: %s: %s", path, message); + free((void *) message); + } else { + + AST_fprint_graphviz(output, ast); + + fclose(output); + } + + free((void *) path); +} + +/** + * @brief Build the given target + * @param unit + * @param target + */ +static void build_target(ModuleFileStack *unit, TargetConfig *target) { + print_message(Info, "Building target: %s", target->name); + + AST_NODE_PTR ast = AST_new_node(empty_location(), AST_Module, NULL); + ModuleFile *file = push_file(unit, target->root_module); + + if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) { + if (setup_target_environment(target) == 0) { + + print_ast_to_file(ast, target); + + // TODO: parse AST to semantic values + // TODO: backend codegen + } + } + + AST_delete_node(ast); + + print_file_statistics(file); +} + +/** + * @brief Compile a single file. + * Creates a single target by the given command line arguments. + * @param unit + * @param argc + * @param argv + */ +static void compile_file(ModuleFileStack *unit, int argc, char *argv[]) { + INFO("compiling basic files..."); + + TargetConfig *target = default_target_config_from_args(argc, argv); + + if (target->root_module == NULL) { + print_message(Error, "No input file specified."); + delete_target_config(target); + return; + } + + build_target(unit, target); + + delete_target_config(target); +} + +/** + * @brief Build all project targets specified by the command line arguments. + * @param unit + * @param config + * @param argc + * @param argv + */ +static void build_project_targets(ModuleFileStack *unit, ProjectConfig *config, int argc, char *argv[]) { + if (argc == 1 && strcmp(argv[0], "all") == 0) { + // build all targets in the project + GHashTableIter iter; + + g_hash_table_iter_init(&iter, config->targets); + + char *key; + TargetConfig *val; + while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) { + build_target(unit, val); + } + } else { + // build all targets given in the arguments + for (int i = 0; i < argc; i++) { + char *target_name = argv[i]; + if (g_hash_table_contains(config->targets, target_name)) { + build_target(unit, g_hash_table_lookup(config->targets, target_name)); + } + } + } +} + +/** + * @brief Build targets from project. Configuration is provided by command line arguments. + * @param unit File storage + * @param argc Number of arguments + * @param argv Array of Arguments + */ +static void build_project(ModuleFileStack *unit, int argc, char *argv[]) { + if (argc <= 0) { + print_message(Error, "No targets specified."); + return; + } + + ProjectConfig *config = default_project_config(); + int err = load_project_config(config); + + if (err == PROJECT_OK) { + build_project_targets(unit, config, argc, argv); + } + + delete_project_config(config); +} + +void run_compiler(int argc, char *argv[]) { + if (argc <= 0) { + INFO("no arguments provided"); + print_help(); + return; + } + + ModuleFileStack files = new_file_stack(); + + if (strcmp(argv[0], "build") == 0) { + build_project(&files, argc - 1, &argv[1]); + } else if (strcmp(argv[0], "compile") == 0) { + compile_file(&files, argc - 1, &argv[1]); + } else { + print_message(Error, "Invalid mode of operation. Rerun with --help."); + } + + if (files.files == NULL) { + print_message(Error, "No input files, nothing to do."); + exit(1); + } + + print_unit_statistics(&files); + delete_files(&files); +} diff --git a/src/compiler.h b/src/compiler.h new file mode 100644 index 0000000..943e47b --- /dev/null +++ b/src/compiler.h @@ -0,0 +1,15 @@ +// +// Created by servostar on 6/2/24. +// + +#ifndef GEMSTONE_COMPILER_H +#define GEMSTONE_COMPILER_H + +/** + * @brief Run the gemstone compiler with the provided command arguments. + * @param argc + * @param argv + */ +void run_compiler(int argc, char *argv[]); + +#endif //GEMSTONE_COMPILER_H diff --git a/src/io/files.c b/src/io/files.c index 09ff395..86cdfdb 100644 --- a/src/io/files.c +++ b/src/io/files.c @@ -6,6 +6,28 @@ #include #include #include +#include + +#ifdef __unix__ + +#include + +#define MAX_PATH_BYTES PATH_MAX + +#elif defined(_WIN32) || defined(WIN32) + +#include + +#define MAX_PATH_BYTES _MAX_PATH + +#endif + +ModuleFileStack new_file_stack() { + ModuleFileStack stack; + stack.files = NULL; + + return stack; +} ModuleFile *push_file(ModuleFileStack *stack, const char *path) { assert(stack != NULL); @@ -92,12 +114,13 @@ void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, c break; } - char absolute_path[PATH_MAX]; - realpath(file->path, absolute_path); + const char *absolute_path = get_absolute_path(file->path); printf("%s%s:%ld:%s %s%s:%s %s\n", BOLD, absolute_path, location->line_start, RESET, accent_color, kind_text, RESET, message); + free((void *) absolute_path); + size_t lines = location->line_end - location->line_start + 1; for (size_t l = 0; l < lines; l++) { @@ -261,7 +284,73 @@ void print_message(Message kind, const char *fmt, ...) { printf("%s%s:%s ", accent_color, kind_text, RESET); vprintf(fmt, args); - printf("\n\n"); + printf("\n"); va_end(args); } + +int create_directory(const char *path) { + assert(path != NULL); + + DEBUG("creating directory: %s", path); + + int result; +#ifdef __unix__ + result = mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); +#elif defined(_WIN32) || defined(WIN32) + result = _mkdir(path); +#endif + + return result; +} + +const char *get_last_error() { + return strdup(strerror(errno)); +} + +const char *get_absolute_path(const char *path) { + assert(path != NULL); + + DEBUG("resolving absolute path of: %s", path); + +#ifdef __unix__ + // use unix specific function + char absolute_path[MAX_PATH_BYTES]; + realpath(path, absolute_path); +#elif defined(_WIN32) || defined(WIN32) + // use Windows CRT specific function + char absolute_path[MAX_PATH_BYTES]; + _fullpath(path, absolute_path, _MAX_PATH); +#endif + + return strdup(absolute_path); +} + +const char* make_file_path(int count, const char* name, const char* ext, ...) { + DEBUG("building file path..."); + + va_list args; + va_start(args, count); // Initialize the va_list with the first variadic argument + + char* path = calloc(MAX_PATH_BYTES, sizeof(char)); + + for (int i = 0; i < count; i++) { + const char* arg = va_arg(args, const char*); + assert(arg != NULL); + + strcat(path, arg); + strcat(path, PATH_SEPARATOR); + } + + va_end(args); // Clean up the va_list + + if (name != NULL) { + strcat(path, name); + } + + if (name != NULL) { + strcat(path, ext); + } + + return path; +} diff --git a/src/io/files.h b/src/io/files.h index 25d5078..7b8b166 100644 --- a/src/io/files.h +++ b/src/io/files.h @@ -8,6 +8,12 @@ #include #include +#if defined(WIN32) || defined(_WIN32) +#define PATH_SEPARATOR "\\" +#else +#define PATH_SEPARATOR "/" +#endif + typedef struct FileDiagnosticStatistics_t { size_t error_count; size_t warning_count; @@ -37,6 +43,12 @@ typedef struct TokenLocation_t { unsigned long int col_end; } TokenLocation; +/** + * @brief Create a new, empty file stack. + * @return + */ +ModuleFileStack new_file_stack(); + /** * @brief Add a new file to the file stack. * @attention The file handle returned will be invalid @@ -65,18 +77,80 @@ void delete_files(ModuleFileStack *stack); TokenLocation new_location(unsigned long int line_start, unsigned long int col_start, unsigned long int line_end, unsigned long int col_end); +/** + * @brief Create a new empty location with all of its contents set to zero + * @return + */ TokenLocation empty_location(void); +/** + * @brief Prints some diagnostic message to stdout. + * This also print the token group and the attached source as context. + * @param file + * @param location + * @param kind + * @param message + */ [[gnu::nonnull(1), gnu::nonnull(2)]] void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, const char *message); [[gnu::nonnull(2)]] +/** + * @brief Print a general message to stdout. Provides no source context like print_diagnostic() + * @param kind + * @param fmt + * @param ... + */ void print_message(Message kind, const char *fmt, ...); +/** + * @brief Print statistics about a specific file. + * Will print the amount of infos, warning and errors emitted during compilation. + * @param file + */ [[gnu::nonnull(1)]] void print_file_statistics(ModuleFile *file); +/** + * @brief Print statistics of all files in the module stack. + * @param file_stack + */ [[gnu::nonnull(1)]] void print_unit_statistics(ModuleFileStack *file_stack); +/** + * @brief Create a new directory. Will return EEXISTS in case the directory already exists. + * @param path + * @return 0 if successful, anything else otherwise + */ +[[gnu::nonnull(1)]] +int create_directory(const char* path); + +/** + * @brief Get a string describing the last error set by errno. + * @return a string that must be freed + */ +[[nodiscard("pointer must be freed")]] +const char* get_last_error(); + +/** + * @brief Resolve the absolute path from a given relative path. + * @param path + * @return + */ +[[gnu::nonnull(1)]] +[[nodiscard("pointer must be freed")]] +const char* get_absolute_path(const char* path); + +/** + * @brief Create a file path from a base name, extension a variable amount of directory path segments. + * @param count Amount of path segments to prepend to the basename + * @param name Basename of the file + * @param ext Extension of the file + * @param ... Path segments without path separator + * @return A relative path of a file + */ +[[nodiscard("pointer must be freed")]] +const char* make_file_path(int count, const char* name, const char* ext, ...); + #endif //GEMSTONE_FILES_H diff --git a/src/main.c b/src/main.c index 397c652..a661dfb 100644 --- a/src/main.c +++ b/src/main.c @@ -1,58 +1,10 @@ #include #include #include -#include #include #include #include -#include -#include - -extern void yyrestart(FILE *); - -[[maybe_unused]] -AST_NODE_PTR root; -[[maybe_unused]] -ModuleFile *current_file; - -/** - * @brief Compile the specified file into AST - * @param ast - * @param file - * @return EXIT_SUCCESS in case the parsing was success full anything lese if not - */ -[[nodiscard("AST may be in invalid state")]] -[[gnu::nonnull(1), gnu::nonnull(1)]] - -static size_t compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) { - assert(file->path != NULL); - assert(ast != NULL); - - file->handle = fopen(file->path, "r"); - - if (file->handle == NULL) { - INFO("unable to open file: %s", file->path); - print_message(Error, "Cannot open file %s: %s", file->path, strerror(errno)); - return 1; - } - - DEBUG("parsing file: %s", file->path); - // setup global state - root = ast; - current_file = file; - yyin = file->handle; - yyrestart(yyin); - lex_reset(); - - yyparse(); - - // clean up global state - // current_file = NULL; - root = NULL; - yyin = NULL; - - return 0; -} +#include /** * @brief Log a debug message to inform about beginning exit procedures @@ -60,17 +12,6 @@ static size_t compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) { */ void notify_exit(void) { DEBUG("Exiting gemstone..."); } -/** - * @brief Closes File after compiling. - * - */ - -void close_file(void) { - if (NULL != yyin) { - fclose(yyin); - } -} - /** * @brief Run compiler setup here * @@ -95,123 +36,12 @@ void setup(void) { DEBUG("finished starting up gemstone..."); } -static void setup_target_environment(const TargetConfig* target) { - if (target->output_directory) { - - } -} - -void build_target(ModuleFileStack *unit, TargetConfig *target) { - print_message(Info, "Compiling file: %s", target->root_module); - - TokenLocation location = new_location(0,0,0,0); - AST_NODE_PTR ast = AST_new_node(location, AST_Module, NULL); - ModuleFile *file = push_file(unit, target->root_module); - - if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) { - setup_target_environment(target); - - if (target->print_ast) { - - } - - // TODO: parse AST to semantic values - // TODO: backend codegen - } - - AST_delete_node(ast); - - print_file_statistics(file); -} - -void compile_file(ModuleFileStack *unit, int argc, char *argv[]) { - INFO("compiling basic files..."); - - TargetConfig *target = default_target_config_from_args(argc, argv); - - if (target->root_module == NULL) { - print_message(Error, "No input file specified."); - delete_target_config(target); - return; - } - - build_target(unit, target); - - delete_target_config(target); -} - -void build_project_targets(ModuleFileStack *unit, ProjectConfig *config, int argc, char *argv[]) { - if (argc == 1 && strcmp(argv[0], "all") == 0) { - GHashTableIter iter; - - g_hash_table_iter_init(&iter, config->targets); - - char *key; - TargetConfig *val; - while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) { - build_target(unit, val); - } - } else { - for (int i = 0; i < argc; i++) { - char *target_name = argv[i]; - if (g_hash_table_contains(config->targets, target_name)) { - build_target(unit, g_hash_table_lookup(config->targets, target_name)); - } - } - } -} - -void build_project(ModuleFileStack *unit, int argc, char *argv[]) { - if (argc <= 0) { - print_message(Error, "No targets specified."); - return; - } - - ProjectConfig *config = default_project_config(); - int err = load_project_config(config); - - if (err == PROJECT_OK) { - build_project_targets(unit, config, argc, argv); - } - - delete_project_config(config); -} - -void configure_run_mode(int argc, char *argv[]) { - if (argc <= 0) { - INFO("no arguments provided"); - print_help(); - return; - } - - ModuleFileStack files; - files.files = NULL; - - if (strcmp(argv[0], "build") == 0) { - build_project(&files, argc - 1, &argv[1]); - } else if (strcmp(argv[0], "compile") == 0) { - compile_file(&files, argc - 1, &argv[1]); - } else { - print_message(Error, "Invalid mode of operation. Rerun with --help."); - } - - if (files.files == NULL) { - print_message(Error, "No input files, nothing to do."); - exit(1); - } - - print_unit_statistics(&files); - delete_files(&files); -} - int main(int argc, char *argv[]) { - setup(); - atexit(close_file); - print_message(Info, "running GSC version %s", GSC_VERSION); + print_message(Info, "Running GSC version %s", GSC_VERSION); - configure_run_mode(argc - 1, &argv[1]); + run_compiler(argc - 1, &argv[1]); return 0; } From 33988beb107faa946896a150e285b53acac9a244 Mon Sep 17 00:00:00 2001 From: servostar Date: Mon, 3 Jun 2024 00:24:17 +0200 Subject: [PATCH 14/21] added proper handling of arguments --- src/cfg/opt.c | 130 ++++++++++++++++++++++++---- src/cfg/opt.h | 21 ++++- src/compiler.c | 57 ++++++------ src/compiler.h | 4 +- src/main.c | 13 ++- src/sys/log.c | 19 +++- src/sys/log.h | 12 ++- tests/ast/CMakeLists.txt | 12 +++ tests/input_file/test_input_file.py | 4 +- tests/logging/CMakeLists.txt | 23 +++++ tests/logging/level.c | 5 +- tests/logging/output.c | 5 +- tests/logging/panic.c | 5 +- tests/logging/streams.c | 5 +- 14 files changed, 250 insertions(+), 65 deletions(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 92577d8..b19e506 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -6,6 +6,84 @@ #include #include #include +#include + +static GHashTable* args = NULL; + +static void clean(void) { + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, args); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + free(value); + } + + g_hash_table_destroy(args); +} + +void parse_options(int argc, char* argv[]) { + args = g_hash_table_new(g_str_hash, g_str_equal); + + atexit(clean); + + for (int i = 0; i < argc; i++) { + Option* option = malloc(sizeof(Option)); + option->is_opt = g_str_has_prefix(argv[i], "--"); + option->string = argv[i] + (option->is_opt ? 2 : 0); + option->index = i; + option->value = NULL; + + char* equals = strchr(argv[i], '='); + if (equals != NULL) { + option->value = equals; + } + + g_hash_table_insert(args, (gpointer) option->string, (gpointer) option); + } +} + +bool is_option_set(const char* option) { + assert(option != NULL); + assert(args != NULL); + return g_hash_table_contains(args, option); +} + +const Option* get_option(const char* option) { + if (g_hash_table_contains(args, option)) { + return g_hash_table_lookup(args, option); + } + + return NULL; +} + +GArray* get_non_options_after(const char* command) { + const Option* command_option = get_option(command); + + if (command_option == NULL) { + return NULL; + } + + GArray* array = g_array_new(FALSE, FALSE, sizeof(const char*)); + + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, args); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + Option* option = (Option*) value; + if (!option->is_opt && command_option->index < option->index) { + g_array_append_val(array, option->string); + } + } + + if (array->len == 0) { + g_array_free(array, FALSE); + return NULL; + } + + return array; +} TargetConfig* default_target_config() { DEBUG("generating default target config..."); @@ -25,32 +103,44 @@ TargetConfig* default_target_config() { return config; } -TargetConfig* default_target_config_from_args(int argc, char *argv[]) { +TargetConfig* default_target_config_from_args() { DEBUG("generating default target from command line..."); TargetConfig* config = default_target_config(); - for (int i = 0; i < argc; i++) { - DEBUG("processing argument: %ld %s", i, argv[i]); - char *option = argv[i]; + if (is_option_set("print-ast")) { + config->print_ast = true; + } else if (is_option_set("print-asm")) { + config->print_asm = true; + } else if (is_option_set("print-ir")) { + config->print_ir = true; + } else if (is_option_set("mode")) { + const Option* opt = get_option("mode"); - if (strcmp(option, "--print-ast") == 0) { - config->print_ast = true; - } else if (strcmp(option, "--print-asm") == 0) { - config->print_asm = true; - } else if (strcmp(option, "--print-ir") == 0) { - config->print_ir = true; - } else if (strcmp(option, "--mode=app") == 0) { - config->mode = Application; - } else if (strcmp(option, "--mode=lib") == 0) { - config->mode = Library; - } else if (config->root_module == NULL) { - config->root_module = strdup(option); - } else { - print_message(Warning, "Got more than one file to compile, using first by ignoring others."); + if (opt->value != NULL) { + if (strcmp(opt->value, "app") == 0) { + config->mode = Application; + } else if (strcmp(opt->value, "lib") == 0) { + config->mode = Library; + } else { + print_message(Warning, "Invalid compilation mode: %s", opt->value); + } } } + GArray* files = get_non_options_after("compile"); + + if (files == NULL) { + print_message(Error, "No input file provided."); + } else { + + if (files->len > 1) { + print_message(Warning, "Got more than one file to compile, using first, ignoring others."); + } + + config->root_module = strdup( ((char**) files->data) [0]); + } + return config; } @@ -66,7 +156,9 @@ void print_help(void) { " --print-ast print resulting abstract syntax tree to a file", " --print-asm print resulting assembly language to a file", " --print-ir print resulting LLVM-IR to a file", - " --mode=[app|lib] set the compilation mode to either application or library" + " --mode=[app|lib] set the compilation mode to either application or library", + " --verbose print logs with level information or higher", + " --debug print debug logs (if not disabled at compile time)" }; for (unsigned int i = 0; i < sizeof(lines) / sizeof(const char *); i++) { diff --git a/src/cfg/opt.h b/src/cfg/opt.h index c54bfb2..9f0b7c0 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -52,11 +52,18 @@ typedef struct ProjectConfig_t { GHashTable* targets; } ProjectConfig; +typedef struct Option_t { + int index; + const char* string; + const char* value; + bool is_opt; +} Option; + TargetConfig* default_target_config(); ProjectConfig* default_project_config(); -TargetConfig* default_target_config_from_args(int argc, char* argv[]); +TargetConfig* default_target_config_from_args(); int load_project_config(ProjectConfig *config); @@ -66,4 +73,16 @@ void delete_project_config(ProjectConfig* config); void delete_target_config(TargetConfig*); +void parse_options(int argc, char* argv[]); + +[[gnu::nonnull(1)]] +bool is_option_set(const char* option); + +[[gnu::nonnull(1)]] +const Option* get_option(const char* option); + +[[gnu::nonnull(1)]] +[[nodiscard("must be freed")]] +GArray* get_non_options_after(const char* command); + #endif //GEMSTONE_OPT_H diff --git a/src/compiler.c b/src/compiler.c index 1889763..79e9b20 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -160,10 +160,10 @@ static void build_target(ModuleFileStack *unit, TargetConfig *target) { * @param argc * @param argv */ -static void compile_file(ModuleFileStack *unit, int argc, char *argv[]) { +static void compile_file(ModuleFileStack *unit) { INFO("compiling basic files..."); - TargetConfig *target = default_target_config_from_args(argc, argv); + TargetConfig *target = default_target_config_from_args(); if (target->root_module == NULL) { print_message(Error, "No input file specified."); @@ -180,11 +180,9 @@ static void compile_file(ModuleFileStack *unit, int argc, char *argv[]) { * @brief Build all project targets specified by the command line arguments. * @param unit * @param config - * @param argc - * @param argv */ -static void build_project_targets(ModuleFileStack *unit, ProjectConfig *config, int argc, char *argv[]) { - if (argc == 1 && strcmp(argv[0], "all") == 0) { +static void build_project_targets(ModuleFileStack *unit, ProjectConfig *config) { + if (is_option_set("all")) { // build all targets in the project GHashTableIter iter; @@ -195,52 +193,51 @@ static void build_project_targets(ModuleFileStack *unit, ProjectConfig *config, while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) { build_target(unit, val); } - } else { - // build all targets given in the arguments - for (int i = 0; i < argc; i++) { - char *target_name = argv[i]; + return; + } + + // build all targets given in the arguments + GArray* targets = get_non_options_after("build"); + + if (targets != NULL) { + for (guint i = 0; i < targets->len; i++) { + const char *target_name = (((Option*) targets->data) + i)->string; + if (g_hash_table_contains(config->targets, target_name)) { build_target(unit, g_hash_table_lookup(config->targets, target_name)); + } else { + print_message(Error, "Unknown target: %s", target_name); } } + + g_array_free(targets, FALSE); + } else { + print_message(Error, "No targets specified."); } } /** * @brief Build targets from project. Configuration is provided by command line arguments. * @param unit File storage - * @param argc Number of arguments - * @param argv Array of Arguments */ -static void build_project(ModuleFileStack *unit, int argc, char *argv[]) { - if (argc <= 0) { - print_message(Error, "No targets specified."); - return; - } - +static void build_project(ModuleFileStack *unit) { ProjectConfig *config = default_project_config(); int err = load_project_config(config); if (err == PROJECT_OK) { - build_project_targets(unit, config, argc, argv); + build_project_targets(unit, config); } delete_project_config(config); } -void run_compiler(int argc, char *argv[]) { - if (argc <= 0) { - INFO("no arguments provided"); - print_help(); - return; - } - +void run_compiler() { ModuleFileStack files = new_file_stack(); - if (strcmp(argv[0], "build") == 0) { - build_project(&files, argc - 1, &argv[1]); - } else if (strcmp(argv[0], "compile") == 0) { - compile_file(&files, argc - 1, &argv[1]); + if (is_option_set("build")) { + build_project(&files); + } else if (is_option_set("compile")) { + compile_file(&files); } else { print_message(Error, "Invalid mode of operation. Rerun with --help."); } diff --git a/src/compiler.h b/src/compiler.h index 943e47b..a82a38f 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -7,9 +7,7 @@ /** * @brief Run the gemstone compiler with the provided command arguments. - * @param argc - * @param argv */ -void run_compiler(int argc, char *argv[]); +void run_compiler(); #endif //GEMSTONE_COMPILER_H diff --git a/src/main.c b/src/main.c index a661dfb..72b397a 100644 --- a/src/main.c +++ b/src/main.c @@ -4,6 +4,7 @@ #include #include #include +#include #include /** @@ -16,8 +17,9 @@ void notify_exit(void) { DEBUG("Exiting gemstone..."); } * @brief Run compiler setup here * */ -void setup(void) { +void setup(int argc, char *argv[]) { // setup preample + parse_options(argc, argv); log_init(); DEBUG("starting gemstone..."); @@ -37,11 +39,16 @@ void setup(void) { } int main(int argc, char *argv[]) { - setup(); + if (argc <= 1) { + print_help(); + exit(1); + } + + setup(argc, argv); print_message(Info, "Running GSC version %s", GSC_VERSION); - run_compiler(argc - 1, &argv[1]); + run_compiler(); return 0; } diff --git a/src/sys/log.c b/src/sys/log.c index 0aeab5f..7dcf94e 100644 --- a/src/sys/log.c +++ b/src/sys/log.c @@ -4,14 +4,29 @@ #include #include #include +#include +#include static struct Logger_t { FILE** streams; size_t stream_count; } GlobalLogger; -void log_init(void) +int runtime_log_level = LOG_LEVEL_WARNING; + +void set_log_level(int level) { + runtime_log_level = level; +} + +void log_init() +{ + if (is_option_set("verbose")) { + set_log_level(LOG_LEVEL_INFORMATION); + } else if (is_option_set("debug")) { + set_log_level(LOG_LEVEL_DEBUG); + } + assert(LOG_DEFAULT_STREAM != NULL); log_register_stream(LOG_DEFAULT_STREAM); } @@ -30,7 +45,7 @@ void log_register_stream(FILE* restrict stream) if (GlobalLogger.streams == NULL) { PANIC("failed to allocate stream buffer"); - } + } } else { diff --git a/src/sys/log.h b/src/sys/log.h index 6b8991d..6db8fe5 100644 --- a/src/sys/log.h +++ b/src/sys/log.h @@ -53,12 +53,22 @@ will not print. #define INFO(format, ...) __LOG(LOG_STRING_INFORMATION, LOG_LEVEL_INFORMATION, format"\n", ##__VA_ARGS__) #define DEBUG(format, ...) __LOG(LOG_STRING_DEBUG, LOG_LEVEL_DEBUG, format"\n", ##__VA_ARGS__) +extern int runtime_log_level; + #define __LOG(level, priority, format, ...) \ do { \ if (LOG_LEVEL <= priority) \ - syslog_logf(level, __FILE_NAME__, __LINE__, __func__, format, ##__VA_ARGS__); \ + if (runtime_log_level <= priority) \ + syslog_logf(level, __FILE_NAME__, __LINE__, __func__, format, ##__VA_ARGS__); \ } while(0) +/** + * @brief Set the runtime log level. Must be one of: LOG_LEVEL_ERROR, LOG_LEVEL_WARNING, + * LOG_LEVEL_INFORMATION, LOG_LEVEL_DEBUG + * @param level the new log level + */ +void set_log_level(int level); + /** * @brief Log a message into all registered streams * diff --git a/tests/ast/CMakeLists.txt b/tests/ast/CMakeLists.txt index 88007e4..5bd19b6 100644 --- a/tests/ast/CMakeLists.txt +++ b/tests/ast/CMakeLists.txt @@ -10,6 +10,12 @@ find_package(PkgConfig REQUIRED) pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0) include_directories(PRIVATE ${GLIB_INCLUDE_DIRS}) +# ------------------------------------------------ # +# Setup TOML-C99 # +# ------------------------------------------------ # + +include_directories(${PROJECT_SOURCE_DIR}/dep/tomlc99) + # ------------------------------------------------------- # # CTEST 1 # test building the syntax tree @@ -19,6 +25,8 @@ add_executable(ast_build_tree ${PROJECT_SOURCE_DIR}/src/sys/log.c ${PROJECT_SOURCE_DIR}/src/io/files.c ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c build_tree.c) set_target_properties(ast_build_tree PROPERTIES @@ -38,6 +46,8 @@ add_executable(ast_print_node ${PROJECT_SOURCE_DIR}/src/sys/log.c ${PROJECT_SOURCE_DIR}/src/io/files.c ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c print_node.c) set_target_properties(ast_print_node PROPERTIES @@ -57,6 +67,8 @@ add_executable(ast_graphviz ${PROJECT_SOURCE_DIR}/src/sys/log.c ${PROJECT_SOURCE_DIR}/src/io/files.c ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c print_graphviz.c) set_target_properties(ast_graphviz PROPERTIES diff --git a/tests/input_file/test_input_file.py b/tests/input_file/test_input_file.py index 3721dc0..550f3f4 100644 --- a/tests/input_file/test_input_file.py +++ b/tests/input_file/test_input_file.py @@ -12,7 +12,7 @@ def check_accept(): test_file_name = sys.argv[1] - p = subprocess.run(["./gsc", test_file_name], capture_output=True, text=True) + p = subprocess.run(["./gsc", "compile", test_file_name], capture_output=True, text=True) assert p.returncode == 0 @@ -22,7 +22,7 @@ def check_abort(): logging.basicConfig(level=logging.INFO) - p = subprocess.run("./gsc", capture_output=True, text=True) + p = subprocess.run(["./gsc", "compile"], capture_output=True, text=True) assert p.returncode == 1 diff --git a/tests/logging/CMakeLists.txt b/tests/logging/CMakeLists.txt index d5abada..2bfa385 100644 --- a/tests/logging/CMakeLists.txt +++ b/tests/logging/CMakeLists.txt @@ -8,6 +8,13 @@ include_directories(${PROJECT_SOURCE_DIR}/src) find_package(PkgConfig REQUIRED) pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +include_directories(PRIVATE ${GLIB_INCLUDE_DIRS}) + +# ------------------------------------------------ # +# Setup TOML-C99 # +# ------------------------------------------------ # + +include_directories(${PROJECT_SOURCE_DIR}/dep/tomlc99) # ------------------------------------------------------- # # CTEST 1 @@ -16,11 +23,15 @@ pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0) add_executable(logging_output ${PROJECT_SOURCE_DIR}/src/sys/log.c ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/src/io/files.c output.c) set_target_properties(logging_output PROPERTIES OUTPUT_NAME "output" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) +target_link_libraries(logging_output PkgConfig::GLIB) +target_link_libraries(logging_output tomlc99) add_test(NAME logging_output WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_output) @@ -32,11 +43,15 @@ add_test(NAME logging_output add_executable(logging_panic ${PROJECT_SOURCE_DIR}/src/sys/log.c ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/src/io/files.c panic.c) set_target_properties(logging_panic PROPERTIES OUTPUT_NAME "panic" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) +target_link_libraries(logging_panic PkgConfig::GLIB) +target_link_libraries(logging_panic tomlc99) add_test(NAME logging_panic WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_panic) @@ -48,11 +63,15 @@ add_test(NAME logging_panic add_executable(logging_streams ${PROJECT_SOURCE_DIR}/src/sys/log.c ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/src/io/files.c streams.c) set_target_properties(logging_streams PROPERTIES OUTPUT_NAME "stream" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) +target_link_libraries(logging_streams PkgConfig::GLIB) +target_link_libraries(logging_streams tomlc99) add_test(NAME logging_streams WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_stream) @@ -64,11 +83,15 @@ add_test(NAME logging_streams add_executable(logging_level ${PROJECT_SOURCE_DIR}/src/sys/log.c ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/src/io/files.c level.c) set_target_properties(logging_level PROPERTIES OUTPUT_NAME "level" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) +target_link_libraries(logging_level PkgConfig::GLIB) +target_link_libraries(logging_level tomlc99) add_test(NAME logging_level WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_level) diff --git a/tests/logging/level.c b/tests/logging/level.c index fd37cc7..ed89205 100644 --- a/tests/logging/level.c +++ b/tests/logging/level.c @@ -4,11 +4,14 @@ #include "sys/log.h" #include +#include #define LOG_LEVEL LOG_LEVEL_WARNING -int main(void) { +int main(int argc, char* argv[]) { + parse_options(argc, argv); log_init(); + set_log_level(LOG_LEVEL_DEBUG); col_init(); DEBUG("logging some debug..."); diff --git a/tests/logging/output.c b/tests/logging/output.c index dde7d90..a6cfdb2 100644 --- a/tests/logging/output.c +++ b/tests/logging/output.c @@ -4,9 +4,12 @@ #include "sys/log.h" #include +#include -int main(void) { +int main(int argc, char* argv[]) { + parse_options(argc, argv); log_init(); + set_log_level(LOG_LEVEL_DEBUG); col_init(); DEBUG("logging some debug..."); diff --git a/tests/logging/panic.c b/tests/logging/panic.c index ecd8815..f64f3a1 100644 --- a/tests/logging/panic.c +++ b/tests/logging/panic.c @@ -3,9 +3,12 @@ // #include "sys/log.h" +#include -int main(void) { +int main(int argc, char* argv[]) { + parse_options(argc, argv); log_init(); + set_log_level(LOG_LEVEL_DEBUG); // this should appear in stderr INFO("before exit"); diff --git a/tests/logging/streams.c b/tests/logging/streams.c index 97599da..5dd7cc2 100644 --- a/tests/logging/streams.c +++ b/tests/logging/streams.c @@ -4,6 +4,7 @@ #include "sys/log.h" #include +#include static FILE* file; @@ -13,8 +14,10 @@ void close_file(void) { } } -int main(void) { +int main(int argc, char* argv[]) { + parse_options(argc, argv); log_init(); + set_log_level(LOG_LEVEL_DEBUG); // this should appear in stderr INFO("should only be in stderr"); From f16c10d94bcaadc329cd01aef20ab8176c834d06 Mon Sep 17 00:00:00 2001 From: servostar Date: Mon, 3 Jun 2024 00:31:22 +0200 Subject: [PATCH 15/21] fixed not passing sdk check --- src/compiler.c | 2 +- src/io/files.c | 2 +- src/io/files.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler.c b/src/compiler.c index 79e9b20..69e4e02 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -110,7 +110,7 @@ static void print_ast_to_file(AST_NODE_PTR ast, TargetConfig *target) { return; // create file path to write graphviz to - const char *path = make_file_path(1, target->name, ".gv", target->archive_directory); + const char *path = make_file_path(target->name, ".gv", 1, target->archive_directory); FILE *output = fopen((const char *) path, "w"); if (output == NULL) { diff --git a/src/io/files.c b/src/io/files.c index 86cdfdb..0ea8993 100644 --- a/src/io/files.c +++ b/src/io/files.c @@ -326,7 +326,7 @@ const char *get_absolute_path(const char *path) { return strdup(absolute_path); } -const char* make_file_path(int count, const char* name, const char* ext, ...) { +const char* make_file_path(const char* name, const char* ext, int count, ...) { DEBUG("building file path..."); va_list args; diff --git a/src/io/files.h b/src/io/files.h index 7b8b166..61c7ebb 100644 --- a/src/io/files.h +++ b/src/io/files.h @@ -151,6 +151,6 @@ const char* get_absolute_path(const char* path); * @return A relative path of a file */ [[nodiscard("pointer must be freed")]] -const char* make_file_path(int count, const char* name, const char* ext, ...); +const char* make_file_path(const char* name, const char* ext, int count, ...); #endif //GEMSTONE_FILES_H From e25fd4b559042cd2c45cf87c5217998b6705897a Mon Sep 17 00:00:00 2001 From: servostar Date: Mon, 3 Jun 2024 10:14:25 +0200 Subject: [PATCH 16/21] added missing --help --- src/main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main.c b/src/main.c index 72b397a..948ce04 100644 --- a/src/main.c +++ b/src/main.c @@ -46,6 +46,11 @@ int main(int argc, char *argv[]) { setup(argc, argv); + if (is_option_set("help")) { + print_help(); + exit(0); + } + print_message(Info, "Running GSC version %s", GSC_VERSION); run_compiler(); From 22ed50f441dec1dc904f24178f44f17a670e56d2 Mon Sep 17 00:00:00 2001 From: servostar Date: Mon, 3 Jun 2024 10:18:33 +0200 Subject: [PATCH 17/21] made error to info for not finding project file --- src/cfg/opt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index b19e506..016072a 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -305,7 +305,7 @@ int load_project_config(ProjectConfig *config) { FILE *config_file = fopen(PROJECT_CONFIG_FILE, "r"); if (config_file == NULL) { print_message(Error, "Cannot open file %s: %s", PROJECT_CONFIG_FILE, strerror(errno)); - ERROR("project file not found"); + INFO("project file not found"); return PROJECT_TOML_ERR; } From 4404cea01925fe4744dc272868b7425b23ee1788 Mon Sep 17 00:00:00 2001 From: servostar Date: Mon, 3 Jun 2024 11:08:25 +0200 Subject: [PATCH 18/21] fixed option handling --- src/cfg/opt.c | 56 +++++++++++++++++++++++++++++++++++---------------- src/main.c | 5 ++++- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 016072a..ad86285 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -17,6 +17,7 @@ static void clean(void) { while (g_hash_table_iter_next(&iter, &key, &value)) { free(value); + free(key); } g_hash_table_destroy(args); @@ -30,13 +31,14 @@ void parse_options(int argc, char* argv[]) { for (int i = 0; i < argc; i++) { Option* option = malloc(sizeof(Option)); option->is_opt = g_str_has_prefix(argv[i], "--"); - option->string = argv[i] + (option->is_opt ? 2 : 0); + option->string = strdup(argv[i] + (option->is_opt ? 2 : 0)); option->index = i; option->value = NULL; - char* equals = strchr(argv[i], '='); + char* equals = strchr(option->string, '='); if (equals != NULL) { - option->value = equals; + option->value = equals + 1; + *equals = 0; } g_hash_table_insert(args, (gpointer) option->string, (gpointer) option); @@ -110,11 +112,17 @@ TargetConfig* default_target_config_from_args() { if (is_option_set("print-ast")) { config->print_ast = true; - } else if (is_option_set("print-asm")) { + } + + if (is_option_set("print-asm")) { config->print_asm = true; - } else if (is_option_set("print-ir")) { + } + + if (is_option_set("print-ir")) { config->print_ir = true; - } else if (is_option_set("mode")) { + } + + if (is_option_set("mode")) { const Option* opt = get_option("mode"); if (opt->value != NULL) { @@ -128,6 +136,14 @@ TargetConfig* default_target_config_from_args() { } } + if (is_option_set("output")) { + const Option* opt = get_option("output"); + + if (opt->value != NULL) { + config->name = strdup(opt->value); + } + } + GArray* files = get_non_options_after("compile"); if (files == NULL) { @@ -139,6 +155,8 @@ TargetConfig* default_target_config_from_args() { } config->root_module = strdup( ((char**) files->data) [0]); + + g_array_free(files, TRUE); } return config; @@ -148,17 +166,21 @@ void print_help(void) { DEBUG("printing help dialog..."); const char *lines[] = { - "Gemstone Compiler (C) GPL-2.0", - "Build a project target: gsc build [target]|all", - "Compile non-project file: gsc compile [file]", - "Options:", - " --version print the version", - " --print-ast print resulting abstract syntax tree to a file", - " --print-asm print resulting assembly language to a file", - " --print-ir print resulting LLVM-IR to a file", - " --mode=[app|lib] set the compilation mode to either application or library", - " --verbose print logs with level information or higher", - " --debug print debug logs (if not disabled at compile time)" + "Gemstone Compiler (c) GPL-2.0", + "Build a project target: gsc build [target]|all", + "Compile non-project file: gsc compile [file]", + "Output information: gsc