From 20ef894f8825f4469c7248127e35d65d0bcd09bc Mon Sep 17 00:00:00 2001 From: Ur Mom Date: Tue, 14 May 2024 21:55:42 +0200 Subject: [PATCH 01/27] Initial commit --- src/main.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main.c b/src/main.c index 03ab6b9..70fc423 100644 --- a/src/main.c +++ b/src/main.c @@ -13,6 +13,14 @@ extern FILE *yyin; */ void notify_exit(void) { DEBUG("Exiting gemstone..."); } +size_t check_option(const char* name) { +if (0 != name){ + return 1; +} else { + return 0; +}; +}; + /** * @brief Closes File after compiling. * From 027b54d0872eeb69a039fecef4e75a9f0123923a Mon Sep 17 00:00:00 2001 From: Ur Mom Date: Tue, 14 May 2024 22:32:44 +0200 Subject: [PATCH 02/27] Second commit --- src/main.c | 50 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/src/main.c b/src/main.c index 70fc423..c9eda55 100644 --- a/src/main.c +++ b/src/main.c @@ -7,19 +7,44 @@ extern FILE *yyin; +// Global array to store options +char options[5][10]; +int num_options = 0; + /** * @brief Log a debug message to inform about beginning exit procedures * */ void notify_exit(void) { DEBUG("Exiting gemstone..."); } + +/** + * @brief add option to global option array + * + */ + +void add_option(const char* option) { + if (num_options < 5 ) { + strcpy(options[num_options], option); + num_options++; + } else { + PANIC("Too Many Options given"); + } +} + +/** + * @brief Check if Option is set + * + */ + size_t check_option(const char* name) { -if (0 != name){ - return 1; -} else { - return 0; -}; -}; + for (int i = 0; i < num_options; i++) { + if (strcmp(options[i], name) == 0) { + return 1; + } + } + return 0; +} /** * @brief Closes File after compiling. @@ -38,7 +63,6 @@ void close_file(void) { */ void setup(void) { // setup preample - log_init(); DEBUG("starting gemstone..."); @@ -54,6 +78,18 @@ void setup(void) { int main(int argc, char *argv[]) { + // Iteration through arguments + for (int i = 1; i < argc; i++) { + // Check if the argument starts with "--" + if (argv[i][0] == '-' && argv[i][1] == '-') { + // Extract option name + char option[10]; + strcpy(option, argv[i] + 2); + + // Add option to the global array + add_option(option); + } + } setup(); atexit(close_file); From 8c17749923b4088fb41eeaa65dbac60d474676fc Mon Sep 17 00:00:00 2001 From: servostar Date: Sun, 26 May 2024 14:09:53 +0200 Subject: [PATCH 03/27] refactored transmute rule and added bracketed expressions --- src/lex/lexer.l | 1 + src/yacc/parser.y | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lex/lexer.l b/src/lex/lexer.l index 7c0cc0c..a3a2f32 100644 --- a/src/lex/lexer.l +++ b/src/lex/lexer.l @@ -52,6 +52,7 @@ "float" {DEBUG("\"%s\" tokenized with \'KeyFloat\'", yytext); return(KeyFloat);}; "self" {DEBUG("\"%s\" tokenized with \'KeySelf\'", yytext); return(KeySelf);}; "as" {DEBUG("\"%s\" tokenized with \'KeyAs'", yytext); return (KeyAs);}; +"to" {DEBUG("\"%s\" tokenized with \'KeyTo'", yytext); return (KeyTo);}; "short" {DEBUG("\"%s\" tokenized with \'KeyShort\'", yytext); return(KeyShort);}; "long" {DEBUG("\"%s\" tokenized with \'KeyLong\'", yytext); return(KeyLong);}; "half" {DEBUG("\"%s\" tokenized with \'KeyHalf\'", yytext); return(KeyHalf);}; diff --git a/src/yacc/parser.y b/src/yacc/parser.y index 753c7bc..8e6baf9 100644 --- a/src/yacc/parser.y +++ b/src/yacc/parser.y @@ -72,6 +72,7 @@ %token KeyFloat %token KeySelf %token KeyAs +%token KeyTo %token ValInt %token Ident %token ValFloat @@ -128,7 +129,7 @@ %left '+' '-' %left '*' '/' %left OpNot OpBitnot -%left KeyAs +%left KeyAs KeyTo %left '(' ')' %% @@ -154,6 +155,7 @@ expr: ValFloat {$$ = AST_new_node(AST_Float, $1);} | boxselfaccess{$$ = $1;} | typecast{$$ = $1;} | reinterpretcast{$$ = $1;} + | '(' expr ')' {$$=$2;} exprlist: expr ',' exprlist {AST_push_node($3, $1); $$ = $3;} @@ -289,9 +291,9 @@ typecast: expr KeyAs type %prec KeyAs {AST_NODE_PTR cast = AST_new_node(AST_Typ $$ = cast; DEBUG("Type-Cast"); }; -reinterpretcast: '(' type ')' expr { AST_NODE_PTR cast = AST_new_node(AST_Transmute, NULL); - AST_push_node(cast, $4); - AST_push_node(cast, $2); +reinterpretcast: expr KeyTo type %prec KeyTo { AST_NODE_PTR cast = AST_new_node(AST_Transmute, NULL); + AST_push_node(cast, $1); + AST_push_node(cast, $3); $$ = cast; DEBUG("Reinterpret-Cast"); }; From 472a4a623c8af480b0ace97b32af7210de2812e9 Mon Sep 17 00:00:00 2001 From: servostar Date: Thu, 30 May 2024 21:06:03 +0200 Subject: [PATCH 04/27] feature: added error diagnostics and the ability to parse multiple files --- src/ast/ast.c | 17 ++- src/ast/ast.h | 5 +- src/io/files.c | 224 ++++++++++++++++++++++++++++++++++++ src/io/files.h | 77 +++++++++++++ src/lex/util.c | 14 ++- src/lex/util.h | 2 + src/main.c | 116 +++++++++++++------ src/sys/log.h | 2 +- src/yacc/parser.y | 286 ++++++++++++++++++---------------------------- 9 files changed, 530 insertions(+), 213 deletions(-) create mode 100644 src/io/files.c create mode 100644 src/io/files.h diff --git a/src/ast/ast.c b/src/ast/ast.c index 892af1f..8c65510 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -5,7 +5,7 @@ #include #include -struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value) { +struct AST_Node_t *AST_new_node(TokenLocation location, enum AST_SyntaxElement_t kind, const char* value) { DEBUG("creating new AST node: %d \"%s\"", kind, value); assert(kind < AST_ELEMENT_COUNT); @@ -23,6 +23,7 @@ struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value node->child_count = 0; node->kind = kind; node->value = value; + node->location = location; return node; } @@ -114,6 +115,14 @@ const char* AST_node_to_string(const struct AST_Node_t* node) { return string; } +static inline unsigned long int min(unsigned long int a, unsigned long int b) { + return a > b ? b : a; +} + +static inline unsigned long int max(unsigned long int a, unsigned long int b) { + return a > b ? a : b; +} + void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) { DEBUG("Adding new node %p to %p", child, owner); assert(owner != NULL); @@ -134,6 +143,12 @@ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) { PANIC("failed to allocate children array of AST node"); } + owner->location.col_end = max(owner->location.col_end, child->location.col_end); + owner->location.line_end = max(owner->location.line_end, child->location.line_end); + + owner->location.col_start = min(owner->location.col_start, child->location.col_start); + owner->location.line_start = min(owner->location.line_start, child->location.line_start); + assert(owner->children != NULL); owner->children[owner->child_count++] = child; diff --git a/src/ast/ast.h b/src/ast/ast.h index ff8297b..7edbe81 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -3,6 +3,7 @@ #define _AST_H_ #include +#include /** * @brief The type of a AST node @@ -94,6 +95,8 @@ struct AST_Node_t { // optional value: integer literal, string literal, ... const char* value; + TokenLocation location; + // number of child nodes ownd by this node // length of children array size_t child_count; @@ -133,7 +136,7 @@ const char* AST_node_to_string(const struct AST_Node_t* node); [[maybe_unused]] [[nodiscard("pointer must be freed")]] [[gnu::returns_nonnull]] -struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value); +struct AST_Node_t *AST_new_node(TokenLocation location, enum AST_SyntaxElement_t kind, const char* value); /** * @brief Deallocate this node and all of its children. diff --git a/src/io/files.c b/src/io/files.c new file mode 100644 index 0000000..97daed1 --- /dev/null +++ b/src/io/files.c @@ -0,0 +1,224 @@ +// +// Created by servostar on 5/30/24. +// + +#include +#include +#include +#include + +ModuleFile *push_file(ModuleFileStack *stack, const char *path) { + assert(stack != NULL); + + // lazy init of heap stack + if (stack->files == NULL) { + stack->files = g_array_new(FALSE, FALSE, sizeof(ModuleFile)); + } + + ModuleFile new_file = { + .path = path, + .handle = NULL + }; + + g_array_append_val(stack->files, new_file); + + return ((ModuleFile *) stack->files->data) + stack->files->len - 1; +} + +void delete_files(ModuleFileStack *stack) { + for (size_t i = 0; i < stack->files->len; i++) { + ModuleFile *file = ((ModuleFile *) stack->files->data) + i; + + if (file->handle != NULL) { + DEBUG("closing file: %s", file->path); + fclose(file->handle); + } + + } + + g_array_free(stack->files, TRUE); + DEBUG("deleted module file stack"); +} + +#define SEEK_BUF_BYTES 256 + +static inline unsigned long int min(unsigned long int a, unsigned long int b) { + return a > b ? b : a; +} + +// behaves like fgets except that it has defined behavior when n == 1 +static void custom_fgets(char *buffer, size_t n, FILE *stream) { + if (n == 1) { + buffer[0] = (char) fgetc(stream); + buffer[1] = 0; + } else { + fgets(buffer, (int) n, stream); + } +} + +void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, const char *message) { + assert(file->handle != NULL); + assert(location != NULL); + assert(message != NULL); + + // reset to start + rewind(file->handle); + + char *buffer = alloca(SEEK_BUF_BYTES); + unsigned long int line_count = 1; + + // seek to first line + while (line_count < location->line_start && fgets(buffer, SEEK_BUF_BYTES, file->handle) != NULL) { + line_count += strchr(buffer, '\n') != NULL; + } + + const char *accent_color = RESET; + const char *kind_text = "unknown"; + switch (kind) { + case Info: + kind_text = "info"; + accent_color = CYAN; + file->statistics.info_count++; + break; + case Warning: + kind_text = "warning"; + accent_color = YELLOW; + file->statistics.warning_count++; + break; + case Error: + kind_text = "error"; + accent_color = RED; + file->statistics.error_count++; + break; + } + + char absolute_path[PATH_MAX]; + realpath(file->path, absolute_path); + + printf("%s%s:%ld:%s %s%s:%s %s\n", BOLD, absolute_path, location->line_start, RESET, accent_color, kind_text, RESET, + message); + + size_t lines = location->line_end - location->line_start + 1; + + for (size_t l = 0; l < lines; l++) { + printf(" %4ld | ", location->line_start + l); + + size_t limit; + size_t chars = 0; + + // print line before token group start + limit = min(location->col_start, SEEK_BUF_BYTES); + while (limit > 1) { + custom_fgets(buffer, (int) limit, file->handle); + chars += printf("%s", buffer); + limit = min(location->col_start - chars, SEEK_BUF_BYTES); + + if (strchr(buffer, '\n') != NULL) { + goto cont; + } + } + + printf("%s", accent_color); + + chars = 0; + limit = min(location->col_end - location->col_start + 1, SEEK_BUF_BYTES); + while (limit > 0) { + custom_fgets(buffer, (int) limit, file->handle); + chars += printf("%s", buffer); + limit = min(location->col_end - location->col_start + 1 - chars, SEEK_BUF_BYTES); + + if (strchr(buffer, '\n') != NULL) { + goto cont; + } + } + + printf("%s", RESET); + + // print rest of the line + do { + custom_fgets(buffer, SEEK_BUF_BYTES, file->handle); + printf("%s", buffer); + } while (strchr(buffer, '\n') == NULL); + + cont: + printf("%s", RESET); + } + + printf(" | "); + for (size_t i = 1; i < location->col_start; i++) { + printf(" "); + } + + printf("%s", accent_color); + printf("^"); + for (size_t i = 0; i < location->col_end - location->col_start; i++) { + printf("~"); + } + + printf("%s\n\n", RESET); +} + +TokenLocation new_location(unsigned long int line_start, unsigned long int col_start, unsigned long int line_end, + unsigned long int col_end) { + TokenLocation location; + + location.line_start = line_start; + location.line_end = line_end; + location.col_start = col_start; + location.col_end = col_end; + + return location; +} + +void print_file_statistics(ModuleFile *file) { + if (file->statistics.info_count + file->statistics.warning_count + file->statistics.error_count < 1) { + return; + } + + printf("File %s generated ", file->path); + + if (file->statistics.info_count > 0) { + printf("%ld notice(s) ", file->statistics.info_count); + } + + if (file->statistics.warning_count > 0) { + printf("%ld warning(s) ", file->statistics.warning_count); + } + + if (file->statistics.error_count > 0) { + printf("%ld error(s) ", file->statistics.error_count); + } + + printf("\n\n"); +} + +void print_unit_statistics(ModuleFileStack *file_stack) { + FileDiagnosticStatistics stats; + stats.info_count = 0; + stats.warning_count = 0; + stats.error_count = 0; + + for (size_t i = 0; i < file_stack->files->len; i++) { + ModuleFile *file = (ModuleFile *) file_stack->files->data; + + stats.info_count += file->statistics.warning_count; + stats.warning_count += file->statistics.warning_count; + stats.error_count += file->statistics.error_count; + } + + printf("%d files generated ", file_stack->files->len); + + if (stats.info_count > 0) { + printf("%ld notice(s) ", stats.info_count); + } + + if (stats.warning_count > 0) { + printf("%ld warning(s) ", stats.warning_count); + } + + if (stats.error_count > 0) { + printf("%ld error(s) ", stats.error_count); + } + + printf("\n\n"); +} diff --git a/src/io/files.h b/src/io/files.h new file mode 100644 index 0000000..5441805 --- /dev/null +++ b/src/io/files.h @@ -0,0 +1,77 @@ +// +// Created by servostar on 5/30/24. +// + +#ifndef GEMSTONE_FILES_H +#define GEMSTONE_FILES_H + +#include +#include + +typedef struct FileDiagnosticStatistics_t { + size_t error_count; + size_t warning_count; + size_t info_count; +} FileDiagnosticStatistics; + +typedef struct ModuleFile_t { + const char *path; + FILE *handle; + FileDiagnosticStatistics statistics; +} ModuleFile; + +typedef struct ModuleFileStack_t { + GArray *files; +} ModuleFileStack; + +typedef enum Message_t { + Info, + Warning, + Error +} Message; + +typedef struct TokenLocation_t { + unsigned long int line_start; + unsigned long int col_start; + unsigned long int line_end; + unsigned long int col_end; +} TokenLocation; + +/** + * @brief Add a new file to the file stack. + * @attention The file handle returned will be invalid + * @param stack + * @param path + * @return A new file module + */ +[[gnu::nonnull(1), gnu::nonnull(2)]] +ModuleFile *push_file(ModuleFileStack *stack, const char *path); + +/** + * @brief Delete all files in the stack and the stack itself + * @param stack + */ +[[gnu::nonnull(1)]] +void delete_files(ModuleFileStack *stack); + +/** + * Create a new token location + * @param line_start + * @param col_start + * @param line_end + * @param col_end + * @return + */ +TokenLocation new_location(unsigned long int line_start, unsigned long int col_start, unsigned long int line_end, + unsigned long int col_end); + +[[gnu::nonnull(1), gnu::nonnull(2)]] +void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, const char *message); + +[[gnu::nonnull(1)]] +void print_file_statistics(ModuleFile *file); + +[[gnu::nonnull(1)]] +void print_unit_statistics(ModuleFileStack *file_stack); + +#endif //GEMSTONE_FILES_H diff --git a/src/lex/util.c b/src/lex/util.c index b5b92ac..81f2855 100644 --- a/src/lex/util.c +++ b/src/lex/util.c @@ -25,6 +25,16 @@ void lex_init(void) { atexit(lex_deinit); } +void lex_reset(void) { + eof = 0; + nRow = 0; + nBuffer = 0; + lBuffer = 0; + nTokenStart = 0; + nTokenLength = 0; + nTokenNextStart = 0; +} + void beginToken(char *t) { nTokenStart = nTokenNextStart; nTokenLength = (int) strlen(t); @@ -41,7 +51,7 @@ int nextChar(char *dst) { if (eof) return 0; - + while (nBuffer >= lBuffer) { frc = getNextLine(); if (frc != 0) { @@ -57,7 +67,7 @@ int nextChar(char *dst) { int getNextLine(void) { char *p; - + nBuffer = 0; nTokenStart = -1; nTokenNextStart = 1; diff --git a/src/lex/util.h b/src/lex/util.h index 12e0837..6939526 100644 --- a/src/lex/util.h +++ b/src/lex/util.h @@ -16,6 +16,8 @@ extern char* buffer; */ void lex_init(void); +void lex_reset(void); + /** * @brief Begin counting a new token. This will fill the global struct yylloc. * @param t the text of the token. Must be null terminated diff --git a/src/main.c b/src/main.c index 27bca44..0b75508 100644 --- a/src/main.c +++ b/src/main.c @@ -4,11 +4,52 @@ #include #include #include +#include +#include -#define LOG_LEVEL LOG_LEVEL_DEBUG +extern void yyrestart(FILE *); -extern FILE *yyin; +[[maybe_unused]] AST_NODE_PTR root; +[[maybe_unused]] +ModuleFile *current_file; + +/** + * @brief Compile the specified file into AST + * @param ast + * @param file + * @return EXIT_SUCCESS in case the parsing was success full anything lese if not + */ +[[nodiscard("AST may be in invalid state")]] +[[gnu::nonnull(1), gnu::nonnull(1)]] +static size_t compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) { + assert(file->path != NULL); + assert(ast != NULL); + + file->handle = fopen(file->path, "r"); + + if (file->handle == NULL) { + INFO("unable to open file: %s", file->path); + return 1; + } + + DEBUG("parsing file: %s", file->path); + // setup global state + root = ast; + current_file = file; + yyin = file->handle; + yyrestart(yyin); + lex_reset(); + + yyparse(); + + // clean up global state + // current_file = NULL; + root = NULL; + yyin = NULL; + + return 0; +} /** * @brief Log a debug message to inform about beginning exit procedures @@ -22,9 +63,9 @@ void notify_exit(void) { DEBUG("Exiting gemstone..."); } */ void close_file(void) { - if (NULL != yyin) { - fclose(yyin); - } + if (NULL != yyin) { + fclose(yyin); + } } /** @@ -32,52 +73,57 @@ void close_file(void) { * */ void setup(void) { - // setup preample + // setup preample - log_init(); - DEBUG("starting gemstone..."); + log_init(); + DEBUG("starting gemstone..."); #if LOG_LEVEL <= LOG_LEVEL_DEBUG - atexit(¬ify_exit); + atexit(¬ify_exit); #endif - // actual setup - AST_init(); + // actual setup + AST_init(); - col_init(); + col_init(); - lex_init(); + lex_init(); - DEBUG("finished starting up gemstone..."); + DEBUG("finished starting up gemstone..."); } int main(int argc, char *argv[]) { - setup(); - atexit(close_file); + setup(); + atexit(close_file); - // Check for file input as argument - if (2 != argc) { - INFO("Usage: %s \n", argv[0]); - PANIC("No File could be found"); - } + ModuleFileStack files; + files.files = NULL; - // filename as first argument - char *filename = argv[1]; + for (int i = 1; i < argc; i++) { + printf("Compiling file: %s\n\n", argv[i]); - FILE *file = fopen(filename, "r"); + TokenLocation location = { + .line_start = 0, + .line_end = 0, + .col_start = 0, + .col_end = 0 + }; + AST_NODE_PTR ast = AST_new_node(location, AST_Module, NULL); + ModuleFile *file = push_file(&files, argv[i]); - if (NULL == file) { - PANIC("File couldn't be opened!"); - } - yyin = file; + if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) { + // TODO: parse AST to semantic values + // TODO: backend codegen + } - root = AST_new_node(AST_Module, NULL); - yyparse(); + AST_delete_node(ast); - FILE *output = fopen("test.txt", "w"); - AST_fprint_graphviz(output, root); - fclose(output); - AST_delete_node(root); - return 0; + print_file_statistics(file); + } + + print_unit_statistics(&files); + + delete_files(&files); + return 0; } diff --git a/src/sys/log.h b/src/sys/log.h index 6b8991d..50fded1 100644 --- a/src/sys/log.h +++ b/src/sys/log.h @@ -10,7 +10,7 @@ #define LOG_LEVEL_INFORMATION 1 #define LOG_LEVEL_DEBUG 0 -#define LOG_LEVEL LOG_LEVEL_DEBUG +#define LOG_LEVEL LOG_LEVEL_ERROR #define LOG_STRING_PANIC "Critical" #define LOG_STRING_FATAL "Fatal" diff --git a/src/yacc/parser.y b/src/yacc/parser.y index 753c7bc..d599c8d 100644 --- a/src/yacc/parser.y +++ b/src/yacc/parser.y @@ -5,8 +5,9 @@ #include #include #include + #include extern int yylineno; - + extern ModuleFile* current_file; int yyerror(const char*); @@ -15,7 +16,8 @@ extern int yylex(); extern AST_NODE_PTR root; - + + #define new_loc() new_location(yylloc.first_line, yylloc.first_column, yylloc.last_line, yylloc.last_column) } %union { @@ -144,11 +146,11 @@ programbody: moduleimport {$$ = $1;} -expr: ValFloat {$$ = AST_new_node(AST_Float, $1);} - | ValInt {$$ = AST_new_node(AST_Int, $1);} - | ValMultistr {$$ = AST_new_node(AST_String, $1);} - | ValStr {$$ = AST_new_node(AST_String, $1);} - | Ident {$$ = AST_new_node(AST_Ident, $1);} +expr: ValFloat {$$ = AST_new_node(new_loc(), AST_Float, $1);} + | ValInt {$$ = AST_new_node(new_loc(), AST_Int, $1);} + | ValMultistr {$$ = AST_new_node(new_loc(), AST_String, $1);} + | ValStr {$$ = AST_new_node(new_loc(), AST_String, $1);} + | Ident {$$ = AST_new_node(new_loc(), AST_Ident, $1);} | operation {$$ = $1;} | boxaccess {$$ = $1;} | boxselfaccess{$$ = $1;} @@ -157,22 +159,22 @@ expr: ValFloat {$$ = AST_new_node(AST_Float, $1);} exprlist: expr ',' exprlist {AST_push_node($3, $1); $$ = $3;} - | expr {AST_NODE_PTR list = AST_new_node(AST_ExprList, NULL); + | expr {AST_NODE_PTR list = AST_new_node(new_loc(), AST_ExprList, NULL); AST_push_node(list, $1); $$ = list;}; argumentlist: argumentlist '(' exprlist ')' {AST_push_node($1, $3); $$ = $1;} - | '(' exprlist ')'{AST_NODE_PTR list = AST_new_node(AST_ArgList, NULL); + | '(' exprlist ')'{AST_NODE_PTR list = AST_new_node(new_loc(), AST_ArgList, NULL); AST_push_node(list, $2); $$ = list;} | argumentlist '(' ')' - | '(' ')'{AST_NODE_PTR list = AST_new_node(AST_ArgList, NULL); + | '(' ')'{AST_NODE_PTR list = AST_new_node(new_loc(), AST_ArgList, NULL); $$ = list;}; -fundef: KeyFun Ident paramlist '{' statementlist'}' {AST_NODE_PTR fun = AST_new_node(AST_Fun, NULL); - AST_NODE_PTR ident = AST_new_node(AST_Ident, $2); +fundef: KeyFun Ident paramlist '{' statementlist'}' {AST_NODE_PTR fun = AST_new_node(new_loc(), AST_Fun, NULL); + AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $2); AST_push_node(fun, ident); AST_push_node(fun, $3); AST_push_node(fun, $5); @@ -182,66 +184,66 @@ fundef: KeyFun Ident paramlist '{' statementlist'}' {AST_NODE_PTR fun = AST_new_ paramlist: paramlist '(' params ')' {AST_push_node($1, $3); $$ = $1;} | paramlist '(' ')'{$$ = $1;} - | '(' params ')' {AST_NODE_PTR list = AST_new_node(AST_List, NULL); + | '(' params ')' {AST_NODE_PTR list = AST_new_node(new_loc(), AST_List, NULL); AST_push_node(list, $2); $$ = list;} - | '(' ')' {$$ = AST_new_node(AST_List, NULL);}; + | '(' ')' {$$ = AST_new_node(new_loc(), AST_List, NULL);}; -params: IOqualifyier paramdecl ',' params {AST_NODE_PTR parameter = AST_new_node(AST_Parameter, NULL); +params: IOqualifyier paramdecl ',' params {AST_NODE_PTR parameter = AST_new_node(new_loc(), AST_Parameter, NULL); AST_push_node(parameter, $1); AST_push_node(parameter, $2); AST_push_node($4, parameter); $$ = $4;} - | IOqualifyier paramdecl {AST_NODE_PTR list = AST_new_node(AST_ParamList, NULL); - AST_NODE_PTR parameter = AST_new_node(AST_Parameter, NULL); + | IOqualifyier paramdecl {AST_NODE_PTR list = AST_new_node(new_loc(), AST_ParamList, NULL); + AST_NODE_PTR parameter = AST_new_node(new_loc(), AST_Parameter, NULL); AST_push_node(parameter, $1); AST_push_node(parameter, $2); AST_push_node(list, parameter); $$ = list;}; -IOqualifyier: KeyIn { AST_NODE_PTR in = AST_new_node(AST_Qualifyier, "in"); - AST_NODE_PTR list = AST_new_node(AST_List, NULL); +IOqualifyier: KeyIn { AST_NODE_PTR in = AST_new_node(new_loc(), AST_Qualifyier, "in"); + AST_NODE_PTR list = AST_new_node(new_loc(), AST_List, NULL); AST_push_node(list, in); $$ = list;} - | KeyOut{ AST_NODE_PTR out = AST_new_node(AST_Qualifyier, "out"); - AST_NODE_PTR list = AST_new_node(AST_List, NULL); + | KeyOut{ AST_NODE_PTR out = AST_new_node(new_loc(), AST_Qualifyier, "out"); + AST_NODE_PTR list = AST_new_node(new_loc(), AST_List, NULL); AST_push_node(list, out); $$ = list;} - | KeyIn KeyOut{ AST_NODE_PTR in = AST_new_node(AST_Qualifyier, "in"); - AST_NODE_PTR out = AST_new_node(AST_Qualifyier, "out"); - AST_NODE_PTR list = AST_new_node(AST_List, NULL); + | KeyIn KeyOut{ AST_NODE_PTR in = AST_new_node(new_loc(), AST_Qualifyier, "in"); + AST_NODE_PTR out = AST_new_node(new_loc(), AST_Qualifyier, "out"); + AST_NODE_PTR list = AST_new_node(new_loc(), AST_List, NULL); AST_push_node(list, in); AST_push_node(list, out); $$ = list;} - | KeyOut KeyIn{ AST_NODE_PTR in = AST_new_node(AST_Qualifyier, "in"); - AST_NODE_PTR out = AST_new_node(AST_Qualifyier, "out"); - AST_NODE_PTR list = AST_new_node(AST_List, NULL); + | KeyOut KeyIn{ AST_NODE_PTR in = AST_new_node(new_loc(), AST_Qualifyier, "in"); + AST_NODE_PTR out = AST_new_node(new_loc(), AST_Qualifyier, "out"); + AST_NODE_PTR list = AST_new_node(new_loc(), AST_List, NULL); AST_push_node(list, in); AST_push_node(list, out); $$ = list;} - | {$$ = AST_new_node(AST_List, NULL);}; + | {$$ = AST_new_node(new_loc(), AST_List, NULL);}; -paramdecl: type ':' Ident { AST_NODE_PTR paramdecl = AST_new_node(AST_ParamDecl, NULL); +paramdecl: type ':' Ident { AST_NODE_PTR paramdecl = AST_new_node(new_loc(), AST_ParamDecl, NULL); AST_push_node(paramdecl, $1); - AST_NODE_PTR ident = AST_new_node(AST_Ident, $3); + AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $3); AST_push_node(paramdecl, ident); $$ = paramdecl; DEBUG("Param-Declaration"); }; -box: KeyType KeyBox ':' Ident '{' boxbody '}' {AST_NODE_PTR box = AST_new_node(AST_Box, NULL); - AST_NODE_PTR ident = AST_new_node(AST_Ident, $4); +box: KeyType KeyBox ':' Ident '{' boxbody '}' {AST_NODE_PTR box = AST_new_node(new_loc(), AST_Box, NULL); + AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $4); AST_push_node(box, ident); AST_push_node(box, $6); $$ = box; DEBUG("Box"); } - | KeyType KeyBox ':' Ident '{' '}' {AST_NODE_PTR box = AST_new_node(AST_Box, NULL); - AST_NODE_PTR ident = AST_new_node(AST_Ident, $4); + | KeyType KeyBox ':' Ident '{' '}' {AST_NODE_PTR box = AST_new_node(new_loc(), AST_Box, NULL); + AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $4); AST_push_node(box, ident); $$ = box;}; boxbody: boxbody boxcontent {AST_push_node($1, $2); $$ = $1;} - | boxcontent {AST_NODE_PTR list = AST_new_node(AST_List, NULL); + | boxcontent {AST_NODE_PTR list = AST_new_node(new_loc(), AST_List, NULL); AST_push_node(list, $1); $$ = list;}; @@ -249,66 +251,66 @@ boxcontent: decl { $$ = $1;DEBUG("Box decl Content"); } | definition { $$ = $1;DEBUG("Box def Content"); } | fundef { $$ = $1;DEBUG("Box fun Content"); }; -boxselfaccess: KeySelf '.' Ident {AST_NODE_PTR boxselfaccess = AST_new_node(AST_List, NULL); - AST_NODE_PTR self = AST_new_node(AST_Ident, "self"); +boxselfaccess: KeySelf '.' Ident {AST_NODE_PTR boxselfaccess = AST_new_node(new_loc(), AST_List, NULL); + AST_NODE_PTR self = AST_new_node(new_loc(), AST_Ident, "self"); AST_push_node(boxselfaccess, self); - AST_NODE_PTR identlist = AST_new_node(AST_IdentList, NULL); - AST_NODE_PTR ident = AST_new_node(AST_Ident, $3); + AST_NODE_PTR identlist = AST_new_node(new_loc(), AST_IdentList, NULL); + AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $3); AST_push_node(identlist,ident); AST_push_node(boxselfaccess, identlist); $$ = boxselfaccess;} - | KeySelf '.' boxaccess {AST_NODE_PTR boxselfaccess = AST_new_node(AST_List, NULL); - AST_NODE_PTR self = AST_new_node(AST_Ident, "self"); + | KeySelf '.' boxaccess {AST_NODE_PTR boxselfaccess = AST_new_node(new_loc(), AST_List, NULL); + AST_NODE_PTR self = AST_new_node(new_loc(), AST_Ident, "self"); AST_push_node(boxselfaccess, self); AST_push_node(boxselfaccess, $3); $$ = boxselfaccess;}; -boxaccess: Ident '.' Ident {AST_NODE_PTR identlist = AST_new_node(AST_IdentList, NULL); - AST_NODE_PTR ident1 = AST_new_node(AST_Ident, $1); - AST_NODE_PTR ident2 = AST_new_node(AST_Ident, $3); +boxaccess: Ident '.' Ident {AST_NODE_PTR identlist = AST_new_node(new_loc(), AST_IdentList, NULL); + AST_NODE_PTR ident1 = AST_new_node(new_loc(), AST_Ident, $1); + AST_NODE_PTR ident2 = AST_new_node(new_loc(), AST_Ident, $3); AST_push_node(identlist,ident1); AST_push_node(identlist,ident2); $$ = identlist;} - | Ident '.' boxaccess {AST_NODE_PTR ident = AST_new_node(AST_Ident, $1); + | Ident '.' boxaccess {AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $1); AST_push_node($3,ident); $$ = $3;}; -boxcall: boxaccess argumentlist {AST_NODE_PTR boxcall = AST_new_node(AST_Call, NULL); +boxcall: boxaccess argumentlist {AST_NODE_PTR boxcall = AST_new_node(new_loc(), AST_Call, NULL); AST_push_node(boxcall, $1); AST_push_node(boxcall, $2); $$ = boxcall;} - | boxselfaccess argumentlist {AST_NODE_PTR boxcall = AST_new_node(AST_Call, NULL); + | boxselfaccess argumentlist {AST_NODE_PTR boxcall = AST_new_node(new_loc(), AST_Call, NULL); AST_push_node(boxcall, $1); AST_push_node(boxcall, $2); $$ = boxcall;}; -typecast: expr KeyAs type %prec KeyAs {AST_NODE_PTR cast = AST_new_node(AST_Typecast, NULL); +typecast: expr KeyAs type %prec KeyAs {AST_NODE_PTR cast = AST_new_node(new_loc(), AST_Typecast, NULL); AST_push_node(cast, $1); AST_push_node(cast, $3); $$ = cast; DEBUG("Type-Cast"); }; -reinterpretcast: '(' type ')' expr { AST_NODE_PTR cast = AST_new_node(AST_Transmute, NULL); +reinterpretcast: '(' type ')' expr { AST_NODE_PTR cast = AST_new_node(new_loc(), AST_Transmute, NULL); AST_push_node(cast, $4); AST_push_node(cast, $2); $$ = cast; DEBUG("Reinterpret-Cast"); }; -funcall: Ident argumentlist {AST_NODE_PTR funcall = AST_new_node(AST_Call, NULL); - AST_NODE_PTR ident = AST_new_node(AST_Ident, $1); +funcall: Ident argumentlist {AST_NODE_PTR funcall = AST_new_node(new_loc(), AST_Call, NULL); + AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $1); AST_push_node(funcall, ident); AST_push_node(funcall, $2); $$ = funcall; DEBUG("Function call"); }; -moduleimport: KeyImport ValStr {$$ = AST_new_node(AST_Import, $2); +moduleimport: KeyImport ValStr {$$ = AST_new_node(new_loc(), AST_Import, $2); DEBUG("Module-Import"); }; statementlist: statementlist statement {AST_push_node($1, $2); $$ = $1;} - | statement {AST_NODE_PTR list = AST_new_node(AST_StmtList, NULL); + | statement {AST_NODE_PTR list = AST_new_node(new_loc(), AST_StmtList, NULL); AST_push_node(list, $1); $$ = list;}; @@ -320,16 +322,16 @@ statement: assign {$$ = $1;} | funcall {$$ = $1;} | boxcall{$$ = $1;}; -branchif: KeyIf expr '{' statementlist '}' { AST_NODE_PTR branch = AST_new_node(AST_If, NULL); +branchif: KeyIf expr '{' statementlist '}' { AST_NODE_PTR branch = AST_new_node(new_loc(), AST_If, NULL); AST_push_node(branch, $2); AST_push_node(branch, $4); $$ = branch; }; -branchelse: KeyElse '{' statementlist '}' { AST_NODE_PTR branch = AST_new_node(AST_Else, NULL); +branchelse: KeyElse '{' statementlist '}' { AST_NODE_PTR branch = AST_new_node(new_loc(), AST_Else, NULL); AST_push_node(branch, $3); $$ = branch; }; -branchelseif: KeyElse KeyIf expr '{' statementlist '}' { AST_NODE_PTR branch = AST_new_node(AST_IfElse, NULL); +branchelseif: KeyElse KeyIf expr '{' statementlist '}' { AST_NODE_PTR branch = AST_new_node(new_loc(), AST_IfElse, NULL); AST_push_node(branch, $3); AST_push_node(branch, $5); $$ = branch; }; @@ -337,49 +339,49 @@ branchelseif: KeyElse KeyIf expr '{' statementlist '}' { AST_NODE_PTR branch = A branchfull: branchhalf { $$ = $1;}; |branchhalf branchelse { AST_push_node($1 , $2); $$ = $1; } -branchhalf: branchif { AST_NODE_PTR branch = AST_new_node(AST_Stmt, NULL); +branchhalf: branchif { AST_NODE_PTR branch = AST_new_node(new_loc(), AST_Stmt, NULL); AST_push_node(branch, $1); $$ = branch; } | branchhalf branchelseif { AST_push_node($1 , $2); $$ = $1; }; -while: KeyWhile expr '{' statementlist '}' {AST_NODE_PTR whilenode = AST_new_node(AST_While, NULL); +while: KeyWhile expr '{' statementlist '}' {AST_NODE_PTR whilenode = AST_new_node(new_loc(), AST_While, NULL); AST_push_node(whilenode, $2); AST_push_node(whilenode, $4); $$ = whilenode;}; -identlist: Ident ',' identlist {AST_NODE_PTR ident = AST_new_node(AST_Ident, $1); +identlist: Ident ',' identlist {AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $1); AST_push_node($3, ident); $$ = $3;} - | Ident {AST_NODE_PTR list = AST_new_node(AST_IdentList, NULL); - AST_NODE_PTR ident = AST_new_node(AST_Ident, $1); + | Ident {AST_NODE_PTR list = AST_new_node(new_loc(), AST_IdentList, NULL); + AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $1); AST_push_node(list, ident); $$ = list;}; -decl: type ':' identlist {AST_NODE_PTR decl = AST_new_node(AST_Decl, NULL); +decl: type ':' identlist {AST_NODE_PTR decl = AST_new_node(new_loc(), AST_Decl, NULL); AST_push_node(decl, $1); AST_push_node(decl, $3); $$ = decl;} - | storagequalifier type ':' identlist {AST_NODE_PTR decl = AST_new_node(AST_Decl, NULL); + | storagequalifier type ':' identlist {AST_NODE_PTR decl = AST_new_node(new_loc(), AST_Decl, NULL); AST_push_node(decl, $1); AST_push_node(decl, $2); AST_push_node(decl, $4); $$ = decl;} -definition: decl '=' expr { AST_NODE_PTR def = AST_new_node(AST_Def, NULL); +definition: decl '=' expr { AST_NODE_PTR def = AST_new_node(new_loc(), AST_Def, NULL); AST_push_node(def, $1); AST_push_node(def, $3); $$ = def; DEBUG("Definition"); }; -storagequalifier: KeyGlobal {$$ = AST_new_node(AST_Storage, "global");} - | KeyStatic {$$ = AST_new_node(AST_Storage, "static");} - | KeyLocal {$$ = AST_new_node(AST_Storage, "local");}; +storagequalifier: KeyGlobal {$$ = AST_new_node(new_loc(), AST_Storage, "global");} + | KeyStatic {$$ = AST_new_node(new_loc(), AST_Storage, "static");} + | KeyLocal {$$ = AST_new_node(new_loc(), AST_Storage, "local");}; -assign: Ident '=' expr { AST_NODE_PTR assign = AST_new_node(AST_Assign, NULL); - AST_NODE_PTR ident = AST_new_node(AST_Ident, $1); +assign: Ident '=' expr { AST_NODE_PTR assign = AST_new_node(new_loc(), AST_Assign, NULL); + AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $1); AST_push_node(assign, ident); AST_push_node(assign, $3); $$ = assign; @@ -388,60 +390,60 @@ assign: Ident '=' expr { AST_NODE_PTR assign = AST_new_node(AST_Assign, NULL); | boxaccess '=' expr | boxselfaccess '=' expr ; -sign: KeySigned {$$ = AST_new_node(AST_Sign, "signed");} - | KeyUnsigned{$$ = AST_new_node(AST_Sign, "unsigned");}; +sign: KeySigned {$$ = AST_new_node(new_loc(), AST_Sign, "signed");} + | KeyUnsigned{$$ = AST_new_node(new_loc(), AST_Sign, "unsigned");}; -typedef: KeyType type':' Ident {AST_NODE_PTR typeDef = AST_new_node(AST_Typedef, NULL); +typedef: KeyType type':' Ident {AST_NODE_PTR typeDef = AST_new_node(new_loc(), AST_Typedef, NULL); AST_push_node(typeDef, $2); - AST_NODE_PTR ident = AST_new_node(AST_Ident, $4); + AST_NODE_PTR ident = AST_new_node(new_loc(), AST_Ident, $4); AST_push_node(typeDef, ident); $$ = typeDef;}; -scale: scale KeyShort {AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "short"); +scale: scale KeyShort {AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "short"); AST_push_node($1, shortnode); $$ = $1;} - | scale KeyHalf {AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "half"); + | scale KeyHalf {AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "half"); AST_push_node($1, shortnode); $$ = $1;} - | scale KeyLong {AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "long"); + | scale KeyLong {AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "long"); AST_push_node($1, shortnode); $$ = $1;} - | scale KeyDouble {AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "double"); + | scale KeyDouble {AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "double"); AST_push_node($1, shortnode); $$ = $1;} - | KeyShort {AST_NODE_PTR scale = AST_new_node(AST_List, NULL); - AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "short"); + | KeyShort {AST_NODE_PTR scale = AST_new_node(new_loc(), AST_List, NULL); + AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "short"); AST_push_node(scale, shortnode); $$ = scale;} - | KeyHalf {AST_NODE_PTR scale = AST_new_node(AST_List, NULL); - AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "half"); + | KeyHalf {AST_NODE_PTR scale = AST_new_node(new_loc(), AST_List, NULL); + AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "half"); AST_push_node(scale, shortnode); $$ = scale;} - | KeyLong {AST_NODE_PTR scale = AST_new_node(AST_List, NULL); - AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "long"); + | KeyLong {AST_NODE_PTR scale = AST_new_node(new_loc(), AST_List, NULL); + AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "long"); AST_push_node(scale, shortnode); $$ = scale;} - | KeyDouble {AST_NODE_PTR scale = AST_new_node(AST_List, NULL); - AST_NODE_PTR shortnode = AST_new_node(AST_Scale, "double"); + | KeyDouble {AST_NODE_PTR scale = AST_new_node(new_loc(), AST_List, NULL); + AST_NODE_PTR shortnode = AST_new_node(new_loc(), AST_Scale, "double"); AST_push_node(scale, shortnode); $$ = scale;}; -typekind: Ident {$$ = AST_new_node(AST_Typekind, $1);} - | KeyInt {$$ = AST_new_node(AST_Typekind, "int");} - | KeyFloat {$$ = AST_new_node(AST_Typekind, "float");}; +typekind: Ident {$$ = AST_new_node(new_loc(), AST_Typekind, $1);} + | KeyInt {$$ = AST_new_node(new_loc(), AST_Typekind, "int");} + | KeyFloat {$$ = AST_new_node(new_loc(), AST_Typekind, "float");}; -type: typekind {AST_NODE_PTR type = AST_new_node(AST_Type, NULL); +type: typekind {AST_NODE_PTR type = AST_new_node(new_loc(), AST_Type, NULL); AST_push_node(type, $1); $$ = type;} - | scale typekind {AST_NODE_PTR type = AST_new_node(AST_Type, NULL); + | scale typekind {AST_NODE_PTR type = AST_new_node(new_loc(), AST_Type, NULL); AST_push_node(type, $1); AST_push_node(type, $2); $$ = type;} - | sign typekind {AST_NODE_PTR type = AST_new_node(AST_Type, NULL); + | sign typekind {AST_NODE_PTR type = AST_new_node(new_loc(), AST_Type, NULL); AST_push_node(type, $1); AST_push_node(type, $2); $$ = type;} - | sign scale typekind {AST_NODE_PTR type = AST_new_node(AST_Type, NULL); + | sign scale typekind {AST_NODE_PTR type = AST_new_node(new_loc(), AST_Type, NULL); AST_push_node(type, $1); AST_push_node(type, $2); AST_push_node(type, $3); @@ -452,136 +454,74 @@ operation: oparith {$$ = $1;} | opbool {$$ = $1;} | opbit {$$ = $1;}; -oparith: expr '+' expr {AST_NODE_PTR add = AST_new_node(AST_Add, NULL); +oparith: expr '+' expr {AST_NODE_PTR add = AST_new_node(new_loc(), AST_Add, NULL); AST_push_node(add, $1); AST_push_node(add, $3); $$ = add;} - | expr '-' expr {AST_NODE_PTR subtract = AST_new_node(AST_Sub, NULL); + | expr '-' expr {AST_NODE_PTR subtract = AST_new_node(new_loc(), AST_Sub, NULL); AST_push_node(subtract, $1); AST_push_node(subtract, $3); $$ = subtract;} - | expr '*' expr {AST_NODE_PTR mul = AST_new_node(AST_Mul, NULL); + | expr '*' expr {AST_NODE_PTR mul = AST_new_node(new_loc(), AST_Mul, NULL); AST_push_node(mul, $1); AST_push_node(mul, $3); $$ = mul;} - | expr '/' expr {AST_NODE_PTR div = AST_new_node(AST_Div, NULL); + | expr '/' expr {AST_NODE_PTR div = AST_new_node(new_loc(), AST_Div, NULL); AST_push_node(div, $1); AST_push_node(div, $3); $$ = div;} - | '-' expr %prec '*'{AST_NODE_PTR negator = AST_new_node(AST_Negate, NULL); + | '-' expr %prec '*'{AST_NODE_PTR negator = AST_new_node(new_loc(), AST_Negate, NULL); AST_push_node(negator, $2); $$ = negator;}; -oplogic: expr OpEquals expr {AST_NODE_PTR equals = AST_new_node(AST_Eq, NULL); +oplogic: expr OpEquals expr {AST_NODE_PTR equals = AST_new_node(new_loc(), AST_Eq, NULL); AST_push_node(equals, $1); AST_push_node(equals, $3); $$ = equals;} - | expr '<' expr {AST_NODE_PTR less = AST_new_node(AST_Less, NULL); + | expr '<' expr {AST_NODE_PTR less = AST_new_node(new_loc(), AST_Less, NULL); AST_push_node(less, $1); AST_push_node(less, $3); $$ = less;} - | expr '>' expr{AST_NODE_PTR greater = AST_new_node(AST_Greater, NULL); + | expr '>' expr{AST_NODE_PTR greater = AST_new_node(new_loc(), AST_Greater, NULL); AST_push_node(greater, $1); AST_push_node(greater, $3); $$ = greater;}; -opbool: expr OpAnd expr {AST_NODE_PTR and = AST_new_node(AST_BoolAnd, NULL); +opbool: expr OpAnd expr {AST_NODE_PTR and = AST_new_node(new_loc(), AST_BoolAnd, NULL); AST_push_node(and, $1); AST_push_node(and, $3); $$ = and;} - | expr OpOr expr{AST_NODE_PTR or = AST_new_node(AST_BoolOr, NULL); + | expr OpOr expr{AST_NODE_PTR or = AST_new_node(new_loc(), AST_BoolOr, NULL); AST_push_node(or, $1); AST_push_node(or, $3); $$ = or;} - | expr OpXor expr{AST_NODE_PTR xor = AST_new_node(AST_BoolXor, NULL); + | expr OpXor expr{AST_NODE_PTR xor = AST_new_node(new_loc(), AST_BoolXor, NULL); AST_push_node(xor, $1); AST_push_node(xor, $3); $$ = xor;} - | OpNot expr %prec OpAnd{AST_NODE_PTR not = AST_new_node(AST_BoolNot, NULL); + | OpNot expr %prec OpAnd{AST_NODE_PTR not = AST_new_node(new_loc(), AST_BoolNot, NULL); AST_push_node(not, $2); $$ = not;}; -opbit: expr OpBitand expr {AST_NODE_PTR and = AST_new_node(AST_BitAnd, NULL); +opbit: expr OpBitand expr {AST_NODE_PTR and = AST_new_node(new_loc(), AST_BitAnd, NULL); AST_push_node(and, $1); AST_push_node(and, $3); $$ = and;} - | expr OpBitor expr{AST_NODE_PTR or = AST_new_node(AST_BitOr, NULL); + | expr OpBitor expr{AST_NODE_PTR or = AST_new_node(new_loc(), AST_BitOr, NULL); AST_push_node(or, $1); AST_push_node(or, $3); $$ = or;} - | expr OpBitxor expr{AST_NODE_PTR xor = AST_new_node(AST_BitXor, NULL); + | expr OpBitxor expr{AST_NODE_PTR xor = AST_new_node(new_loc(), AST_BitXor, NULL); AST_push_node(xor, $1); AST_push_node(xor, $3); $$ = xor;} - | OpBitnot expr %prec OpBitand{AST_NODE_PTR not = AST_new_node(AST_BitNot, NULL); + | OpBitnot expr %prec OpBitand{AST_NODE_PTR not = AST_new_node(new_loc(), AST_BitNot, NULL); AST_push_node(not, $2); $$ = not;}; %% - -const char* ERROR = "error"; -const char* WARNING = "warning"; -const char* NOTE = "note"; - -int print_message(const char* kind, const char* message) { - // number of characters written - int char_count = 0; - // highlight to use - char* HIGHLIGHT = CYAN; - - // convert message kind into color - if (kind == ERROR) { - HIGHLIGHT = RED; - } else if (kind == WARNING) { - HIGHLIGHT = YELLOW; - } - - // print message - char_count += printf("%sfilename:%d:%d%s:%s%s %s: %s%s\n", BOLD, yylloc.first_line, yylloc.first_column, RESET, HIGHLIGHT, BOLD, kind, RESET, message); - - // print line in which error occurred - - char_count += printf(" %4d | ", yylloc.first_line); - - for (int i = 0; i < yylloc.first_column - 1; i++) { - if (buffer[i] == '\n') { - break; - } - printf("%c", buffer[i]); - } - - char_count += printf("%s%s", BOLD, HIGHLIGHT); - - for (int i = yylloc.first_column - 1; i < yylloc.last_column; i++) { - if (buffer[i] == '\n') { - break; - } - char_count += printf("%c", buffer[i]); - } - - char_count += printf("%s", RESET); - - for (int i = yylloc.last_column; buffer[i] != '\0' && buffer[i] != '\n'; i++) { - printf("%c", buffer[i]); - } - - char_count += printf("\n | "); - - for (int i = 0; i < yylloc.first_column - 1; i++) { - char_count += printf(" "); - } - - char_count += printf("%s^", HIGHLIGHT); - - for (int i = 0; i < yylloc.last_column - yylloc.first_column; i++) { - printf("~"); - } - - char_count += printf("%s\n\n", RESET); - - return char_count; -} - int yyerror(const char *s) { - return print_message(ERROR, s); + TokenLocation location = new_loc(); + print_diagnostic(current_file, &location, Error, s); + return 0; } \ No newline at end of file From cb8c7647bf859513ec815221b054fbd77cd1aba3 Mon Sep 17 00:00:00 2001 From: servostar Date: Thu, 30 May 2024 21:43:14 +0200 Subject: [PATCH 05/27] fixed: failing tests due to changes --- CMakeLists.txt | 2 +- src/io/files.c | 11 +++++++++++ src/io/files.h | 2 ++ src/sys/log.h | 2 +- tests/ast/CMakeLists.txt | 17 +++++++++++++++++ tests/ast/build_tree.c | 16 ++++++++-------- tests/ast/gen_graph.c | 12 ++++++------ tests/ast/print_graphviz.c | 16 ++++++++-------- tests/ast/print_node.c | 2 +- tests/logging/CMakeLists.txt | 11 +++++++++++ tests/logging/level.c | 2 ++ tests/logging/output.c | 2 ++ 12 files changed, 70 insertions(+), 25 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b304447..cab901a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,7 +76,7 @@ add_custom_command(OUTPUT ${YACC_GENERATED_SOURCE_FILE} find_package(PkgConfig REQUIRED) pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0) - + # ------------------------------------------------ # # Source # # ------------------------------------------------ # diff --git a/src/io/files.c b/src/io/files.c index 97daed1..2d4fda7 100644 --- a/src/io/files.c +++ b/src/io/files.c @@ -170,6 +170,17 @@ TokenLocation new_location(unsigned long int line_start, unsigned long int col_s return location; } +TokenLocation empty_location(void) { + TokenLocation location; + + location.line_start = 0; + location.line_end = 0; + location.col_start = 0; + location.col_end = 0; + + return location; +} + void print_file_statistics(ModuleFile *file) { if (file->statistics.info_count + file->statistics.warning_count + file->statistics.error_count < 1) { return; diff --git a/src/io/files.h b/src/io/files.h index 5441805..076b8d0 100644 --- a/src/io/files.h +++ b/src/io/files.h @@ -65,6 +65,8 @@ void delete_files(ModuleFileStack *stack); TokenLocation new_location(unsigned long int line_start, unsigned long int col_start, unsigned long int line_end, unsigned long int col_end); +TokenLocation empty_location(void); + [[gnu::nonnull(1), gnu::nonnull(2)]] void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, const char *message); diff --git a/src/sys/log.h b/src/sys/log.h index 50fded1..6b8991d 100644 --- a/src/sys/log.h +++ b/src/sys/log.h @@ -10,7 +10,7 @@ #define LOG_LEVEL_INFORMATION 1 #define LOG_LEVEL_DEBUG 0 -#define LOG_LEVEL LOG_LEVEL_ERROR +#define LOG_LEVEL LOG_LEVEL_DEBUG #define LOG_STRING_PANIC "Critical" #define LOG_STRING_FATAL "Fatal" diff --git a/tests/ast/CMakeLists.txt b/tests/ast/CMakeLists.txt index 455130b..88007e4 100644 --- a/tests/ast/CMakeLists.txt +++ b/tests/ast/CMakeLists.txt @@ -2,6 +2,14 @@ include(CTest) include_directories(${PROJECT_SOURCE_DIR}/src) +# ------------------------------------------------ # +# Setup Glib 2.0 # +# ------------------------------------------------ # + +find_package(PkgConfig REQUIRED) +pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +include_directories(PRIVATE ${GLIB_INCLUDE_DIRS}) + # ------------------------------------------------------- # # CTEST 1 # test building the syntax tree @@ -9,11 +17,14 @@ include_directories(${PROJECT_SOURCE_DIR}/src) add_executable(ast_build_tree ${PROJECT_SOURCE_DIR}/src/ast/ast.c ${PROJECT_SOURCE_DIR}/src/sys/log.c + ${PROJECT_SOURCE_DIR}/src/io/files.c + ${PROJECT_SOURCE_DIR}/src/sys/col.c build_tree.c) set_target_properties(ast_build_tree PROPERTIES OUTPUT_NAME "build_tree" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast) +target_link_libraries(ast_build_tree PkgConfig::GLIB) add_test(NAME ast_build_tree WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_build_tree) @@ -25,11 +36,14 @@ add_test(NAME ast_build_tree add_executable(ast_print_node ${PROJECT_SOURCE_DIR}/src/ast/ast.c ${PROJECT_SOURCE_DIR}/src/sys/log.c + ${PROJECT_SOURCE_DIR}/src/io/files.c + ${PROJECT_SOURCE_DIR}/src/sys/col.c print_node.c) set_target_properties(ast_print_node PROPERTIES OUTPUT_NAME "print_node" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast) +target_link_libraries(ast_print_node PkgConfig::GLIB) add_test(NAME ast_print_node WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_print_node) @@ -41,11 +55,14 @@ add_test(NAME ast_print_node add_executable(ast_graphviz ${PROJECT_SOURCE_DIR}/src/ast/ast.c ${PROJECT_SOURCE_DIR}/src/sys/log.c + ${PROJECT_SOURCE_DIR}/src/io/files.c + ${PROJECT_SOURCE_DIR}/src/sys/col.c print_graphviz.c) set_target_properties(ast_graphviz PROPERTIES OUTPUT_NAME "print_graphviz" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast) +target_link_libraries(ast_graphviz PkgConfig::GLIB) add_test(NAME ast_graphviz WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_print_graphviz) diff --git a/tests/ast/build_tree.c b/tests/ast/build_tree.c index dec7b85..2d2459b 100644 --- a/tests/ast/build_tree.c +++ b/tests/ast/build_tree.c @@ -6,22 +6,22 @@ #include void generate_statement(const AST_NODE_PTR stmt) { - const AST_NODE_PTR add = AST_new_node(AST_Add, NULL); + const AST_NODE_PTR add = AST_new_node(empty_location(), AST_Add, NULL); - AST_push_node(add, AST_new_node(AST_Int, "3")); - AST_push_node(add, AST_new_node(AST_Int, "6")); + AST_push_node(add, AST_new_node(empty_location(), AST_Int, "3")); + AST_push_node(add, AST_new_node(empty_location(), AST_Int, "6")); AST_push_node(stmt, add); } void generate_branch(const AST_NODE_PTR stmt) { - const AST_NODE_PTR branch = AST_new_node(AST_If, NULL); - const AST_NODE_PTR gt = AST_new_node(AST_Greater, NULL); + const AST_NODE_PTR branch = AST_new_node(empty_location(), AST_If, NULL); + const AST_NODE_PTR gt = AST_new_node(empty_location(), AST_Greater, NULL); AST_push_node(branch, gt); - AST_push_node(gt, AST_new_node(AST_Float, "2.3")); - AST_push_node(gt, AST_new_node(AST_Float, "0.79")); + AST_push_node(gt, AST_new_node(empty_location(), AST_Float, "2.3")); + AST_push_node(gt, AST_new_node(empty_location(), AST_Float, "0.79")); AST_push_node(stmt, branch); @@ -30,7 +30,7 @@ void generate_branch(const AST_NODE_PTR stmt) { int main(void) { - const AST_NODE_PTR root = AST_new_node(AST_Stmt, NULL); + const AST_NODE_PTR root = AST_new_node(empty_location(), AST_Stmt, NULL); generate_branch(root); diff --git a/tests/ast/gen_graph.c b/tests/ast/gen_graph.c index 186d571..c4e2f92 100644 --- a/tests/ast/gen_graph.c +++ b/tests/ast/gen_graph.c @@ -7,15 +7,15 @@ int main(void) { - struct AST_Node_t* node = AST_new_node(AST_If, NULL); + struct AST_Node_t* node = AST_new_node(empty_location(), AST_If, NULL); - struct AST_Node_t* child = AST_new_node(AST_Add, NULL); - AST_push_node(child, AST_new_node(AST_Int, "43")); - AST_push_node(child, AST_new_node(AST_Int, "9")); + struct AST_Node_t* child = AST_new_node(empty_location(), AST_Add, NULL); + AST_push_node(child, AST_new_node(empty_location(), AST_Int, "43")); + AST_push_node(child, AST_new_node(empty_location(), AST_Int, "9")); AST_push_node(node, child); - AST_push_node(node, AST_new_node(AST_Expr, NULL)); - AST_push_node(node, AST_new_node(AST_Expr, NULL)); + AST_push_node(node, AST_new_node(empty_location(), AST_Expr, NULL)); + AST_push_node(node, AST_new_node(empty_location(), AST_Expr, NULL)); FILE* out = fopen("ast.gv", "w+"); // convert this file ^^^^^^ diff --git a/tests/ast/print_graphviz.c b/tests/ast/print_graphviz.c index 6df64b4..c0f69af 100644 --- a/tests/ast/print_graphviz.c +++ b/tests/ast/print_graphviz.c @@ -6,22 +6,22 @@ #include void generate_statement(const AST_NODE_PTR stmt) { - const AST_NODE_PTR add = AST_new_node(AST_Add, NULL); + const AST_NODE_PTR add = AST_new_node(empty_location(), AST_Add, NULL); - AST_push_node(add, AST_new_node(AST_Int, "3")); - AST_push_node(add, AST_new_node(AST_Int, "6")); + AST_push_node(add, AST_new_node(empty_location(), AST_Int, "3")); + AST_push_node(add, AST_new_node(empty_location(), AST_Int, "6")); AST_push_node(stmt, add); } void generate_branch(const AST_NODE_PTR stmt) { - const AST_NODE_PTR branch = AST_new_node(AST_If, NULL); - const AST_NODE_PTR gt = AST_new_node(AST_Greater, NULL); + const AST_NODE_PTR branch = AST_new_node(empty_location(), AST_If, NULL); + const AST_NODE_PTR gt = AST_new_node(empty_location(), AST_Greater, NULL); AST_push_node(branch, gt); - AST_push_node(gt, AST_new_node(AST_Float, "2.3")); - AST_push_node(gt, AST_new_node(AST_Float, "0.79")); + AST_push_node(gt, AST_new_node(empty_location(), AST_Float, "2.3")); + AST_push_node(gt, AST_new_node(empty_location(), AST_Float, "0.79")); AST_push_node(stmt, branch); @@ -32,7 +32,7 @@ int main(void) { AST_init(); - const AST_NODE_PTR root = AST_new_node(AST_Stmt, NULL); + const AST_NODE_PTR root = AST_new_node(empty_location(), AST_Stmt, NULL); generate_branch(root); diff --git a/tests/ast/print_node.c b/tests/ast/print_node.c index 38e8a41..47db685 100644 --- a/tests/ast/print_node.c +++ b/tests/ast/print_node.c @@ -8,7 +8,7 @@ int main(void) { AST_init(); - const AST_NODE_PTR node = AST_new_node(0, "value"); + const AST_NODE_PTR node = AST_new_node(empty_location(), 0, "value"); for (size_t i = 0; i < AST_ELEMENT_COUNT; i++) { // set kind diff --git a/tests/logging/CMakeLists.txt b/tests/logging/CMakeLists.txt index 0f14079..d5abada 100644 --- a/tests/logging/CMakeLists.txt +++ b/tests/logging/CMakeLists.txt @@ -2,12 +2,20 @@ include(CTest) include_directories(${PROJECT_SOURCE_DIR}/src) +# ------------------------------------------------ # +# Setup Glib 2.0 # +# ------------------------------------------------ # + +find_package(PkgConfig REQUIRED) +pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0) + # ------------------------------------------------------- # # CTEST 1 # test the default output of the logger add_executable(logging_output ${PROJECT_SOURCE_DIR}/src/sys/log.c + ${PROJECT_SOURCE_DIR}/src/sys/col.c output.c) set_target_properties(logging_output PROPERTIES @@ -23,6 +31,7 @@ add_test(NAME logging_output add_executable(logging_panic ${PROJECT_SOURCE_DIR}/src/sys/log.c + ${PROJECT_SOURCE_DIR}/src/sys/col.c panic.c) set_target_properties(logging_panic PROPERTIES @@ -38,6 +47,7 @@ add_test(NAME logging_panic add_executable(logging_streams ${PROJECT_SOURCE_DIR}/src/sys/log.c + ${PROJECT_SOURCE_DIR}/src/sys/col.c streams.c) set_target_properties(logging_streams PROPERTIES @@ -53,6 +63,7 @@ add_test(NAME logging_streams add_executable(logging_level ${PROJECT_SOURCE_DIR}/src/sys/log.c + ${PROJECT_SOURCE_DIR}/src/sys/col.c level.c) set_target_properties(logging_level PROPERTIES diff --git a/tests/logging/level.c b/tests/logging/level.c index 6a4f055..fd37cc7 100644 --- a/tests/logging/level.c +++ b/tests/logging/level.c @@ -3,11 +3,13 @@ // #include "sys/log.h" +#include #define LOG_LEVEL LOG_LEVEL_WARNING int main(void) { log_init(); + col_init(); DEBUG("logging some debug..."); INFO("logging some info..."); diff --git a/tests/logging/output.c b/tests/logging/output.c index 7accfc5..dde7d90 100644 --- a/tests/logging/output.c +++ b/tests/logging/output.c @@ -3,9 +3,11 @@ // #include "sys/log.h" +#include int main(void) { log_init(); + col_init(); DEBUG("logging some debug..."); INFO("logging some info..."); From d60ebd6812495f0ee29f9f5713d43e3942109b62 Mon Sep 17 00:00:00 2001 From: servostar Date: Thu, 30 May 2024 21:46:44 +0200 Subject: [PATCH 06/27] fixed: return 1 if no input files specified --- src/main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main.c b/src/main.c index 0b75508..4888ee5 100644 --- a/src/main.c +++ b/src/main.c @@ -122,6 +122,11 @@ int main(int argc, char *argv[]) { print_file_statistics(file); } + if (files.files == NULL) { + printf("No input files, nothing to do.\n\n"); + exit(1); + } + print_unit_statistics(&files); delete_files(&files); From 347a2f0088c2e9b29b402d973aff9259f97eb961 Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 12:05:22 +0200 Subject: [PATCH 07/27] added git submodule tomlc99 --- .gitmodules | 3 +++ dep/tomlc99 | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 dep/tomlc99 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..be66b92 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "dep/tomlc99"] + path = dep/tomlc99 + url = https://github.com/cktan/tomlc99.git diff --git a/dep/tomlc99 b/dep/tomlc99 new file mode 160000 index 0000000..5221b3d --- /dev/null +++ b/dep/tomlc99 @@ -0,0 +1 @@ +Subproject commit 5221b3d3d66c25a1dc6f0372b4f824f1202fe398 From e4c36f341b7a6f6110f79ce80c82a697f8e305fa Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 12:13:55 +0200 Subject: [PATCH 08/27] added target for tomlc99 --- CMakeLists.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index be613ed..09a66d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,6 +66,22 @@ add_custom_command(OUTPUT ${YACC_GENERATED_SOURCE_FILE} COMMENT "generate C source file for parser" VERBATIM) +# ------------------------------------------------ # +# TOML-C99 # +# ------------------------------------------------ # + +add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c + COMMAND git + ARGS submodule update --init --recursive --checkout + COMMENT "checkout dependency TOML-C99" + VERBATIM) + +add_library(tomlc99 ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c) +set_target_properties(tomlc99 + PROPERTIES + OUTPUT_NAME "toml" + ARCHIVE_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/dep) + # ------------------------------------------------ # # Source # # ------------------------------------------------ # @@ -93,6 +109,8 @@ set_target_properties(release OUTPUT_NAME "gsc" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/release) +target_link_libraries(release tomlc99) + # FIXME: cannot compile with /O2 because of /RTC1 flag if (MSVC) set(RELEASE_FLAGS) @@ -125,6 +143,8 @@ set_target_properties(debug OUTPUT_NAME "gsc" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/debug) +target_link_libraries(debug tomlc99) + if (MSVC) set(DEBUG_FLAGS /DEBUG) else() @@ -154,6 +174,8 @@ set_target_properties(check OUTPUT_NAME "gsc" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/check) +target_link_libraries(check tomlc99) + if (MSVC) set(CHECK_FLAGS /DEBUG /WX) else() From e69af85e3770856910bcf7550f1212d3712366b6 Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 16:15:59 +0200 Subject: [PATCH 09/27] added options --- CMakeLists.txt | 2 + src/cfg/opt.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++ src/cfg/opt.h | 34 ++++++++++++++ src/main.c | 44 ------------------ 4 files changed, 154 insertions(+), 44 deletions(-) create mode 100644 src/cfg/opt.c create mode 100644 src/cfg/opt.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 09a66d9..4aa5707 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,6 +82,8 @@ set_target_properties(tomlc99 OUTPUT_NAME "toml" ARCHIVE_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/dep) +include_directories(${PROJECT_SOURCE_DIR}/dep/tomlc99) + # ------------------------------------------------ # # Source # # ------------------------------------------------ # diff --git a/src/cfg/opt.c b/src/cfg/opt.c new file mode 100644 index 0000000..bb74e51 --- /dev/null +++ b/src/cfg/opt.c @@ -0,0 +1,118 @@ +// +// Created by servostar on 5/31/24. +// + +#include +#include +#include + +TargetConfig default_target_config() { + TargetConfig config; + + config.name = "debug"; + config.print_ast = false; + config.print_asm = false; + config.print_ir = false; + + return config; +} + +TargetConfig default_target_config_from_args(int argc, char *argv[]) { + TargetConfig config = default_target_config(); + + for (int i = 0; i < argc; i++) { + char *option = argv[i]; + + if (strcmp(option, "--print-ast") == 0) { + config.print_ast = true; + } else if (strcmp(option, "--print-asm") == 0) { + config.print_asm = true; + } else if (strcmp(option, "--print-ir") == 0) { + config.print_ir = true; + } + } + + return config; +} + +void print_help(void) { + const char *lines[] = { + "Gemstone Compiler (C) GPL-2.0\n", + "Compile file(s): gsc [files]", + "Build project (build.toml): gsc [directory] [target]|all", + "Options:", + " --print-ast print resulting abstract syntax tree to a file", + " --print-asm print resulting assembly language to a file", + " --print-ir print resulting LLVM-IR to a file" + }; + + for (unsigned int i = 0; i < sizeof(lines) / sizeof(const char *); i++) { + printf("%s\n", lines[i]); + } +} + +#define PROJECT_OK 0 +#define PROJECT_TOML_ERR 1 +#define PROJECT_SEMANTIC_ERR 2 + +static int parse_project_table(ProjectConfig *config, toml_table_t *project_table) { + // project name + toml_datum_t name = toml_string_in(project_table, "name"); + if (!name.ok) { + printf("Invalid project configuration: project must have a name\n\n"); + return PROJECT_SEMANTIC_ERR; + } + + config->name = name.u.s; + + // project version + toml_datum_t version = toml_string_in(project_table, "version"); + if (version.ok) { + config->name = version.u.s; + } + + // author names + toml_array_t *authors = toml_array_in(project_table, "authors"); + if (authors) { + unsigned int len = 0; + config->authors = malloc(sizeof(const char *) * 2); + + for (int i = 0;; i++) { + toml_datum_t author = toml_string_at(authors, i); + if (!author.ok) + break; + + char** new_authors = realloc(config->authors, sizeof(const char *) * ++len); + config->authors = new_authors; + config->authors[len] = author.u.s; + } + } +} + +int load_project_config(ProjectConfig *config) { + FILE *config_file = fopen("build.toml", "r"); + if (config_file == NULL) { + return PROJECT_TOML_ERR; + } + + char err_buf[200]; + + toml_table_t *conf = toml_parse_file(config_file, err_buf, sizeof(err_buf)); + fclose(config_file); + + if (!conf) { + printf("Invalid project configuration: %s\n\n", err_buf); + return PROJECT_SEMANTIC_ERR; + } + + toml_table_t *project = toml_table_in(conf, "project"); + if (project) { + parse_project_table(config, project); + + return PROJECT_OK; + } + printf("Invalid project configuration: missing project table\n\n"); + + toml_free(conf); + return PROJECT_SEMANTIC_ERR; +} diff --git a/src/cfg/opt.h b/src/cfg/opt.h new file mode 100644 index 0000000..cf89d4f --- /dev/null +++ b/src/cfg/opt.h @@ -0,0 +1,34 @@ +// +// Created by servostar on 5/31/24. +// + +#ifndef GEMSTONE_OPT_H +#define GEMSTONE_OPT_H + +#include + +typedef struct TargetConfig_t { + char* name; + bool print_ast; + bool print_asm; + bool print_ir; +} TargetConfig; + +typedef struct ProjectConfig_t { + // name of the project + char* name; + // description + char* desc; + // version + char* version; + // list of authors + char** authors; +} ProjectConfig; + +TargetConfig default_target_config(); + +TargetConfig default_target_config_from_args(int argc, char* argv[]); + +void print_help(void); + +#endif //GEMSTONE_OPT_H diff --git a/src/main.c b/src/main.c index e9914ef..83e9705 100644 --- a/src/main.c +++ b/src/main.c @@ -9,10 +9,6 @@ extern FILE *yyin; -// Global array to store options -char options[5][10]; -int num_options = 0; - /** * @brief Log a debug message to inform about beginning exit procedures * @@ -20,34 +16,6 @@ int num_options = 0; void notify_exit(void) { DEBUG("Exiting gemstone..."); } -/** - * @brief add option to global option array - * - */ - -void add_option(const char* option) { - if (num_options < 5 ) { - strcpy(options[num_options], option); - num_options++; - } else { - PANIC("Too Many Options given"); - } -} - -/** - * @brief Check if Option is set - * - */ - -size_t check_option(const char* name) { - for (int i = 0; i < num_options; i++) { - if (strcmp(options[i], name) == 0) { - return 1; - } - } - return 0; -} - /** * @brief Closes File after compiling. * @@ -84,18 +52,6 @@ void setup(void) { int main(int argc, char *argv[]) { - // Iteration through arguments - for (int i = 1; i < argc; i++) { - // Check if the argument starts with "--" - if (argv[i][0] == '-' && argv[i][1] == '-') { - // Extract option name - char option[10]; - strcpy(option, argv[i] + 2); - - // Add option to the global array - add_option(option); - } - } setup(); atexit(close_file); From c527c99392704f9c1a1a3e654c9d102283911250 Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 16:33:48 +0200 Subject: [PATCH 10/27] added license and description to project --- src/cfg/opt.c | 22 ++++++++++++++++------ src/cfg/opt.h | 5 ++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index bb74e51..10d1540 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -4,7 +4,6 @@ #include #include -#include TargetConfig default_target_config() { TargetConfig config; @@ -74,19 +73,30 @@ static int parse_project_table(ProjectConfig *config, toml_table_t *project_tabl // author names toml_array_t *authors = toml_array_in(project_table, "authors"); if (authors) { - unsigned int len = 0; - config->authors = malloc(sizeof(const char *) * 2); + config->authors = g_array_new(FALSE, FALSE, sizeof(char*)); for (int i = 0;; i++) { toml_datum_t author = toml_string_at(authors, i); if (!author.ok) break; - char** new_authors = realloc(config->authors, sizeof(const char *) * ++len); - config->authors = new_authors; - config->authors[len] = author.u.s; + g_array_append_val(config->authors, author.u.s); } } + + // project description + toml_datum_t description = toml_string_in(project_table, "description"); + if (description.ok) { + config->desc = description.u.s; + } + + // project license + toml_datum_t license = toml_string_in(project_table, "license"); + if (license.ok) { + config->license = license.u.s; + } + + return PROJECT_OK; } int load_project_config(ProjectConfig *config) { diff --git a/src/cfg/opt.h b/src/cfg/opt.h index cf89d4f..5c9e6b5 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -6,6 +6,7 @@ #define GEMSTONE_OPT_H #include +#include typedef struct TargetConfig_t { char* name; @@ -21,8 +22,10 @@ typedef struct ProjectConfig_t { char* desc; // version char* version; + // license + char* license; // list of authors - char** authors; + GArray* authors; } ProjectConfig; TargetConfig default_target_config(); From 5fb10bca8815de1fa9c786f976c580391c56d98a Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 16:55:43 +0200 Subject: [PATCH 11/27] added option to compile for app or lib --- src/cfg/opt.c | 11 ++++++++--- src/cfg/opt.h | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 10d1540..6a1862f 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -28,6 +28,10 @@ TargetConfig default_target_config_from_args(int argc, char *argv[]) { config.print_asm = true; } else if (strcmp(option, "--print-ir") == 0) { config.print_ir = true; + } else if (strcmp(option, "--mode=app") == 0) { + config.mode = Application; + } else if (strcmp(option, "--mode=lib") == 0) { + config.mode = Library; } } @@ -40,9 +44,10 @@ void print_help(void) { "Compile file(s): gsc [files]", "Build project (build.toml): gsc [directory] [target]|all", "Options:", - " --print-ast print resulting abstract syntax tree to a file", - " --print-asm print resulting assembly language to a file", - " --print-ir print resulting LLVM-IR to a file" + " --print-ast print resulting abstract syntax tree to a file", + " --print-asm print resulting assembly language to a file", + " --print-ir print resulting LLVM-IR to a file", + " --mode=[app|lib] set the compilation mode to either application or library" }; for (unsigned int i = 0; i < sizeof(lines) / sizeof(const char *); i++) { diff --git a/src/cfg/opt.h b/src/cfg/opt.h index 5c9e6b5..cd00b62 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -8,11 +8,26 @@ #include #include +typedef enum TargetCompilationMode_t { + Application, + Library +} TargetCompilationMode; + typedef struct TargetConfig_t { char* name; bool print_ast; bool print_asm; bool print_ir; + // root module file which imports all submodules + char* root_module; + // output directory for binaries + char* output_directory; + // output directory for intermediate representations (LLVM-IR, Assembly, ...) + char* archive_directory; + // mode of compilation + TargetCompilationMode mode; + // number between 1 and 3 + int optimization_level; } TargetConfig; typedef struct ProjectConfig_t { From 68ca76cb458dd037426c343c9b6d07c9514337d5 Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 16:57:25 +0200 Subject: [PATCH 12/27] added missing default target options --- src/cfg/opt.c | 5 +++++ src/cfg/opt.h | 1 + 2 files changed, 6 insertions(+) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 6a1862f..95ec3a1 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -12,6 +12,11 @@ TargetConfig default_target_config() { config.print_ast = false; config.print_asm = false; config.print_ir = false; + config.mode = Application; + config.archive_directory = "archive"; + config.archive_directory = "bin"; + config.optimization_level = 1; + config.root_module = NULL; return config; } diff --git a/src/cfg/opt.h b/src/cfg/opt.h index cd00b62..c407523 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -19,6 +19,7 @@ typedef struct TargetConfig_t { bool print_asm; bool print_ir; // root module file which imports all submodules + // if this is NULL use the first commandline argument as root module char* root_module; // output directory for binaries char* output_directory; From 76b011511ae7fbc041105af8a434fc25cb3e4678 Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 19:09:28 +0200 Subject: [PATCH 13/27] targets are now read in --- src/cfg/opt.c | 110 +++++++++++++++++++++++++++++++++++++++++--------- src/cfg/opt.h | 1 + 2 files changed, 91 insertions(+), 20 deletions(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 95ec3a1..feeafcb 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -5,6 +5,8 @@ #include #include +#define MAX_TARGETS_PER_PROJECT 100 + TargetConfig default_target_config() { TargetConfig config; @@ -64,26 +66,45 @@ void print_help(void) { #define PROJECT_TOML_ERR 1 #define PROJECT_SEMANTIC_ERR 2 +static void get_bool(bool* boolean, toml_table_t *table, const char* name) { + toml_datum_t datum = toml_bool_in(table, name); + + if (datum.ok) { + *boolean = datum.u.b; + } +} + +static void get_str(char** string, toml_table_t *table, const char* name) { + toml_datum_t datum = toml_string_in(table, name); + + if (datum.ok) { + *string = datum.u.s; + } +} + +static void get_int(int* integer, toml_table_t *table, const char* name) { + toml_datum_t datum = toml_int_in(table, name); + + if (datum.ok) { + *integer = (int) datum.u.i; + } +} + static int parse_project_table(ProjectConfig *config, toml_table_t *project_table) { // project name - toml_datum_t name = toml_string_in(project_table, "name"); - if (!name.ok) { + get_str(&config->name, project_table, "version"); + if (config->name == NULL) { printf("Invalid project configuration: project must have a name\n\n"); return PROJECT_SEMANTIC_ERR; } - config->name = name.u.s; - // project version - toml_datum_t version = toml_string_in(project_table, "version"); - if (version.ok) { - config->name = version.u.s; - } + get_str(&config->name, project_table, "version"); // author names toml_array_t *authors = toml_array_in(project_table, "authors"); if (authors) { - config->authors = g_array_new(FALSE, FALSE, sizeof(char*)); + config->authors = g_array_new(FALSE, FALSE, sizeof(char *)); for (int i = 0;; i++) { toml_datum_t author = toml_string_at(authors, i); @@ -95,15 +116,63 @@ static int parse_project_table(ProjectConfig *config, toml_table_t *project_tabl } // project description - toml_datum_t description = toml_string_in(project_table, "description"); - if (description.ok) { - config->desc = description.u.s; + get_str(&config->desc, project_table, "description"); + // project license + get_str(&config->license, project_table, "license"); + + return PROJECT_OK; +} + +static int get_mode_from_str(TargetCompilationMode* mode, const char* name) { + if (strcmp(name, "application") == 0) { + *mode = Application; + return PROJECT_OK; + } else if (strcmp(name, "library") == 0) { + *mode = Library; + return PROJECT_OK; + } + printf("Invalid project configuration, mode is invalid: %s\n\n", name); + return PROJECT_SEMANTIC_ERR; +} + +static int parse_target(ProjectConfig *config, toml_table_t *target_table, const char* name) { + TargetConfig target_config = default_target_config(); + + target_config.name = (char*) name; + + get_bool(&target_config.print_ast, target_table, "print_ast"); + get_bool(&target_config.print_asm, target_table, "print_asm"); + get_bool(&target_config.print_ir, target_table, "print_ir"); + + get_str(&target_config.root_module, target_table, "root"); + get_str(&target_config.output_directory, target_table, "output"); + get_str(&target_config.archive_directory, target_table, "archive"); + + get_int(&target_config.optimization_level, target_table, "opt"); + + char* mode = NULL; + get_str(&mode, target_table, "mode"); + int err = get_mode_from_str(&target_config.mode, mode); + if (err != PROJECT_OK) { + return err; } - // project license - toml_datum_t license = toml_string_in(project_table, "license"); - if (license.ok) { - config->license = license.u.s; + g_array_append_val(config->targets, target_config); + + return PROJECT_OK; +} + +static int parse_targets(ProjectConfig *config, toml_table_t *root) { + toml_table_t *targets = toml_table_in(root, "target"); + + for (int i = 0; i < MAX_TARGETS_PER_PROJECT; i++) { + const char *key = toml_key_in(targets, i); + + if (!key) + break; + + toml_table_t *target = toml_table_in(targets, key); + parse_target(config, target, key); } return PROJECT_OK; @@ -127,11 +196,12 @@ int load_project_config(ProjectConfig *config) { toml_table_t *project = toml_table_in(conf, "project"); if (project) { - parse_project_table(config, project); - - return PROJECT_OK; + if (parse_project_table(config, project) == PROJECT_OK) { + return parse_targets(config, project); + } + } else { + printf("Invalid project configuration: missing project table\n\n"); } - printf("Invalid project configuration: missing project table\n\n"); toml_free(conf); return PROJECT_SEMANTIC_ERR; diff --git a/src/cfg/opt.h b/src/cfg/opt.h index c407523..ec259e9 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -42,6 +42,7 @@ typedef struct ProjectConfig_t { char* license; // list of authors GArray* authors; + GArray* targets; } ProjectConfig; TargetConfig default_target_config(); From 8f24596779b8cf7fc50f1283bc2862ef9941151f Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 31 May 2024 21:25:37 +0200 Subject: [PATCH 14/27] fixed implementation vaults --- CMakeLists.txt | 5 ++ src/cfg/opt.c | 169 +++++++++++++++++++++++++++++++++++++------------ src/cfg/opt.h | 21 +++++- src/main.c | 139 +++++++++++++++++++++++++++++++--------- 4 files changed, 260 insertions(+), 74 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ca09790..d3763ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,8 @@ set(CMAKE_C_STANDARD_REQUIRED TRUE) set(GEMSTONE_TEST_DIR ${PROJECT_SOURCE_DIR}/tests) set(GEMSTONE_BINARY_DIR ${PROJECT_SOURCE_DIR}/bin) +add_compile_definitions(GSC_VERSION="${PROJECT_VERSION}") + include(CTest) if(BUILD_TESTING) @@ -144,6 +146,9 @@ target_compile_options(release PUBLIC ${FLAGS} ${RELEASE_FLAGS}) # add src directory as include path target_include_directories(release PUBLIC src) +# disable assertions +target_compile_definitions(release PUBLIC NDEBUG="1") + # ------------------------------------------------ # # Target DEBUG # # ------------------------------------------------ # diff --git a/src/cfg/opt.c b/src/cfg/opt.c index feeafcb..ca22b6a 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -4,41 +4,47 @@ #include #include +#include -#define MAX_TARGETS_PER_PROJECT 100 +TargetConfig* default_target_config() { + DEBUG("generating default target config..."); -TargetConfig default_target_config() { - TargetConfig config; + TargetConfig* config = malloc(sizeof(TargetConfig)); - config.name = "debug"; - config.print_ast = false; - config.print_asm = false; - config.print_ir = false; - config.mode = Application; - config.archive_directory = "archive"; - config.archive_directory = "bin"; - config.optimization_level = 1; - config.root_module = NULL; + config->name = NULL; + config->print_ast = false; + config->print_asm = false; + config->print_ir = false; + config->mode = Application; + config->archive_directory = NULL; + config->archive_directory = NULL; + config->optimization_level = 1; + config->root_module = NULL; return config; } -TargetConfig default_target_config_from_args(int argc, char *argv[]) { - TargetConfig config = default_target_config(); +TargetConfig* default_target_config_from_args(int argc, char *argv[]) { + DEBUG("generating default target from command line..."); + + TargetConfig* config = default_target_config(); for (int i = 0; i < argc; i++) { + DEBUG("processing argument: %ld %s", i, argv[i]); char *option = argv[i]; if (strcmp(option, "--print-ast") == 0) { - config.print_ast = true; + config->print_ast = true; } else if (strcmp(option, "--print-asm") == 0) { - config.print_asm = true; + config->print_asm = true; } else if (strcmp(option, "--print-ir") == 0) { - config.print_ir = true; + config->print_ir = true; } else if (strcmp(option, "--mode=app") == 0) { - config.mode = Application; + config->mode = Application; } else if (strcmp(option, "--mode=lib") == 0) { - config.mode = Library; + config->mode = Library; + } else { + config->root_module = option; } } @@ -46,11 +52,14 @@ TargetConfig default_target_config_from_args(int argc, char *argv[]) { } void print_help(void) { + DEBUG("printing help dialog..."); + const char *lines[] = { - "Gemstone Compiler (C) GPL-2.0\n", - "Compile file(s): gsc [files]", - "Build project (build.toml): gsc [directory] [target]|all", + "Gemstone Compiler (C) GPL-2.0", + "Build a project target: gsc build [target]|all", + "Compile non-project file: gsc compile [file]", "Options:", + " --version print the version", " --print-ast print resulting abstract syntax tree to a file", " --print-asm print resulting assembly language to a file", " --print-ir print resulting LLVM-IR to a file", @@ -62,35 +71,42 @@ void print_help(void) { } } -#define PROJECT_OK 0 -#define PROJECT_TOML_ERR 1 -#define PROJECT_SEMANTIC_ERR 2 - static void get_bool(bool* boolean, toml_table_t *table, const char* name) { + DEBUG("retrieving boolean %s", name); + toml_datum_t datum = toml_bool_in(table, name); if (datum.ok) { *boolean = datum.u.b; + DEBUG("boolean has value: %d", datum.u.b); } } static void get_str(char** string, toml_table_t *table, const char* name) { + DEBUG("retrieving string %s", name); + toml_datum_t datum = toml_string_in(table, name); if (datum.ok) { *string = datum.u.s; + DEBUG("string has value: %s", datum.u.s); } } static void get_int(int* integer, toml_table_t *table, const char* name) { + DEBUG("retrieving integer %s", name); + toml_datum_t datum = toml_int_in(table, name); if (datum.ok) { *integer = (int) datum.u.i; + DEBUG("integer has value: %ld", datum.u.i); } } static int parse_project_table(ProjectConfig *config, toml_table_t *project_table) { + DEBUG("parsing project table..."); + // project name get_str(&config->name, project_table, "version"); if (config->name == NULL) { @@ -99,7 +115,7 @@ static int parse_project_table(ProjectConfig *config, toml_table_t *project_tabl } // project version - get_str(&config->name, project_table, "version"); + get_str(&config->name, project_table, "name"); // author names toml_array_t *authors = toml_array_in(project_table, "authors"); @@ -136,34 +152,44 @@ static int get_mode_from_str(TargetCompilationMode* mode, const char* name) { } static int parse_target(ProjectConfig *config, toml_table_t *target_table, const char* name) { - TargetConfig target_config = default_target_config(); + DEBUG("parsing target table..."); - target_config.name = (char*) name; + TargetConfig* target_config = default_target_config(); - get_bool(&target_config.print_ast, target_table, "print_ast"); - get_bool(&target_config.print_asm, target_table, "print_asm"); - get_bool(&target_config.print_ir, target_table, "print_ir"); + target_config->name = (char*) name; - get_str(&target_config.root_module, target_table, "root"); - get_str(&target_config.output_directory, target_table, "output"); - get_str(&target_config.archive_directory, target_table, "archive"); + get_bool(&target_config->print_ast, target_table, "print_ast"); + get_bool(&target_config->print_asm, target_table, "print_asm"); + get_bool(&target_config->print_ir, target_table, "print_ir"); - get_int(&target_config.optimization_level, target_table, "opt"); + get_str(&target_config->root_module, target_table, "root"); + get_str(&target_config->output_directory, target_table, "output"); + get_str(&target_config->archive_directory, target_table, "archive"); + + get_int(&target_config->optimization_level, target_table, "opt"); char* mode = NULL; get_str(&mode, target_table, "mode"); - int err = get_mode_from_str(&target_config.mode, mode); + int err = get_mode_from_str(&target_config->mode, mode); if (err != PROJECT_OK) { return err; } - g_array_append_val(config->targets, target_config); + g_hash_table_insert(config->targets, target_config->name, target_config); return PROJECT_OK; } static int parse_targets(ProjectConfig *config, toml_table_t *root) { + DEBUG("parsing targets of project \"%s\"", config->name); + toml_table_t *targets = toml_table_in(root, "target"); + if (targets == NULL) { + printf("Project has no targets\n"); + return PROJECT_SEMANTIC_ERR; + } + + config->targets = g_hash_table_new(g_str_hash, g_str_equal); for (int i = 0; i < MAX_TARGETS_PER_PROJECT; i++) { const char *key = toml_key_in(targets, i); @@ -179,8 +205,12 @@ static int parse_targets(ProjectConfig *config, toml_table_t *root) { } int load_project_config(ProjectConfig *config) { - FILE *config_file = fopen("build.toml", "r"); + DEBUG("loading project config..."); + + FILE *config_file = fopen(PROJECT_CONFIG_FILE, "r"); if (config_file == NULL) { + printf("Cannot open file %s: %s\n", PROJECT_CONFIG_FILE, strerror(errno)); + ERROR("project file not found"); return PROJECT_TOML_ERR; } @@ -197,7 +227,7 @@ int load_project_config(ProjectConfig *config) { toml_table_t *project = toml_table_in(conf, "project"); if (project) { if (parse_project_table(config, project) == PROJECT_OK) { - return parse_targets(config, project); + return parse_targets(config, conf); } } else { printf("Invalid project configuration: missing project table\n\n"); @@ -206,3 +236,62 @@ int load_project_config(ProjectConfig *config) { toml_free(conf); return PROJECT_SEMANTIC_ERR; } + +void delete_target_config(TargetConfig* config) { + if (config->root_module != NULL) { + free(config->root_module); + } + if (config->archive_directory != NULL) { + free(config->archive_directory); + } + if (config->name != NULL) { + free(config->name); + } + if (config->output_directory != NULL) { + free(config->output_directory); + } + free(config); +} + +void delete_project_config(ProjectConfig* config) { + if (config->name != NULL) { + free(config->name); + } + if (config->authors != NULL) { + g_array_free(config->authors, TRUE); + } + if (config->desc != NULL) { + free(config->desc); + } + if (config->license != NULL) { + free(config->license); + } + if (config->targets != NULL) { + GHashTableIter iter; + + g_hash_table_iter_init(&iter, config->targets); + + char* key; + TargetConfig* val; + while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) { + delete_target_config(val); + } + + g_hash_table_destroy(config->targets); + } + + free(config); +} + +ProjectConfig* default_project_config() { + ProjectConfig* config = malloc(sizeof(ProjectConfig)); + + config->authors = NULL; + config->name = NULL; + config->targets = NULL; + config->license = NULL; + config->version = NULL; + config->desc = NULL; + + return config; +} diff --git a/src/cfg/opt.h b/src/cfg/opt.h index ec259e9..c54bfb2 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -8,6 +8,13 @@ #include #include +#define MAX_TARGETS_PER_PROJECT 100 +#define PROJECT_CONFIG_FILE "build.toml" + +#define PROJECT_OK 0 +#define PROJECT_TOML_ERR 1 +#define PROJECT_SEMANTIC_ERR 2 + typedef enum TargetCompilationMode_t { Application, Library @@ -42,13 +49,21 @@ typedef struct ProjectConfig_t { char* license; // list of authors GArray* authors; - GArray* targets; + GHashTable* targets; } ProjectConfig; -TargetConfig default_target_config(); +TargetConfig* default_target_config(); -TargetConfig default_target_config_from_args(int argc, char* argv[]); +ProjectConfig* default_project_config(); + +TargetConfig* default_target_config_from_args(int argc, char* argv[]); + +int load_project_config(ProjectConfig *config); void print_help(void); +void delete_project_config(ProjectConfig* config); + +void delete_target_config(TargetConfig*); + #endif //GEMSTONE_OPT_H diff --git a/src/main.c b/src/main.c index 4888ee5..c0be30b 100644 --- a/src/main.c +++ b/src/main.c @@ -6,6 +6,7 @@ #include #include #include +#include extern void yyrestart(FILE *); @@ -22,6 +23,7 @@ ModuleFile *current_file; */ [[nodiscard("AST may be in invalid state")]] [[gnu::nonnull(1), gnu::nonnull(1)]] + static size_t compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) { assert(file->path != NULL); assert(ast != NULL); @@ -30,6 +32,7 @@ static size_t compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) { if (file->handle == NULL) { INFO("unable to open file: %s", file->path); + printf("Cannot open file %s: %s\n", file->path, strerror(errno)); return 1; } @@ -92,43 +95,117 @@ void setup(void) { DEBUG("finished starting up gemstone..."); } +void build_target(ModuleFileStack *unit, TargetConfig *target) { + printf("Compiling file: %s\n\n", target->root_module); + + TokenLocation location = { + .line_start = 0, + .line_end = 0, + .col_start = 0, + .col_end = 0 + }; + AST_NODE_PTR ast = AST_new_node(location, AST_Module, NULL); + ModuleFile *file = push_file(unit, target->root_module); + + if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) { + // TODO: parse AST to semantic values + // TODO: backend codegen + } + + AST_delete_node(ast); + + print_file_statistics(file); +} + +void compile_file(ModuleFileStack *unit, int argc, char *argv[]) { + INFO("compiling basic files..."); + + TargetConfig *target = default_target_config_from_args(argc, argv); + + if (target->root_module == NULL) { + printf("No input file specified\n"); + delete_target_config(target); + return; + } + + build_target(unit, target); + + delete_target_config(target); +} + +void build_project_targets(ModuleFileStack *unit, ProjectConfig *config, const char *filter) { + if (strcmp(filter, "all") == 0) { + GHashTableIter iter; + + g_hash_table_iter_init(&iter, config->targets); + + char* key; + TargetConfig* val; + while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) { + build_target(unit, val); + } + } else if (g_hash_table_contains(config->targets, filter)) { + build_target(unit, g_hash_table_lookup(config->targets, filter)); + } +} + +void build_project(ModuleFileStack *unit, int argc, char *argv[]) { + if (argc >= 1) { + ProjectConfig* config = default_project_config(); + int err = load_project_config(config); + + if (err == PROJECT_OK) { + if (argc == 1) { + build_project_targets(unit, config, "all"); + } else { + build_project_targets(unit, config, argv[0]); + } + } + + delete_project_config(config); + + } else { + printf("Expected 1 target to run\n"); + } +} + +void configure_run_mode(int argc, char *argv[]) { + if (argc > 1) { + + ModuleFileStack files; + files.files = NULL; + + if (strcmp(argv[1], "build") == 0) { + build_project(&files, argc - 2, &argv[2]); + } else if (strcmp(argv[1], "compile") == 0) { + compile_file(&files, argc - 2, &argv[2]); + } else { + printf("invalid mode of operation\n"); + } + + if (files.files == NULL) { + printf("No input files, nothing to do.\n\n"); + exit(1); + } + + print_unit_statistics(&files); + delete_files(&files); + + return; + } + INFO("no arguments provided"); + + print_help(); +} + int main(int argc, char *argv[]) { setup(); atexit(close_file); - ModuleFileStack files; - files.files = NULL; + printf("running GSC version %s\n", GSC_VERSION); - for (int i = 1; i < argc; i++) { - printf("Compiling file: %s\n\n", argv[i]); + configure_run_mode(argc, argv); - TokenLocation location = { - .line_start = 0, - .line_end = 0, - .col_start = 0, - .col_end = 0 - }; - AST_NODE_PTR ast = AST_new_node(location, AST_Module, NULL); - ModuleFile *file = push_file(&files, argv[i]); - - if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) { - // TODO: parse AST to semantic values - // TODO: backend codegen - } - - AST_delete_node(ast); - - print_file_statistics(file); - } - - if (files.files == NULL) { - printf("No input files, nothing to do.\n\n"); - exit(1); - } - - print_unit_statistics(&files); - - delete_files(&files); return 0; } From 3a8796a462373c044899ed3ce3bb185508cd76e8 Mon Sep 17 00:00:00 2001 From: servostar Date: Sat, 1 Jun 2024 01:00:22 +0200 Subject: [PATCH 15/27] fixed segfaults --- src/cfg/opt.c | 2 +- src/io/files.c | 34 ++++++++++++++- src/io/files.h | 3 ++ src/main.c | 113 +++++++++++++++++++++++++------------------------ 4 files changed, 94 insertions(+), 58 deletions(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index ca22b6a..3f5b829 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -44,7 +44,7 @@ TargetConfig* default_target_config_from_args(int argc, char *argv[]) { } else if (strcmp(option, "--mode=lib") == 0) { config->mode = Library; } else { - config->root_module = option; + config->root_module = strdup(option); } } diff --git a/src/io/files.c b/src/io/files.c index 2d4fda7..09ff395 100644 --- a/src/io/files.c +++ b/src/io/files.c @@ -217,7 +217,11 @@ void print_unit_statistics(ModuleFileStack *file_stack) { stats.error_count += file->statistics.error_count; } - printf("%d files generated ", file_stack->files->len); + if (stats.info_count + stats.warning_count + stats.error_count < 1) { + return; + } + + printf("%d file(s) generated ", file_stack->files->len); if (stats.info_count > 0) { printf("%ld notice(s) ", stats.info_count); @@ -233,3 +237,31 @@ void print_unit_statistics(ModuleFileStack *file_stack) { printf("\n\n"); } + +void print_message(Message kind, const char *fmt, ...) { + const char *accent_color = RESET; + const char *kind_text = "unknown"; + switch (kind) { + case Info: + kind_text = "info"; + accent_color = CYAN; + break; + case Warning: + kind_text = "warning"; + accent_color = YELLOW; + break; + case Error: + kind_text = "error"; + accent_color = RED; + break; + } + + va_list args; + va_start(args, fmt); + + printf("%s%s:%s ", accent_color, kind_text, RESET); + vprintf(fmt, args); + printf("\n\n"); + + va_end(args); +} diff --git a/src/io/files.h b/src/io/files.h index 076b8d0..25d5078 100644 --- a/src/io/files.h +++ b/src/io/files.h @@ -70,6 +70,9 @@ TokenLocation empty_location(void); [[gnu::nonnull(1), gnu::nonnull(2)]] void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, const char *message); +[[gnu::nonnull(2)]] +void print_message(Message kind, const char *fmt, ...); + [[gnu::nonnull(1)]] void print_file_statistics(ModuleFile *file); diff --git a/src/main.c b/src/main.c index c0be30b..a309dc9 100644 --- a/src/main.c +++ b/src/main.c @@ -32,7 +32,7 @@ static size_t compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) { if (file->handle == NULL) { INFO("unable to open file: %s", file->path); - printf("Cannot open file %s: %s\n", file->path, strerror(errno)); + print_message(Error, "Cannot open file %s: %s", file->path, strerror(errno)); return 1; } @@ -96,18 +96,20 @@ void setup(void) { } void build_target(ModuleFileStack *unit, TargetConfig *target) { - printf("Compiling file: %s\n\n", target->root_module); + print_message(Info, "Compiling file: %s", target->root_module); - TokenLocation location = { - .line_start = 0, - .line_end = 0, - .col_start = 0, - .col_end = 0 - }; + TokenLocation location = new_location(0,0,0,0); AST_NODE_PTR ast = AST_new_node(location, AST_Module, NULL); ModuleFile *file = push_file(unit, target->root_module); if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) { + + + + if (target->print_ast) { + + } + // TODO: parse AST to semantic values // TODO: backend codegen } @@ -123,7 +125,7 @@ void compile_file(ModuleFileStack *unit, int argc, char *argv[]) { TargetConfig *target = default_target_config_from_args(argc, argv); if (target->root_module == NULL) { - printf("No input file specified\n"); + print_message(Error, "No input file specified."); delete_target_config(target); return; } @@ -133,69 +135,68 @@ void compile_file(ModuleFileStack *unit, int argc, char *argv[]) { delete_target_config(target); } -void build_project_targets(ModuleFileStack *unit, ProjectConfig *config, const char *filter) { - if (strcmp(filter, "all") == 0) { +void build_project_targets(ModuleFileStack *unit, ProjectConfig *config, int argc, char *argv[]) { + if (argc == 1 && strcmp(argv[0], "all") == 0) { GHashTableIter iter; g_hash_table_iter_init(&iter, config->targets); - char* key; - TargetConfig* val; + char *key; + TargetConfig *val; while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) { build_target(unit, val); } - } else if (g_hash_table_contains(config->targets, filter)) { - build_target(unit, g_hash_table_lookup(config->targets, filter)); + } else { + for (int i = 0; i < argc; i++) { + char *target_name = argv[i]; + if (g_hash_table_contains(config->targets, target_name)) { + build_target(unit, g_hash_table_lookup(config->targets, target_name)); + } + } } } void build_project(ModuleFileStack *unit, int argc, char *argv[]) { - if (argc >= 1) { - ProjectConfig* config = default_project_config(); - int err = load_project_config(config); - - if (err == PROJECT_OK) { - if (argc == 1) { - build_project_targets(unit, config, "all"); - } else { - build_project_targets(unit, config, argv[0]); - } - } - - delete_project_config(config); - - } else { - printf("Expected 1 target to run\n"); + if (argc <= 0) { + print_message(Error, "No targets specified."); + return; } + + ProjectConfig *config = default_project_config(); + int err = load_project_config(config); + + if (err == PROJECT_OK) { + build_project_targets(unit, config, argc, argv); + } + + delete_project_config(config); } void configure_run_mode(int argc, char *argv[]) { - if (argc > 1) { - - ModuleFileStack files; - files.files = NULL; - - if (strcmp(argv[1], "build") == 0) { - build_project(&files, argc - 2, &argv[2]); - } else if (strcmp(argv[1], "compile") == 0) { - compile_file(&files, argc - 2, &argv[2]); - } else { - printf("invalid mode of operation\n"); - } - - if (files.files == NULL) { - printf("No input files, nothing to do.\n\n"); - exit(1); - } - - print_unit_statistics(&files); - delete_files(&files); - + if (argc <= 0) { + INFO("no arguments provided"); + print_help(); return; } - INFO("no arguments provided"); - print_help(); + ModuleFileStack files; + files.files = NULL; + + if (strcmp(argv[0], "build") == 0) { + build_project(&files, argc - 1, &argv[1]); + } else if (strcmp(argv[0], "compile") == 0) { + compile_file(&files, argc - 1, &argv[1]); + } else { + print_message(Error, "Invalid mode of operation. Rerun with --help."); + } + + if (files.files == NULL) { + print_message(Error, "No input files, nothing to do."); + exit(1); + } + + print_unit_statistics(&files); + delete_files(&files); } int main(int argc, char *argv[]) { @@ -203,9 +204,9 @@ int main(int argc, char *argv[]) { setup(); atexit(close_file); - printf("running GSC version %s\n", GSC_VERSION); + print_message(Info, "running GSC version %s", GSC_VERSION); - configure_run_mode(argc, argv); + configure_run_mode(argc - 1, &argv[1]); return 0; } From 01f5ef953d8f56546d95ae00ec0491989fef87f7 Mon Sep 17 00:00:00 2001 From: servostar Date: Sat, 1 Jun 2024 14:52:59 +0200 Subject: [PATCH 16/27] fixed devkit not cloning submodules --- CMakeLists.txt | 2 ++ Dockerfile | 2 ++ src/cfg/opt.c | 29 ++++++++++++++++------------- src/main.c | 9 +++++++-- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d3763ea..b5a44cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,8 @@ pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0) # TOML-C99 # # ------------------------------------------------ # +execute_process(COMMAND git submodule init -- dep/tomlc99) + add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c COMMAND git ARGS submodule update --init --recursive --checkout diff --git a/Dockerfile b/Dockerfile index 68265d8..bb287f7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,5 +10,7 @@ COPY --chown=lorang CMakeLists.txt /home/lorang/ COPY --chown=lorang run-check-test.sh /home/lorang/ COPY --chown=lorang .env /home/lorang/ COPY --chown=lorang run-docker-build.sh /home/lorang/ +COPY --chown=lorang dep /home/lorang/dep +COPY --chown=lorang .git /home/lorang/.git RUN cmake . diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 3f5b829..92577d8 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -5,19 +5,20 @@ #include #include #include +#include TargetConfig* default_target_config() { DEBUG("generating default target config..."); TargetConfig* config = malloc(sizeof(TargetConfig)); - config->name = NULL; + config->name = strdup("out"); config->print_ast = false; config->print_asm = false; config->print_ir = false; config->mode = Application; - config->archive_directory = NULL; - config->archive_directory = NULL; + config->archive_directory = strdup("archive"); + config->output_directory = strdup("bin"); config->optimization_level = 1; config->root_module = NULL; @@ -43,8 +44,10 @@ TargetConfig* default_target_config_from_args(int argc, char *argv[]) { config->mode = Application; } else if (strcmp(option, "--mode=lib") == 0) { config->mode = Library; - } else { + } else if (config->root_module == NULL) { config->root_module = strdup(option); + } else { + print_message(Warning, "Got more than one file to compile, using first by ignoring others."); } } @@ -185,7 +188,7 @@ static int parse_targets(ProjectConfig *config, toml_table_t *root) { toml_table_t *targets = toml_table_in(root, "target"); if (targets == NULL) { - printf("Project has no targets\n"); + print_message(Warning, "Project has no targets"); return PROJECT_SEMANTIC_ERR; } @@ -209,7 +212,7 @@ int load_project_config(ProjectConfig *config) { FILE *config_file = fopen(PROJECT_CONFIG_FILE, "r"); if (config_file == NULL) { - printf("Cannot open file %s: %s\n", PROJECT_CONFIG_FILE, strerror(errno)); + print_message(Error, "Cannot open file %s: %s", PROJECT_CONFIG_FILE, strerror(errno)); ERROR("project file not found"); return PROJECT_TOML_ERR; } @@ -220,17 +223,17 @@ int load_project_config(ProjectConfig *config) { fclose(config_file); if (!conf) { - printf("Invalid project configuration: %s\n\n", err_buf); + print_message(Error, "Invalid project configuration: %s", err_buf); return PROJECT_SEMANTIC_ERR; } toml_table_t *project = toml_table_in(conf, "project"); - if (project) { - if (parse_project_table(config, project) == PROJECT_OK) { - return parse_targets(config, conf); - } - } else { - printf("Invalid project configuration: missing project table\n\n"); + if (!project) { + print_message(Error, "Invalid project configuration: missing project table."); + } + + if (parse_project_table(config, project) == PROJECT_OK) { + return parse_targets(config, conf); } toml_free(conf); diff --git a/src/main.c b/src/main.c index a309dc9..397c652 100644 --- a/src/main.c +++ b/src/main.c @@ -95,6 +95,12 @@ void setup(void) { DEBUG("finished starting up gemstone..."); } +static void setup_target_environment(const TargetConfig* target) { + if (target->output_directory) { + + } +} + void build_target(ModuleFileStack *unit, TargetConfig *target) { print_message(Info, "Compiling file: %s", target->root_module); @@ -103,8 +109,7 @@ void build_target(ModuleFileStack *unit, TargetConfig *target) { ModuleFile *file = push_file(unit, target->root_module); if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) { - - + setup_target_environment(target); if (target->print_ast) { From fcbfb548c335c14eac497fc8635e0019dbb09314 Mon Sep 17 00:00:00 2001 From: servostar Date: Sun, 2 Jun 2024 20:57:59 +0200 Subject: [PATCH 17/27] added doxygen and added new compiler module --- src/compiler.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++ src/compiler.h | 15 +++ src/io/files.c | 95 +++++++++++++++++- src/io/files.h | 74 ++++++++++++++ src/main.c | 176 +--------------------------------- 5 files changed, 439 insertions(+), 176 deletions(-) create mode 100644 src/compiler.c create mode 100644 src/compiler.h diff --git a/src/compiler.c b/src/compiler.c new file mode 100644 index 0000000..1889763 --- /dev/null +++ b/src/compiler.c @@ -0,0 +1,255 @@ +// +// Created by servostar on 6/2/24. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern void yyrestart(FILE *); + +// Module AST node used by the parser for AST construction. +[[maybe_unused]] +AST_NODE_PTR root; +// Current file which gets compiled the parser. +// NOTE: due to global state no concurrent compilation is possible +// on parser level. +[[maybe_unused]] +ModuleFile *current_file; + +/** + * @brief Compile the specified file into AST + * @param ast Initialized AST module node to build program rules + * @param file The file to be processed + * @return EXIT_SUCCESS in case the parsing was success full anything lese if not + */ +[[nodiscard("AST may be in invalid state")]] +[[gnu::nonnull(1), gnu::nonnull(1)]] +static int compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) { + assert(file->path != NULL); + assert(ast != NULL); + + file->handle = fopen(file->path, "r"); + + if (file->handle == NULL) { + INFO("unable to open file: %s", file->path); + print_message(Error, "Cannot open file %s: %s", file->path, strerror(errno)); + return EXIT_FAILURE; + } + + DEBUG("parsing file: %s", file->path); + // setup global state + root = ast; + current_file = file; + yyin = file->handle; + yyrestart(yyin); + lex_reset(); + + yyparse(); + + // clean up global state + // current_file = NULL; + root = NULL; + yyin = NULL; + + return EXIT_SUCCESS; +} + +/** + * @brief Setup the environment of the target. + * @param target + * @return EXIT_SUCCESS if successful EXIT_FAILURE otherwise + */ +static int setup_target_environment(const TargetConfig *target) { + DEBUG("setting up environment for target: %s", target->name); + + assert(target->output_directory != NULL); + assert(target->archive_directory != NULL); + + int result; + + result = create_directory(target->archive_directory); + if (result != 0 && errno != EEXIST) { + const char *message = get_last_error(); + assert(message != NULL); + + print_message(Error, "Unable to create directory: %s: %s", target->archive_directory, message); + free((void *) message); + return EXIT_FAILURE; + } + + result = create_directory(target->output_directory); + if (result != 0 && errno != EEXIST) { + const char *message = get_last_error(); + assert(message != NULL); + + print_message(Error, "Unable to create directory: %s: %s", target->output_directory, message); + free((void *) message); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +/** + * @brief Print the supplied AST of the specified target to a graphviz ".gv" file + * @param ast + * @param target + */ +static void print_ast_to_file(AST_NODE_PTR ast, TargetConfig *target) { + assert(ast != NULL); + assert(target != NULL); + + if (!target->print_ast) + return; + + // create file path to write graphviz to + const char *path = make_file_path(1, target->name, ".gv", target->archive_directory); + + FILE *output = fopen((const char *) path, "w"); + if (output == NULL) { + const char *message = get_last_error(); + print_message(Error, "Unable to open file for syntax tree at: %s: %s", path, message); + free((void *) message); + } else { + + AST_fprint_graphviz(output, ast); + + fclose(output); + } + + free((void *) path); +} + +/** + * @brief Build the given target + * @param unit + * @param target + */ +static void build_target(ModuleFileStack *unit, TargetConfig *target) { + print_message(Info, "Building target: %s", target->name); + + AST_NODE_PTR ast = AST_new_node(empty_location(), AST_Module, NULL); + ModuleFile *file = push_file(unit, target->root_module); + + if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) { + if (setup_target_environment(target) == 0) { + + print_ast_to_file(ast, target); + + // TODO: parse AST to semantic values + // TODO: backend codegen + } + } + + AST_delete_node(ast); + + print_file_statistics(file); +} + +/** + * @brief Compile a single file. + * Creates a single target by the given command line arguments. + * @param unit + * @param argc + * @param argv + */ +static void compile_file(ModuleFileStack *unit, int argc, char *argv[]) { + INFO("compiling basic files..."); + + TargetConfig *target = default_target_config_from_args(argc, argv); + + if (target->root_module == NULL) { + print_message(Error, "No input file specified."); + delete_target_config(target); + return; + } + + build_target(unit, target); + + delete_target_config(target); +} + +/** + * @brief Build all project targets specified by the command line arguments. + * @param unit + * @param config + * @param argc + * @param argv + */ +static void build_project_targets(ModuleFileStack *unit, ProjectConfig *config, int argc, char *argv[]) { + if (argc == 1 && strcmp(argv[0], "all") == 0) { + // build all targets in the project + GHashTableIter iter; + + g_hash_table_iter_init(&iter, config->targets); + + char *key; + TargetConfig *val; + while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) { + build_target(unit, val); + } + } else { + // build all targets given in the arguments + for (int i = 0; i < argc; i++) { + char *target_name = argv[i]; + if (g_hash_table_contains(config->targets, target_name)) { + build_target(unit, g_hash_table_lookup(config->targets, target_name)); + } + } + } +} + +/** + * @brief Build targets from project. Configuration is provided by command line arguments. + * @param unit File storage + * @param argc Number of arguments + * @param argv Array of Arguments + */ +static void build_project(ModuleFileStack *unit, int argc, char *argv[]) { + if (argc <= 0) { + print_message(Error, "No targets specified."); + return; + } + + ProjectConfig *config = default_project_config(); + int err = load_project_config(config); + + if (err == PROJECT_OK) { + build_project_targets(unit, config, argc, argv); + } + + delete_project_config(config); +} + +void run_compiler(int argc, char *argv[]) { + if (argc <= 0) { + INFO("no arguments provided"); + print_help(); + return; + } + + ModuleFileStack files = new_file_stack(); + + if (strcmp(argv[0], "build") == 0) { + build_project(&files, argc - 1, &argv[1]); + } else if (strcmp(argv[0], "compile") == 0) { + compile_file(&files, argc - 1, &argv[1]); + } else { + print_message(Error, "Invalid mode of operation. Rerun with --help."); + } + + if (files.files == NULL) { + print_message(Error, "No input files, nothing to do."); + exit(1); + } + + print_unit_statistics(&files); + delete_files(&files); +} diff --git a/src/compiler.h b/src/compiler.h new file mode 100644 index 0000000..943e47b --- /dev/null +++ b/src/compiler.h @@ -0,0 +1,15 @@ +// +// Created by servostar on 6/2/24. +// + +#ifndef GEMSTONE_COMPILER_H +#define GEMSTONE_COMPILER_H + +/** + * @brief Run the gemstone compiler with the provided command arguments. + * @param argc + * @param argv + */ +void run_compiler(int argc, char *argv[]); + +#endif //GEMSTONE_COMPILER_H diff --git a/src/io/files.c b/src/io/files.c index 09ff395..86cdfdb 100644 --- a/src/io/files.c +++ b/src/io/files.c @@ -6,6 +6,28 @@ #include #include #include +#include + +#ifdef __unix__ + +#include + +#define MAX_PATH_BYTES PATH_MAX + +#elif defined(_WIN32) || defined(WIN32) + +#include + +#define MAX_PATH_BYTES _MAX_PATH + +#endif + +ModuleFileStack new_file_stack() { + ModuleFileStack stack; + stack.files = NULL; + + return stack; +} ModuleFile *push_file(ModuleFileStack *stack, const char *path) { assert(stack != NULL); @@ -92,12 +114,13 @@ void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, c break; } - char absolute_path[PATH_MAX]; - realpath(file->path, absolute_path); + const char *absolute_path = get_absolute_path(file->path); printf("%s%s:%ld:%s %s%s:%s %s\n", BOLD, absolute_path, location->line_start, RESET, accent_color, kind_text, RESET, message); + free((void *) absolute_path); + size_t lines = location->line_end - location->line_start + 1; for (size_t l = 0; l < lines; l++) { @@ -261,7 +284,73 @@ void print_message(Message kind, const char *fmt, ...) { printf("%s%s:%s ", accent_color, kind_text, RESET); vprintf(fmt, args); - printf("\n\n"); + printf("\n"); va_end(args); } + +int create_directory(const char *path) { + assert(path != NULL); + + DEBUG("creating directory: %s", path); + + int result; +#ifdef __unix__ + result = mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); +#elif defined(_WIN32) || defined(WIN32) + result = _mkdir(path); +#endif + + return result; +} + +const char *get_last_error() { + return strdup(strerror(errno)); +} + +const char *get_absolute_path(const char *path) { + assert(path != NULL); + + DEBUG("resolving absolute path of: %s", path); + +#ifdef __unix__ + // use unix specific function + char absolute_path[MAX_PATH_BYTES]; + realpath(path, absolute_path); +#elif defined(_WIN32) || defined(WIN32) + // use Windows CRT specific function + char absolute_path[MAX_PATH_BYTES]; + _fullpath(path, absolute_path, _MAX_PATH); +#endif + + return strdup(absolute_path); +} + +const char* make_file_path(int count, const char* name, const char* ext, ...) { + DEBUG("building file path..."); + + va_list args; + va_start(args, count); // Initialize the va_list with the first variadic argument + + char* path = calloc(MAX_PATH_BYTES, sizeof(char)); + + for (int i = 0; i < count; i++) { + const char* arg = va_arg(args, const char*); + assert(arg != NULL); + + strcat(path, arg); + strcat(path, PATH_SEPARATOR); + } + + va_end(args); // Clean up the va_list + + if (name != NULL) { + strcat(path, name); + } + + if (name != NULL) { + strcat(path, ext); + } + + return path; +} diff --git a/src/io/files.h b/src/io/files.h index 25d5078..7b8b166 100644 --- a/src/io/files.h +++ b/src/io/files.h @@ -8,6 +8,12 @@ #include #include +#if defined(WIN32) || defined(_WIN32) +#define PATH_SEPARATOR "\\" +#else +#define PATH_SEPARATOR "/" +#endif + typedef struct FileDiagnosticStatistics_t { size_t error_count; size_t warning_count; @@ -37,6 +43,12 @@ typedef struct TokenLocation_t { unsigned long int col_end; } TokenLocation; +/** + * @brief Create a new, empty file stack. + * @return + */ +ModuleFileStack new_file_stack(); + /** * @brief Add a new file to the file stack. * @attention The file handle returned will be invalid @@ -65,18 +77,80 @@ void delete_files(ModuleFileStack *stack); TokenLocation new_location(unsigned long int line_start, unsigned long int col_start, unsigned long int line_end, unsigned long int col_end); +/** + * @brief Create a new empty location with all of its contents set to zero + * @return + */ TokenLocation empty_location(void); +/** + * @brief Prints some diagnostic message to stdout. + * This also print the token group and the attached source as context. + * @param file + * @param location + * @param kind + * @param message + */ [[gnu::nonnull(1), gnu::nonnull(2)]] void print_diagnostic(ModuleFile *file, TokenLocation *location, Message kind, const char *message); [[gnu::nonnull(2)]] +/** + * @brief Print a general message to stdout. Provides no source context like print_diagnostic() + * @param kind + * @param fmt + * @param ... + */ void print_message(Message kind, const char *fmt, ...); +/** + * @brief Print statistics about a specific file. + * Will print the amount of infos, warning and errors emitted during compilation. + * @param file + */ [[gnu::nonnull(1)]] void print_file_statistics(ModuleFile *file); +/** + * @brief Print statistics of all files in the module stack. + * @param file_stack + */ [[gnu::nonnull(1)]] void print_unit_statistics(ModuleFileStack *file_stack); +/** + * @brief Create a new directory. Will return EEXISTS in case the directory already exists. + * @param path + * @return 0 if successful, anything else otherwise + */ +[[gnu::nonnull(1)]] +int create_directory(const char* path); + +/** + * @brief Get a string describing the last error set by errno. + * @return a string that must be freed + */ +[[nodiscard("pointer must be freed")]] +const char* get_last_error(); + +/** + * @brief Resolve the absolute path from a given relative path. + * @param path + * @return + */ +[[gnu::nonnull(1)]] +[[nodiscard("pointer must be freed")]] +const char* get_absolute_path(const char* path); + +/** + * @brief Create a file path from a base name, extension a variable amount of directory path segments. + * @param count Amount of path segments to prepend to the basename + * @param name Basename of the file + * @param ext Extension of the file + * @param ... Path segments without path separator + * @return A relative path of a file + */ +[[nodiscard("pointer must be freed")]] +const char* make_file_path(int count, const char* name, const char* ext, ...); + #endif //GEMSTONE_FILES_H diff --git a/src/main.c b/src/main.c index 397c652..a661dfb 100644 --- a/src/main.c +++ b/src/main.c @@ -1,58 +1,10 @@ #include #include #include -#include #include #include #include -#include -#include - -extern void yyrestart(FILE *); - -[[maybe_unused]] -AST_NODE_PTR root; -[[maybe_unused]] -ModuleFile *current_file; - -/** - * @brief Compile the specified file into AST - * @param ast - * @param file - * @return EXIT_SUCCESS in case the parsing was success full anything lese if not - */ -[[nodiscard("AST may be in invalid state")]] -[[gnu::nonnull(1), gnu::nonnull(1)]] - -static size_t compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) { - assert(file->path != NULL); - assert(ast != NULL); - - file->handle = fopen(file->path, "r"); - - if (file->handle == NULL) { - INFO("unable to open file: %s", file->path); - print_message(Error, "Cannot open file %s: %s", file->path, strerror(errno)); - return 1; - } - - DEBUG("parsing file: %s", file->path); - // setup global state - root = ast; - current_file = file; - yyin = file->handle; - yyrestart(yyin); - lex_reset(); - - yyparse(); - - // clean up global state - // current_file = NULL; - root = NULL; - yyin = NULL; - - return 0; -} +#include /** * @brief Log a debug message to inform about beginning exit procedures @@ -60,17 +12,6 @@ static size_t compile_file_to_ast(AST_NODE_PTR ast, ModuleFile *file) { */ void notify_exit(void) { DEBUG("Exiting gemstone..."); } -/** - * @brief Closes File after compiling. - * - */ - -void close_file(void) { - if (NULL != yyin) { - fclose(yyin); - } -} - /** * @brief Run compiler setup here * @@ -95,123 +36,12 @@ void setup(void) { DEBUG("finished starting up gemstone..."); } -static void setup_target_environment(const TargetConfig* target) { - if (target->output_directory) { - - } -} - -void build_target(ModuleFileStack *unit, TargetConfig *target) { - print_message(Info, "Compiling file: %s", target->root_module); - - TokenLocation location = new_location(0,0,0,0); - AST_NODE_PTR ast = AST_new_node(location, AST_Module, NULL); - ModuleFile *file = push_file(unit, target->root_module); - - if (compile_file_to_ast(ast, file) == EXIT_SUCCESS) { - setup_target_environment(target); - - if (target->print_ast) { - - } - - // TODO: parse AST to semantic values - // TODO: backend codegen - } - - AST_delete_node(ast); - - print_file_statistics(file); -} - -void compile_file(ModuleFileStack *unit, int argc, char *argv[]) { - INFO("compiling basic files..."); - - TargetConfig *target = default_target_config_from_args(argc, argv); - - if (target->root_module == NULL) { - print_message(Error, "No input file specified."); - delete_target_config(target); - return; - } - - build_target(unit, target); - - delete_target_config(target); -} - -void build_project_targets(ModuleFileStack *unit, ProjectConfig *config, int argc, char *argv[]) { - if (argc == 1 && strcmp(argv[0], "all") == 0) { - GHashTableIter iter; - - g_hash_table_iter_init(&iter, config->targets); - - char *key; - TargetConfig *val; - while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) { - build_target(unit, val); - } - } else { - for (int i = 0; i < argc; i++) { - char *target_name = argv[i]; - if (g_hash_table_contains(config->targets, target_name)) { - build_target(unit, g_hash_table_lookup(config->targets, target_name)); - } - } - } -} - -void build_project(ModuleFileStack *unit, int argc, char *argv[]) { - if (argc <= 0) { - print_message(Error, "No targets specified."); - return; - } - - ProjectConfig *config = default_project_config(); - int err = load_project_config(config); - - if (err == PROJECT_OK) { - build_project_targets(unit, config, argc, argv); - } - - delete_project_config(config); -} - -void configure_run_mode(int argc, char *argv[]) { - if (argc <= 0) { - INFO("no arguments provided"); - print_help(); - return; - } - - ModuleFileStack files; - files.files = NULL; - - if (strcmp(argv[0], "build") == 0) { - build_project(&files, argc - 1, &argv[1]); - } else if (strcmp(argv[0], "compile") == 0) { - compile_file(&files, argc - 1, &argv[1]); - } else { - print_message(Error, "Invalid mode of operation. Rerun with --help."); - } - - if (files.files == NULL) { - print_message(Error, "No input files, nothing to do."); - exit(1); - } - - print_unit_statistics(&files); - delete_files(&files); -} - int main(int argc, char *argv[]) { - setup(); - atexit(close_file); - print_message(Info, "running GSC version %s", GSC_VERSION); + print_message(Info, "Running GSC version %s", GSC_VERSION); - configure_run_mode(argc - 1, &argv[1]); + run_compiler(argc - 1, &argv[1]); return 0; } From 33988beb107faa946896a150e285b53acac9a244 Mon Sep 17 00:00:00 2001 From: servostar Date: Mon, 3 Jun 2024 00:24:17 +0200 Subject: [PATCH 18/27] added proper handling of arguments --- src/cfg/opt.c | 130 ++++++++++++++++++++++++---- src/cfg/opt.h | 21 ++++- src/compiler.c | 57 ++++++------ src/compiler.h | 4 +- src/main.c | 13 ++- src/sys/log.c | 19 +++- src/sys/log.h | 12 ++- tests/ast/CMakeLists.txt | 12 +++ tests/input_file/test_input_file.py | 4 +- tests/logging/CMakeLists.txt | 23 +++++ tests/logging/level.c | 5 +- tests/logging/output.c | 5 +- tests/logging/panic.c | 5 +- tests/logging/streams.c | 5 +- 14 files changed, 250 insertions(+), 65 deletions(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 92577d8..b19e506 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -6,6 +6,84 @@ #include #include #include +#include + +static GHashTable* args = NULL; + +static void clean(void) { + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, args); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + free(value); + } + + g_hash_table_destroy(args); +} + +void parse_options(int argc, char* argv[]) { + args = g_hash_table_new(g_str_hash, g_str_equal); + + atexit(clean); + + for (int i = 0; i < argc; i++) { + Option* option = malloc(sizeof(Option)); + option->is_opt = g_str_has_prefix(argv[i], "--"); + option->string = argv[i] + (option->is_opt ? 2 : 0); + option->index = i; + option->value = NULL; + + char* equals = strchr(argv[i], '='); + if (equals != NULL) { + option->value = equals; + } + + g_hash_table_insert(args, (gpointer) option->string, (gpointer) option); + } +} + +bool is_option_set(const char* option) { + assert(option != NULL); + assert(args != NULL); + return g_hash_table_contains(args, option); +} + +const Option* get_option(const char* option) { + if (g_hash_table_contains(args, option)) { + return g_hash_table_lookup(args, option); + } + + return NULL; +} + +GArray* get_non_options_after(const char* command) { + const Option* command_option = get_option(command); + + if (command_option == NULL) { + return NULL; + } + + GArray* array = g_array_new(FALSE, FALSE, sizeof(const char*)); + + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, args); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + Option* option = (Option*) value; + if (!option->is_opt && command_option->index < option->index) { + g_array_append_val(array, option->string); + } + } + + if (array->len == 0) { + g_array_free(array, FALSE); + return NULL; + } + + return array; +} TargetConfig* default_target_config() { DEBUG("generating default target config..."); @@ -25,32 +103,44 @@ TargetConfig* default_target_config() { return config; } -TargetConfig* default_target_config_from_args(int argc, char *argv[]) { +TargetConfig* default_target_config_from_args() { DEBUG("generating default target from command line..."); TargetConfig* config = default_target_config(); - for (int i = 0; i < argc; i++) { - DEBUG("processing argument: %ld %s", i, argv[i]); - char *option = argv[i]; + if (is_option_set("print-ast")) { + config->print_ast = true; + } else if (is_option_set("print-asm")) { + config->print_asm = true; + } else if (is_option_set("print-ir")) { + config->print_ir = true; + } else if (is_option_set("mode")) { + const Option* opt = get_option("mode"); - if (strcmp(option, "--print-ast") == 0) { - config->print_ast = true; - } else if (strcmp(option, "--print-asm") == 0) { - config->print_asm = true; - } else if (strcmp(option, "--print-ir") == 0) { - config->print_ir = true; - } else if (strcmp(option, "--mode=app") == 0) { - config->mode = Application; - } else if (strcmp(option, "--mode=lib") == 0) { - config->mode = Library; - } else if (config->root_module == NULL) { - config->root_module = strdup(option); - } else { - print_message(Warning, "Got more than one file to compile, using first by ignoring others."); + if (opt->value != NULL) { + if (strcmp(opt->value, "app") == 0) { + config->mode = Application; + } else if (strcmp(opt->value, "lib") == 0) { + config->mode = Library; + } else { + print_message(Warning, "Invalid compilation mode: %s", opt->value); + } } } + GArray* files = get_non_options_after("compile"); + + if (files == NULL) { + print_message(Error, "No input file provided."); + } else { + + if (files->len > 1) { + print_message(Warning, "Got more than one file to compile, using first, ignoring others."); + } + + config->root_module = strdup( ((char**) files->data) [0]); + } + return config; } @@ -66,7 +156,9 @@ void print_help(void) { " --print-ast print resulting abstract syntax tree to a file", " --print-asm print resulting assembly language to a file", " --print-ir print resulting LLVM-IR to a file", - " --mode=[app|lib] set the compilation mode to either application or library" + " --mode=[app|lib] set the compilation mode to either application or library", + " --verbose print logs with level information or higher", + " --debug print debug logs (if not disabled at compile time)" }; for (unsigned int i = 0; i < sizeof(lines) / sizeof(const char *); i++) { diff --git a/src/cfg/opt.h b/src/cfg/opt.h index c54bfb2..9f0b7c0 100644 --- a/src/cfg/opt.h +++ b/src/cfg/opt.h @@ -52,11 +52,18 @@ typedef struct ProjectConfig_t { GHashTable* targets; } ProjectConfig; +typedef struct Option_t { + int index; + const char* string; + const char* value; + bool is_opt; +} Option; + TargetConfig* default_target_config(); ProjectConfig* default_project_config(); -TargetConfig* default_target_config_from_args(int argc, char* argv[]); +TargetConfig* default_target_config_from_args(); int load_project_config(ProjectConfig *config); @@ -66,4 +73,16 @@ void delete_project_config(ProjectConfig* config); void delete_target_config(TargetConfig*); +void parse_options(int argc, char* argv[]); + +[[gnu::nonnull(1)]] +bool is_option_set(const char* option); + +[[gnu::nonnull(1)]] +const Option* get_option(const char* option); + +[[gnu::nonnull(1)]] +[[nodiscard("must be freed")]] +GArray* get_non_options_after(const char* command); + #endif //GEMSTONE_OPT_H diff --git a/src/compiler.c b/src/compiler.c index 1889763..79e9b20 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -160,10 +160,10 @@ static void build_target(ModuleFileStack *unit, TargetConfig *target) { * @param argc * @param argv */ -static void compile_file(ModuleFileStack *unit, int argc, char *argv[]) { +static void compile_file(ModuleFileStack *unit) { INFO("compiling basic files..."); - TargetConfig *target = default_target_config_from_args(argc, argv); + TargetConfig *target = default_target_config_from_args(); if (target->root_module == NULL) { print_message(Error, "No input file specified."); @@ -180,11 +180,9 @@ static void compile_file(ModuleFileStack *unit, int argc, char *argv[]) { * @brief Build all project targets specified by the command line arguments. * @param unit * @param config - * @param argc - * @param argv */ -static void build_project_targets(ModuleFileStack *unit, ProjectConfig *config, int argc, char *argv[]) { - if (argc == 1 && strcmp(argv[0], "all") == 0) { +static void build_project_targets(ModuleFileStack *unit, ProjectConfig *config) { + if (is_option_set("all")) { // build all targets in the project GHashTableIter iter; @@ -195,52 +193,51 @@ static void build_project_targets(ModuleFileStack *unit, ProjectConfig *config, while (g_hash_table_iter_next(&iter, (gpointer) &key, (gpointer) &val)) { build_target(unit, val); } - } else { - // build all targets given in the arguments - for (int i = 0; i < argc; i++) { - char *target_name = argv[i]; + return; + } + + // build all targets given in the arguments + GArray* targets = get_non_options_after("build"); + + if (targets != NULL) { + for (guint i = 0; i < targets->len; i++) { + const char *target_name = (((Option*) targets->data) + i)->string; + if (g_hash_table_contains(config->targets, target_name)) { build_target(unit, g_hash_table_lookup(config->targets, target_name)); + } else { + print_message(Error, "Unknown target: %s", target_name); } } + + g_array_free(targets, FALSE); + } else { + print_message(Error, "No targets specified."); } } /** * @brief Build targets from project. Configuration is provided by command line arguments. * @param unit File storage - * @param argc Number of arguments - * @param argv Array of Arguments */ -static void build_project(ModuleFileStack *unit, int argc, char *argv[]) { - if (argc <= 0) { - print_message(Error, "No targets specified."); - return; - } - +static void build_project(ModuleFileStack *unit) { ProjectConfig *config = default_project_config(); int err = load_project_config(config); if (err == PROJECT_OK) { - build_project_targets(unit, config, argc, argv); + build_project_targets(unit, config); } delete_project_config(config); } -void run_compiler(int argc, char *argv[]) { - if (argc <= 0) { - INFO("no arguments provided"); - print_help(); - return; - } - +void run_compiler() { ModuleFileStack files = new_file_stack(); - if (strcmp(argv[0], "build") == 0) { - build_project(&files, argc - 1, &argv[1]); - } else if (strcmp(argv[0], "compile") == 0) { - compile_file(&files, argc - 1, &argv[1]); + if (is_option_set("build")) { + build_project(&files); + } else if (is_option_set("compile")) { + compile_file(&files); } else { print_message(Error, "Invalid mode of operation. Rerun with --help."); } diff --git a/src/compiler.h b/src/compiler.h index 943e47b..a82a38f 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -7,9 +7,7 @@ /** * @brief Run the gemstone compiler with the provided command arguments. - * @param argc - * @param argv */ -void run_compiler(int argc, char *argv[]); +void run_compiler(); #endif //GEMSTONE_COMPILER_H diff --git a/src/main.c b/src/main.c index a661dfb..72b397a 100644 --- a/src/main.c +++ b/src/main.c @@ -4,6 +4,7 @@ #include #include #include +#include #include /** @@ -16,8 +17,9 @@ void notify_exit(void) { DEBUG("Exiting gemstone..."); } * @brief Run compiler setup here * */ -void setup(void) { +void setup(int argc, char *argv[]) { // setup preample + parse_options(argc, argv); log_init(); DEBUG("starting gemstone..."); @@ -37,11 +39,16 @@ void setup(void) { } int main(int argc, char *argv[]) { - setup(); + if (argc <= 1) { + print_help(); + exit(1); + } + + setup(argc, argv); print_message(Info, "Running GSC version %s", GSC_VERSION); - run_compiler(argc - 1, &argv[1]); + run_compiler(); return 0; } diff --git a/src/sys/log.c b/src/sys/log.c index 0aeab5f..7dcf94e 100644 --- a/src/sys/log.c +++ b/src/sys/log.c @@ -4,14 +4,29 @@ #include #include #include +#include +#include static struct Logger_t { FILE** streams; size_t stream_count; } GlobalLogger; -void log_init(void) +int runtime_log_level = LOG_LEVEL_WARNING; + +void set_log_level(int level) { + runtime_log_level = level; +} + +void log_init() +{ + if (is_option_set("verbose")) { + set_log_level(LOG_LEVEL_INFORMATION); + } else if (is_option_set("debug")) { + set_log_level(LOG_LEVEL_DEBUG); + } + assert(LOG_DEFAULT_STREAM != NULL); log_register_stream(LOG_DEFAULT_STREAM); } @@ -30,7 +45,7 @@ void log_register_stream(FILE* restrict stream) if (GlobalLogger.streams == NULL) { PANIC("failed to allocate stream buffer"); - } + } } else { diff --git a/src/sys/log.h b/src/sys/log.h index 6b8991d..6db8fe5 100644 --- a/src/sys/log.h +++ b/src/sys/log.h @@ -53,12 +53,22 @@ will not print. #define INFO(format, ...) __LOG(LOG_STRING_INFORMATION, LOG_LEVEL_INFORMATION, format"\n", ##__VA_ARGS__) #define DEBUG(format, ...) __LOG(LOG_STRING_DEBUG, LOG_LEVEL_DEBUG, format"\n", ##__VA_ARGS__) +extern int runtime_log_level; + #define __LOG(level, priority, format, ...) \ do { \ if (LOG_LEVEL <= priority) \ - syslog_logf(level, __FILE_NAME__, __LINE__, __func__, format, ##__VA_ARGS__); \ + if (runtime_log_level <= priority) \ + syslog_logf(level, __FILE_NAME__, __LINE__, __func__, format, ##__VA_ARGS__); \ } while(0) +/** + * @brief Set the runtime log level. Must be one of: LOG_LEVEL_ERROR, LOG_LEVEL_WARNING, + * LOG_LEVEL_INFORMATION, LOG_LEVEL_DEBUG + * @param level the new log level + */ +void set_log_level(int level); + /** * @brief Log a message into all registered streams * diff --git a/tests/ast/CMakeLists.txt b/tests/ast/CMakeLists.txt index 88007e4..5bd19b6 100644 --- a/tests/ast/CMakeLists.txt +++ b/tests/ast/CMakeLists.txt @@ -10,6 +10,12 @@ find_package(PkgConfig REQUIRED) pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0) include_directories(PRIVATE ${GLIB_INCLUDE_DIRS}) +# ------------------------------------------------ # +# Setup TOML-C99 # +# ------------------------------------------------ # + +include_directories(${PROJECT_SOURCE_DIR}/dep/tomlc99) + # ------------------------------------------------------- # # CTEST 1 # test building the syntax tree @@ -19,6 +25,8 @@ add_executable(ast_build_tree ${PROJECT_SOURCE_DIR}/src/sys/log.c ${PROJECT_SOURCE_DIR}/src/io/files.c ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c build_tree.c) set_target_properties(ast_build_tree PROPERTIES @@ -38,6 +46,8 @@ add_executable(ast_print_node ${PROJECT_SOURCE_DIR}/src/sys/log.c ${PROJECT_SOURCE_DIR}/src/io/files.c ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c print_node.c) set_target_properties(ast_print_node PROPERTIES @@ -57,6 +67,8 @@ add_executable(ast_graphviz ${PROJECT_SOURCE_DIR}/src/sys/log.c ${PROJECT_SOURCE_DIR}/src/io/files.c ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/dep/tomlc99/toml.c print_graphviz.c) set_target_properties(ast_graphviz PROPERTIES diff --git a/tests/input_file/test_input_file.py b/tests/input_file/test_input_file.py index 3721dc0..550f3f4 100644 --- a/tests/input_file/test_input_file.py +++ b/tests/input_file/test_input_file.py @@ -12,7 +12,7 @@ def check_accept(): test_file_name = sys.argv[1] - p = subprocess.run(["./gsc", test_file_name], capture_output=True, text=True) + p = subprocess.run(["./gsc", "compile", test_file_name], capture_output=True, text=True) assert p.returncode == 0 @@ -22,7 +22,7 @@ def check_abort(): logging.basicConfig(level=logging.INFO) - p = subprocess.run("./gsc", capture_output=True, text=True) + p = subprocess.run(["./gsc", "compile"], capture_output=True, text=True) assert p.returncode == 1 diff --git a/tests/logging/CMakeLists.txt b/tests/logging/CMakeLists.txt index d5abada..2bfa385 100644 --- a/tests/logging/CMakeLists.txt +++ b/tests/logging/CMakeLists.txt @@ -8,6 +8,13 @@ include_directories(${PROJECT_SOURCE_DIR}/src) find_package(PkgConfig REQUIRED) pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +include_directories(PRIVATE ${GLIB_INCLUDE_DIRS}) + +# ------------------------------------------------ # +# Setup TOML-C99 # +# ------------------------------------------------ # + +include_directories(${PROJECT_SOURCE_DIR}/dep/tomlc99) # ------------------------------------------------------- # # CTEST 1 @@ -16,11 +23,15 @@ pkg_search_module(GLIB REQUIRED IMPORTED_TARGET glib-2.0) add_executable(logging_output ${PROJECT_SOURCE_DIR}/src/sys/log.c ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/src/io/files.c output.c) set_target_properties(logging_output PROPERTIES OUTPUT_NAME "output" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) +target_link_libraries(logging_output PkgConfig::GLIB) +target_link_libraries(logging_output tomlc99) add_test(NAME logging_output WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_output) @@ -32,11 +43,15 @@ add_test(NAME logging_output add_executable(logging_panic ${PROJECT_SOURCE_DIR}/src/sys/log.c ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/src/io/files.c panic.c) set_target_properties(logging_panic PROPERTIES OUTPUT_NAME "panic" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) +target_link_libraries(logging_panic PkgConfig::GLIB) +target_link_libraries(logging_panic tomlc99) add_test(NAME logging_panic WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_panic) @@ -48,11 +63,15 @@ add_test(NAME logging_panic add_executable(logging_streams ${PROJECT_SOURCE_DIR}/src/sys/log.c ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/src/io/files.c streams.c) set_target_properties(logging_streams PROPERTIES OUTPUT_NAME "stream" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) +target_link_libraries(logging_streams PkgConfig::GLIB) +target_link_libraries(logging_streams tomlc99) add_test(NAME logging_streams WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_stream) @@ -64,11 +83,15 @@ add_test(NAME logging_streams add_executable(logging_level ${PROJECT_SOURCE_DIR}/src/sys/log.c ${PROJECT_SOURCE_DIR}/src/sys/col.c + ${PROJECT_SOURCE_DIR}/src/cfg/opt.c + ${PROJECT_SOURCE_DIR}/src/io/files.c level.c) set_target_properties(logging_level PROPERTIES OUTPUT_NAME "level" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) +target_link_libraries(logging_level PkgConfig::GLIB) +target_link_libraries(logging_level tomlc99) add_test(NAME logging_level WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_level) diff --git a/tests/logging/level.c b/tests/logging/level.c index fd37cc7..ed89205 100644 --- a/tests/logging/level.c +++ b/tests/logging/level.c @@ -4,11 +4,14 @@ #include "sys/log.h" #include +#include #define LOG_LEVEL LOG_LEVEL_WARNING -int main(void) { +int main(int argc, char* argv[]) { + parse_options(argc, argv); log_init(); + set_log_level(LOG_LEVEL_DEBUG); col_init(); DEBUG("logging some debug..."); diff --git a/tests/logging/output.c b/tests/logging/output.c index dde7d90..a6cfdb2 100644 --- a/tests/logging/output.c +++ b/tests/logging/output.c @@ -4,9 +4,12 @@ #include "sys/log.h" #include +#include -int main(void) { +int main(int argc, char* argv[]) { + parse_options(argc, argv); log_init(); + set_log_level(LOG_LEVEL_DEBUG); col_init(); DEBUG("logging some debug..."); diff --git a/tests/logging/panic.c b/tests/logging/panic.c index ecd8815..f64f3a1 100644 --- a/tests/logging/panic.c +++ b/tests/logging/panic.c @@ -3,9 +3,12 @@ // #include "sys/log.h" +#include -int main(void) { +int main(int argc, char* argv[]) { + parse_options(argc, argv); log_init(); + set_log_level(LOG_LEVEL_DEBUG); // this should appear in stderr INFO("before exit"); diff --git a/tests/logging/streams.c b/tests/logging/streams.c index 97599da..5dd7cc2 100644 --- a/tests/logging/streams.c +++ b/tests/logging/streams.c @@ -4,6 +4,7 @@ #include "sys/log.h" #include +#include static FILE* file; @@ -13,8 +14,10 @@ void close_file(void) { } } -int main(void) { +int main(int argc, char* argv[]) { + parse_options(argc, argv); log_init(); + set_log_level(LOG_LEVEL_DEBUG); // this should appear in stderr INFO("should only be in stderr"); From f16c10d94bcaadc329cd01aef20ab8176c834d06 Mon Sep 17 00:00:00 2001 From: servostar Date: Mon, 3 Jun 2024 00:31:22 +0200 Subject: [PATCH 19/27] fixed not passing sdk check --- src/compiler.c | 2 +- src/io/files.c | 2 +- src/io/files.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler.c b/src/compiler.c index 79e9b20..69e4e02 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -110,7 +110,7 @@ static void print_ast_to_file(AST_NODE_PTR ast, TargetConfig *target) { return; // create file path to write graphviz to - const char *path = make_file_path(1, target->name, ".gv", target->archive_directory); + const char *path = make_file_path(target->name, ".gv", 1, target->archive_directory); FILE *output = fopen((const char *) path, "w"); if (output == NULL) { diff --git a/src/io/files.c b/src/io/files.c index 86cdfdb..0ea8993 100644 --- a/src/io/files.c +++ b/src/io/files.c @@ -326,7 +326,7 @@ const char *get_absolute_path(const char *path) { return strdup(absolute_path); } -const char* make_file_path(int count, const char* name, const char* ext, ...) { +const char* make_file_path(const char* name, const char* ext, int count, ...) { DEBUG("building file path..."); va_list args; diff --git a/src/io/files.h b/src/io/files.h index 7b8b166..61c7ebb 100644 --- a/src/io/files.h +++ b/src/io/files.h @@ -151,6 +151,6 @@ const char* get_absolute_path(const char* path); * @return A relative path of a file */ [[nodiscard("pointer must be freed")]] -const char* make_file_path(int count, const char* name, const char* ext, ...); +const char* make_file_path(const char* name, const char* ext, int count, ...); #endif //GEMSTONE_FILES_H From e25fd4b559042cd2c45cf87c5217998b6705897a Mon Sep 17 00:00:00 2001 From: servostar Date: Mon, 3 Jun 2024 10:14:25 +0200 Subject: [PATCH 20/27] added missing --help --- src/main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main.c b/src/main.c index 72b397a..948ce04 100644 --- a/src/main.c +++ b/src/main.c @@ -46,6 +46,11 @@ int main(int argc, char *argv[]) { setup(argc, argv); + if (is_option_set("help")) { + print_help(); + exit(0); + } + print_message(Info, "Running GSC version %s", GSC_VERSION); run_compiler(); From 22ed50f441dec1dc904f24178f44f17a670e56d2 Mon Sep 17 00:00:00 2001 From: servostar Date: Mon, 3 Jun 2024 10:18:33 +0200 Subject: [PATCH 21/27] made error to info for not finding project file --- src/cfg/opt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index b19e506..016072a 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -305,7 +305,7 @@ int load_project_config(ProjectConfig *config) { FILE *config_file = fopen(PROJECT_CONFIG_FILE, "r"); if (config_file == NULL) { print_message(Error, "Cannot open file %s: %s", PROJECT_CONFIG_FILE, strerror(errno)); - ERROR("project file not found"); + INFO("project file not found"); return PROJECT_TOML_ERR; } From 4404cea01925fe4744dc272868b7425b23ee1788 Mon Sep 17 00:00:00 2001 From: servostar Date: Mon, 3 Jun 2024 11:08:25 +0200 Subject: [PATCH 22/27] fixed option handling --- src/cfg/opt.c | 56 +++++++++++++++++++++++++++++++++++---------------- src/main.c | 5 ++++- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/src/cfg/opt.c b/src/cfg/opt.c index 016072a..ad86285 100644 --- a/src/cfg/opt.c +++ b/src/cfg/opt.c @@ -17,6 +17,7 @@ static void clean(void) { while (g_hash_table_iter_next(&iter, &key, &value)) { free(value); + free(key); } g_hash_table_destroy(args); @@ -30,13 +31,14 @@ void parse_options(int argc, char* argv[]) { for (int i = 0; i < argc; i++) { Option* option = malloc(sizeof(Option)); option->is_opt = g_str_has_prefix(argv[i], "--"); - option->string = argv[i] + (option->is_opt ? 2 : 0); + option->string = strdup(argv[i] + (option->is_opt ? 2 : 0)); option->index = i; option->value = NULL; - char* equals = strchr(argv[i], '='); + char* equals = strchr(option->string, '='); if (equals != NULL) { - option->value = equals; + option->value = equals + 1; + *equals = 0; } g_hash_table_insert(args, (gpointer) option->string, (gpointer) option); @@ -110,11 +112,17 @@ TargetConfig* default_target_config_from_args() { if (is_option_set("print-ast")) { config->print_ast = true; - } else if (is_option_set("print-asm")) { + } + + if (is_option_set("print-asm")) { config->print_asm = true; - } else if (is_option_set("print-ir")) { + } + + if (is_option_set("print-ir")) { config->print_ir = true; - } else if (is_option_set("mode")) { + } + + if (is_option_set("mode")) { const Option* opt = get_option("mode"); if (opt->value != NULL) { @@ -128,6 +136,14 @@ TargetConfig* default_target_config_from_args() { } } + if (is_option_set("output")) { + const Option* opt = get_option("output"); + + if (opt->value != NULL) { + config->name = strdup(opt->value); + } + } + GArray* files = get_non_options_after("compile"); if (files == NULL) { @@ -139,6 +155,8 @@ TargetConfig* default_target_config_from_args() { } config->root_module = strdup( ((char**) files->data) [0]); + + g_array_free(files, TRUE); } return config; @@ -148,17 +166,21 @@ void print_help(void) { DEBUG("printing help dialog..."); const char *lines[] = { - "Gemstone Compiler (C) GPL-2.0", - "Build a project target: gsc build [target]|all", - "Compile non-project file: gsc compile [file]", - "Options:", - " --version print the version", - " --print-ast print resulting abstract syntax tree to a file", - " --print-asm print resulting assembly language to a file", - " --print-ir print resulting LLVM-IR to a file", - " --mode=[app|lib] set the compilation mode to either application or library", - " --verbose print logs with level information or higher", - " --debug print debug logs (if not disabled at compile time)" + "Gemstone Compiler (c) GPL-2.0", + "Build a project target: gsc build [target]|all", + "Compile non-project file: gsc compile [file]", + "Output information: gsc