From 76f5a0fd7fde277a3fb47f396c38f9fb1ca70a03 Mon Sep 17 00:00:00 2001 From: servostar Date: Sun, 1 Sep 2024 16:55:53 +0200 Subject: [PATCH] rework of module import --- lib/build.toml | 13 ---- lib/src/build.toml | 22 +++++++ lib/src/{glibc => libc}/bootstrap.gsc | 4 +- lib/src/{glibc => libc}/os.gsc | 0 lib/src/{glibc => libc}/std.gsc | 0 lib/src/{glibc => libc}/types.gsc | 0 src/ast/ast.c | 23 +++++++ src/ast/ast.h | 3 + src/cfg/opt.c | 50 ++++++++++---- src/cfg/opt.h | 8 +++ src/compiler.c | 93 ++++++++++++++++++++++++--- src/link/lldc/lldc.c | 14 +++- src/llvm/link/lld.c | 26 +++----- src/set/set.c | 13 +--- tests/box/build.toml | 11 +++- 15 files changed, 211 insertions(+), 69 deletions(-) delete mode 100644 lib/build.toml create mode 100644 lib/src/build.toml rename lib/src/{glibc => libc}/bootstrap.gsc (89%) rename lib/src/{glibc => libc}/os.gsc (100%) rename lib/src/{glibc => libc}/std.gsc (100%) rename lib/src/{glibc => libc}/types.gsc (100%) diff --git a/lib/build.toml b/lib/build.toml deleted file mode 100644 index b62f714..0000000 --- a/lib/build.toml +++ /dev/null @@ -1,13 +0,0 @@ -[project] -name = "gscstd" -version = "0.1.0" -description = "glibc build of the gemstone standard library" -license = "GPL-2.0" -authors = [ "Sven Vogel " ] - -[target.gscstd] -import-paths = [ "glibc" ] -root = "glibc/std.gsc" -mode = "library" -output = "bin" -archive = "archive" diff --git a/lib/src/build.toml b/lib/src/build.toml new file mode 100644 index 0000000..2fb2f85 --- /dev/null +++ b/lib/src/build.toml @@ -0,0 +1,22 @@ +[project] +name = "gscstd" +version = "0.1.0" +description = "Test applications for the GSC standard library." +license = "GPL-2.0" +authors = [ "Sven Vogel " ] + +[[targets]] +name = "gsc-libc" +import-paths = [ "libc" ] +root = "libc/std.gsc" +mode = "library" +output = "bin" +archive = "archive" +driver = "ld.lld" +print_asm = true +print_ir = true +print_ast = true + +[[targets.dependencies]] +# link against system default libc shared library +libc = { shared = true } diff --git a/lib/src/glibc/bootstrap.gsc b/lib/src/libc/bootstrap.gsc similarity index 89% rename from lib/src/glibc/bootstrap.gsc rename to lib/src/libc/bootstrap.gsc index 0f028c5..caa0546 100644 --- a/lib/src/glibc/bootstrap.gsc +++ b/lib/src/libc/bootstrap.gsc @@ -2,13 +2,13 @@ # Bootstrap module for libgscstd-glibc # used on GNU/Linux operating systems -import "os" +include "os" # main function is to be implemented by the application source fun main() # entrypoint function -fun start() { +fun _start() { main() exit(0 as i32) } diff --git a/lib/src/glibc/os.gsc b/lib/src/libc/os.gsc similarity index 100% rename from lib/src/glibc/os.gsc rename to lib/src/libc/os.gsc diff --git a/lib/src/glibc/std.gsc b/lib/src/libc/std.gsc similarity index 100% rename from lib/src/glibc/std.gsc rename to lib/src/libc/std.gsc diff --git a/lib/src/glibc/types.gsc b/lib/src/libc/types.gsc similarity index 100% rename from lib/src/glibc/types.gsc rename to lib/src/libc/types.gsc diff --git a/src/ast/ast.c b/src/ast/ast.c index b6e59f6..a5cae45 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -336,6 +336,29 @@ void AST_merge_modules(AST_NODE_PTR dst, size_t k, AST_NODE_PTR src) { AST_delete_node(src); } +void AST_import_module(AST_NODE_PTR dst, size_t k, AST_NODE_PTR src) { + assert(dst != NULL); + assert(src != NULL); + + size_t elements = src->children->len; + for (size_t i = 0; i < elements; i++) { + AST_NODE_PTR node = AST_remove_child(src, 0); + + if (node->kind == AST_FunDef) { + AST_NODE_PTR decl = AST_new_node(node->location, AST_FunDecl, NULL); + + for (int u = 0; u < node->children->len - 1; u++) { + AST_push_node(decl, AST_get_node(node, u)); + } + + node = decl; + } + + AST_insert_node(dst, k + i, node); + } + AST_delete_node(src); +} + void AST_insert_node(AST_NODE_PTR owner, size_t idx, AST_NODE_PTR child) { assert(owner != NULL); assert(child != NULL); diff --git a/src/ast/ast.h b/src/ast/ast.h index be836e2..d4b8b8d 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -230,6 +230,9 @@ AST_NODE_PTR AST_get_node_by_kind(AST_NODE_PTR owner, [[gnu::nonnull(1), gnu::nonnull(3)]] void AST_merge_modules(AST_NODE_PTR dst, size_t i, AST_NODE_PTR src); +[[gnu::nonnull(1), gnu::nonnull(3)]] +void AST_import_module(AST_NODE_PTR dst, size_t i, AST_NODE_PTR src); + [[gnu::nonnull(1), gnu::nonnull(3)]] void AST_insert_node(AST_NODE_PTR owner, size_t idx, AST_NODE_PTR child); diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 00c8043..f673a57 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -401,13 +401,24 @@ static int get_mode_from_str(TargetCompilationMode* mode, const char* name) { return PROJECT_SEMANTIC_ERR; } +static int parse_dependency(Dependency* dependency, toml_table_t* table, char* name) { + dependency->name = mem_strdup(MemoryNamespaceOpt, name); + + get_str(&dependency->path, table, "path"); + get_str(&dependency->target, table, "target"); + + dependency->libraries = mem_new_g_array(MemoryNamespaceOpt, sizeof(char*)); + + return PROJECT_OK; +} + static int parse_target(const ProjectConfig* config, - const toml_table_t* target_table, const char* name) { + const toml_table_t* target_table) { DEBUG("parsing target table..."); TargetConfig* target_config = default_target_config(); - target_config->name = (char*) name; + get_str(&target_config->name, target_table, "name"); get_bool(&target_config->print_ast, target_table, "print_ast"); get_bool(&target_config->print_asm, target_table, "print_asm"); @@ -442,13 +453,33 @@ static int parse_target(const ProjectConfig* config, g_hash_table_insert(config->targets, target_config->name, target_config); + toml_table_t* dependencies = toml_table_in(target_table, "dependencies"); + if (dependencies) { + target_config->dependencies = mem_new_g_hash_table(MemoryNamespaceOpt, g_str_hash, g_str_equal); + for (int i = 0; i < toml_table_ntab(dependencies); i++) { + char* key = (char*) toml_key_in(dependencies, i); + + if (key == NULL) { + break; + } + + toml_table_t* dependency_table = toml_table_in(dependencies, key); + Dependency* dependency = mem_alloc(MemoryNamespaceOpt, sizeof(Dependency)); + if (parse_dependency(dependency, dependency_table, key) == PROJECT_SEMANTIC_ERR) { + return PROJECT_SEMANTIC_ERR; + } + + g_hash_table_insert(target_config->dependencies, mem_strdup(MemoryNamespaceOpt, key), dependency); + } + } + return PROJECT_OK; } static int parse_targets(ProjectConfig* config, const toml_table_t* root) { DEBUG("parsing targets of project \"%s\"", config->name); - toml_table_t* targets = toml_table_in(root, "target"); + toml_array_t* targets = toml_array_in(root, "targets"); if (targets == NULL) { print_message(Warning, "Project has no targets"); return PROJECT_SEMANTIC_ERR; @@ -457,16 +488,9 @@ static int parse_targets(ProjectConfig* config, const toml_table_t* root) { config->targets = mem_new_g_hash_table(MemoryNamespaceOpt, g_str_hash, g_str_equal); - for (int i = 0; i < toml_table_ntab(targets); i++) { - const char* key = toml_key_in(targets, i); - - if (key == NULL) { - break; - } - - toml_table_t* target = toml_table_in(targets, key); - parse_target(config, target, - mem_strdup(MemoryNamespaceOpt, (char*) key)); + for (int i = 0; i < toml_array_nelem(targets); i++) { + toml_table_t* target = toml_table_at(targets, i); + parse_target(config, target); } return PROJECT_OK; diff --git a/src/cfg/opt.h b/src/cfg/opt.h index af81ce5..c168df0 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -34,6 +34,13 @@ typedef enum TargetCompilationMode_t { Library } TargetCompilationMode; +typedef struct Dependency_t { + char* name; + char* path; + char* target; + GArray* libraries; +} Dependency; + /** * @brief A target defines a source file which is to be compiled into a specific * format. Additionally properties such as output folders can be set. @@ -66,6 +73,7 @@ typedef struct TargetConfig_t { // treat parser warnings as errors bool gsc_fatal_warnings; GArray* import_paths; + GHashTable* dependencies; } TargetConfig; /** diff --git a/src/compiler.c b/src/compiler.c index 01711c2..3d12803 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -14,6 +15,7 @@ #include #include #include +#include #include #define GRAPHVIZ_FILE_EXTENSION "gv" @@ -21,11 +23,18 @@ extern void yyrestart(FILE*); // Module AST node used by the parser for AST construction. -[[maybe_unused]] AST_NODE_PTR root; +[[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; +[[maybe_unused]] +ModuleFile* current_file; + +static int build_project_targets(ModuleFileStack* unit, + const ProjectConfig* config); + +static int build_target(ModuleFileStack* unit, const TargetConfig* target); /** * @brief Compile the specified file into AST @@ -230,12 +239,84 @@ static int compile_module_with_dependencies(ModuleFileStack* unit, for (size_t i = 0; i < AST_get_child_count(root_module); i++) { AST_NODE_PTR child = AST_get_node(root_module, i); - if (child->kind == AST_Import || child->kind == AST_Include) { + if (child->kind == AST_Import) { + if (g_hash_table_contains(target->dependencies, child->value)) { + Dependency* dependency = + g_hash_table_lookup(target->dependencies, child->value); + + gchar* cwd = g_get_current_dir(); + chdir(dependency->path); + + ProjectConfig* new_config = default_project_config(); + if (load_project_config(new_config)) { + print_message(Error, "Failed to load project config: `%s`", + child->value); + return EXIT_FAILURE; + } + + TargetConfig* dep_target = g_hash_table_lookup(new_config->targets, dependency->target); + if (build_target(unit, dep_target)) { + print_message(Error, "Failed to build project config: `%s`", + child->value); + return EXIT_FAILURE; + } + + GPathBuf* buf = g_path_buf_new_from_path(dependency->path); + TargetConfig* dep_conf = g_hash_table_lookup(new_config->targets, dependency->target); + char* root_mod = dep_conf->root_module; + g_path_buf_push(buf, root_mod); + char* rel_path = g_path_buf_to_path(buf); + + GPathBuf* dep_bin = g_path_buf_new(); + g_path_buf_push(dep_bin, dependency->path); + g_path_buf_push(dep_bin, dep_conf->archive_directory); + g_path_buf_push(dep_bin, g_strjoin(".", dep_conf->name, "o", NULL)); + char* dep_bin_path = g_path_buf_to_path(dep_bin); + + g_array_append_val(dependency->libraries, dep_bin_path); + + const char* path = + get_absolute_import_path(target, rel_path); + + if (g_hash_table_contains(imports, path)) { + continue; + } + + ModuleFile* imported_file = push_file(unit, path); + AST_NODE_PTR imported_module = + AST_new_node(empty_location(imported_file), AST_Module, NULL); + + if (compile_file_to_ast(imported_module, imported_file) + == EXIT_SUCCESS) { + AST_import_module(root_module, i + 1, imported_module); + } else { + return EXIT_FAILURE; + } + + g_hash_table_insert(imports, (gpointer) path, NULL); + + g_path_buf_pop(buf); + gchar* directory = g_path_buf_to_path(buf); + gchar* cached_directory = + mem_strdup(MemoryNamespaceLld, directory); + g_free(directory); + g_array_append_val(target->import_paths, cached_directory); + + chdir(cwd); + + } else { + print_message(Error, "Cannot resolve path for import: `%s`", + child->value); + return EXIT_FAILURE; + } + + } else if (child->kind == AST_Include) { const char* path = get_absolute_import_path(target, child->value); if (path == NULL) { - print_message(Error, "Cannot resolve path for import: `%s`", + print_message(Error, + "Cannot resolve path for include: `%s`", child->value); return EXIT_FAILURE; } @@ -305,10 +386,6 @@ static int build_target(ModuleFileStack* unit, const TargetConfig* target) { } } - mem_purge_namespace(MemoryNamespaceLex); - mem_purge_namespace(MemoryNamespaceAst); - mem_purge_namespace(MemoryNamespaceSet); - print_file_statistics(file); return err; diff --git a/src/link/lldc/lldc.c b/src/link/lldc/lldc.c index 7052aaf..4005603 100644 --- a/src/link/lldc/lldc.c +++ b/src/link/lldc/lldc.c @@ -10,6 +10,10 @@ extern int lld_main(int Argc, const char **Argv, const char **outstr); +const char* FLAGS[] = { + "" +}; + bool lldc_link(TargetLinkConfig* config) { GArray* arguments = mem_new_g_array(MemoryNamespaceLld, sizeof(char*)); @@ -21,6 +25,13 @@ bool lldc_link(TargetLinkConfig* config) { char* obj = g_array_index(config->object_file_names, char*, i); g_array_append_val(arguments, obj); } + + for (int i = 0; i < sizeof(FLAGS)/sizeof(char*); i++) { + char* flag = (char*) FLAGS[i]; + + g_array_append_val(arguments, flag); + } + char* output_flag = "-o"; g_array_append_val(arguments, output_flag); g_array_append_val(arguments, config->output_file); @@ -33,10 +44,9 @@ bool lldc_link(TargetLinkConfig* config) { const char* message = NULL; const bool code = lld_main(arguments->len, (const char**) arguments->data, &message); - free((void*) message); - if (!code) { print_message(Error, message); + free((void*) message); } return code; diff --git a/src/llvm/link/lld.c b/src/llvm/link/lld.c index ff6097b..83a676d 100644 --- a/src/llvm/link/lld.c +++ b/src/llvm/link/lld.c @@ -89,26 +89,18 @@ TargetLinkConfig* lld_create_link_config(__attribute__((unused)) // resolve absolute paths to dependent library object files DEBUG("resolving target dependencies..."); - for (guint i = 0; i < module->imports->len; i++) { - const char* dependency = g_array_index(module->imports, const char*, i); + GHashTableIter iter; - const char* library = g_strjoin("", "libgsc", dependency, ".o", NULL); + g_hash_table_iter_init(&iter, target_config->dependencies); + char* key; + Dependency* dep; + while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &dep)) { - const char* dependency_object = - get_absolute_link_path(target_config, library); - if (dependency_object == NULL) { - ERROR("failed to resolve path to dependency object: %s", library); - print_message(Warning, - "failed to resolve path to dependency object: %s", - dependency); - lld_delete_link_config(config); - lld_delete_link_config(config); - g_free((void*) library); - return NULL; + for (guint k = 0; k < dep->libraries->len; k++) { + char* lib = g_array_index(dep->libraries, char*, k); + + g_array_append_val(config->object_file_names, lib); } - g_free((void*) library); - g_array_append_val(config->object_file_names, dependency_object); - INFO("resolved path of target object: %s", dependency_object); } INFO("resolved %d dependencies", config->object_file_names->len); diff --git a/src/set/set.c b/src/set/set.c index 2340389..e752299 100644 --- a/src/set/set.c +++ b/src/set/set.c @@ -2442,7 +2442,7 @@ int addFunction(const char* name, Function* function) { function->name); return SEMANTIC_ERROR; } - g_hash_table_insert(declaredFunctions, (gpointer) name, function); + g_hash_table_insert(definedFunctions, (gpointer) name, function); } else if (function->kind == FunctionDeclarationKind) { if (g_hash_table_contains(declaredFunctions, name)) { Function* declaredFunction = @@ -2893,16 +2893,7 @@ Module* create_set(AST_NODE_PTR currentNode) { return NULL; } - if (g_hash_table_contains(functions, function->name)) { - print_diagnostic( - &function->impl.definition.nodePtr->location, Error, - "Multiple definition of function: `%s`", - function->name); - return NULL; - } - - g_hash_table_insert(functions, (gpointer) function->name, - function); + g_hash_table_insert(rootModule->functions, function->name, function); DEBUG("created function successfully"); break; diff --git a/tests/box/build.toml b/tests/box/build.toml index 8ac2824..6b20110 100644 --- a/tests/box/build.toml +++ b/tests/box/build.toml @@ -5,12 +5,17 @@ description = "Test applications for the GSC standard library." license = "GPL-2.0" authors = [ "Sven Vogel " ] -[target.prime] +[[targets]] +name = "prime" import-paths = [ "." ] root = "main.gsc" mode = "application" output = "bin" archive = "archive" +driver = "ld.lld" +print_asm = true +print_ir = true +print_ast = true -[dependencies] -gscstd = { path = "../../lib" } +[targets.dependencies] +std = { path = "../../lib/src", target = "gsc-libc" }