diff --git a/.env b/.env index 6eb74fb..b31e782 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -SDK=0.2.5-alpine-3.19.1 +SDK=0.2.6-alpine-3.19.1 diff --git a/Dockerfile b/Dockerfile index a68c334..6674f53 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -FROM servostar/gemstone:sdk-0.2.5-alpine-3.19.1 +FROM servostar/gemstone:sdk-0.2.6-alpine-3.19.1 LABEL authors="servostar" -LABEL version="0.2.5" +LABEL version="0.2.6" LABEL description="docker image for setting up the build pipeline on SDK" LABEL website="https://github.com/Servostar/gemstone" diff --git a/README.md b/README.md index daaaf32..70d8240 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,74 @@
- gemstone logo - +
+

+ Open source programming language compiler based on LLVM, GLib and GNU Bison/Flex +
+ capable of multi target cross compilation powered by simple build system. +

+
+ + + + + +
+ + +
-
-## Gemstone +## About Gemstone is a programming language compiler (short: GSC) written in C based on flex and GNU bison. It uses LLVM to produce optimized native binaries for many platforms and uses its own builtin build system for more complex project management. +## Architecture + +Gemstone is a LALR enabled non-reentrant compiler utilizing a linear flow of components. The compiler has multiple stages of operation, each representing a crucial step in compilation. + +```mermaid +--- +title: GSC Architecture Overview +--- +flowchart LR + lex["`**Lexical Analysis** + tokenization via flex`"] + bison["`**Syntax Analysis** + parsing via bison`"] + set["`**Semantic Analysis** + parse tree generation`"] + llvm["`**Codegen** + code generation via LLVM`"] + driver["`**Linking** + Linkage via Clang/GCC`"] + + start(("Start")) --> lex + + subgraph compile AST + lex --> bison + end + + subgraph Validation + bison --> import{"Import/Include?"} + import --> |yes| ast[[compile AST]] --> merge["Merge AST"] --> import + import --> |no| set + set --> llvm + end + + stop(("End")) + + subgraph Codegen + llvm --> lib{"Produce Library?"} + lib -->|no| driver --> executable(["Executable"]) + lib -->|yes| library(["Library"]) + end + + library --> stop + executable --> stop +``` + ## Dependencies (build) ### Windows 11 @@ -78,33 +136,23 @@ For creating the build pipeline build the Dockerfile in the root folder of this Then the make targets are generated. Running `make release` will build gemstone from source in release mode. The generated binaries can be found either in `bin/release/gsc` or `bin/debug/gsc` depending on the chosen target. The following graph visualizes the build pipeline: -``` - SDK (environment) - │ - │ configure build environment - │ cmake, make, gcc, yacc, lex - │ - ▼ - Devkit (pipeline) - │ - │ create build pipeline - │ create make targets - ▼ - Pipeline - - -yacc (generate files) GCC (compile) Extra Source Files (src/*.c) -│ │ │ -├─ parser.tab.h ─────────────►│◄────────────────────┘ -│ │ -└─ parser.tab.c ─────────────►│ - │ -lex (generate file) │ -│ │ -└─ lexer.ll.c ──────────────►│ - │ - ▼ - gsc +```mermaid +flowchart LR + + subgraph Docker + alpine[Alpine Linux] --> sdk[SDK] --> dev[Devkit] + end + + subgraph Build + dev --> |generate parser| bison[Bison] + dev --> |generate lexer| flex[Flex] + bison --> cc[GCC/Clang/MSVC] + flex --> cc + cc --> debug + cc --> release + cc --> check + end + ``` ## Docker images diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 8be6998..097546c 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -16,10 +16,16 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/../bin/std") # add native module libraries file(GLOB_RECURSE STDLIB_IO_SOURCE_FILES src/io/*.c) -add_library(io ${STDLIB_IO_SOURCE_FILES}) +add_library(gscio ${STDLIB_IO_SOURCE_FILES}) file(GLOB_RECURSE STDLIB_MEM_SOURCE_FILES src/mem/*.c) -add_library(mem ${STDLIB_MEM_SOURCE_FILES}) +add_library(gscmem ${STDLIB_MEM_SOURCE_FILES}) file(GLOB_RECURSE STDLIB_OS_SOURCE_FILES src/os/*.c) -add_library(os ${STDLIB_OS_SOURCE_FILES}) +add_library(gscos ${STDLIB_OS_SOURCE_FILES}) + +# Complete standard library +add_library(gscstd + ${STDLIB_IO_SOURCE_FILES} + ${STDLIB_MEM_SOURCE_FILES} + ${STDLIB_OS_SOURCE_FILES}) diff --git a/lib/src/entry/entrypoint.c b/lib/src/entry/entrypoint.c index c41e482..75cf927 100644 --- a/lib/src/entry/entrypoint.c +++ b/lib/src/entry/entrypoint.c @@ -2,9 +2,6 @@ // Created by servostar on 6/10/24. // -extern void entry(void); - int main(int argc, char* argv[]) { - entry(); return 0; } diff --git a/lib/src/io.gsc b/lib/src/io.gsc index db8de9b..c729644 100644 --- a/lib/src/io.gsc +++ b/lib/src/io.gsc @@ -6,7 +6,7 @@ # | Generic Input/Output | # `----------------------------------------` -import "def.gsc" +include "def.gsc" # platform specific handle to an I/O device # can be a file, buffer, window or something else diff --git a/lib/src/mem.gsc b/lib/src/mem.gsc index 0307666..b9df686 100644 --- a/lib/src/mem.gsc +++ b/lib/src/mem.gsc @@ -6,7 +6,7 @@ # | Memory Management | # `----------------------------------------` -import "def.gsc" +include "def.gsc" # Allocate `len` bytes of heap memory # Returns a pointer to the memory as `ptr` diff --git a/lib/src/os.gsc b/lib/src/os.gsc index bfd6958..5f7a190 100644 --- a/lib/src/os.gsc +++ b/lib/src/os.gsc @@ -6,7 +6,7 @@ # | Operating System | # `----------------------------------------` -import "def.gsc" +include "def.gsc" # Return a hard coded C string identifying the underlying operating system # Will return one of the following: diff --git a/lib/src/std.gsc b/lib/src/std.gsc index cf690e3..36d6a75 100644 --- a/lib/src/std.gsc +++ b/lib/src/std.gsc @@ -7,10 +7,10 @@ # `----------------------------------------` # standard type definitions -import "def.gsc" +include "def.gsc" # I/O operations -import "io.gsc" +include "io.gsc" # memory management -import "mem.gsc" +include "mem.gsc" diff --git a/sdk/Dockerfile b/sdk/Dockerfile index 1cba1d3..5bfaf89 100644 --- a/sdk/Dockerfile +++ b/sdk/Dockerfile @@ -1,11 +1,11 @@ FROM alpine:3.19.1 LABEL authors="servostar" -LABEL version="0.2.5" +LABEL version="0.2.6" LABEL description="base image for building the gemstone programming language compiler" LABEL website="https://github.com/Servostar/gemstone" # install dependencies -RUN apk add build-base gcc make cmake bison flex git python3 graphviz glib glib-dev llvm17-libs llvm17-dev +RUN apk add build-base gcc clang make cmake bison flex git python3 graphviz glib glib-dev llvm17-libs llvm17-dev # create user for build RUN adduser --disabled-password lorang diff --git a/src/ast/ast.c b/src/ast/ast.c index 7da6bf5..9f00f56 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -101,6 +101,7 @@ const char* AST_node_to_string(const struct AST_Node_t* node) { case AST_Ident: case AST_Macro: case AST_Import: + case AST_Include: case AST_Storage: case AST_Typekind: case AST_Sign: diff --git a/src/ast/ast.h b/src/ast/ast.h index 5c71d2c..a7be0be 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -79,6 +79,7 @@ enum AST_SyntaxElement_t { AST_AddressOf, AST_Dereference, AST_Reference, + AST_Include, AST_ELEMENT_COUNT }; diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 57ed113..8a946b6 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -9,6 +9,7 @@ #include #include #include +#include static GHashTable* args = NULL; @@ -68,7 +69,7 @@ GArray* get_non_options_after(const char* command) { return NULL; } - GArray* array = g_array_new(FALSE, FALSE, sizeof(const char*)); + GArray* array = mem_new_g_array(MemoryNamespaceOpt, sizeof(const char*)); GHashTableIter iter; gpointer key, value; @@ -82,7 +83,6 @@ GArray* get_non_options_after(const char* command) { } if (array->len == 0) { - g_array_free(array, FALSE); return NULL; } @@ -98,12 +98,13 @@ TargetConfig* default_target_config() { config->print_ast = false; config->print_asm = false; config->print_ir = false; + config->driver = mem_strdup(MemoryNamespaceOpt, DEFAULT_DRIVER); config->mode = Application; config->archive_directory = mem_strdup(MemoryNamespaceOpt, "archive"); config->output_directory = mem_strdup(MemoryNamespaceOpt, "bin"); config->optimization_level = 1; config->root_module = NULL; - config->link_search_paths = g_array_new(FALSE, FALSE, sizeof(char*)); + config->link_search_paths = mem_new_g_array(MemoryNamespaceOpt, sizeof(char*)); config->lld_fatal_warnings = FALSE; config->gsc_fatal_warnings = FALSE; config->import_paths = mem_new_g_array(MemoryNamespaceOpt, sizeof(char*)); @@ -160,9 +161,18 @@ TargetConfig* default_target_config_from_args() { } } - // TODO: free vvvvvvvvvvvvv + if (is_option_set("driver")) { + const Option* opt = get_option("driver"); + + if (opt->value != NULL) { + config->driver = mem_strdup(MemoryNamespaceOpt, (char*) opt->value); + } + } + char* cwd = g_get_current_dir(); - g_array_append_val(config->link_search_paths, cwd); + char* cached_cwd = mem_strdup(MemoryNamespaceOpt, cwd); + g_array_append_val(config->link_search_paths, cached_cwd); + free(cwd); if (is_option_set("link-paths")) { const Option* opt = get_option("link-paths"); @@ -174,7 +184,7 @@ TargetConfig* default_target_config_from_args() { while((end = strchr(start, ',')) != NULL) { const int len = end - start; - char* link_path = malloc(len + 1); + char* link_path = mem_alloc(MemoryNamespaceOpt, len + 1); memcpy(link_path, start, len); link_path[len] = 0; @@ -185,7 +195,7 @@ TargetConfig* default_target_config_from_args() { const int len = strlen(start); if (len > 0) { - char* link_path = malloc(len + 1); + char* link_path = mem_alloc(MemoryNamespaceOpt, len + 1); memcpy(link_path, start, len); link_path[len] = 0; @@ -204,14 +214,42 @@ TargetConfig* default_target_config_from_args() { print_message(Warning, "Got more than one file to compile, using first, ignoring others."); } - config->root_module = mem_strdup(MemoryNamespaceOpt, ((char**) files->data) [0]); - - g_array_free(files, TRUE); + config->root_module = mem_strdup(MemoryNamespaceOpt, g_array_index(files, char*, 0)); } char* default_import_path = mem_strdup(MemoryNamespaceOpt, "."); g_array_append_val(config->import_paths, default_import_path); + if (is_option_set("import-paths")) { + const Option* opt = get_option("import-paths"); + + if (opt->value != NULL) { + + const char* start = opt->value; + const char* end = NULL; + while((end = strchr(start, ',')) != NULL) { + + const int len = end - start; + char* import_path = mem_alloc(MemoryNamespaceOpt, len + 1); + memcpy(import_path, start, len); + import_path[len] = 0; + + g_array_append_val(config->import_paths, import_path); + + start = end; + } + + const int len = strlen(start); + if (len > 0) { + char* import_path = mem_alloc(MemoryNamespaceOpt, len + 1); + memcpy(import_path, start, len); + import_path[len] = 0; + + g_array_append_val(config->import_paths, import_path); + } + } + } + return config; } @@ -229,6 +267,7 @@ void print_help(void) { " --print-ir print resulting LLVM-IR to a file", " --mode=[app|lib] set the compilation mode to either application or library", " --output=name name of output files without extension", + " --driver set binary driver to use", " --link-paths=[paths,] set a list of directories to for libraries in", " --all-fatal-warnings treat all warnings as errors", " --lld-fatal-warnings treat linker warnings as errors", @@ -238,6 +277,7 @@ void print_help(void) { " --debug print debug logs (if not disabled at compile time)", " --version print the version", " --list-targets print a list of all available targets supported", + " --list-driver print a list of all available binary driver", " --help print this help dialog", " --color-always always colorize output", " --print-gc-stats print statistics of the garbage collector" @@ -281,6 +321,21 @@ static void get_int(int* integer, const toml_table_t *table, const char* name) { } } +static void get_array(GArray* array, const toml_table_t *table, const char* name) { + const toml_array_t* toml_array = toml_array_in(table, name); + + if (toml_array) { + for (int i = 0; i < toml_array_nelem(toml_array); i++) { + toml_datum_t value = toml_string_at(toml_array, i); + + if (value.ok) { + char* copy = mem_strdup(MemoryNamespaceOpt, value.u.s); + g_array_append_val(array, copy); + } + } + } +} + static int parse_project_table(ProjectConfig *config, const toml_table_t *project_table) { DEBUG("parsing project table..."); @@ -297,7 +352,7 @@ static int parse_project_table(ProjectConfig *config, const toml_table_t *projec // 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 = mem_new_g_array(MemoryNamespaceOpt, sizeof(char *)); for (int i = 0;; i++) { toml_datum_t author = toml_string_at(authors, i); @@ -328,7 +383,7 @@ static int get_mode_from_str(TargetCompilationMode* mode, const char* name) { *mode = Library; return PROJECT_OK; } - printf("Invalid project configuration, mode is invalid: %s\n\n", name); + print_message(Error, "Invalid project configuration, mode is invalid: %s", name); return PROJECT_SEMANTIC_ERR; } @@ -343,6 +398,7 @@ static int parse_target(const ProjectConfig *config, const toml_table_t *target_ get_bool(&target_config->print_asm, target_table, "print_asm"); get_bool(&target_config->print_ir, target_table, "print_ir"); + get_str(&target_config->driver, target_table, "driver"); 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"); @@ -357,6 +413,15 @@ static int parse_target(const ProjectConfig *config, const toml_table_t *target_ if (err != PROJECT_OK) { return err; } + char* cwd = g_get_current_dir(); + char* cached_cwd = mem_strdup(MemoryNamespaceOpt, cwd); + free(cwd); + + g_array_append_val(target_config->link_search_paths, cached_cwd); + get_array(target_config->link_search_paths, target_table, "link-paths"); + + g_array_append_val(target_config->import_paths, cached_cwd); + get_array(target_config->import_paths, target_table, "import-paths"); g_hash_table_insert(config->targets, target_config->name, target_config); @@ -372,16 +437,16 @@ static int parse_targets(ProjectConfig *config, const toml_table_t *root) { return PROJECT_SEMANTIC_ERR; } - config->targets = g_hash_table_new(g_str_hash, g_str_equal); + config->targets = mem_new_g_hash_table(MemoryNamespaceOpt, g_str_hash, g_str_equal); - for (int i = 0; i < MAX_TARGETS_PER_PROJECT; i++) { + 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, key); + parse_target(config, target, mem_strdup(MemoryNamespaceOpt, (char*) key)); } return PROJECT_OK; @@ -393,8 +458,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)); - INFO("project file not found"); - return PROJECT_TOML_ERR; + return PROJECT_SEMANTIC_ERR; } char err_buf[TOML_ERROR_MSG_BUF]; @@ -402,21 +466,24 @@ int load_project_config(ProjectConfig *config) { toml_table_t *conf = toml_parse_file(config_file, err_buf, sizeof(err_buf)); fclose(config_file); - if (conf == NULL) { + if (conf != NULL) { + int status = PROJECT_SEMANTIC_ERR; + toml_table_t *project = toml_table_in(conf, "project"); + + if (project != NULL) { + if (parse_project_table(config, project) == PROJECT_OK) { + status = parse_targets(config, conf); + } + } else { + print_message(Error, "Invalid project configuration: missing project table."); + } + + toml_free(conf); + return status; + } else { print_message(Error, "Invalid project configuration: %s", err_buf); - return PROJECT_SEMANTIC_ERR; } - toml_table_t *project = toml_table_in(conf, "project"); - if (project == NULL) { - 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); return PROJECT_SEMANTIC_ERR; } @@ -435,9 +502,8 @@ void delete_target_config(TargetConfig* config) { } if (config->link_search_paths) { for (guint i = 0; i < config->link_search_paths->len; i++) { - free(g_array_index(config->link_search_paths, char*, i)); + mem_free(g_array_index(config->link_search_paths, char*, i)); } - g_array_free(config->link_search_paths, TRUE); } mem_free(config); } @@ -447,7 +513,7 @@ void delete_project_config(ProjectConfig* config) { mem_free(config->name); } if (config->authors != NULL) { - g_array_free(config->authors, TRUE); + mem_free(config->authors); } if (config->desc != NULL) { mem_free(config->desc); @@ -466,7 +532,7 @@ void delete_project_config(ProjectConfig* config) { delete_target_config(val); } - g_hash_table_destroy(config->targets); + mem_free(config->targets); } mem_free_from(MemoryNamespaceOpt, config); @@ -484,3 +550,16 @@ ProjectConfig* default_project_config() { return config; } + +static void* toml_cached_malloc(size_t bytes) { + return mem_alloc(MemoryNamespaceTOML, bytes); +} + +static void toml_cached_free(void* ptr) { + mem_free(ptr); +} + +void init_toml() { + INFO("setting up cached memory for TOML C99..."); + toml_set_memutil(toml_cached_malloc, toml_cached_free); +} diff --git a/src/cfg/opt.h b/src/cfg/opt.h index 9b61def..06e6f1d 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -24,6 +24,7 @@ typedef struct TargetLinkConfig_t { // colorize linker output bool colorize; char* output_file; + char* driver; } TargetLinkConfig; typedef enum TargetCompilationMode_t { @@ -50,6 +51,8 @@ typedef struct TargetConfig_t { char* output_directory; // output directory for intermediate representations (LLVM-IR, Assembly, ...) char* archive_directory; + // binary driver for executable generation + char* driver; // mode of compilation TargetCompilationMode mode; // number between 1 and 3 @@ -183,4 +186,6 @@ const Option* get_option(const char* option); [[nodiscard("must be freed")]] GArray* get_non_options_after(const char* command); +void init_toml(); + #endif //GEMSTONE_OPT_H diff --git a/src/compiler.c b/src/compiler.c index 289daf7..a515009 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -165,11 +165,20 @@ static void run_backend_codegen(const Module* module, const TargetConfig* target print_message(Info, "Compilation finished successfully"); err = deinit_backend(); + if (err.kind != Success) { + ERROR("Unable to deinit backend: %s", err.impl.message); + } } const char* get_absolute_import_path(const TargetConfig* config, const char* import_target_name) { INFO("resolving absolute path for import target: %s", import_target_name); + if (!g_str_has_suffix(import_target_name, ".gsc")) { + char* full_filename = g_strjoin("", import_target_name, ".gsc", NULL); + import_target_name = mem_strdup(MemoryNamespaceLld, full_filename); + g_free(full_filename); + } + for (guint i = 0; i < config->import_paths->len; i++) { const char* import_directory_path = g_array_index(config->import_paths, char*, i); @@ -180,15 +189,16 @@ const char* get_absolute_import_path(const TargetConfig* config, const char* imp const gboolean exists = g_file_test(canonical, G_FILE_TEST_EXISTS); const gboolean is_dir = g_file_test(canonical, G_FILE_TEST_IS_DIR); + char* cached_canonical = mem_strdup(MemoryNamespaceLld, canonical); + g_free(path); g_free(cwd); + g_free(canonical); if (exists && !is_dir) { - INFO("import target found at: %s", canonical); - return canonical; + INFO("import target found at: %s", cached_canonical); + return cached_canonical; } - - g_free(canonical); } // file not found @@ -204,7 +214,7 @@ static int compile_module_with_dependencies(ModuleFileStack *unit, ModuleFile* f 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) { + if (child->kind == AST_Import || child->kind == AST_Include) { const char* path = get_absolute_import_path(target, child->value); if (path == NULL) { @@ -228,7 +238,9 @@ static int compile_module_with_dependencies(ModuleFileStack *unit, ModuleFile* f g_hash_table_insert(imports, (gpointer) path, NULL); gchar* directory = g_path_get_dirname(path); - g_array_append_val(target->import_paths, directory); + gchar* cached_directory = mem_strdup(MemoryNamespaceLld, directory); + g_free(directory); + g_array_append_val(target->import_paths, cached_directory); } } } else { @@ -318,7 +330,7 @@ static void build_project_targets(ModuleFileStack *unit, const ProjectConfig *co if (targets != NULL) { for (guint i = 0; i < targets->len; i++) { - const char *target_name = (((Option*) targets->data) + i)->string; + const char *target_name = g_array_index(targets, const char*, i); if (g_hash_table_contains(config->targets, target_name)) { build_target(unit, g_hash_table_lookup(config->targets, target_name)); @@ -327,7 +339,7 @@ static void build_project_targets(ModuleFileStack *unit, const ProjectConfig *co } } - g_array_free(targets, FALSE); + mem_free(targets); } else { print_message(Error, "No targets specified."); } diff --git a/src/io/files.c b/src/io/files.c index dc4d044..ddd018c 100644 --- a/src/io/files.c +++ b/src/io/files.c @@ -42,7 +42,7 @@ ModuleFile *push_file(ModuleFileStack *stack, const char *path) { // lazy init of heap stack if (stack->files == NULL) { - stack->files = g_array_new(FALSE, FALSE, sizeof(ModuleFile*)); + stack->files = mem_new_g_array(MemoryNamespaceStatic, sizeof(ModuleFile*)); } ModuleFile* new_file = mem_alloc(MemoryNamespaceStatic, sizeof(ModuleFile)); @@ -66,9 +66,10 @@ void delete_files(ModuleFileStack *stack) { fclose(file->handle); } + mem_free((void*) file); } - g_array_free(stack->files, TRUE); + mem_free(stack->files); DEBUG("deleted module file stack"); } @@ -330,8 +331,11 @@ const char *get_absolute_path(const char *path) { DEBUG("resolving absolute path of: %s", path); char* cwd = g_get_current_dir(); - char* canoical = g_canonicalize_filename(path, cwd); + char* canonical = g_canonicalize_filename(path, cwd); g_free(cwd); - return canoical; + char* cached_canonical = mem_strdup(MemoryNamespaceStatic, canonical); + g_free(canonical); + + return cached_canonical; } diff --git a/src/lex/lexer.l b/src/lex/lexer.l index bfde8ff..e955b0e 100644 --- a/src/lex/lexer.l +++ b/src/lex/lexer.l @@ -81,6 +81,7 @@ "!" {DEBUG("\"%s\" tokenized with \'OpBitnot\'", yytext); return(OpBitnot);}; "^" {DEBUG("\"%s\" tokenized with \'OpBitxor\'", yytext); return(OpBitxor);}; "import" {DEBUG("\"%s\" tokenized with \'KeyImport\'", yytext); return(KeyImport);}; +"include" {DEBUG("\"%s\" tokenized with \'KeyInclude\'", yytext); return(KeyInclude);}; "silent" {DEBUG("\"%s\" tokenized with \'KeySilent\'", yytext); return(KeySilent);}; "box" {DEBUG("\"%s\" tokenized with \'KeyBox\'", yytext); return(KeyBox);}; "typeof" {DEBUG("\"%s\" tokenized with \'FunTypeof\'", yytext); return(FunTypeof);}; diff --git a/src/link/clang/driver.c b/src/link/clang/driver.c new file mode 100644 index 0000000..6d4e0cf --- /dev/null +++ b/src/link/clang/driver.c @@ -0,0 +1,42 @@ +// +// Created by servostar on 18.07.24. +// + +#include +#include +#include + +bool clang_link(TargetLinkConfig* config) { + + GString* commandString = g_string_new(""); + + g_string_append(commandString, "clang"); + + for (guint i = 0; i < config->object_file_names->len; i++) { + g_string_append(commandString, " "); + g_string_append(commandString, g_array_index(config->object_file_names, char*, i)); + } + + g_string_append(commandString, " -o "); + g_string_append(commandString, config->output_file); + + print_message(Info, "invoking binary link with: %s", commandString->str); + + if (system(commandString->str)) { + return false; + } + + g_string_free(commandString, true); + + return true; +} + +BinaryDriver* clang_get_driver() { + + BinaryDriver* driver = mem_alloc(MemoryNamespaceLld, sizeof (BinaryDriver)); + + driver->name = "clang"; + driver->link_func = &clang_link; + + return driver; +} diff --git a/src/link/clang/driver.h b/src/link/clang/driver.h new file mode 100644 index 0000000..e8f15da --- /dev/null +++ b/src/link/clang/driver.h @@ -0,0 +1,14 @@ +// +// Created by servostar on 18.07.24. +// + +#ifndef GEMSTONE_CLANG_DRIVER_H +#define GEMSTONE_CLANG_DRIVER_H + +#include + +bool clang_link(TargetLinkConfig* config); + +BinaryDriver* clang_get_driver(); + +#endif // GEMSTONE_CLANG_DRIVER_H diff --git a/src/link/driver.h b/src/link/driver.h new file mode 100644 index 0000000..9eba996 --- /dev/null +++ b/src/link/driver.h @@ -0,0 +1,20 @@ +// +// Created by servostar on 18.07.24. +// + +#ifndef GEMSTONE_DRIVER_H +#define GEMSTONE_DRIVER_H + +#include + +#define DEFAULT_DRIVER "clang" + +//! @brief Function a binary driver used to link files +typedef bool (*driver_link)(TargetLinkConfig*); + +typedef struct BinaryDriver_t { + const char* name; + driver_link link_func; +} BinaryDriver; + +#endif //GEMSTONE_DRIVER_H diff --git a/src/link/gcc/driver.c b/src/link/gcc/driver.c new file mode 100644 index 0000000..854bf0c --- /dev/null +++ b/src/link/gcc/driver.c @@ -0,0 +1,42 @@ +// +// Created by servostar on 18.07.24. +// + +#include +#include +#include + +bool gcc_link(TargetLinkConfig* config) { + + GString* commandString = g_string_new(""); + + g_string_append(commandString, "gcc"); + + for (guint i = 0; i < config->object_file_names->len; i++) { + g_string_append(commandString, " "); + g_string_append(commandString, g_array_index(config->object_file_names, char*, i)); + } + + g_string_append(commandString, " -o "); + g_string_append(commandString, config->output_file); + + print_message(Info, "invoking binary link with: %s", commandString->str); + + if (system(commandString->str)) { + return false; + } + + g_string_free(commandString, true); + + return true; +} + +BinaryDriver* gcc_get_driver() { + + BinaryDriver* driver = mem_alloc(MemoryNamespaceLld, sizeof (BinaryDriver)); + + driver->name = "gcc"; + driver->link_func = &gcc_link; + + return driver; +} diff --git a/src/link/gcc/driver.h b/src/link/gcc/driver.h new file mode 100644 index 0000000..8227353 --- /dev/null +++ b/src/link/gcc/driver.h @@ -0,0 +1,14 @@ +// +// Created by servostar on 18.07.24. +// + +#ifndef GEMSTONE_GCC_DRIVER_H +#define GEMSTONE_GCC_DRIVER_H + +#include + +bool gcc_link(TargetLinkConfig* config); + +BinaryDriver* gcc_get_driver(); + +#endif // GEMSTONE_GCC_DRIVER_H diff --git a/src/link/lib.c b/src/link/lib.c new file mode 100644 index 0000000..6e4aaf1 --- /dev/null +++ b/src/link/lib.c @@ -0,0 +1,72 @@ +// +// Created by servostar on 18.07.24. +// + +#include +#include +#include +#include + +#include +#include + +static driver_init AVAILABLE_DRIVER[] = { + clang_get_driver, + gcc_get_driver +}; + +static GHashTable* binary_driver = NULL; + +void link_init() { + INFO("initializing binary driver..."); + + if (binary_driver == NULL) { + binary_driver = mem_new_g_hash_table(MemoryNamespaceLld, g_str_hash, g_str_equal); + + for (unsigned long int i = 0; i < sizeof(AVAILABLE_DRIVER)/sizeof(driver_init); i++) { + BinaryDriver* driver = AVAILABLE_DRIVER[i](); + + if (driver == NULL) { + ERROR("failed to init driver by index: %d", i); + continue; + } + + g_hash_table_insert(binary_driver, (gpointer) driver->name, driver); + INFO("initialized `%s` driver", driver->name); + } + } else { + PANIC("binary driver already initialized"); + } +} + +bool link_run(TargetLinkConfig* config) { + + if (g_hash_table_contains(binary_driver, config->driver)) { + print_message(Info, "Invoking binary driver: %s", config->driver); + + BinaryDriver* driver = g_hash_table_lookup(binary_driver, config->driver); + + if (!driver->link_func(config)) { + print_message(Error, "Driver %s failed", config->driver); + return false; + } + return true; + + } else { + print_message(Error, "Binary driver not available: `%s`", config->driver); + return false; + } +} + +void link_print_available_driver() { + printf("Available binary driver:\n"); + + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, binary_driver); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + + printf(" - %s\n", (char*) key); + } +} diff --git a/src/link/lib.h b/src/link/lib.h new file mode 100644 index 0000000..74ac352 --- /dev/null +++ b/src/link/lib.h @@ -0,0 +1,18 @@ +// +// Created by servostar on 18.07.24. +// + +#ifndef GEMSTONE_LIB_H +#define GEMSTONE_LIB_H + +#include + +typedef BinaryDriver* (*driver_init)(); + +void link_init(); + +bool link_run(TargetLinkConfig*); + +void link_print_available_driver(); + +#endif //GEMSTONE_LIB_H diff --git a/src/llvm/backend.c b/src/llvm/backend.c index 870987d..07f4954 100644 --- a/src/llvm/backend.c +++ b/src/llvm/backend.c @@ -7,6 +7,7 @@ #include #include #include +#include Target create_native_target() { DEBUG("creating native target..."); @@ -54,7 +55,11 @@ static char* create_target_output_name(const TargetConfig* config) { prefix = "lib"; } - return g_strjoin("", prefix, config->name, NULL); + char* name = g_strjoin("", prefix, config->name, NULL); + char* cached_name = mem_strdup(MemoryNamespaceLlvm, name); + g_free(name); + + return cached_name; } Target create_target_from_config(const TargetConfig* config) { diff --git a/src/llvm/link/lld.c b/src/llvm/link/lld.c index e1a2175..79eec2a 100644 --- a/src/llvm/link/lld.c +++ b/src/llvm/link/lld.c @@ -6,6 +6,7 @@ #include #include #include +#include const char* get_absolute_link_path(const TargetConfig* config, const char* link_target_name) { INFO("resolving absolute path for link target: %s", link_target_name); @@ -13,22 +14,24 @@ const char* get_absolute_link_path(const TargetConfig* config, const char* link_ for (guint i = 0; i < config->link_search_paths->len; i++) { const char* link_directory_path = g_array_index(config->link_search_paths, char*, i); + INFO("searching at: %s", link_directory_path); + char* path = g_build_filename(link_directory_path, link_target_name, NULL); char* cwd = g_get_current_dir(); char* canonical = g_canonicalize_filename(path, cwd); + char* cached_canonical = mem_strdup(MemoryNamespaceLld, canonical); const gboolean exists = g_file_test(canonical, G_FILE_TEST_EXISTS); const gboolean is_dir = g_file_test(canonical, G_FILE_TEST_IS_DIR); g_free(path); g_free(cwd); + g_free(canonical); if (exists && !is_dir) { - INFO("link target found at: %s", canonical); - return canonical; + INFO("link target found at: %s", cached_canonical); + return cached_canonical; } - - g_free(canonical); } // file not found @@ -41,47 +44,54 @@ TargetLinkConfig* lld_create_link_config(__attribute__((unused)) const Target* t TargetLinkConfig* config = mem_alloc(MemoryNamespaceLld, sizeof(TargetLinkConfig)); config->fatal_warnings = target_config->lld_fatal_warnings; - config->object_file_names = g_array_new(FALSE, FALSE, sizeof(char*)); + config->object_file_names = mem_new_g_array(MemoryNamespaceLld, sizeof(char*)); config->colorize = stdout_supports_ansi_esc(); + config->driver = target_config->driver; // append build object file char* basename = g_strjoin(".", target_config->name, "o", NULL); char* filename = g_build_filename(target_config->archive_directory, basename, NULL); + g_free(basename); const char* target_object = get_absolute_link_path(target_config, (const char*) filename); if (target_object == NULL) { ERROR("failed to resolve path to target object: %s", filename); + g_free(filename); lld_delete_link_config(config); + g_free(filename); return NULL; } + g_free(filename); { // output file after linking basename = g_strjoin(".", target_config->name, "out", NULL); filename = g_build_filename(target_config->output_directory, basename, NULL); - config->output_file = filename; + config->output_file = mem_strdup(MemoryNamespaceLld, filename); + + g_free(basename); + g_free(filename); } g_array_append_val(config->object_file_names, target_object); INFO("resolved path of target object: %s", target_object); - // if it is an app, add entrypoint library - if (target_config->mode == Application) { - char* entrypoint = g_strdup("libentrypoint.a"); - g_array_append_val(module->imports, entrypoint); - } - // 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); - const char* dependency_object = get_absolute_link_path(target_config, dependency); + const char* library = g_strjoin("", "libgsc", dependency, ".a", NULL); + + const char* dependency_object = get_absolute_link_path(target_config, library); if (dependency_object == NULL) { - ERROR("failed to resolve path to dependency object: %s", dependency); + 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; } + g_free((void*) library); g_array_append_val(config->object_file_names, dependency_object); INFO("resolved path of target object: %s", dependency_object); } @@ -91,59 +101,16 @@ TargetLinkConfig* lld_create_link_config(__attribute__((unused)) const Target* t return config; } -GArray* lld_create_lld_arguments(TargetLinkConfig* config) { - GArray* argv = g_array_new(TRUE, FALSE, sizeof(char*)); - - gchar* arg = g_strdup("ld.lld"); - g_array_append_val(argv, arg); - - if (config->fatal_warnings) { - arg = g_strdup("--fatal-warnings"); - g_array_append_val(argv, arg); - } - - if (config->colorize) { - arg = g_strdup("--color-diagnostics=always"); - g_array_append_val(argv, arg); - } - - { - arg = g_strjoin("", "-o", config->output_file, NULL); - g_array_append_val(argv, arg); - } - - for (guint i = 0; i < config->object_file_names->len; i++) { - char* object_file_path = g_array_index(config->object_file_names, char*, i); - arg = g_strjoin("", object_file_path, NULL); - g_array_append_val(argv, arg); - } - - return argv; -} - BackendError lld_link_target(TargetLinkConfig* config) { - DEBUG("linking target..."); - BackendError err = SUCCESS; - GArray* argv = lld_create_lld_arguments(config); + if (link_run(config)) { + return SUCCESS; + } - INFO("Linking target..."); - - char* arguments = g_strjoinv(" ", (char**) argv->data); - print_message(Info, "%s", arguments); - g_free(arguments); - - INFO("done linking target..."); - - g_array_free(argv, TRUE); - - return err; + return new_backend_impl_error(Implementation, NULL, "linking failed"); } void lld_delete_link_config(TargetLinkConfig* config) { - for (guint i = 0; i < config->object_file_names->len; i++) { - free((void*) g_array_index(config->object_file_names, const char*, i)); - } - g_array_free(config->object_file_names, TRUE); + mem_free(config->object_file_names); mem_free(config); } diff --git a/src/llvm/llvm-ir/expr.c b/src/llvm/llvm-ir/expr.c index 254a969..d3a3c66 100644 --- a/src/llvm/llvm-ir/expr.c +++ b/src/llvm/llvm-ir/expr.c @@ -387,6 +387,49 @@ BackendError impl_variable_load(LLVMBackendCompileUnit *unit, LLVMLocalScope *sc return SUCCESS; } +BackendError impl_parameter_load(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope, + LLVMBuilderRef builder, Parameter *parameter, + LLVMBool reference, + LLVMValueRef *llvm_result) { + + LLVMValueRef llvm_variable = NULL; + if (g_hash_table_contains(scope->func_scope->params, parameter->name)) { + llvm_variable = g_hash_table_lookup(scope->func_scope->params, parameter->name); + } + + Type* type; + + ParameterDeclaration decl; + + if (parameter->kind == ParameterDeclarationKind) { + decl = parameter->impl.definiton.declaration; + } else { + decl = parameter->impl.declaration; + } + type = decl.type; + + if (llvm_variable == NULL) { + return new_backend_impl_error(Implementation, NULL, "Variable not found"); + } + + if (decl.qualifier == In || reference) { + *llvm_result = llvm_variable; + } else { + // no referencing, load value + LLVMTypeRef llvm_type; + + get_type_impl(unit, scope->func_scope->global_scope, type, &llvm_type); + + if (LLVMGetTypeKind(LLVMTypeOf(llvm_variable)) == LLVMPointerTypeKind) { + *llvm_result = LLVMBuildLoad2(builder, llvm_type, llvm_variable, ""); + } else { + *llvm_result = llvm_variable; + } + } + + return SUCCESS; +} + BackendError impl_address_of(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope, LLVMBuilderRef builder, AddressOf* addressOf, LLVMValueRef *llvm_result) { @@ -405,7 +448,12 @@ BackendError impl_deref(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope, LLVMValueRef *llvm_result) { BackendError err; - LLVMValueRef llvm_pointer = get_variable(scope, dereference->variable->impl.variable->name); + LLVMValueRef llvm_pointer = NULL; + err = impl_expr(unit, scope, builder, dereference->variable, TRUE, &llvm_pointer); + if (err.kind != Success) { + return err; + } + LLVMTypeRef llvm_deref_type = NULL; err = get_type_impl(unit, scope->func_scope->global_scope, dereference->variable->result->impl.reference, &llvm_deref_type); if (err.kind != Success) { @@ -454,6 +502,11 @@ BackendError impl_expr(LLVMBackendCompileUnit *unit, LLVMLocalScope *scope, reference, llvm_result); break; + case ExpressionKindParameter: + err = impl_parameter_load(unit, scope, builder, expr->impl.parameter, + reference, + llvm_result); + break; case ExpressionKindAddressOf: err = impl_address_of(unit, scope, builder, &expr->impl.addressOf, llvm_result); diff --git a/src/llvm/llvm-ir/func.c b/src/llvm/llvm-ir/func.c index 473d6ad..032aa3b 100644 --- a/src/llvm/llvm-ir/func.c +++ b/src/llvm/llvm-ir/func.c @@ -26,7 +26,7 @@ void delete_local_scope(LLVMLocalScope* scope) { free(scope); } -static LLVMValueRef get_parameter(const LLVMFuncScope* scope, +LLVMValueRef get_parameter(const LLVMFuncScope* scope, const char* name) { if (g_hash_table_contains(scope->params, name)) { return g_hash_table_lookup(scope->params, name); @@ -44,11 +44,6 @@ LLVMValueRef get_variable(const LLVMLocalScope* scope, const char* name) { return get_variable(scope->parent_scope, name); } - LLVMValueRef param = get_parameter(scope->func_scope, name); - if (param != NULL) { - return param; - } - LLVMValueRef global_var = get_global_variable(scope->func_scope->global_scope, (char*) name); return global_var; } @@ -108,7 +103,7 @@ BackendError impl_func_type(LLVMBackendCompileUnit* unit, DEBUG("implementing function declaration: %s()", func->name); BackendError err = SUCCESS; - GArray* llvm_params = g_array_new(FALSE, FALSE, sizeof(LLVMTypeRef)); + GArray* llvm_params = mem_new_g_array(MemoryNamespaceLlvm, sizeof(LLVMTypeRef)); GArray* func_params = NULL; if (func->kind == FunctionDeclarationKind) { @@ -140,8 +135,6 @@ BackendError impl_func_type(LLVMBackendCompileUnit* unit, g_hash_table_insert(scope->functions, (char*) func->name, llvm_fun_type); - g_array_free(llvm_params, FALSE); - return err; } diff --git a/src/llvm/llvm-ir/func.h b/src/llvm/llvm-ir/func.h index 5ab4621..349135a 100644 --- a/src/llvm/llvm-ir/func.h +++ b/src/llvm/llvm-ir/func.h @@ -28,6 +28,8 @@ void delete_local_scope(LLVMLocalScope*); LLVMValueRef get_variable(const LLVMLocalScope* scope, const char* name); +LLVMValueRef get_parameter(const LLVMFuncScope* scope, const char* name); + LLVMBool is_parameter(const LLVMLocalScope* scope, const char* name); BackendError impl_function_types(LLVMBackendCompileUnit* unit, diff --git a/src/llvm/llvm-ir/stmt.c b/src/llvm/llvm-ir/stmt.c index 6a3f41a..9f20c20 100644 --- a/src/llvm/llvm-ir/stmt.c +++ b/src/llvm/llvm-ir/stmt.c @@ -12,6 +12,27 @@ #include #include +BackendError impl_param_load( + LLVMBackendCompileUnit *unit, + LLVMBuilderRef builder, + LLVMLocalScope *scope, + const StorageExpr *expr, + LLVMValueRef* storage_target) { + BackendError err = SUCCESS; + + if (expr->impl.parameter->impl.declaration.qualifier == Out || expr->impl.parameter->impl.declaration.qualifier == InOut) { + LLVMTypeRef llvm_type = NULL; + err = get_type_impl(unit, scope->func_scope->global_scope, expr->impl.parameter->impl.declaration.type, &llvm_type); + if (err.kind != Success) { + return err; + } + + *storage_target = LLVMBuildLoad2(builder, llvm_type, *storage_target, "strg.param.out.load"); + } + + return err; +} + BackendError impl_storage_expr( LLVMBackendCompileUnit *unit, LLVMBuilderRef @@ -27,6 +48,10 @@ BackendError impl_storage_expr( *storage_target = get_variable(scope, expr->impl.variable->name); break; + case StorageExprKindParameter: + *storage_target = + get_parameter(scope->func_scope, expr->impl.parameter->name); + break; case StorageExprKindDereference: LLVMValueRef index = NULL; @@ -41,16 +66,29 @@ BackendError impl_storage_expr( return err; } + if (expr->impl.dereference.array->kind == StorageExprKindParameter) { + err = impl_param_load(unit, builder, scope, expr->impl.dereference.array, &array); + if (err.kind != Success) { + return err; + } + } + + if (expr->impl.dereference.array->kind == StorageExprKindDereference) { + LLVMTypeRef deref_type = NULL; + err = get_type_impl(unit, scope->func_scope->global_scope, expr->impl.dereference.array->target_type, &deref_type); + if (err.kind != Success) { + return err; + } + + array = LLVMBuildLoad2(builder, deref_type, array, "strg.deref.indirect-load"); + } + LLVMTypeRef deref_type = NULL; err = get_type_impl(unit, scope->func_scope->global_scope, expr->target_type, &deref_type); if (err.kind != Success) { return err; } - if (expr->target_type->kind == TypeKindReference) { - array = LLVMBuildLoad2(builder, deref_type, array, "strg.deref.indirect-load"); - } - *storage_target = LLVMBuildGEP2(builder, deref_type, array, &index, 1, "strg.deref"); break; @@ -73,7 +111,7 @@ BackendError impl_assign_stmt( DEBUG("implementing assignment for variable: %p", assignment); LLVMValueRef llvm_value = NULL; - err = impl_expr(unit, scope, builder, assignment->value, TRUE, &llvm_value); + err = impl_expr(unit, scope, builder, assignment->value, false, &llvm_value); if (err.kind != Success) { return err; } @@ -206,19 +244,24 @@ BackendError impl_func_call(LLVMBackendCompileUnit *unit, param_list = call->function->impl.declaration.parameter; } - LLVMBool reference = FALSE; - Parameter parameter = g_array_index(param_list, Parameter, i); - if (is_parameter_out(¶meter)) { - reference = TRUE; - } + Parameter param = g_array_index(param_list, Parameter, i); LLVMValueRef llvm_arg = NULL; - err = impl_expr(unit, scope, builder, arg, reference, &llvm_arg); + err = impl_expr(unit, scope, builder, arg, is_parameter_out(¶m), &llvm_arg); if (err.kind != Success) { break; } + if (is_parameter_out(¶m)) { + if ((arg->kind == ExpressionKindParameter && !is_parameter_out(arg->impl.parameter)) || arg->kind != ExpressionKindParameter) { + LLVMValueRef index = LLVMConstInt(LLVMInt32Type(), 0, false); + LLVMTypeRef llvm_type = NULL; + get_type_impl(unit, scope->func_scope->global_scope, param.impl.declaration.type, &llvm_type); + llvm_arg = LLVMBuildGEP2(builder, llvm_type, llvm_arg, &index, 1, ""); + } + } + arguments[i] = llvm_arg; } diff --git a/src/llvm/llvm-ir/types.c b/src/llvm/llvm-ir/types.c index f699e97..c64ed6e 100644 --- a/src/llvm/llvm-ir/types.c +++ b/src/llvm/llvm-ir/types.c @@ -321,7 +321,7 @@ BackendError impl_types(LLVMBackendCompileUnit* unit, LLVMGlobalScope* scope, gpointer key = NULL; gpointer val = NULL; - BackendError err; + BackendError err = SUCCESS; while (g_hash_table_iter_next(&iterator, &key, &val) != FALSE) { err = impl_type_define(unit, (Typedefine*) val, (const char*)key, scope); diff --git a/src/llvm/parser.c b/src/llvm/parser.c index 9f07ae8..be7ae7c 100644 --- a/src/llvm/parser.c +++ b/src/llvm/parser.c @@ -90,10 +90,11 @@ BackendError emit_module_to_file(LLVMBackendCompileUnit* unit, ERROR("failed to emit code: %s", error); err = new_backend_impl_error(Implementation, NULL, "failed to emit code"); - LLVMDisposeMessage(error); + } else { print_message(Info, "Generated code was written to: %s", filename); } + LLVMDisposeMessage(error); g_free((void*) filename); g_free((void*) basename); @@ -124,9 +125,9 @@ BackendError export_object(LLVMBackendCompileUnit* unit, const Target* target, ERROR("failed to create target machine: %s", error); err = new_backend_impl_error(Implementation, NULL, "unable to create target machine"); - LLVMDisposeMessage(error); return err; } + LLVMDisposeMessage(error); DEBUG("Creating target machine..."); LLVMTargetMachineRef target_machine = LLVMCreateTargetMachine( @@ -147,6 +148,8 @@ BackendError export_object(LLVMBackendCompileUnit* unit, const Target* target, err = emit_module_to_file(unit, target_machine, LLVMObjectFile, error, config); + LLVMDisposeTargetMachine(target_machine); + return err; } @@ -223,9 +226,9 @@ static BackendError build_module(LLVMBackendCompileUnit* unit, char* error = NULL; if (LLVMVerifyModule(unit->module, LLVMReturnStatusAction, &error)) { print_message(Error, "Unable to compile due to: %s", error); - LLVMDisposeMessage(error); err = new_backend_impl_error(Implementation, NULL, "LLVM backend verification error, see stdout"); } + LLVMDisposeMessage(error); return err; } @@ -259,9 +262,13 @@ BackendError parse_module(const Module* module, const TargetConfig* config) { if (config->mode == Application) { TargetLinkConfig* link_config = lld_create_link_config(&target, config, module); - lld_link_target(link_config); + if (link_config != NULL) { + err = lld_link_target(link_config); - lld_delete_link_config(link_config); + lld_delete_link_config(link_config); + } else { + err = new_backend_impl_error(Implementation, NULL, "libclang error"); + } } } diff --git a/src/main.c b/src/main.c index 5b222a4..0367f8b 100644 --- a/src/main.c +++ b/src/main.c @@ -7,6 +7,7 @@ #include #include #include +#include /** * @brief Log a debug message to inform about beginning exit procedures @@ -38,6 +39,10 @@ void setup(int argc, char *argv[]) { lex_init(); + link_init(); + + init_toml(); + DEBUG("finished starting up gemstone..."); } @@ -64,6 +69,11 @@ int main(int argc, char *argv[]) { exit(0); } + if (is_option_set("list-driver")) { + link_print_available_driver(); + exit(0); + } + run_compiler(); if (is_option_set("print-gc-stats")) { diff --git a/src/mem/cache.h b/src/mem/cache.h index 486e84c..cb47d06 100644 --- a/src/mem/cache.h +++ b/src/mem/cache.h @@ -15,6 +15,7 @@ typedef char* MemoryNamespaceName; #define MemoryNamespaceLex "Lexer" #define MemoryNamespaceLog "Logging" #define MemoryNamespaceOpt "Options" +#define MemoryNamespaceTOML "TOML" #define MemoryNamespaceSet "SET" #define MemoryNamespaceLlvm "LLVM" #define MemoryNamespaceLld "LLD" diff --git a/src/set/set.c b/src/set/set.c index 1ad9652..0d41b2e 100644 --- a/src/set/set.c +++ b/src/set/set.c @@ -1,7 +1,6 @@ #include #include #include -#include #include #include #include @@ -20,7 +19,7 @@ int createTypeCastFromExpression(Expression *expression, Type *resultType, Expre bool compareTypes(Type *leftType, Type *rightType); -char* type_to_string(Type* type); +char *type_to_string(Type *type); const Type ShortShortUnsingedIntType = { .kind = TypeKindComposite, @@ -335,8 +334,8 @@ int createRef(AST_NODE_PTR currentNode, Type **reftype) { assert(currentNode->children->len == 1); assert(AST_get_node(currentNode, 0)->kind == AST_Type); - Type *type = malloc(sizeof(Type)); - Type *referenceType = malloc(sizeof(Type)); + Type *type = mem_alloc(MemoryNamespaceSet, sizeof(Type)); + Type *referenceType = mem_alloc(MemoryNamespaceSet, sizeof(Type)); referenceType->kind = TypeKindReference; referenceType->nodePtr = currentNode; @@ -459,8 +458,8 @@ int createDef(AST_NODE_PTR currentNode, GArray **variables) { } if (!compareTypes(def.declaration.type, name->result)) { - char* expected_type = type_to_string(def.declaration.type); - char* gotten_type = type_to_string(name->result); + char *expected_type = type_to_string(def.declaration.type); + char *gotten_type = type_to_string(name->result); print_diagnostic(&name->nodePtr->location, Warning, "expected `%s` got `%s`", expected_type, gotten_type); @@ -490,8 +489,8 @@ int createDef(AST_NODE_PTR currentNode, GArray **variables) { return status; } -char* type_to_string(Type* type) { - char* string = NULL; +char *type_to_string(Type *type) { + char *string = NULL; switch (type->kind) { case TypeKindPrimitive: @@ -510,27 +509,32 @@ char* type_to_string(Type* type) { if (type->impl.composite.scale < 1.0) { for (int i = 0; i < (int) (type->impl.composite.scale * 4); i++) { - char* concat = g_strconcat("half ", string, NULL); - string = concat; + char *concat = g_strconcat("half ", string, NULL); + string = mem_strdup(MemoryNamespaceSet, concat); + g_free(concat); } } else if (type->impl.composite.scale > 1.0) { for (int i = 0; i < (int) type->impl.composite.scale; i++) { - char* concat = g_strconcat("long ", string, NULL); - string = concat; + char *concat = g_strconcat("long ", string, NULL); + string = mem_strdup(MemoryNamespaceSet, concat); + g_free(concat); } } if (type->impl.composite.sign == Unsigned) { - char* concat = g_strconcat("unsigned ", string, NULL); - string = concat; + char *concat = g_strconcat("unsigned ", string, NULL); + string = mem_strdup(MemoryNamespaceSet, concat); + g_free(concat); } break; } case TypeKindReference: { - char* type_string = type_to_string(type->impl.reference); - char* concat = g_strconcat("ref ", type_string, NULL); - string = concat; + char *type_string = type_to_string(type->impl.reference); + char *concat = g_strconcat("ref ", type_string, NULL); + mem_free(type_string); + string = mem_strdup(MemoryNamespaceSet, concat); + g_free(concat); break; } case TypeKindBox: @@ -541,6 +545,18 @@ char* type_to_string(Type* type) { return string; } +int getParameter(const char *name, Parameter **parameter) { + // loop through all variable scope and find a variable + if (functionParameter != NULL) { + if (g_hash_table_contains(functionParameter, name)) { + *parameter = g_hash_table_lookup(functionParameter, name); + return SEMANTIC_OK; + } + } + + return SEMANTIC_ERROR; +} + int getVariableFromScope(const char *name, Variable **variable) { assert(name != NULL); assert(variable != NULL); @@ -548,13 +564,6 @@ int getVariableFromScope(const char *name, Variable **variable) { DEBUG("getting var from scope"); int found = 0; - // loop through all variable scope and find a variable - if (functionParameter != NULL) { - if (g_hash_table_contains(functionParameter, name)) { - *variable = g_hash_table_lookup(functionParameter, name); - found += 1; - } - } for (size_t i = 0; i < Scope->len; i++) { GHashTable *variable_table = g_array_index(Scope, GHashTable*, i); @@ -612,6 +621,7 @@ int fillTablesWithVars(GHashTable *variableTable, const GArray *variables) { } [[nodiscard("type must be freed")]] + TypeValue createTypeValue(AST_NODE_PTR currentNode) { DEBUG("create TypeValue"); TypeValue value; @@ -726,7 +736,7 @@ int createArithOperation(Expression *ParentExpression, AST_NODE_PTR currentNode, DEBUG("create arithmetic operation"); ParentExpression->impl.operation.kind = Arithmetic; ParentExpression->impl.operation.nodePtr = currentNode; - ParentExpression->impl.operation.operands = g_array_new(FALSE, FALSE, sizeof(Expression *)); + ParentExpression->impl.operation.operands = mem_new_g_array(MemoryNamespaceSet, sizeof(Expression *)); assert(expectedChildCount == currentNode->children->len); @@ -804,7 +814,7 @@ int createRelationalOperation(Expression *ParentExpression, AST_NODE_PTR current // fill kind and Nodeptr ParentExpression->impl.operation.kind = Relational; ParentExpression->impl.operation.nodePtr = currentNode; - ParentExpression->impl.operation.operands = g_array_new(FALSE, FALSE, sizeof(Expression *)); + ParentExpression->impl.operation.operands = mem_new_g_array(MemoryNamespaceSet, sizeof(Expression *)); // fill Operands for (size_t i = 0; i < currentNode->children->len; i++) { @@ -863,7 +873,7 @@ int createBoolOperation(Expression *ParentExpression, AST_NODE_PTR currentNode) // fill kind and Nodeptr ParentExpression->impl.operation.kind = Boolean; ParentExpression->impl.operation.nodePtr = currentNode; - ParentExpression->impl.operation.operands = g_array_new(FALSE, FALSE, sizeof(Expression *)); + ParentExpression->impl.operation.operands = mem_new_g_array(MemoryNamespaceSet, sizeof(Expression *)); // fill Operands for (size_t i = 0; i < currentNode->children->len; i++) { @@ -947,7 +957,7 @@ int createBoolNotOperation(Expression *ParentExpression, AST_NODE_PTR currentNod //fill kind and Nodeptr ParentExpression->impl.operation.kind = Boolean; ParentExpression->impl.operation.nodePtr = currentNode; - ParentExpression->impl.operation.operands = g_array_new(FALSE, FALSE, sizeof(Expression *)); + ParentExpression->impl.operation.operands = mem_new_g_array(MemoryNamespaceSet, sizeof(Expression *)); //fill Operand Expression *expression = createExpression(AST_get_node(currentNode, 0)); @@ -1015,7 +1025,7 @@ int createBitOperation(Expression *ParentExpression, AST_NODE_PTR currentNode) { // fill kind and Nodeptr ParentExpression->impl.operation.kind = Boolean; ParentExpression->impl.operation.nodePtr = currentNode; - ParentExpression->impl.operation.operands = g_array_new(FALSE, FALSE, sizeof(Expression *)); + ParentExpression->impl.operation.operands = mem_new_g_array(MemoryNamespaceSet, sizeof(Expression *)); // fill Operands for (size_t i = 0; i < currentNode->children->len; i++) { @@ -1152,7 +1162,7 @@ int createBitNotOperation(Expression *ParentExpression, AST_NODE_PTR currentNode //fill kind and Nodeptr ParentExpression->impl.operation.kind = Bitwise; ParentExpression->impl.operation.nodePtr = currentNode; - ParentExpression->impl.operation.operands = g_array_new(FALSE, FALSE, sizeof(Expression *)); + ParentExpression->impl.operation.operands = mem_new_g_array(MemoryNamespaceSet, sizeof(Expression *)); //fill Operand Expression *expression = createExpression(AST_get_node(currentNode, 0)); @@ -1425,7 +1435,7 @@ int createAddressOf(Expression *ParentExpression, AST_NODE_PTR currentNode) { return SEMANTIC_ERROR; } - Type *resultType = malloc(sizeof(Type)); + Type *resultType = mem_alloc(MemoryNamespaceSet, sizeof(Type)); resultType->nodePtr = currentNode; resultType->kind = TypeKindReference; resultType->impl.reference = address_of.variable->result; @@ -1435,6 +1445,14 @@ int createAddressOf(Expression *ParentExpression, AST_NODE_PTR currentNode) { return SEMANTIC_OK; } +IO_Qualifier getParameterQualifier(Parameter *parameter) { + if (parameter->kind == ParameterDeclarationKind) { + return parameter->impl.declaration.qualifier; + } else { + return parameter->impl.definiton.declaration.qualifier; + } +} + Expression *createExpression(AST_NODE_PTR currentNode) { DEBUG("create Expression"); Expression *expression = mem_alloc(MemoryNamespaceSet, sizeof(Expression)); @@ -1455,23 +1473,33 @@ Expression *createExpression(AST_NODE_PTR currentNode) { case AST_Ident: DEBUG("find var"); expression->kind = ExpressionKindVariable; - int status = getVariableFromScope(currentNode->value, &(expression->impl.variable)); + int status = getVariableFromScope(currentNode->value, &expression->impl.variable); if (status == SEMANTIC_ERROR) { - DEBUG("Identifier is not in current scope"); - print_diagnostic(¤tNode->location, Error, "Variable not found"); - return NULL; - } - switch (expression->impl.variable->kind) { - case VariableKindDeclaration: + expression->kind = ExpressionKindParameter; + status = getParameter(currentNode->value, &expression->impl.parameter); + if (status == SEMANTIC_ERROR) { + DEBUG("Identifier is not in current scope"); + print_diagnostic(¤tNode->location, Error, "Unknown identifier: `%s`", currentNode->value); + return NULL; + } + + if (getParameterQualifier(expression->impl.parameter) == Out) { + print_diagnostic(¤tNode->location, Error, "Parameter is write-only: `%s`", + currentNode->value); + return NULL; + } + + if (expression->impl.parameter->kind == ParameterDeclarationKind) { + expression->result = expression->impl.parameter->impl.declaration.type; + } else { + expression->result = expression->impl.parameter->impl.definiton.declaration.type; + } + } else { + if (expression->impl.variable->kind == VariableKindDeclaration) { expression->result = expression->impl.variable->impl.declaration.type; - DEBUG("%d", expression->impl.variable->impl.declaration.type->kind); - break; - case VariableKindDefinition: + } else { expression->result = expression->impl.variable->impl.definiton.declaration.type; - break; - default: - PANIC("current Variable should not be an BoxMember"); - break; + } } break; case AST_Add: @@ -1603,7 +1631,7 @@ bool compareTypes(Type *leftType, Type *rightType) { return FALSE; } -Type* getVariableType(Variable* variable) { +Type *getVariableType(Variable *variable) { if (variable->kind == VariableKindDeclaration) { return variable->impl.declaration.type; } else { @@ -1611,15 +1639,33 @@ Type* getVariableType(Variable* variable) { } } -int createStorageExpr(StorageExpr* expr, AST_NODE_PTR node) { +Type *getParameterType(Parameter *parameter) { + if (parameter->kind == ParameterDeclarationKind) { + return parameter->impl.declaration.type; + } else { + return parameter->impl.definiton.declaration.type; + } +} + +int createStorageExpr(StorageExpr *expr, AST_NODE_PTR node) { switch (node->kind) { case AST_Ident: expr->kind = StorageExprKindVariable; int status = getVariableFromScope(node->value, &expr->impl.variable); if (status == SEMANTIC_ERROR) { - return SEMANTIC_ERROR; + + expr->kind = StorageExprKindParameter; + status = getParameter(node->value, &expr->impl.parameter); + if (status == SEMANTIC_ERROR) { + print_diagnostic(&node->location, Error, "Unknown token: `%s`", node->value); + return SEMANTIC_ERROR; + } else { + expr->target_type = getParameterType(expr->impl.parameter); + } + + } else { + expr->target_type = getVariableType(expr->impl.variable); } - expr->target_type = getVariableType(expr->impl.variable); break; case AST_Dereference: expr->kind = StorageExprKindDereference; @@ -1629,7 +1675,7 @@ int createStorageExpr(StorageExpr* expr, AST_NODE_PTR node) { expr->impl.dereference.index = createExpression(index_node); expr->impl.dereference.array = mem_alloc(MemoryNamespaceSet, sizeof(StorageExpr)); - if (createStorageExpr(expr->impl.dereference.array, array_node) == SEMANTIC_ERROR){ + if (createStorageExpr(expr->impl.dereference.array, array_node) == SEMANTIC_ERROR) { return SEMANTIC_ERROR; } @@ -1656,17 +1702,31 @@ int createAssign(Statement *ParentStatement, AST_NODE_PTR currentNode) { assign.nodePtr = currentNode; assign.destination = mem_alloc(MemoryNamespaceSet, sizeof(StorageExpr)); - int status = createStorageExpr(assign.destination, AST_get_node(currentNode, 0)); + AST_NODE_PTR strg_expr = AST_get_node(currentNode, 0); + int status = createStorageExpr(assign.destination, strg_expr); if (status == SEMANTIC_ERROR) { return SEMANTIC_ERROR; } + if (strg_expr->kind == AST_Parameter) { + if (getParameterQualifier(assign.destination->impl.parameter) == In) { + print_diagnostic(¤tNode->location, Error, "Parameter is read-only: `%s`", + assign.destination->impl.parameter->name); + return SEMANTIC_ERROR; + } + } + assign.value = createExpression(AST_get_node(currentNode, 1)); if (assign.value == NULL) { return SEMANTIC_ERROR; } - // TODO: check assignment type compatability + if (!compareTypes(assign.destination->target_type, assign.value->result)) { + print_diagnostic(&assign.value->nodePtr->location, Error, "assignment requires `%s` but got `%s`", + type_to_string(assign.destination->target_type), + type_to_string(assign.value->result)); + return SEMANTIC_ERROR; + } ParentStatement->impl.assignment = assign; return SEMANTIC_OK; @@ -1677,18 +1737,19 @@ int createStatement(Block *block, AST_NODE_PTR currentNode); int fillBlock(Block *block, AST_NODE_PTR currentNode) { DEBUG("start filling Block"); block->nodePtr = currentNode; - block->statemnts = g_array_new(FALSE, FALSE, sizeof(Statement *)); - GHashTable *lowerScope = g_hash_table_new(g_str_hash, g_str_equal); + block->statemnts = mem_new_g_array(MemoryNamespaceSet, sizeof(Statement *)); + GHashTable *lowerScope = mem_new_g_hash_table(MemoryNamespaceSet, g_str_hash, g_str_equal); g_array_append_val(Scope, lowerScope); for (size_t i = 0; i < currentNode->children->len; i++) { - int signal = createStatement(block, AST_get_node(currentNode, i)); + AST_NODE_PTR stmt_node = AST_get_node(currentNode, i); + int signal = createStatement(block, stmt_node); if (signal) { return SEMANTIC_ERROR; } } - g_hash_table_destroy(lowerScope); + mem_free(lowerScope); g_array_remove_index(Scope, Scope->len - 1); DEBUG("created Block successfully"); @@ -1867,7 +1928,7 @@ int createfuncall(Statement *parentStatement, AST_NODE_PTR currentNode) { for (size_t i = 0; i < argsListNode->children->len; i++) { AST_NODE_PTR currentExprList = AST_get_node(argsListNode, i); - for (int j = ((int) currentExprList->children->len) -1; j >= 0; j--) { + for (int j = ((int) currentExprList->children->len) - 1; j >= 0; j--) { AST_NODE_PTR expr_node = AST_get_node(currentExprList, j); Expression *expr = createExpression(expr_node); if (expr == NULL) { @@ -1889,7 +1950,7 @@ int createStatement(Block *Parentblock, AST_NODE_PTR currentNode) { switch (currentNode->kind) { case AST_Decl: { - GArray *variable = g_array_new(FALSE, FALSE, sizeof(Variable *)); + GArray *variable = mem_new_g_array(MemoryNamespaceSet, sizeof(Variable *)); int status = createDecl(currentNode, &variable); if (status) { @@ -1908,7 +1969,7 @@ int createStatement(Block *Parentblock, AST_NODE_PTR currentNode) { break; case AST_Def: { - GArray *variable = g_array_new(FALSE, FALSE, sizeof(Variable *)); + GArray *variable = mem_new_g_array(MemoryNamespaceSet, sizeof(Variable *)); if (createDef(currentNode, &variable)) { return SEMANTIC_ERROR; @@ -2003,30 +2064,21 @@ int createParam(GArray *Paramlist, AST_NODE_PTR currentNode) { if (set_get_type_impl(AST_get_node(paramdecl, 0), &(decl.type))) { return SEMANTIC_ERROR; } - Parameter param; - param.nodePtr = currentNode; - param.kind = ParameterDeclarationKind; - param.impl.declaration = decl; - param.name = AST_get_node(paramdecl, 1)->value; + Parameter *param = mem_alloc(MemoryNamespaceSet, sizeof(Parameter)); + param->nodePtr = currentNode; + param->kind = ParameterDeclarationKind; + param->impl.declaration = decl; + param->name = AST_get_node(paramdecl, 1)->value; - DEBUG("param name: %s", param.name); - g_array_append_val(Paramlist, param); + DEBUG("param name: %s", param->name); + g_array_append_val(Paramlist, *param); - DEBUG("create var for param"); - - Variable *paramvar = mem_alloc(MemoryNamespaceSet, sizeof(Variable)); - paramvar->kind = VariableKindDeclaration; - paramvar->name = param.name; - paramvar->nodePtr = currentNode; - paramvar->impl.declaration.nodePtr = currentNode; - paramvar->impl.declaration.qualifier = Local; - paramvar->impl.declaration.type = param.impl.declaration.type; - - if (g_hash_table_contains(functionParameter, param.name)) { - print_diagnostic(¶m.nodePtr->location, Error, "Names of function parameters must be unique: %s", param.name); + if (g_hash_table_contains(functionParameter, param->name)) { + print_diagnostic(¶m->nodePtr->location, Error, "Names of function parameters must be unique: %s", + param->name); return SEMANTIC_ERROR; } - g_hash_table_insert(functionParameter, (gpointer) param.name, paramvar); + g_hash_table_insert(functionParameter, (gpointer) param->name, param); DEBUG("created param successfully"); return SEMANTIC_OK; @@ -2043,7 +2095,7 @@ int createFunDef(Function *Parentfunction, AST_NODE_PTR currentNode) { fundef.nodePtr = currentNode; fundef.name = nameNode->value; fundef.body = mem_alloc(MemoryNamespaceSet, sizeof(Block)); - fundef.parameter = g_array_new(FALSE, FALSE, sizeof(Parameter)); + fundef.parameter = mem_new_g_array(MemoryNamespaceSet, sizeof(Parameter)); DEBUG("paramlistlist child count: %i", paramlistlist->children->len); for (size_t i = 0; i < paramlistlist->children->len; i++) { @@ -2106,7 +2158,8 @@ bool compareParameter(GArray *leftParameter, GArray *rightParameter) { int addFunction(const char *name, Function *function) { if (function->kind == FunctionDefinitionKind) { if (g_hash_table_contains(definedFunctions, name)) { - print_diagnostic(&function->nodePtr->location, Error, "Multiple definition of function: `%s`", function->name); + print_diagnostic(&function->nodePtr->location, Error, "Multiple definition of function: `%s`", + function->name); return SEMANTIC_ERROR; } g_hash_table_insert(declaredFunctions, (gpointer) name, function); @@ -2117,7 +2170,8 @@ int addFunction(const char *name, Function *function) { function->impl.declaration.parameter); // a function can have multiple declartations but all have to be identical if (result == FALSE) { - print_diagnostic(&function->nodePtr->location, Error, "Divergent declaration of function: `%s`", function->name); + print_diagnostic(&function->nodePtr->location, Error, "Divergent declaration of function: `%s`", + function->name); return SEMANTIC_ERROR; } } @@ -2175,7 +2229,7 @@ int createFunDecl(Function *Parentfunction, AST_NODE_PTR currentNode) { int createFunction(Function *function, AST_NODE_PTR currentNode) { assert(currentNode->kind == AST_Fun); - functionParameter = g_hash_table_new(g_str_hash, g_str_equal); + functionParameter = mem_new_g_hash_table(MemoryNamespaceSet, g_str_hash, g_str_equal); if (currentNode->children->len == 2) { int signal = createFunDecl(function, currentNode); @@ -2191,7 +2245,7 @@ int createFunction(Function *function, AST_NODE_PTR currentNode) { PANIC("function should have 2 or 3 children"); } - g_hash_table_destroy(functionParameter); + mem_free(functionParameter); functionParameter = NULL; int result = addFunction(function->name, function); @@ -2359,32 +2413,34 @@ int createTypeDef(GHashTable *types, AST_NODE_PTR currentNode) { Module *create_set(AST_NODE_PTR currentNode) { DEBUG("create root Module"); //create tables for types - declaredComposites = g_hash_table_new(g_str_hash, g_str_equal); - declaredBoxes = g_hash_table_new(g_str_hash, g_str_equal); - declaredFunctions = g_hash_table_new(g_str_hash, g_str_equal); - definedFunctions = g_hash_table_new(g_str_hash, g_str_equal); + declaredComposites = mem_new_g_hash_table(MemoryNamespaceSet, g_str_hash, g_str_equal); + declaredBoxes = mem_new_g_hash_table(MemoryNamespaceSet, g_str_hash, g_str_equal); + declaredFunctions = mem_new_g_hash_table(MemoryNamespaceSet, g_str_hash, g_str_equal); + definedFunctions = mem_new_g_hash_table(MemoryNamespaceSet, g_str_hash, g_str_equal); //create scope - Scope = g_array_new(FALSE, FALSE, sizeof(GHashTable *)); + Scope = mem_new_g_array(MemoryNamespaceSet, sizeof(GHashTable *)); //building current scope for module - GHashTable *globalscope = g_hash_table_new(g_str_hash, g_str_equal); - globalscope = g_hash_table_new(g_str_hash, g_str_equal); + GHashTable *globalscope = mem_new_g_hash_table(MemoryNamespaceSet, g_str_hash, g_str_equal); + globalscope = mem_new_g_hash_table(MemoryNamespaceSet, g_str_hash, g_str_equal); g_array_append_val(Scope, globalscope); Module *rootModule = mem_alloc(MemoryNamespaceSet, sizeof(Module)); - GHashTable *boxes = g_hash_table_new(g_str_hash, g_str_equal); - GHashTable *types = g_hash_table_new(g_str_hash, g_str_equal); - GHashTable *functions = g_hash_table_new(g_str_hash, g_str_equal); - GHashTable *variables = g_hash_table_new(g_str_hash, g_str_equal); - GArray *imports = g_array_new(FALSE, FALSE, sizeof(const char *)); + GHashTable *boxes = mem_new_g_hash_table(MemoryNamespaceSet, g_str_hash, g_str_equal); + GHashTable *types = mem_new_g_hash_table(MemoryNamespaceSet, g_str_hash, g_str_equal); + GHashTable *functions = mem_new_g_hash_table(MemoryNamespaceSet, g_str_hash, g_str_equal); + GHashTable *variables = mem_new_g_hash_table(MemoryNamespaceSet, g_str_hash, g_str_equal); + GArray *imports = mem_new_g_array(MemoryNamespaceSet, sizeof(const char *)); + GArray *includes = mem_new_g_array(MemoryNamespaceSet, sizeof(const char *)); rootModule->boxes = boxes; rootModule->types = types; rootModule->functions = functions; rootModule->variables = variables; rootModule->imports = imports; + rootModule->includes = includes; DEBUG("created Module struct"); @@ -2456,6 +2512,10 @@ Module *create_set(AST_NODE_PTR currentNode) { DEBUG("create Import"); g_array_append_val(imports, AST_get_node(currentNode, i)->value); break; + case AST_Include: + DEBUG("create Include"); + g_array_append_val(includes, AST_get_node(currentNode, i)->value); + break; default: INFO("Provided source file could not be parsed because of semantic error."); break; diff --git a/src/set/types.h b/src/set/types.h index 2eb6e44..828f14e 100644 --- a/src/set/types.h +++ b/src/set/types.h @@ -432,6 +432,7 @@ typedef enum ExpressionKind_t { ExpressionKindTransmute, ExpressionKindConstant, ExpressionKindVariable, + ExpressionKindParameter, ExpressionKindDereference, ExpressionKindAddressOf, } ExpressionKind; @@ -446,6 +447,7 @@ typedef struct Expression_t { Transmute transmute; TypeValue constant; Variable* variable; + Parameter* parameter; Dereference dereference; AddressOf addressOf; } impl; @@ -526,6 +528,7 @@ typedef struct Branch_t { typedef enum StorageExprKind_t { StorageExprKindVariable, + StorageExprKindParameter, StorageExprKindBoxAccess, StorageExprKindDereference, } StorageExprKind; @@ -535,6 +538,7 @@ typedef struct StorageExpr_t { Type* target_type; union StorageExprImpl { Variable* variable; + Parameter* parameter; BoxAccess boxAccess; StorageDereference dereference; } impl; @@ -580,6 +584,7 @@ typedef struct Module_t { GHashTable* variables; // to be resolved after the module has been parsed completely GArray* imports; + GArray* includes; } Module; diff --git a/src/yacc/parser.y b/src/yacc/parser.y index 70f3069..29dd70c 100644 --- a/src/yacc/parser.y +++ b/src/yacc/parser.y @@ -53,6 +53,7 @@ %type opbool %type opbit %type moduleimport +%type moduleinclude %type programbody %type fundef %type fundecl @@ -109,6 +110,7 @@ %token OpBitnot %token OpBitxor %token KeyImport +%token KeyInclude %token KeySilent %token KeyBox %token FunTypeof @@ -142,6 +144,7 @@ program: program programbody {AST_push_node(root, $2); | programbody {AST_push_node(root, $1);}; programbody: moduleimport {$$ = $1;} + | moduleinclude {$$ = $1;} | fundef{$$ = $1;} | fundecl{$$ = $1;} | box{$$ = $1;} @@ -328,6 +331,9 @@ funcall: Ident argumentlist {AST_NODE_PTR funcall = AST_new_node(new_loc(), AST_ moduleimport: KeyImport ValStr {$$ = AST_new_node(new_loc(), AST_Import, $2); DEBUG("Module-Import"); }; +moduleinclude: KeyInclude ValStr {$$ = AST_new_node(new_loc(), AST_Include, $2); + DEBUG("Module-Include"); }; + statementlist: statementlist statement {AST_push_node($1, $2); $$ = $1;} | statement {AST_NODE_PTR list = AST_new_node(new_loc(), AST_StmtList, NULL); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3ce0770..456af55 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,3 +13,5 @@ add_subdirectory(glib) add_subdirectory(llvm) add_subdirectory(project) add_subdirectory(cache) +add_subdirectory(hello_world) +add_subdirectory(driver) diff --git a/tests/ast/test_ast.py b/tests/ast/test_ast.py index d7f9665..b153c33 100644 --- a/tests/ast/test_ast.py +++ b/tests/ast/test_ast.py @@ -84,6 +84,7 @@ def run_check_print_node(): 53 address of 54 deref 55 ref +56 value """ == p.stdout diff --git a/tests/driver/CMakeLists.txt b/tests/driver/CMakeLists.txt new file mode 100644 index 0000000..85fd68c --- /dev/null +++ b/tests/driver/CMakeLists.txt @@ -0,0 +1,13 @@ +include(CTest) + +# ------------------------------------------------------- # +# CTEST 1 +# test compilation and execution with GCC driver + +add_test(NAME driver_gcc + WORKING_DIRECTORY ${GEMSTONE_TEST_DIR}/driver/gcc + COMMAND python ${GEMSTONE_TEST_DIR}/driver/gcc/test_driver_gcc.py) + +add_test(NAME driver_clang + WORKING_DIRECTORY ${GEMSTONE_TEST_DIR}/driver/clang + COMMAND python ${GEMSTONE_TEST_DIR}/driver/clang/test_driver_clang.py) diff --git a/tests/driver/clang/build.toml b/tests/driver/clang/build.toml new file mode 100644 index 0000000..f200572 --- /dev/null +++ b/tests/driver/clang/build.toml @@ -0,0 +1,19 @@ +[project] +name = "driver compilation test" +version = "0.1.0" +description = "Print a string to stdout" +license = "GPL-2.0" +authors = [ "Sven Vogel " ] + +[target.release] +link-paths = [ "../../../bin/std" ] +import-paths = [ "../../../lib/src" ] +driver = "clang" +root = "main.gsc" +mode = "application" +output = "bin" +archive = "archive" +print_ast = false +print_asm = false +print_ir = false +opt = 3 diff --git a/tests/driver/clang/main.gsc b/tests/driver/clang/main.gsc new file mode 100644 index 0000000..70314a2 --- /dev/null +++ b/tests/driver/clang/main.gsc @@ -0,0 +1,28 @@ + +import "std" + +fun cstrlen(in cstr: str)(out u32: len) { + u32: idx = 0 as u32 + + while !(str[idx] == 0) { + idx = idx + 1 + } + + len = idx +} + +fun printcstr(in cstr: msg) { + u32: len = 0 + cstrlen(msg)(len) + + handle: stdout = 0 + getStdoutHandle()(stdout) + + u32: written = 0 + writeBytes(stdout, msg, len)(written) +} + +fun main() { + cstr: msg = "Hello, world!\n" + printcstr(msg) +} diff --git a/tests/driver/clang/test_driver_clang.py b/tests/driver/clang/test_driver_clang.py new file mode 100644 index 0000000..ae8f2b1 --- /dev/null +++ b/tests/driver/clang/test_driver_clang.py @@ -0,0 +1,29 @@ +import os.path +import subprocess +import logging +from logging import info + + +def check_clang(): + info("testing Clang driver...") + + logging.basicConfig(level=logging.INFO) + + p = subprocess.run(["../../../bin/check/gsc", "build", "release", "--verbose", "--driver=clang"], capture_output=True, text=True) + + print(p.stdout) + + assert p.returncode == 0 + + p = subprocess.run(["bin/release.out"], capture_output=True, text=True) + + print(p.stdout) + + assert "Hello, world!" in p.stdout + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + info("check if binary exists...") + assert os.path.exists("../../../bin/check/gsc") + + check_clang() diff --git a/tests/driver/gcc/build.toml b/tests/driver/gcc/build.toml new file mode 100644 index 0000000..85e76d9 --- /dev/null +++ b/tests/driver/gcc/build.toml @@ -0,0 +1,19 @@ +[project] +name = "driver compilation test" +version = "0.1.0" +description = "Print a string to stdout" +license = "GPL-2.0" +authors = [ "Sven Vogel " ] + +[target.release] +link-paths = [ "../../../bin/std" ] +import-paths = [ "../../../lib/src" ] +driver = "gcc" +root = "main.gsc" +mode = "application" +output = "bin" +archive = "archive" +print_ast = false +print_asm = false +print_ir = false +opt = 3 diff --git a/tests/driver/gcc/main.gsc b/tests/driver/gcc/main.gsc new file mode 100644 index 0000000..70314a2 --- /dev/null +++ b/tests/driver/gcc/main.gsc @@ -0,0 +1,28 @@ + +import "std" + +fun cstrlen(in cstr: str)(out u32: len) { + u32: idx = 0 as u32 + + while !(str[idx] == 0) { + idx = idx + 1 + } + + len = idx +} + +fun printcstr(in cstr: msg) { + u32: len = 0 + cstrlen(msg)(len) + + handle: stdout = 0 + getStdoutHandle()(stdout) + + u32: written = 0 + writeBytes(stdout, msg, len)(written) +} + +fun main() { + cstr: msg = "Hello, world!\n" + printcstr(msg) +} diff --git a/tests/driver/gcc/test_driver_gcc.py b/tests/driver/gcc/test_driver_gcc.py new file mode 100644 index 0000000..a081961 --- /dev/null +++ b/tests/driver/gcc/test_driver_gcc.py @@ -0,0 +1,29 @@ +import os.path +import subprocess +import logging +from logging import info + + +def check_gcc(): + info("testing GCC driver...") + + logging.basicConfig(level=logging.INFO) + + p = subprocess.run(["../../../bin/check/gsc", "build", "release", "--verbose", "--driver=gcc"], capture_output=True, text=True) + + print(p.stdout) + + assert p.returncode == 0 + + p = subprocess.run(["bin/release.out"], capture_output=True, text=True) + + print(p.stdout) + + assert "Hello, world!" in p.stdout + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + info("check if binary exists...") + assert os.path.exists("../../../bin/check/gsc") + + check_gcc() diff --git a/tests/hello_world/CMakeLists.txt b/tests/hello_world/CMakeLists.txt new file mode 100644 index 0000000..6ca46a7 --- /dev/null +++ b/tests/hello_world/CMakeLists.txt @@ -0,0 +1,9 @@ +include(CTest) + +# ------------------------------------------------------- # +# CTEST 1 +# test a simple project + +add_test(NAME hello_world + WORKING_DIRECTORY ${GEMSTONE_TEST_DIR}/hello_world + COMMAND python ${GEMSTONE_TEST_DIR}/hello_world/test_hello_world.py) diff --git a/tests/hello_world/build.toml b/tests/hello_world/build.toml new file mode 100644 index 0000000..71fbfd3 --- /dev/null +++ b/tests/hello_world/build.toml @@ -0,0 +1,19 @@ +[project] +name = "print C string test" +version = "0.1.0" +description = "Print a string to stdout" +license = "GPL-2.0" +authors = [ "Sven Vogel " ] + +[target.release] +link-paths = [ "../../bin/std" ] +import-paths = [ "../../lib/src" ] +driver = "gcc" +root = "main.gsc" +mode = "application" +output = "bin" +archive = "archive" +print_ast = false +print_asm = false +print_ir = false +opt = 3 diff --git a/tests/hello_world/main.gsc b/tests/hello_world/main.gsc new file mode 100644 index 0000000..70314a2 --- /dev/null +++ b/tests/hello_world/main.gsc @@ -0,0 +1,28 @@ + +import "std" + +fun cstrlen(in cstr: str)(out u32: len) { + u32: idx = 0 as u32 + + while !(str[idx] == 0) { + idx = idx + 1 + } + + len = idx +} + +fun printcstr(in cstr: msg) { + u32: len = 0 + cstrlen(msg)(len) + + handle: stdout = 0 + getStdoutHandle()(stdout) + + u32: written = 0 + writeBytes(stdout, msg, len)(written) +} + +fun main() { + cstr: msg = "Hello, world!\n" + printcstr(msg) +} diff --git a/tests/hello_world/test_hello_world.py b/tests/hello_world/test_hello_world.py new file mode 100644 index 0000000..a9f801d --- /dev/null +++ b/tests/hello_world/test_hello_world.py @@ -0,0 +1,29 @@ +import os.path +import subprocess +import logging +from logging import info + + +def check_build_and_run(): + info("testing compilation of hello world...") + + logging.basicConfig(level=logging.INFO) + + p = subprocess.run(["../../bin/check/gsc", "build", "release", "--verbose"], capture_output=True, text=True) + + print(p.stdout) + + assert p.returncode == 0 + + p = subprocess.run(["bin/release.out"], capture_output=True, text=True) + + print(p.stdout) + + assert "Hello, world!" in p.stdout + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + info("check if binary exists...") + assert os.path.exists("../../bin/check/gsc") + + check_build_and_run() diff --git a/tests/input_file/CMakeLists.txt b/tests/input_file/CMakeLists.txt index 263e769..23244fb 100644 --- a/tests/input_file/CMakeLists.txt +++ b/tests/input_file/CMakeLists.txt @@ -6,4 +6,4 @@ include(CTest) add_test(NAME input_file_check WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/bin/check - COMMAND python ${GEMSTONE_TEST_DIR}/input_file/test_input_file.py ${GEMSTONE_TEST_DIR}/input_file/test.gem) + COMMAND python ${GEMSTONE_TEST_DIR}/input_file/test_input_file.py ${GEMSTONE_TEST_DIR}/input_file/test.gsc) diff --git a/tests/input_file/test.gem b/tests/input_file/test.gem deleted file mode 100644 index 991288d..0000000 --- a/tests/input_file/test.gem +++ /dev/null @@ -1,6 +0,0 @@ - -import "std.io" - -fun main { - print("Hello, World!!!") -} \ No newline at end of file diff --git a/tests/input_file/test.gsc b/tests/input_file/test.gsc new file mode 100644 index 0000000..3479ad3 --- /dev/null +++ b/tests/input_file/test.gsc @@ -0,0 +1,4 @@ + +fun main(out int: ret) { + ret = 56 as int +}