From 4f5cf6408e81f3405f081730b80cd623251daa34 Mon Sep 17 00:00:00 2001 From: servostar Date: Tue, 23 Apr 2024 15:58:07 +0200 Subject: [PATCH 01/31] added blueprint for AST library --- src/ast/ast.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/ast/ast.h | 35 ++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 src/ast/ast.c create mode 100644 src/ast/ast.h diff --git a/src/ast/ast.c b/src/ast/ast.c new file mode 100644 index 0000000..3a1a8fc --- /dev/null +++ b/src/ast/ast.c @@ -0,0 +1,59 @@ + +#include +#include +#include +#include + +struct AST_Node_t *AST_new_node(void) { + struct AST_Node_t *node = malloc(sizeof(struct AST_Node_t)); + + if (node == NULL) { + PANIC("failed to allocate AST node"); + } + + // init to discrete state + node->parent = NULL; + node->children = NULL; + node->child_count = 0; + + return node; +} + +void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) { + // if there are no children for now + if (owner->child_count == 0) { + owner->children = malloc(sizeof(struct AST_Node_t *)); + + } else { + size_t size = sizeof(struct AST_Node_t *) * (owner->child_count + 1); + owner->children = realloc(owner->children, size); + } + + if (owner->children == NULL) { + PANIC("failed to allocate children array of AST node"); + } + + owner->children[owner->child_count++] = child; +} + +struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) { + if (owner == NULL) { + PANIC("AST owner node is NULL"); + } + + if (owner->children == NULL) { + PANIC("AST owner node has no children"); + } + + struct AST_Node_t *child = owner->children[idx]; + + if (child == NULL) { + PANIC("child node is NULL"); + } + + return child; +} + +void AST_delete_node(struct AST_Node_t *_) { +#warning "FIXME: not implemented" +} diff --git a/src/ast/ast.h b/src/ast/ast.h new file mode 100644 index 0000000..a222832 --- /dev/null +++ b/src/ast/ast.h @@ -0,0 +1,35 @@ + +#ifndef _AST_H_ +#define _AST_H_ + +#include + +struct AST_Node_t { + // parent node that owns this node + struct AST_Node_t *parent; + + // number of child nodes ownd by this node + // length of children array + size_t child_count; + // variable amount of child nodes + struct AST_Node_t **children; +}; + +// create a new initialized (empty) node +struct AST_Node_t *AST_new_node(void); + +void AST_delete_node(struct AST_Node_t *); + +// add a new child node +void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child); + +// get a specific child node +struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx); + +// visit this and all of its child nodes calling the given function +// for every node +void AST_visit_nodes_recurse(struct AST_Node_t *root, + void (*for_each)(struct AST_Node_t *node, + size_t depth)); + +#endif From e918139f88317a72d9320137f633ff967f7ea3ae Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 26 Apr 2024 15:37:17 +0200 Subject: [PATCH 02/31] added recurse --- src/ast/ast.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/ast/ast.c b/src/ast/ast.c index 3a1a8fc..db600ee 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -57,3 +57,20 @@ struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) { void AST_delete_node(struct AST_Node_t *_) { #warning "FIXME: not implemented" } + +static void __AST_visit_nodes_recurse2(struct AST_Node_t *root, + void (*for_each)(struct AST_Node_t *node, + size_t depth), + size_t depth) { + (for_each)(root, 0); + + for (size_t i = 0; i < root->child_count; i++) { + __AST_visit_nodes_recurse2(root->children[i], for_each, depth + 1); + } +} + +void AST_visit_nodes_recurse(struct AST_Node_t *root, + void (*for_each)(struct AST_Node_t *node, + size_t depth)) { + __AST_visit_nodes_recurse2(root, for_each, 0); +} From 914935aafb2a8732bb51f1fac6f47841b7643c89 Mon Sep 17 00:00:00 2001 From: servostar Date: Tue, 30 Apr 2024 14:01:00 +0200 Subject: [PATCH 03/31] added function to convert AST to graphviz diagram --- src/ast/ast.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++--- src/ast/ast.h | 23 ++++++++++++++++- src/main.c | 24 ++++++++++++++++- 3 files changed, 113 insertions(+), 5 deletions(-) diff --git a/src/ast/ast.c b/src/ast/ast.c index db600ee..2397f34 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -1,10 +1,11 @@ #include +#include #include #include #include -struct AST_Node_t *AST_new_node(void) { +struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value) { struct AST_Node_t *node = malloc(sizeof(struct AST_Node_t)); if (node == NULL) { @@ -15,10 +16,36 @@ struct AST_Node_t *AST_new_node(void) { node->parent = NULL; node->children = NULL; node->child_count = 0; + node->kind = kind; + node->value = value; return node; } +const char* AST_node_to_string(struct AST_Node_t* node) { + const char* string = "unknown"; + + switch (node->kind) { + case AST_Expression: + string = "expression"; + break; + case AST_Statement: + string = "statement"; + break; + case AST_Branch: + string = "if"; + break; + case AST_IntegerLiteral: + string = node->value; + break; + case AST_OperatorAdd: + string = "+"; + break; + } + + return string; +} + void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) { // if there are no children for now if (owner->child_count == 0) { @@ -54,8 +81,16 @@ struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) { return child; } -void AST_delete_node(struct AST_Node_t *_) { -#warning "FIXME: not implemented" +void AST_delete_node(struct AST_Node_t *node) { + if (node == NULL) { + PANIC("Node to free is NULL"); + } + + if (node->children != NULL) { + for (size_t i = 0; i < node->child_count; i++) { + AST_delete_node(node->children[i]); + } + } } static void __AST_visit_nodes_recurse2(struct AST_Node_t *root, @@ -74,3 +109,33 @@ void AST_visit_nodes_recurse(struct AST_Node_t *root, size_t depth)) { __AST_visit_nodes_recurse2(root, for_each, 0); } + +void AST_fprint_graphviz_node_definition(FILE* stream, struct AST_Node_t* node) { + + fprintf(stream, "\tnode%p [label=\"%s\"]\n", (void*) node, AST_node_to_string(node)); + + if (node->children != NULL) { + for (size_t i = 0; i < node->child_count; i++) { + AST_fprint_graphviz_node_definition(stream, node->children[i]); + } + } +} + +void AST_fprint_graphviz_node_connection(FILE* stream, struct AST_Node_t* node) { + + if (node->children != NULL) { + for (size_t i = 0; i < node->child_count; i++) { + fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) node->children[i]); + AST_fprint_graphviz_node_connection(stream, node->children[i]); + } + } +} + +void AST_fprint_graphviz(FILE* stream, struct AST_Node_t* root) { + fprintf(stream, "graph {\n"); + + AST_fprint_graphviz_node_definition(stream, root); + AST_fprint_graphviz_node_connection(stream, root); + + fprintf(stream, "}\n"); +} diff --git a/src/ast/ast.h b/src/ast/ast.h index a222832..e07ff19 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -4,10 +4,24 @@ #include +// Syntax elements which are stored in a syntax tree +enum AST_SyntaxElement_t { + AST_Statement, + AST_Expression, + AST_Branch, + AST_OperatorAdd, + AST_IntegerLiteral +}; + struct AST_Node_t { // parent node that owns this node struct AST_Node_t *parent; + // type of AST node: if, declration, ... + enum AST_SyntaxElement_t kind; + // optional value: integer literal, string literal, ... + const char* value; + // number of child nodes ownd by this node // length of children array size_t child_count; @@ -15,8 +29,12 @@ struct AST_Node_t { struct AST_Node_t **children; }; +// return a string representation of the nodes type and its value +// does not take into account its children or parent +const char* AST_node_to_string(struct AST_Node_t* node); + // create a new initialized (empty) node -struct AST_Node_t *AST_new_node(void); +struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value); void AST_delete_node(struct AST_Node_t *); @@ -32,4 +50,7 @@ void AST_visit_nodes_recurse(struct AST_Node_t *root, void (*for_each)(struct AST_Node_t *node, size_t depth)); +// print a graphviz diagram of the supplied node (as root node) into stream +void AST_fprint_graphviz(FILE* stream, struct AST_Node_t* node); + #endif diff --git a/src/main.c b/src/main.c index 8bb8127..b94149a 100644 --- a/src/main.c +++ b/src/main.c @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -36,6 +38,26 @@ void setup(void) int main(void) { setup(); - yyparse(); + struct AST_Node_t* node = AST_new_node(AST_Branch, NULL); + + struct AST_Node_t* child = AST_new_node(AST_OperatorAdd, NULL); + AST_push_node(child, AST_new_node(AST_IntegerLiteral, "43")); + AST_push_node(child, AST_new_node(AST_IntegerLiteral, "9")); + + AST_push_node(node, child); + AST_push_node(node, AST_new_node(AST_Expression, NULL)); + AST_push_node(node, AST_new_node(AST_Expression, NULL)); + + FILE* out = fopen("ast.gv", "w+"); + // convert this file ^^^^^^ + // to an svg with: `dot -Tsvg ast.gv > graph.svg` + + AST_fprint_graphviz(out, node); + + AST_delete_node(node); + + fflush(out); + fclose(out); + return 0; } From b386c1104388961fb168a38736a02f66c65adbfb Mon Sep 17 00:00:00 2001 From: servostar Date: Thu, 2 May 2024 08:26:18 +0200 Subject: [PATCH 04/31] added first test --- .gitignore | 3 ++- CMakeLists.txt | 15 ++++++++++++--- tests/CMakeLists.txt | 9 +++++++++ tests/logging/CMakeLists.txt | 9 +++++++++ tests/logging/check_output.py | 22 ++++++++++++++++++++++ tests/logging/main.c | 16 ++++++++++++++++ 6 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/logging/CMakeLists.txt create mode 100644 tests/logging/check_output.py create mode 100644 tests/logging/main.c diff --git a/.gitignore b/.gitignore index eee6202..bb676ce 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ Makefile lexer.ll.c parser.tab.c parser.tab.h -build \ No newline at end of file +build +/Testing/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 1cfeefc..079fc47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,15 @@ project(gemstone DESCRIPTION "programming language compiler" LANGUAGES C) +set(GEMSTONE_TEST_DIR ${PROJECT_SOURCE_DIR}/tests) +set(GEMSTONE_BINARY_DIR ${PROJECT_SOURCE_DIR}/bin) + +include(CTest) + +if(BUILD_TESTING) + add_subdirectory(tests) +endif() + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # ------------------------------------------------ # @@ -79,7 +88,7 @@ add_executable(release set_target_properties(release PROPERTIES OUTPUT_NAME "gsc" - RUNTIME_OUTPUT_DIRECTORY "bin/release") + RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/release) # FIXME: cannot compile with /O2 because of /RTC1 flag if (MSVC) @@ -111,7 +120,7 @@ add_executable(debug set_target_properties(debug PROPERTIES OUTPUT_NAME "gsc" - RUNTIME_OUTPUT_DIRECTORY "bin/debug") + RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/debug) if (MSVC) set(DEBUG_FLAGS /DEBUG) @@ -140,7 +149,7 @@ add_executable(check set_target_properties(check PROPERTIES OUTPUT_NAME "gsc" - RUNTIME_OUTPUT_DIRECTORY "bin/check") + RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/check) if (MSVC) set(CHECK_FLAGS /DEBUG /WX) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..2602430 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,9 @@ +include(CTest) + +set(PROJECT_BINARY_DIR bin) +set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/tests) +set(CTEST_BINARY_DIRECTORY ${PROJECT_BINARY_DIR}/tests) + +# Provide test to run here or include another CMakeLists.txt + +add_subdirectory(logging) \ No newline at end of file diff --git a/tests/logging/CMakeLists.txt b/tests/logging/CMakeLists.txt new file mode 100644 index 0000000..ebc9962 --- /dev/null +++ b/tests/logging/CMakeLists.txt @@ -0,0 +1,9 @@ +include(CTest) + +add_executable(logging ${PROJECT_SOURCE_DIR}/src/sys/log.c main.c) +target_include_directories(logging PUBLIC ${PROJECT_SOURCE_DIR}/src) +set_target_properties(logging + PROPERTIES + OUTPUT_NAME "output" + RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) +add_test(NAME logging WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/check_output.py) diff --git a/tests/logging/check_output.py b/tests/logging/check_output.py new file mode 100644 index 0000000..b908cda --- /dev/null +++ b/tests/logging/check_output.py @@ -0,0 +1,22 @@ +import subprocess + + +def run_logger_test(): + p = subprocess.run("bin/tests/logging/output", capture_output=True, text=True) + + # check exit code + if p.returncode != 0: + exit(p.returncode) + + output = p.stderr + + # check if logs appear in default log output (stderr) + + assert "logging some debug..." in output + assert "logging some info..." in output + assert "logging some warning..." in output + assert "logging some error..." in output + + +if __name__ == "__main__": + run_logger_test() diff --git a/tests/logging/main.c b/tests/logging/main.c new file mode 100644 index 0000000..7accfc5 --- /dev/null +++ b/tests/logging/main.c @@ -0,0 +1,16 @@ +// +// Created by servostar on 5/1/24. +// + +#include "sys/log.h" + +int main(void) { + log_init(); + + DEBUG("logging some debug..."); + INFO("logging some info..."); + WARN("logging some warning..."); + ERROR("logging some error..."); + + return 0; +} From 4b24e0645cd9da75c3e42b2b6d5a167fba1cbb84 Mon Sep 17 00:00:00 2001 From: servostar Date: Thu, 2 May 2024 09:49:14 +0200 Subject: [PATCH 05/31] added test for logger panic --- tests/logging/CMakeLists.txt | 24 +++++++++-- tests/logging/check_output.py | 22 ---------- tests/logging/{main.c => output.c} | 0 tests/logging/panic.c | 20 ++++++++++ tests/logging/test_logging.py | 64 ++++++++++++++++++++++++++++++ 5 files changed, 104 insertions(+), 26 deletions(-) delete mode 100644 tests/logging/check_output.py rename tests/logging/{main.c => output.c} (100%) create mode 100644 tests/logging/panic.c create mode 100644 tests/logging/test_logging.py diff --git a/tests/logging/CMakeLists.txt b/tests/logging/CMakeLists.txt index ebc9962..9c90431 100644 --- a/tests/logging/CMakeLists.txt +++ b/tests/logging/CMakeLists.txt @@ -1,9 +1,25 @@ include(CTest) -add_executable(logging ${PROJECT_SOURCE_DIR}/src/sys/log.c main.c) -target_include_directories(logging PUBLIC ${PROJECT_SOURCE_DIR}/src) -set_target_properties(logging +# ------------------------------------------------------- # +# CTEST 1 +# test the default output of the logger + +add_executable(logging_output ${PROJECT_SOURCE_DIR}/src/sys/log.c output.c) +target_include_directories(logging_output PUBLIC ${PROJECT_SOURCE_DIR}/src) +set_target_properties(logging_output PROPERTIES OUTPUT_NAME "output" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) -add_test(NAME logging WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/check_output.py) +add_test(NAME logging_output WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_output) + +# ------------------------------------------------------- # +# CTEST 1 +# test the panic functionality of the logger + +add_executable(logging_panic ${PROJECT_SOURCE_DIR}/src/sys/log.c panic.c) +target_include_directories(logging_panic PUBLIC ${PROJECT_SOURCE_DIR}/src) +set_target_properties(logging_panic + PROPERTIES + OUTPUT_NAME "panic" + RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) +add_test(NAME logging_panic WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_panic) diff --git a/tests/logging/check_output.py b/tests/logging/check_output.py deleted file mode 100644 index b908cda..0000000 --- a/tests/logging/check_output.py +++ /dev/null @@ -1,22 +0,0 @@ -import subprocess - - -def run_logger_test(): - p = subprocess.run("bin/tests/logging/output", capture_output=True, text=True) - - # check exit code - if p.returncode != 0: - exit(p.returncode) - - output = p.stderr - - # check if logs appear in default log output (stderr) - - assert "logging some debug..." in output - assert "logging some info..." in output - assert "logging some warning..." in output - assert "logging some error..." in output - - -if __name__ == "__main__": - run_logger_test() diff --git a/tests/logging/main.c b/tests/logging/output.c similarity index 100% rename from tests/logging/main.c rename to tests/logging/output.c diff --git a/tests/logging/panic.c b/tests/logging/panic.c new file mode 100644 index 0000000..ecd8815 --- /dev/null +++ b/tests/logging/panic.c @@ -0,0 +1,20 @@ +// +// Created by servostar on 5/2/24. +// + +#include "sys/log.h" + +int main(void) { + log_init(); + + // this should appear in stderr + INFO("before exit"); + + PANIC("oooops something happened"); + + // this should NOT appear in stderr + // ^^^ + ERROR("after exit"); + + return 0; +} diff --git a/tests/logging/test_logging.py b/tests/logging/test_logging.py new file mode 100644 index 0000000..da88b19 --- /dev/null +++ b/tests/logging/test_logging.py @@ -0,0 +1,64 @@ +import subprocess +import sys +import logging +from logging import info, error + +BIN_DIR = "bin/tests/logging/" + + +def run_logger_test(): + info("started check output...") + + p = subprocess.run(BIN_DIR + "output", capture_output=True, text=True) + + info("checking exit code...") + + # check exit code + assert p.returncode == 0 + + output = p.stderr + + # check if logs appear in default log output (stderr) + info("checking stderr...") + + assert "logging some debug..." in output + assert "logging some info..." in output + assert "logging some warning..." in output + assert "logging some error..." in output + + +def run_check_panic(): + info("started check panic...") + + p = subprocess.run(BIN_DIR + "panic", capture_output=True, text=True) + + info("checking exit code...") + + # check exit code + assert p.returncode == 1 + + output = p.stderr + + # check if logs appear (not) in default log output (stderr) + info("checking stderr...") + + assert "before exit" in output + assert "oooops something happened" in output + assert "after exit" not in output + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + + target = sys.argv[1] + + info(f"starting logging test suite with target: {target}") + + match target: + case "check_output": + run_logger_test() + case "check_panic": + run_check_panic() + case _: + error(f"unknown target: {target}") + exit(1) From 7bfe49c8eda7c12b49a63132f77748bbc3651536 Mon Sep 17 00:00:00 2001 From: servostar Date: Thu, 2 May 2024 09:49:50 +0200 Subject: [PATCH 06/31] renamed python test function for logger output --- tests/logging/test_logging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/logging/test_logging.py b/tests/logging/test_logging.py index da88b19..54f5d75 100644 --- a/tests/logging/test_logging.py +++ b/tests/logging/test_logging.py @@ -6,7 +6,7 @@ from logging import info, error BIN_DIR = "bin/tests/logging/" -def run_logger_test(): +def run_check_output(): info("started check output...") p = subprocess.run(BIN_DIR + "output", capture_output=True, text=True) From dc240acb9dc84cee1f290ae3af3597f2c6c5c919 Mon Sep 17 00:00:00 2001 From: servostar Date: Thu, 2 May 2024 09:59:42 +0200 Subject: [PATCH 07/31] added section about writing tests to README --- README.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/README.md b/README.md index a1e442b..faca249 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,28 @@ Requires: - bison - flex +## Writing Tests + +Since the project is build and configured through CMake it makes sense to rely for tests +on CTest. All tests are located in the subfolder `tests`. In this directory is a CMakeLists.txt which specifies which tests +are to be run. Actual tests are located in folders within tests and contain a final CMakeLists.txt which specifies what to run +for a single test. + +``` +tests + └─ test_group1 + └─ CMakeLists.txt # specify tests in this group + └─ ... # test files of group 1 + + └─ test_group2 + └─ CMakeLists.txt # specify tests in this group + └─ ... # test files of group 2 + + └─ CMakeLists.txt # specify test groups to run + +CMakeLists.txt # build configuration +``` + ## Development with VSCode/Codium Recommended extensions for getting a decent experience are the following: From 138937af57af8ca4d26e002d2881ed29e73e3e96 Mon Sep 17 00:00:00 2001 From: servostar Date: Thu, 2 May 2024 10:04:00 +0200 Subject: [PATCH 08/31] fixed output test and reformatted CMake --- tests/logging/CMakeLists.txt | 20 ++++++++++++++------ tests/logging/test_logging.py | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/tests/logging/CMakeLists.txt b/tests/logging/CMakeLists.txt index 9c90431..0db2412 100644 --- a/tests/logging/CMakeLists.txt +++ b/tests/logging/CMakeLists.txt @@ -1,25 +1,33 @@ include(CTest) +include_directories(${PROJECT_SOURCE_DIR}/src) + # ------------------------------------------------------- # # CTEST 1 # test the default output of the logger -add_executable(logging_output ${PROJECT_SOURCE_DIR}/src/sys/log.c output.c) -target_include_directories(logging_output PUBLIC ${PROJECT_SOURCE_DIR}/src) +add_executable(logging_output + ${PROJECT_SOURCE_DIR}/src/sys/log.c + output.c) set_target_properties(logging_output PROPERTIES OUTPUT_NAME "output" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) -add_test(NAME logging_output WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_output) +add_test(NAME logging_output + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_output) # ------------------------------------------------------- # # CTEST 1 # test the panic functionality of the logger -add_executable(logging_panic ${PROJECT_SOURCE_DIR}/src/sys/log.c panic.c) -target_include_directories(logging_panic PUBLIC ${PROJECT_SOURCE_DIR}/src) +add_executable(logging_panic + ${PROJECT_SOURCE_DIR}/src/sys/log.c + panic.c) set_target_properties(logging_panic PROPERTIES OUTPUT_NAME "panic" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) -add_test(NAME logging_panic WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_panic) +add_test(NAME logging_panic + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_panic) diff --git a/tests/logging/test_logging.py b/tests/logging/test_logging.py index 54f5d75..86816fd 100644 --- a/tests/logging/test_logging.py +++ b/tests/logging/test_logging.py @@ -56,7 +56,7 @@ if __name__ == "__main__": match target: case "check_output": - run_logger_test() + run_check_output() case "check_panic": run_check_panic() case _: From ebf526d6dac1b37171cda151cd2d8ac7e75d2dba Mon Sep 17 00:00:00 2001 From: servostar Date: Thu, 2 May 2024 10:32:27 +0200 Subject: [PATCH 09/31] added test for multiple output logger streams --- tests/logging/CMakeLists.txt | 17 +++++++++++++++- tests/logging/streams.c | 33 +++++++++++++++++++++++++++++++ tests/logging/test_logging.py | 37 +++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 tests/logging/streams.c diff --git a/tests/logging/CMakeLists.txt b/tests/logging/CMakeLists.txt index 0db2412..dee6ddf 100644 --- a/tests/logging/CMakeLists.txt +++ b/tests/logging/CMakeLists.txt @@ -18,7 +18,7 @@ add_test(NAME logging_output COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_output) # ------------------------------------------------------- # -# CTEST 1 +# CTEST 2 # test the panic functionality of the logger add_executable(logging_panic @@ -31,3 +31,18 @@ set_target_properties(logging_panic add_test(NAME logging_panic WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_panic) + +# ------------------------------------------------------- # +# CTEST 3 +# test the ability to write to multiple output streams + +add_executable(logging_streams + ${PROJECT_SOURCE_DIR}/src/sys/log.c + streams.c) +set_target_properties(logging_streams + PROPERTIES + OUTPUT_NAME "stream" + RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) +add_test(NAME logging_streams + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_stream) diff --git a/tests/logging/streams.c b/tests/logging/streams.c new file mode 100644 index 0000000..97599da --- /dev/null +++ b/tests/logging/streams.c @@ -0,0 +1,33 @@ +// +// Created by servostar on 5/2/24. +// + +#include "sys/log.h" +#include + +static FILE* file; + +void close_file(void) { + if (file != NULL) { + fclose(file); + } +} + +int main(void) { + log_init(); + + // this should appear in stderr + INFO("should only be in stderr"); + + file = fopen("tmp/test.log", "w"); + if (file == NULL) { + PANIC("could not open file"); + } + atexit(close_file); + + log_register_stream(file); + + INFO("should be in both"); + + return 0; +} diff --git a/tests/logging/test_logging.py b/tests/logging/test_logging.py index 86816fd..0d32d48 100644 --- a/tests/logging/test_logging.py +++ b/tests/logging/test_logging.py @@ -2,6 +2,7 @@ import subprocess import sys import logging from logging import info, error +import os BIN_DIR = "bin/tests/logging/" @@ -47,6 +48,40 @@ def run_check_panic(): assert "after exit" not in output +def run_check_stream(): + info("started check panic...") + + info("creating temporary folder...") + + if not os.path.exists("tmp"): + os.mkdir("tmp") + + info("cleaning temporary folder...") + + if os.path.exists("tmp/test.log"): + os.remove("tmp/test.log") + + info("launching test binary...") + + p = subprocess.run(BIN_DIR + "stream", capture_output=True, text=True) + + info("checking exit code...") + + # check exit code + assert p.returncode == 0 + + with open("tmp/test.log", "r") as file: + assert "should be in both" in "".join(file.readlines()) + + output = p.stderr + + # check if logs appear (not) in default log output (stderr) + info("checking stderr...") + + assert "should only be in stderr" in output + assert "should be in both" in output + + if __name__ == "__main__": logging.basicConfig(level=logging.INFO) @@ -59,6 +94,8 @@ if __name__ == "__main__": run_check_output() case "check_panic": run_check_panic() + case "check_stream": + run_check_stream() case _: error(f"unknown target: {target}") exit(1) From c099a60ecf649cd76da74a50b5d83297f9918fde Mon Sep 17 00:00:00 2001 From: servostar Date: Thu, 2 May 2024 10:50:24 +0200 Subject: [PATCH 10/31] added test for compile time log level filter --- tests/logging/CMakeLists.txt | 15 +++++++++++++++ tests/logging/level.c | 18 ++++++++++++++++++ tests/logging/test_logging.py | 23 +++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 tests/logging/level.c diff --git a/tests/logging/CMakeLists.txt b/tests/logging/CMakeLists.txt index dee6ddf..0f14079 100644 --- a/tests/logging/CMakeLists.txt +++ b/tests/logging/CMakeLists.txt @@ -46,3 +46,18 @@ set_target_properties(logging_streams add_test(NAME logging_streams WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/logging/test_logging.py check_stream) + +# ------------------------------------------------------- # +# CTEST 4 +# test compile time log level switch + +add_executable(logging_level + ${PROJECT_SOURCE_DIR}/src/sys/log.c + level.c) +set_target_properties(logging_level + PROPERTIES + OUTPUT_NAME "level" + RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/logging) +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 new file mode 100644 index 0000000..6a4f055 --- /dev/null +++ b/tests/logging/level.c @@ -0,0 +1,18 @@ +// +// Created by servostar on 5/2/24. +// + +#include "sys/log.h" + +#define LOG_LEVEL LOG_LEVEL_WARNING + +int main(void) { + log_init(); + + DEBUG("logging some debug..."); + INFO("logging some info..."); + WARN("logging some warning..."); + ERROR("logging some error..."); + + return 0; +} diff --git a/tests/logging/test_logging.py b/tests/logging/test_logging.py index 0d32d48..246a9d5 100644 --- a/tests/logging/test_logging.py +++ b/tests/logging/test_logging.py @@ -28,6 +28,27 @@ def run_check_output(): assert "logging some error..." in output +def run_check_level(): + info("started check level...") + + p = subprocess.run(BIN_DIR + "level", capture_output=True, text=True) + + info("checking exit code...") + + # check exit code + assert p.returncode == 0 + + output = p.stderr + + # check if logs appear in default log output (stderr) + info("checking stderr...") + + assert "logging some debug..." not in output + assert "logging some info..." not in output + assert "logging some warning..." in output + assert "logging some error..." in output + + def run_check_panic(): info("started check panic...") @@ -96,6 +117,8 @@ if __name__ == "__main__": run_check_panic() case "check_stream": run_check_stream() + case "check_level": + run_check_level() case _: error(f"unknown target: {target}") exit(1) From 01cf8345a0ace0624fece387388971922653259a Mon Sep 17 00:00:00 2001 From: servostar Date: Thu, 2 May 2024 12:14:59 +0200 Subject: [PATCH 11/31] reverted change of #aa7c62f0 in devkit added script to run checks and tests in container --- .github/workflows/build-check-sdk.yaml | 4 +-- Dockerfile | 9 ++++-- run-check-test.sh | 40 ++++++++++++++++++++++++++ sdk/Dockerfile | 4 +-- 4 files changed, 50 insertions(+), 7 deletions(-) create mode 100644 run-check-test.sh diff --git a/.github/workflows/build-check-sdk.yaml b/.github/workflows/build-check-sdk.yaml index 5b8f2aa..10a4811 100644 --- a/.github/workflows/build-check-sdk.yaml +++ b/.github/workflows/build-check-sdk.yaml @@ -2,7 +2,7 @@ name: "Build check gemstone in SDK" run-name: SDK build check to ${{ inputs.deploy_target }} by @${{ github.actor }} on: [push, pull_request] env: - SDK: 0.2.1-alpine-3.19.1 + SDK: 0.2.2-alpine-3.19.1 jobs: build-check-sdk: runs-on: ubuntu-latest @@ -11,4 +11,4 @@ jobs: - name: Setup SDK run: docker pull servostar/gemstone:sdk-"$SDK" && docker build --tag gemstone:devkit-"$SDK" . - name: Compile - run: docker run gemstone:devkit-"$SDK" make check + run: docker run gemstone:devkit-"$SDK" sh run-check-test.sh diff --git a/Dockerfile b/Dockerfile index ce2db20..09e34b6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,12 @@ -FROM servostar/gemstone:sdk-0.2.1-alpine-3.19.1 +FROM servostar/gemstone:sdk-0.2.2-alpine-3.19.1 LABEL authors="servostar" -LABEL version="0.2.1" +LABEL version="0.2.2" LABEL description="docker image for setting up the build pipeline on SDK" LABEL website="https://github.com/Servostar/gemstone" -RUN git clone https://github.com/Servostar/gemstone.git /home/lorang +COPY --chown=lorang src /home/lorang/src +COPY --chown=lorang tests /home/lorang/tests +COPY --chown=lorang CMakeLists.txt /home/lorang/ +COPY --chown=lorang run-check-test.sh /home/lorang/ RUN cmake . diff --git a/run-check-test.sh b/run-check-test.sh new file mode 100644 index 0000000..a2a3ab4 --- /dev/null +++ b/run-check-test.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env sh + +# Author: Sven Vogel +# Created: 02.05.2024 +# Description: Builds the project and runs tests +# Returns 0 on success and 1 when something went wrong + +echo "+--------------------------------------+" +echo "| BUILDING all TARGETS |" +echo "+--------------------------------------+" + +make -B +if [ ! $? -eq 0 ]; then + echo "===> failed to build targets" + exit 1 +fi + +echo "+--------------------------------------+" +echo "| RUNNING CODE CHECK |" +echo "+--------------------------------------+" + +make check +if [ ! $? -eq 0 ]; then + echo "===> failed code check..." + exit 1 +fi + +echo "+--------------------------------------+" +echo "| RUNNING TESTS |" +echo "+--------------------------------------+" + +ctest -VV --output-on-failure --schedule-random -j 4 +if [ ! $? -eq 0 ]; then + echo "===> failed tests..." + exit 1 +fi + +echo "+--------------------------------------+" +echo "| COMPLETED CHECK + TESTS SUCCESSFULLY |" +echo "+--------------------------------------+" \ No newline at end of file diff --git a/sdk/Dockerfile b/sdk/Dockerfile index 4dbeb1f..1a6a677 100644 --- a/sdk/Dockerfile +++ b/sdk/Dockerfile @@ -1,11 +1,11 @@ FROM alpine:3.19.1 LABEL authors="servostar" -LABEL version="0.2.1" +LABEL version="0.2.2" LABEL description="base image for building the gemstone programming language compiler" LABEL website="https://github.com/Servostar/gemstone" # install dependencies -RUN apk add build-base gcc make cmake bison flex git +RUN apk add build-base gcc make cmake bison flex git python3 # create user for build RUN adduser --disabled-password lorang From f7a3faad2ea8e25f7bac95cebb34bc9ef1efb39c Mon Sep 17 00:00:00 2001 From: servostar Date: Sat, 4 May 2024 15:33:20 +0200 Subject: [PATCH 12/31] added test for accepting a file as input --- tests/CMakeLists.txt | 3 ++- tests/input_file/CMakeLists.txt | 9 ++++++++ tests/input_file/test.gem | 6 +++++ tests/input_file/test_input_file.py | 36 +++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 tests/input_file/CMakeLists.txt create mode 100644 tests/input_file/test.gem create mode 100644 tests/input_file/test_input_file.py diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2602430..ab8afdc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,4 +6,5 @@ set(CTEST_BINARY_DIRECTORY ${PROJECT_BINARY_DIR}/tests) # Provide test to run here or include another CMakeLists.txt -add_subdirectory(logging) \ No newline at end of file +add_subdirectory(logging) +add_subdirectory(input_file) \ No newline at end of file diff --git a/tests/input_file/CMakeLists.txt b/tests/input_file/CMakeLists.txt new file mode 100644 index 0000000..263e769 --- /dev/null +++ b/tests/input_file/CMakeLists.txt @@ -0,0 +1,9 @@ +include(CTest) + +# ------------------------------------------------------- # +# CTEST 1 +# test if the program accepts a file as input + +add_test(NAME input_file_check + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/bin/check + COMMAND python ${GEMSTONE_TEST_DIR}/input_file/test_input_file.py ${GEMSTONE_TEST_DIR}/input_file/test.gem) diff --git a/tests/input_file/test.gem b/tests/input_file/test.gem new file mode 100644 index 0000000..991288d --- /dev/null +++ b/tests/input_file/test.gem @@ -0,0 +1,6 @@ + +import "std.io" + +fun main { + print("Hello, World!!!") +} \ No newline at end of file diff --git a/tests/input_file/test_input_file.py b/tests/input_file/test_input_file.py new file mode 100644 index 0000000..3721dc0 --- /dev/null +++ b/tests/input_file/test_input_file.py @@ -0,0 +1,36 @@ +import os.path +import subprocess +import sys +import logging +from logging import info + + +def check_accept(): + info("testing handling of input file...") + + logging.basicConfig(level=logging.INFO) + + test_file_name = sys.argv[1] + + p = subprocess.run(["./gsc", test_file_name], capture_output=True, text=True) + + assert p.returncode == 0 + + +def check_abort(): + info("testing handling of missing input file...") + + logging.basicConfig(level=logging.INFO) + + p = subprocess.run("./gsc", capture_output=True, text=True) + + assert p.returncode == 1 + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + info("check if binary exists...") + assert os.path.exists("./gsc") + + check_accept() + check_abort() From c6d8651ab6129e44b26ffe7fc54c4dc28d8bfb3a Mon Sep 17 00:00:00 2001 From: servostar Date: Sun, 5 May 2024 16:31:55 +0200 Subject: [PATCH 13/31] added debug statements --- src/ast/ast.c | 64 +++++++++++++++++++++++++++++++++++---------------- src/main.c | 1 - 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/ast/ast.c b/src/ast/ast.c index 2397f34..3873e1c 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -1,11 +1,12 @@ #include -#include #include #include #include struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value) { + DEBUG("creating new AST node: %d \"%s\"", kind, value); + struct AST_Node_t *node = malloc(sizeof(struct AST_Node_t)); if (node == NULL) { @@ -23,6 +24,8 @@ struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value } const char* AST_node_to_string(struct AST_Node_t* node) { + DEBUG("converting AST node to string: %p", node); + const char* string = "unknown"; switch (node->kind) { @@ -47,12 +50,16 @@ const char* AST_node_to_string(struct AST_Node_t* node) { } void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) { + DEBUG("Adding new node %p to %p", child, owner); + // if there are no children for now if (owner->child_count == 0) { + DEBUG("Allocating new children array"); owner->children = malloc(sizeof(struct AST_Node_t *)); } else { - size_t size = sizeof(struct AST_Node_t *) * (owner->child_count + 1); + DEBUG("Rellocating old children array"); + const size_t size = sizeof(struct AST_Node_t *) * (owner->child_count + 1); owner->children = realloc(owner->children, size); } @@ -64,6 +71,8 @@ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) { } struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) { + DEBUG("retrvieng node %d from %p", idx, owner); + if (owner == NULL) { PANIC("AST owner node is NULL"); } @@ -82,14 +91,18 @@ struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) { } void AST_delete_node(struct AST_Node_t *node) { + DEBUG("Deleting AST node: %p", node); + if (node == NULL) { PANIC("Node to free is NULL"); } - if (node->children != NULL) { - for (size_t i = 0; i < node->child_count; i++) { - AST_delete_node(node->children[i]); - } + if (node->children == NULL) { + return; + } + + for (size_t i = 0; i < node->child_count; i++) { + AST_delete_node(node->children[i]); } } @@ -97,7 +110,9 @@ static void __AST_visit_nodes_recurse2(struct AST_Node_t *root, void (*for_each)(struct AST_Node_t *node, size_t depth), size_t depth) { - (for_each)(root, 0); + DEBUG("Recursive visit of %p at %d with %p", root, depth, for_each); + + (for_each)(root, depth); for (size_t i = 0; i < root->child_count; i++) { __AST_visit_nodes_recurse2(root->children[i], for_each, depth + 1); @@ -107,35 +122,44 @@ static void __AST_visit_nodes_recurse2(struct AST_Node_t *root, void AST_visit_nodes_recurse(struct AST_Node_t *root, void (*for_each)(struct AST_Node_t *node, size_t depth)) { + DEBUG("Starting recursive visit of %p with %p", root, for_each); __AST_visit_nodes_recurse2(root, for_each, 0); } -void AST_fprint_graphviz_node_definition(FILE* stream, struct AST_Node_t* node) { +static void __AST_fprint_graphviz_node_definition(FILE* stream, struct AST_Node_t* node) { + DEBUG("Printing graphviz definition of %p", node); fprintf(stream, "\tnode%p [label=\"%s\"]\n", (void*) node, AST_node_to_string(node)); - if (node->children != NULL) { - for (size_t i = 0; i < node->child_count; i++) { - AST_fprint_graphviz_node_definition(stream, node->children[i]); - } + if (node->children == NULL) { + return; + } + + for (size_t i = 0; i < node->child_count; i++) { + __AST_fprint_graphviz_node_definition(stream, node->children[i]); } } -void AST_fprint_graphviz_node_connection(FILE* stream, struct AST_Node_t* node) { +static void __AST_fprint_graphviz_node_connection(FILE* stream, struct AST_Node_t* node) { + DEBUG("Printing graphviz connection of %p", node); - if (node->children != NULL) { - for (size_t i = 0; i < node->child_count; i++) { - fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) node->children[i]); - AST_fprint_graphviz_node_connection(stream, node->children[i]); - } + if (node->children == NULL) { + return; + } + + for (size_t i = 0; i < node->child_count; i++) { + fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) node->children[i]); + __AST_fprint_graphviz_node_connection(stream, node->children[i]); } } void AST_fprint_graphviz(FILE* stream, struct AST_Node_t* root) { + DEBUG("Starting print of graphviz graph of %p", root); + fprintf(stream, "graph {\n"); - AST_fprint_graphviz_node_definition(stream, root); - AST_fprint_graphviz_node_connection(stream, root); + __AST_fprint_graphviz_node_definition(stream, root); + __AST_fprint_graphviz_node_connection(stream, root); fprintf(stream, "}\n"); } diff --git a/src/main.c b/src/main.c index b94149a..89189b0 100644 --- a/src/main.c +++ b/src/main.c @@ -2,7 +2,6 @@ #include #include #include -#include #define LOG_LEVEL LOG_LEVEL_DEBUG From 03855c5ae69169030aaa616d74ec6aff763cd40c Mon Sep 17 00:00:00 2001 From: servostar Date: Mon, 6 May 2024 10:32:05 +0200 Subject: [PATCH 14/31] added more AST elements --- src/ast/ast.h | 52 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/src/ast/ast.h b/src/ast/ast.h index e07ff19..28aa7bd 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -6,18 +6,58 @@ // Syntax elements which are stored in a syntax tree enum AST_SyntaxElement_t { - AST_Statement, - AST_Expression, - AST_Branch, - AST_OperatorAdd, - AST_IntegerLiteral + AST_Stmt, + AST_Expr, + // Literals + AST_Int, + AST_Float, + AST_String, + // Control flow + AST_While, + AST_If, + AST_IfElse, + AST_Else, + // Variable management + AST_Decl, + AST_Assign, + AST_Def, + AST_Ident, + // Arithmetic operators + AST_Add, + AST_Sub, + AST_Mul, + AST_Div, + // Bitwise operators + AST_BitAnd, + AST_BitOr, + AST_BitXor, + AST_BitNot, + // Boolean operators + AST_BoolAnd, + AST_BoolOr, + AST_BoolXor, + AST_BoolNot, + // Logical operators + AST_Eq, + AST_Greater, + AST_Less, + // Casts + AST_Typecast, // type cast + AST_Transmute, // reinterpret cast + AST_Call, // function call + AST_Macro, // builtin functions: lineno(), filename(), ... + // Defintions + AST_Typedef, + AST_Box, + AST_Fun, + AST_Import }; struct AST_Node_t { // parent node that owns this node struct AST_Node_t *parent; - // type of AST node: if, declration, ... + // type of AST node: if, declaration, ... enum AST_SyntaxElement_t kind; // optional value: integer literal, string literal, ... const char* value; From 86c74f66c2be00a08e78b7f67c71f0f1ab0a0190 Mon Sep 17 00:00:00 2001 From: servostar Date: Tue, 7 May 2024 13:04:22 +0200 Subject: [PATCH 15/31] added mote syntax elements --- src/ast/ast.c | 65 ++++++++++++++++++++++++++++++++++++++++----------- src/ast/ast.h | 12 ++++++++-- src/main.c | 48 ++++++++++++++----------------------- 3 files changed, 79 insertions(+), 46 deletions(-) diff --git a/src/ast/ast.c b/src/ast/ast.c index 3873e1c..6e4c106 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -23,27 +23,64 @@ struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value return node; } +static const char* lookup_table[AST_ELEMENT_COUNT] = { "__UNINIT__" }; + +void AST_init() { + DEBUG("initializing global syntax tree..."); + + INFO("filling lookup table..."); + + lookup_table[AST_Expr] = "expr"; + + lookup_table[AST_Add] = "+"; + lookup_table[AST_Sub] = "-"; + lookup_table[AST_Mul] = "*"; + lookup_table[AST_Div] = "/"; + + lookup_table[AST_BitAnd] = "&"; + lookup_table[AST_BitOr] = "|"; + lookup_table[AST_BitXor] = "^"; + lookup_table[AST_BitNot] = "!"; + + lookup_table[AST_Eq] = "=="; + lookup_table[AST_Less] = "<"; + lookup_table[AST_Greater] = ">"; + + lookup_table[AST_BoolAnd] = "&&"; + lookup_table[AST_BoolOr] = "||"; + lookup_table[AST_BoolXor] = "^^"; + lookup_table[AST_BoolNot] = "!!"; + + lookup_table[AST_While] = "while"; + lookup_table[AST_If] = "if"; + lookup_table[AST_IfElse] = "else if"; + lookup_table[AST_Else] = "else"; + + lookup_table[AST_Decl] = "decl"; + lookup_table[AST_Assign] = "assign"; + lookup_table[AST_Def] = "def"; + + lookup_table[AST_Typedef] = "typedef"; + lookup_table[AST_Box] = "box"; + lookup_table[AST_Fun] = "fun"; +} + const char* AST_node_to_string(struct AST_Node_t* node) { DEBUG("converting AST node to string: %p", node); const char* string = "unknown"; - switch (node->kind) { - case AST_Expression: - string = "expression"; - break; - case AST_Statement: - string = "statement"; - break; - case AST_Branch: - string = "if"; - break; - case AST_IntegerLiteral: + switch(node->kind) { + case AST_Int: + case AST_Float: + case AST_String: + case AST_Ident: + case AST_Macro: + case AST_Import: string = node->value; break; - case AST_OperatorAdd: - string = "+"; - break; + default: + string = lookup_table[node->kind]; } return string; diff --git a/src/ast/ast.h b/src/ast/ast.h index 28aa7bd..486a520 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -6,7 +6,7 @@ // Syntax elements which are stored in a syntax tree enum AST_SyntaxElement_t { - AST_Stmt, + AST_Stmt = 0, AST_Expr, // Literals AST_Int, @@ -50,7 +50,10 @@ enum AST_SyntaxElement_t { AST_Typedef, AST_Box, AST_Fun, - AST_Import + AST_Import, + // amount of variants + // in this enum + AST_ELEMENT_COUNT }; struct AST_Node_t { @@ -69,6 +72,11 @@ struct AST_Node_t { struct AST_Node_t **children; }; +typedef struct AST_Node_t* AST_NODE_PTR; + +// initialize the global AST state +void AST_init(void); + // return a string representation of the nodes type and its value // does not take into account its children or parent const char* AST_node_to_string(struct AST_Node_t* node); diff --git a/src/main.c b/src/main.c index 16b8d35..1689ff0 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,7 @@ -#include -#include #include #include +#include +#include #define LOG_LEVEL LOG_LEVEL_DEBUG @@ -9,7 +9,7 @@ extern FILE* yyin; /** * @brief Log a debug message to inform about beginning exit procedures - * + * */ void notify_exit(void) { @@ -18,7 +18,7 @@ void notify_exit(void) /** * @brief Closes File after compiling. - * + * */ void close_file(void) @@ -31,7 +31,7 @@ void close_file(void) /** * @brief Run compiler setup here - * + * */ void setup(void) { @@ -40,12 +40,13 @@ void setup(void) log_init(); DEBUG("starting gemstone..."); - #if LOG_LEVEL <= LOG_LEVEL_DEBUG +#if LOG_LEVEL <= LOG_LEVEL_DEBUG atexit(¬ify_exit); - #endif +#endif // actual setup - + AST_init(); + DEBUG("finished starting up gemstone..."); } @@ -53,39 +54,26 @@ int main(int argc, char *argv[]) { 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"); } - + // filename as first argument char *filename = argv[1]; FILE *file = fopen(filename, "r"); - struct AST_Node_t* node = AST_new_node(AST_Branch, NULL); + if (NULL == file) + { + PANIC("File couldn't be opened!"); + } + yyin = file; - struct AST_Node_t* child = AST_new_node(AST_OperatorAdd, NULL); - AST_push_node(child, AST_new_node(AST_IntegerLiteral, "43")); - AST_push_node(child, AST_new_node(AST_IntegerLiteral, "9")); - - AST_push_node(node, child); - AST_push_node(node, AST_new_node(AST_Expression, NULL)); - AST_push_node(node, AST_new_node(AST_Expression, NULL)); - - FILE* out = fopen("ast.gv", "w+"); - // convert this file ^^^^^^ - // to an svg with: `dot -Tsvg ast.gv > graph.svg` - - AST_fprint_graphviz(out, node); - - AST_delete_node(node); - - fflush(out); - fclose(out); + yyparse(); return 0; -} +} \ No newline at end of file From f32a16ea8e8f3b5f8c5367a3452d81ac1b00a03f Mon Sep 17 00:00:00 2001 From: servostar Date: Tue, 7 May 2024 15:22:52 +0200 Subject: [PATCH 16/31] added tests for ast --- tests/CMakeLists.txt | 3 ++- tests/ast/CMakeLists.txt | 35 ++++++++++++++++++++++++++++++ tests/ast/build_tree.c | 40 ++++++++++++++++++++++++++++++++++ tests/ast/gen_graph.c | 32 ++++++++++++++++++++++++++++ tests/ast/print_node.c | 19 +++++++++++++++++ tests/ast/test_ast.py | 46 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 tests/ast/CMakeLists.txt create mode 100644 tests/ast/build_tree.c create mode 100644 tests/ast/gen_graph.c create mode 100644 tests/ast/print_node.c create mode 100644 tests/ast/test_ast.py diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ab8afdc..b5c1111 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,4 +7,5 @@ set(CTEST_BINARY_DIRECTORY ${PROJECT_BINARY_DIR}/tests) # Provide test to run here or include another CMakeLists.txt add_subdirectory(logging) -add_subdirectory(input_file) \ No newline at end of file +add_subdirectory(input_file) +add_subdirectory(ast) \ No newline at end of file diff --git a/tests/ast/CMakeLists.txt b/tests/ast/CMakeLists.txt new file mode 100644 index 0000000..81e7b96 --- /dev/null +++ b/tests/ast/CMakeLists.txt @@ -0,0 +1,35 @@ +include(CTest) + +include_directories(${PROJECT_SOURCE_DIR}/src) + +# ------------------------------------------------------- # +# CTEST 1 +# test building the syntax tree + +add_executable(ast_build_tree + ${PROJECT_SOURCE_DIR}/src/ast/ast.c + ${PROJECT_SOURCE_DIR}/src/sys/log.c + build_tree.c) +set_target_properties(ast_build_tree + PROPERTIES + OUTPUT_NAME "build_tree" + RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast) +add_test(NAME ast_build_tree + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_build_tree) + +# ------------------------------------------------------- # +# CTEST 2 +# test node to string output + +add_executable(ast_print_node + ${PROJECT_SOURCE_DIR}/src/ast/ast.c + print_node.c) +set_target_properties(ast_build_tree + PROPERTIES + OUTPUT_NAME "build_tree" + RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast) +add_test(NAME ast_print_node + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_print_node) + diff --git a/tests/ast/build_tree.c b/tests/ast/build_tree.c new file mode 100644 index 0000000..dec7b85 --- /dev/null +++ b/tests/ast/build_tree.c @@ -0,0 +1,40 @@ +// +// Created by servostar on 5/7/24. +// + +#include +#include + +void generate_statement(const AST_NODE_PTR stmt) { + const AST_NODE_PTR add = AST_new_node(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(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); + + 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(stmt, branch); + + generate_statement(branch); +} + +int main(void) { + + const AST_NODE_PTR root = AST_new_node(AST_Stmt, NULL); + + generate_branch(root); + + AST_delete_node(root); + + return 0; +} diff --git a/tests/ast/gen_graph.c b/tests/ast/gen_graph.c new file mode 100644 index 0000000..186d571 --- /dev/null +++ b/tests/ast/gen_graph.c @@ -0,0 +1,32 @@ +// +// Created by servostar on 5/7/24. +// + +#include +#include + +int main(void) { + + struct AST_Node_t* node = AST_new_node(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")); + + 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)); + + FILE* out = fopen("ast.gv", "w+"); + // convert this file ^^^^^^ + // to an svg with: `dot -Tsvg ast.gv > graph.svg` + + AST_fprint_graphviz(out, node); + + AST_delete_node(node); + + fflush(out); + fclose(out); + + return 0; +} diff --git a/tests/ast/print_node.c b/tests/ast/print_node.c new file mode 100644 index 0000000..83d3d43 --- /dev/null +++ b/tests/ast/print_node.c @@ -0,0 +1,19 @@ +// +// Created by servostar on 5/7/24. +// + +#include + +int main(void) { + + const AST_NODE_PTR node = AST_new_node(0, "value"); + + for (size_t i = 0; i < AST_ELEMENT_COUNT; i++) { + // set kind + node->kind = i; + // print symbol + printf("%ld %s\n", i, AST_node_to_string(node)); + } + + AST_delete_node(node); +} diff --git a/tests/ast/test_ast.py b/tests/ast/test_ast.py new file mode 100644 index 0000000..111e9c3 --- /dev/null +++ b/tests/ast/test_ast.py @@ -0,0 +1,46 @@ +import subprocess +import sys +import logging +from logging import info, error +import os + +BIN_DIR = "bin/tests/ast/" + + +def run_check_build_tree(): + info("started check tree build...") + + p = subprocess.run(BIN_DIR + "build_tree", capture_output=True, text=True) + + info("checking exit code...") + + # check exit code + assert p.returncode == 0 + + +def run_check_print_node(): + info("started check node print...") + + p = subprocess.run(BIN_DIR + "print_node", capture_output=True, text=True) + + info("checking exit code...") + + # check exit code + assert p.returncode == 0 + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + + target = sys.argv[1] + + info(f"starting ast test suite with target: {target}") + + match target: + case "check_build_tree": + run_check_build_tree() + case "check_print_node": + run_check_print_node() + case _: + error(f"unknown target: {target}") + exit(1) From 565d50b639253cb833c6039eab36acd5700c8a6a Mon Sep 17 00:00:00 2001 From: servostar Date: Tue, 7 May 2024 15:24:42 +0200 Subject: [PATCH 17/31] added gc module --- src/ast/ast.c | 1 + src/gc/gc.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++ src/gc/gc.h | 24 ++++++++++ src/lex/lexer.l | 1 + src/main.c | 3 ++ src/sys/log.h | 2 +- 6 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 src/gc/gc.c create mode 100644 src/gc/gc.h diff --git a/src/ast/ast.c b/src/ast/ast.c index 6e4c106..a9c820b 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -3,6 +3,7 @@ #include #include #include +#include struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value) { DEBUG("creating new AST node: %d \"%s\"", kind, value); diff --git a/src/gc/gc.c b/src/gc/gc.c new file mode 100644 index 0000000..7648655 --- /dev/null +++ b/src/gc/gc.c @@ -0,0 +1,113 @@ +// +// Created by servostar on 5/7/24. +// + +#include +#include +#include + +#undef malloc +#undef free +#undef realloc + +#define GC_HEAP_PREALLOC_CNT 10 + +typedef struct GC_Heap_Statistics_t { + size_t bytes; +} GC_Heap_Statistics; + +static struct GC_Heap_t { + RAW_PTR* blocks; + size_t cap; + size_t len; + GC_Heap_Statistics statistics; +} GC_GLOBAL_HEAP; + +static void __GC_teardown(void) { + INFO("Used %ld bytes in total", GC_GLOBAL_HEAP.statistics.bytes); + + if (GC_GLOBAL_HEAP.blocks == NULL) { + return; + } + + for (size_t i = 0; i < GC_GLOBAL_HEAP.len; i++) { + free(GC_GLOBAL_HEAP.blocks[i]); + } + + free(GC_GLOBAL_HEAP.blocks); + + GC_GLOBAL_HEAP.blocks = NULL; +} + +static void __GC_check_init(void) { + if (GC_GLOBAL_HEAP.blocks == NULL) { + GC_GLOBAL_HEAP.cap = GC_HEAP_PREALLOC_CNT; + const size_t bytes = sizeof(RAW_PTR) * GC_GLOBAL_HEAP.cap; + GC_GLOBAL_HEAP.blocks = malloc(bytes); + GC_GLOBAL_HEAP.len = 0; + } +} + +void GC_init(void) { + atexit(__GC_teardown); +} + +static void __GC_check_overflow(void) { + if (GC_GLOBAL_HEAP.len >= GC_GLOBAL_HEAP.cap) { + GC_GLOBAL_HEAP.cap += GC_HEAP_PREALLOC_CNT; + const size_t bytes = sizeof(RAW_PTR) * GC_GLOBAL_HEAP.cap; + GC_GLOBAL_HEAP.blocks = realloc(GC_GLOBAL_HEAP.blocks, bytes); + } +} + +RAW_PTR GC_malloc(const size_t bytes) { + const RAW_PTR ptr = malloc(bytes); + + if (ptr == NULL) { + FATAL("unable to allocate memory"); + } + + __GC_check_init(); + __GC_check_overflow(); + + GC_GLOBAL_HEAP.blocks[GC_GLOBAL_HEAP.len++] = ptr; + + GC_GLOBAL_HEAP.statistics.bytes += bytes; + + return ptr; +} + +static void __GC_swap_ptr(RAW_PTR old, RAW_PTR new) { + for (size_t i = 0; i < GC_GLOBAL_HEAP.len; i++) { + if (GC_GLOBAL_HEAP.blocks[i] == old) { + GC_GLOBAL_HEAP.blocks[i] = new; + } + } +} + +RAW_PTR GC_realloc(RAW_PTR ptr, size_t bytes) { + const RAW_PTR new_ptr = (RAW_PTR) realloc(ptr, bytes); + + if (new_ptr == NULL) { + FATAL("unable to reallocate memory"); + } + + __GC_swap_ptr(ptr, new_ptr); + + return new_ptr; +} + +void GC_free(RAW_PTR ptr) { + DEBUG("freeing memory: %p", ptr); + + for (size_t i = 0; i < GC_GLOBAL_HEAP.len; i++) { + + if (ptr == GC_GLOBAL_HEAP.blocks[i]) { + free(GC_GLOBAL_HEAP.blocks[i]); + + GC_GLOBAL_HEAP.len--; + + memcpy(&GC_GLOBAL_HEAP.blocks[i], &GC_GLOBAL_HEAP.blocks[i + 1], GC_GLOBAL_HEAP.len - i); + } + } +} diff --git a/src/gc/gc.h b/src/gc/gc.h new file mode 100644 index 0000000..6fe17a3 --- /dev/null +++ b/src/gc/gc.h @@ -0,0 +1,24 @@ +// +// Created by servostar on 5/7/24. +// + +#ifndef GC_H +#define GC_H + +#include + +#define malloc GC_malloc +#define free GC_free +#define realloc GC_realloc + +typedef void* RAW_PTR; + +void GC_init(void); + +RAW_PTR GC_malloc(size_t bytes); + +RAW_PTR GC_realloc(RAW_PTR ptr, size_t bytes); + +void GC_free(RAW_PTR ptr); + +#endif //GC_H diff --git a/src/lex/lexer.l b/src/lex/lexer.l index 8823195..129af69 100644 --- a/src/lex/lexer.l +++ b/src/lex/lexer.l @@ -2,6 +2,7 @@ %{ #include #include + #include int yyLineNumber = 1; int yylex(); diff --git a/src/main.c b/src/main.c index 1689ff0..0b56d88 100644 --- a/src/main.c +++ b/src/main.c @@ -2,6 +2,7 @@ #include #include #include +#include #define LOG_LEVEL LOG_LEVEL_DEBUG @@ -47,6 +48,8 @@ void setup(void) // actual setup AST_init(); + GC_init(); + DEBUG("finished starting up gemstone..."); } diff --git a/src/sys/log.h b/src/sys/log.h index aa18c88..3b4f7ed 100644 --- a/src/sys/log.h +++ b/src/sys/log.h @@ -2,7 +2,7 @@ #define _SYS_ERR_H_ #include -#include +#include #define LOG_DEFAULT_STREAM stderr From 94efa65ed0c78ca48a70fd8cb16d4a14a21c43b4 Mon Sep 17 00:00:00 2001 From: servostar Date: Wed, 8 May 2024 13:49:06 +0200 Subject: [PATCH 18/31] added test for graphviz --- src/ast/ast.c | 6 ++++ src/ast/ast.h | 1 + tests/ast/CMakeLists.txt | 20 ++++++++++-- tests/ast/print_graphviz.c | 54 ++++++++++++++++++++++++++++++ tests/ast/print_node.c | 4 +++ tests/ast/test_ast.py | 67 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 tests/ast/print_graphviz.c diff --git a/src/ast/ast.c b/src/ast/ast.c index 6e4c106..93420cb 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -30,6 +30,7 @@ void AST_init() { INFO("filling lookup table..."); + lookup_table[AST_Stmt] = "stmt"; lookup_table[AST_Expr] = "expr"; lookup_table[AST_Add] = "+"; @@ -63,6 +64,10 @@ void AST_init() { lookup_table[AST_Typedef] = "typedef"; lookup_table[AST_Box] = "box"; lookup_table[AST_Fun] = "fun"; + + lookup_table[AST_Typecast] = "cast"; + lookup_table[AST_Transmute] = "as"; + lookup_table[AST_Condition] = "condition"; } const char* AST_node_to_string(struct AST_Node_t* node) { @@ -77,6 +82,7 @@ const char* AST_node_to_string(struct AST_Node_t* node) { case AST_Ident: case AST_Macro: case AST_Import: + case AST_Call: string = node->value; break; default: diff --git a/src/ast/ast.h b/src/ast/ast.h index 486a520..0bb9c6e 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -17,6 +17,7 @@ enum AST_SyntaxElement_t { AST_If, AST_IfElse, AST_Else, + AST_Condition, // Variable management AST_Decl, AST_Assign, diff --git a/tests/ast/CMakeLists.txt b/tests/ast/CMakeLists.txt index 81e7b96..455130b 100644 --- a/tests/ast/CMakeLists.txt +++ b/tests/ast/CMakeLists.txt @@ -24,12 +24,28 @@ 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 print_node.c) -set_target_properties(ast_build_tree +set_target_properties(ast_print_node PROPERTIES - OUTPUT_NAME "build_tree" + OUTPUT_NAME "print_node" RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast) add_test(NAME ast_print_node WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_print_node) +# ------------------------------------------------------- # +# CTEST 3 +# test graphviz output + +add_executable(ast_graphviz + ${PROJECT_SOURCE_DIR}/src/ast/ast.c + ${PROJECT_SOURCE_DIR}/src/sys/log.c + print_graphviz.c) +set_target_properties(ast_graphviz + PROPERTIES + OUTPUT_NAME "print_graphviz" + RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast) +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/print_graphviz.c b/tests/ast/print_graphviz.c new file mode 100644 index 0000000..6df64b4 --- /dev/null +++ b/tests/ast/print_graphviz.c @@ -0,0 +1,54 @@ +// +// Created by servostar on 5/8/24. +// + +#include +#include + +void generate_statement(const AST_NODE_PTR stmt) { + const AST_NODE_PTR add = AST_new_node(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(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); + + 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(stmt, branch); + + generate_statement(branch); +} + +int main(void) { + + AST_init(); + + const AST_NODE_PTR root = AST_new_node(AST_Stmt, NULL); + + generate_branch(root); + + FILE* output = fopen("tmp/graph.gv", "w"); + + if (output == NULL) { + PANIC("unable to open file"); + } + + AST_fprint_graphviz(output, root); + + fflush(output); + + fclose(output); + + AST_delete_node(root); + + return 0; +} diff --git a/tests/ast/print_node.c b/tests/ast/print_node.c index 83d3d43..38e8a41 100644 --- a/tests/ast/print_node.c +++ b/tests/ast/print_node.c @@ -6,6 +6,8 @@ int main(void) { + AST_init(); + const AST_NODE_PTR node = AST_new_node(0, "value"); for (size_t i = 0; i < AST_ELEMENT_COUNT; i++) { @@ -16,4 +18,6 @@ int main(void) { } AST_delete_node(node); + + return 0; } diff --git a/tests/ast/test_ast.py b/tests/ast/test_ast.py index 111e9c3..8acd654 100644 --- a/tests/ast/test_ast.py +++ b/tests/ast/test_ast.py @@ -28,6 +28,71 @@ def run_check_print_node(): # check exit code assert p.returncode == 0 + assert """0 stmt +1 expr +2 value +3 value +4 value +5 while +6 if +7 else if +8 else +9 condition +10 decl +11 assign +12 def +13 value +14 + +15 - +16 * +17 / +18 & +19 | +20 ^ +21 ! +22 && +23 || +24 ^^ +25 !! +26 == +27 > +28 < +29 cast +30 as +31 value +32 value +33 typedef +34 box +35 fun +36 value +""" == p.stdout + + +def run_check_print_graphviz(): + info("started check print graphviz...") + + p = subprocess.run(BIN_DIR + "print_graphviz", capture_output=True, text=True) + + info("checking exit code...") + + # check exit code + assert p.returncode == 0 + + info("converting gv to svg...") + + p = subprocess.run(["dot", "-Tsvg", "tmp/graph.gv", "-otmp/graph.svg"]) + + info("checking exit code...") + assert p.returncode == 0 + + info("checking svg output...") + with open("tmp/graph.svg", "r") as file: + string = "".join(file.readlines()) + assert "2.3" in string + assert "0.79" in string + assert "stmt" in string + assert "if" in string + if __name__ == "__main__": logging.basicConfig(level=logging.INFO) @@ -41,6 +106,8 @@ if __name__ == "__main__": run_check_build_tree() case "check_print_node": run_check_print_node() + case "check_print_graphviz": + run_check_print_graphviz() case _: error(f"unknown target: {target}") exit(1) From dcda1158f2922f6d028b10daba275a09b073fa41 Mon Sep 17 00:00:00 2001 From: servostar Date: Wed, 8 May 2024 13:55:40 +0200 Subject: [PATCH 19/31] added tmp folder handler --- tests/ast/test_ast.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/ast/test_ast.py b/tests/ast/test_ast.py index 8acd654..0f9b757 100644 --- a/tests/ast/test_ast.py +++ b/tests/ast/test_ast.py @@ -71,6 +71,19 @@ def run_check_print_node(): def run_check_print_graphviz(): info("started check print graphviz...") + info("creating temporary folder...") + + if not os.path.exists("tmp"): + os.mkdir("tmp") + + info("cleaning temporary folder...") + + if os.path.exists("tmp/graph.gv"): + os.remove("tmp/graph.gv") + + if os.path.exists("tmp/graph.svg"): + os.remove("tmp/graph.svg") + p = subprocess.run(BIN_DIR + "print_graphviz", capture_output=True, text=True) info("checking exit code...") From 4840975b26fc6f815c7ab58ef2acb031acb8f48f Mon Sep 17 00:00:00 2001 From: servostar Date: Wed, 8 May 2024 14:03:35 +0200 Subject: [PATCH 20/31] added graphviz to sdk 0.2.3 --- sdk/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/Dockerfile b/sdk/Dockerfile index 1a6a677..aae7438 100644 --- a/sdk/Dockerfile +++ b/sdk/Dockerfile @@ -1,11 +1,11 @@ FROM alpine:3.19.1 LABEL authors="servostar" -LABEL version="0.2.2" +LABEL version="0.2.3" LABEL description="base image for building the gemstone programming language compiler" LABEL website="https://github.com/Servostar/gemstone" # install dependencies -RUN apk add build-base gcc make cmake bison flex git python3 +RUN apk add build-base gcc make cmake bison flex git python3 graphviz # create user for build RUN adduser --disabled-password lorang From a5cd609f68debf4ab89bae656935225fb95d6393 Mon Sep 17 00:00:00 2001 From: servostar Date: Wed, 8 May 2024 14:06:12 +0200 Subject: [PATCH 21/31] bumped devkit 0.2.3 --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 09e34b6..a8c2f7f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ -FROM servostar/gemstone:sdk-0.2.2-alpine-3.19.1 +FROM servostar/gemstone:sdk-0.2.3-alpine-3.19.1 LABEL authors="servostar" -LABEL version="0.2.2" +LABEL version="0.2.3" LABEL description="docker image for setting up the build pipeline on SDK" LABEL website="https://github.com/Servostar/gemstone" From a3f5bc1fe6ca7c7741cfca496017fa330580994f Mon Sep 17 00:00:00 2001 From: servostar Date: Wed, 8 May 2024 14:11:39 +0200 Subject: [PATCH 22/31] bumped action sdk 0.2.3 --- .github/workflows/build-check-sdk.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-check-sdk.yaml b/.github/workflows/build-check-sdk.yaml index 10a4811..b17b734 100644 --- a/.github/workflows/build-check-sdk.yaml +++ b/.github/workflows/build-check-sdk.yaml @@ -2,7 +2,7 @@ name: "Build check gemstone in SDK" run-name: SDK build check to ${{ inputs.deploy_target }} by @${{ github.actor }} on: [push, pull_request] env: - SDK: 0.2.2-alpine-3.19.1 + SDK: 0.2.3-alpine-3.19.1 jobs: build-check-sdk: runs-on: ubuntu-latest From f05ebf6ac299f3017058bff7c0d12cd6f0d85787 Mon Sep 17 00:00:00 2001 From: Filleo Date: Wed, 8 May 2024 15:51:30 +0200 Subject: [PATCH 23/31] added Self token changed program rule added rules for box removed second assign added storagequalifier added typedefine --- src/lex/lexer.l | 1 + src/yacc/parser.y | 56 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/lex/lexer.l b/src/lex/lexer.l index 1d5b9c2..c9bf739 100644 --- a/src/lex/lexer.l +++ b/src/lex/lexer.l @@ -38,6 +38,7 @@ "int" {DEBUG("\"%s\" tokenized with \'KeyInt'", yytext); return(KeyInt);}; "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);}; "short" {DEBUG("\"%s\" tokenized with \'KeyShort\'", yytext); return(KeyShort);}; "long" {DEBUG("\"%s\" tokenized with \'KeyLong\'", yytext); return(KeyLong);}; diff --git a/src/yacc/parser.y b/src/yacc/parser.y index ddcae10..36c5931 100644 --- a/src/yacc/parser.y +++ b/src/yacc/parser.y @@ -13,6 +13,7 @@ %token KeyInt %token KeyFloat +%token KeySelf %token KeyAs %token ValInt %token Ident @@ -63,15 +64,26 @@ %left OpBitand OpBitor OpBitxor OpBitnot %% -program: statementlist - | fundef; +program: program programbody + | programbody; + +programbody: moduleimport + | fundef + | box + | definition + | decl + | typedef; + + expr: ValFloat | ValInt | ValMultistr | ValStr | Ident - | operation; + | operation + | boxaccess + | boxselfaccess; exprlist: expr ',' exprlist | expr; @@ -98,9 +110,26 @@ IOqualifyier: KeyIn paramdecl: type ':' Ident { DEBUG("Param-Declaration"); }; -funcall: Ident argumentlist { DEBUG("Function call"); }; +box: KeyType KeyBox ':' Ident '{' boxbody '}' { DEBUG("Box"); } + | KeyType KeyBox ':' Ident '{' '}'; -assign: Ident '=' expr { DEBUG("Assignment"); }; +boxbody: boxbody boxcontent + | boxcontent; + +boxcontent: decl { DEBUG("Box decl Content"); } + | definition { DEBUG("Box def Content"); } + | fundef { DEBUG("Box fun Content"); }; + +boxselfaccess: KeySelf '.' Ident + | KeySelf '.' boxaccess; + +boxaccess: Ident '.' Ident + | Ident '.' boxaccess; + +boxcall: boxaccess argumentlist + | boxselfaccess argumentlist; + +funcall: Ident argumentlist { DEBUG("Function call"); }; moduleimport: KeyImport ValStr { DEBUG("Module-Import"); }; @@ -112,7 +141,8 @@ statement: assign | definition | while | branch - | funcall; + | funcall + | boxcall; branchif: KeyIf expr '{' statementlist '}' { DEBUG("if"); }; branchelse: KeyElse '{' statementlist '}' { DEBUG("if-else"); }; @@ -129,15 +159,25 @@ while: KeyWhile expr '{' statementlist '}' { DEBUG("while"); }; identlist: Ident ',' identlist | Ident; -decl: type ':' identlist; +decl: type ':' identlist + | storagequalifier type ':' identlist + definition: decl '=' expr { DEBUG("Definition"); }; -assign: Ident '=' expr { DEBUG("Assignment"); }; +storagequalifier: KeyGlobal + | KeyStatic + | KeyLocal; + +assign: Ident '=' expr { DEBUG("Assignment"); } + | boxaccess '=' expr + | boxselfaccess '=' expr ; sign: KeySigned | KeyUnsigned; +typedef: KeyType type':' Ident; + scale: scale KeyShort | scale KeyHalf | scale KeyLong From 4322797eae10bdf3acc7cc578a8eadbdea35dbaf Mon Sep 17 00:00:00 2001 From: servostar Date: Wed, 8 May 2024 20:44:52 +0200 Subject: [PATCH 24/31] added detach function --- src/ast/ast.c | 81 +++++++++++++++++++++++++++++++++++++-------------- src/ast/ast.h | 3 ++ 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/src/ast/ast.c b/src/ast/ast.c index 93420cb..8adfe31 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -3,11 +3,12 @@ #include #include #include +#include -struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value) { +AST_NODE_PTR AST_new_node(enum AST_SyntaxElement_t kind, const char* value) { DEBUG("creating new AST node: %d \"%s\"", kind, value); - struct AST_Node_t *node = malloc(sizeof(struct AST_Node_t)); + AST_NODE_PTR node = malloc(sizeof(struct AST_Node_t)); if (node == NULL) { PANIC("failed to allocate AST node"); @@ -17,6 +18,7 @@ struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value node->parent = NULL; node->children = NULL; node->child_count = 0; + node->child_cap = 0; node->kind = kind; node->value = value; @@ -25,7 +27,7 @@ struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value static const char* lookup_table[AST_ELEMENT_COUNT] = { "__UNINIT__" }; -void AST_init() { +void AST_init(void) { DEBUG("initializing global syntax tree..."); INFO("filling lookup table..."); @@ -92,17 +94,24 @@ const char* AST_node_to_string(struct AST_Node_t* node) { return string; } -void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) { +#define PRE_ALLOCATION_CNT 10 + +void AST_push_node(AST_NODE_PTR owner, AST_NODE_PTR child) { DEBUG("Adding new node %p to %p", child, owner); + assert(PRE_ALLOCATION_CNT >= 1); + // if there are no children for now - if (owner->child_count == 0) { + if (owner->children == NULL) { + assert(owner->child_count == 0); DEBUG("Allocating new children array"); owner->children = malloc(sizeof(struct AST_Node_t *)); + owner->child_cap = 1; - } else { - DEBUG("Rellocating old children array"); - const size_t size = sizeof(struct AST_Node_t *) * (owner->child_count + 1); + } else if (owner->child_count >= owner->child_cap) { + DEBUG("Reallocating old children array"); + owner->child_cap += PRE_ALLOCATION_CNT; + const size_t size = sizeof(struct AST_Node_t *) * owner->child_cap; owner->children = realloc(owner->children, size); } @@ -113,8 +122,8 @@ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) { owner->children[owner->child_count++] = child; } -struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) { - DEBUG("retrvieng node %d from %p", idx, owner); +AST_NODE_PTR AST_get_node(AST_NODE_PTR owner, size_t idx) { + DEBUG("retrieving node %d from %p", idx, owner); if (owner == NULL) { PANIC("AST owner node is NULL"); @@ -124,7 +133,7 @@ struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) { PANIC("AST owner node has no children"); } - struct AST_Node_t *child = owner->children[idx]; + AST_NODE_PTR child = owner->children[idx]; if (child == NULL) { PANIC("child node is NULL"); @@ -133,20 +142,48 @@ struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) { return child; } -void AST_delete_node(struct AST_Node_t *node) { - DEBUG("Deleting AST node: %p", node); +void AST_delete_node(AST_NODE_PTR node) { + DEBUG("Deleting AST node: %p", node); - if (node == NULL) { - PANIC("Node to free is NULL"); - } + if (node == NULL) { + PANIC("Node to free is NULL"); + } - if (node->children == NULL) { - return; - } + if (node->parent != NULL) { + AST_detach_node(node->parent, node); + } - for (size_t i = 0; i < node->child_count; i++) { - AST_delete_node(node->children[i]); - } + if (node->children == NULL) { + return; + } + + for (size_t i = 0; i < node->child_count; i++) { + if (node->children[i] != node) { + AST_delete_node(node->children[i]); + } else { + WARN("Circular dependency in AST: parent -> child -> parent -> child -> ..."); + } + } + + free(node->children); + free(node); +} + +AST_NODE_PTR AST_detach_node(AST_NODE_PTR parent, AST_NODE_PTR child) { + assert(parent != NULL); + assert(child != NULL); + + for (size_t i = 0; i < parent->child_count; i++) { + if (child == parent->children[i]) { + memcpy(&parent->children[i], &parent->children[i + 1], parent->child_count - i - 1); + parent->child_count--; + return child; + } + } + + WARN("Node not a child of parent"); + + return child; } static void __AST_visit_nodes_recurse2(struct AST_Node_t *root, diff --git a/src/ast/ast.h b/src/ast/ast.h index 0bb9c6e..13437ea 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -69,6 +69,7 @@ struct AST_Node_t { // number of child nodes ownd by this node // length of children array size_t child_count; + size_t child_cap; // variable amount of child nodes struct AST_Node_t **children; }; @@ -90,6 +91,8 @@ void AST_delete_node(struct AST_Node_t *); // add a new child node void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child); +AST_NODE_PTR AST_detach_node(AST_NODE_PTR parent, AST_NODE_PTR child); + // get a specific child node struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx); From abbbe59b083d7c3deee8f0d2ebbf967ff6e81dd5 Mon Sep 17 00:00:00 2001 From: Filleo Date: Wed, 8 May 2024 21:56:33 +0200 Subject: [PATCH 25/31] added string.h in ast.c --- src/ast/ast.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ast/ast.c b/src/ast/ast.c index efd737b..7d2124e 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -1,6 +1,7 @@ #include #include +#include #include #include #include From eac3b23432e2ca3a95d590b40ccf481fe0f7c2cb Mon Sep 17 00:00:00 2001 From: servostar Date: Wed, 8 May 2024 22:06:54 +0200 Subject: [PATCH 26/31] Revert "added detach function" This reverts commit 4322797eae10bdf3acc7cc578a8eadbdea35dbaf. --- src/ast/ast.c | 82 ++++++++++++++------------------------------------- src/ast/ast.h | 3 -- 2 files changed, 22 insertions(+), 63 deletions(-) diff --git a/src/ast/ast.c b/src/ast/ast.c index 7d2124e..7bae47b 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -4,13 +4,11 @@ #include #include #include -#include -#include -AST_NODE_PTR AST_new_node(enum AST_SyntaxElement_t kind, const char* value) { +struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value) { DEBUG("creating new AST node: %d \"%s\"", kind, value); - AST_NODE_PTR node = malloc(sizeof(struct AST_Node_t)); + struct AST_Node_t *node = malloc(sizeof(struct AST_Node_t)); if (node == NULL) { PANIC("failed to allocate AST node"); @@ -20,7 +18,6 @@ AST_NODE_PTR AST_new_node(enum AST_SyntaxElement_t kind, const char* value) { node->parent = NULL; node->children = NULL; node->child_count = 0; - node->child_cap = 0; node->kind = kind; node->value = value; @@ -29,7 +26,7 @@ AST_NODE_PTR AST_new_node(enum AST_SyntaxElement_t kind, const char* value) { static const char* lookup_table[AST_ELEMENT_COUNT] = { "__UNINIT__" }; -void AST_init(void) { +void AST_init() { DEBUG("initializing global syntax tree..."); INFO("filling lookup table..."); @@ -96,24 +93,17 @@ const char* AST_node_to_string(struct AST_Node_t* node) { return string; } -#define PRE_ALLOCATION_CNT 10 - -void AST_push_node(AST_NODE_PTR owner, AST_NODE_PTR child) { +void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) { DEBUG("Adding new node %p to %p", child, owner); - assert(PRE_ALLOCATION_CNT >= 1); - // if there are no children for now - if (owner->children == NULL) { - assert(owner->child_count == 0); + if (owner->child_count == 0) { DEBUG("Allocating new children array"); owner->children = malloc(sizeof(struct AST_Node_t *)); - owner->child_cap = 1; - } else if (owner->child_count >= owner->child_cap) { - DEBUG("Reallocating old children array"); - owner->child_cap += PRE_ALLOCATION_CNT; - const size_t size = sizeof(struct AST_Node_t *) * owner->child_cap; + } else { + DEBUG("Rellocating old children array"); + const size_t size = sizeof(struct AST_Node_t *) * (owner->child_count + 1); owner->children = realloc(owner->children, size); } @@ -124,8 +114,8 @@ void AST_push_node(AST_NODE_PTR owner, AST_NODE_PTR child) { owner->children[owner->child_count++] = child; } -AST_NODE_PTR AST_get_node(AST_NODE_PTR owner, size_t idx) { - DEBUG("retrieving node %d from %p", idx, owner); +struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) { + DEBUG("retrvieng node %d from %p", idx, owner); if (owner == NULL) { PANIC("AST owner node is NULL"); @@ -135,7 +125,7 @@ AST_NODE_PTR AST_get_node(AST_NODE_PTR owner, size_t idx) { PANIC("AST owner node has no children"); } - AST_NODE_PTR child = owner->children[idx]; + struct AST_Node_t *child = owner->children[idx]; if (child == NULL) { PANIC("child node is NULL"); @@ -144,48 +134,20 @@ AST_NODE_PTR AST_get_node(AST_NODE_PTR owner, size_t idx) { return child; } -void AST_delete_node(AST_NODE_PTR node) { - DEBUG("Deleting AST node: %p", node); +void AST_delete_node(struct AST_Node_t *node) { + DEBUG("Deleting AST node: %p", node); - if (node == NULL) { - PANIC("Node to free is NULL"); - } + if (node == NULL) { + PANIC("Node to free is NULL"); + } - if (node->parent != NULL) { - AST_detach_node(node->parent, node); - } + if (node->children == NULL) { + return; + } - if (node->children == NULL) { - return; - } - - for (size_t i = 0; i < node->child_count; i++) { - if (node->children[i] != node) { - AST_delete_node(node->children[i]); - } else { - WARN("Circular dependency in AST: parent -> child -> parent -> child -> ..."); - } - } - - free(node->children); - free(node); -} - -AST_NODE_PTR AST_detach_node(AST_NODE_PTR parent, AST_NODE_PTR child) { - assert(parent != NULL); - assert(child != NULL); - - for (size_t i = 0; i < parent->child_count; i++) { - if (child == parent->children[i]) { - memcpy(&parent->children[i], &parent->children[i + 1], parent->child_count - i - 1); - parent->child_count--; - return child; - } - } - - WARN("Node not a child of parent"); - - return child; + for (size_t i = 0; i < node->child_count; i++) { + AST_delete_node(node->children[i]); + } } static void __AST_visit_nodes_recurse2(struct AST_Node_t *root, diff --git a/src/ast/ast.h b/src/ast/ast.h index 13437ea..0bb9c6e 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -69,7 +69,6 @@ struct AST_Node_t { // number of child nodes ownd by this node // length of children array size_t child_count; - size_t child_cap; // variable amount of child nodes struct AST_Node_t **children; }; @@ -91,8 +90,6 @@ void AST_delete_node(struct AST_Node_t *); // add a new child node void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child); -AST_NODE_PTR AST_detach_node(AST_NODE_PTR parent, AST_NODE_PTR child); - // get a specific child node struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx); From efeb4821284c73b5d4f789a3ffc6a074f4598cb0 Mon Sep 17 00:00:00 2001 From: servostar Date: Wed, 8 May 2024 22:10:14 +0200 Subject: [PATCH 27/31] removed gc --- src/gc/gc.c | 113 ------------------------------------------------ src/gc/gc.h | 24 ---------- src/lex/lexer.l | 1 - src/main.c | 75 ++++++++++++++------------------ src/sys/log.h | 1 - 5 files changed, 32 insertions(+), 182 deletions(-) delete mode 100644 src/gc/gc.c delete mode 100644 src/gc/gc.h diff --git a/src/gc/gc.c b/src/gc/gc.c deleted file mode 100644 index 7648655..0000000 --- a/src/gc/gc.c +++ /dev/null @@ -1,113 +0,0 @@ -// -// Created by servostar on 5/7/24. -// - -#include -#include -#include - -#undef malloc -#undef free -#undef realloc - -#define GC_HEAP_PREALLOC_CNT 10 - -typedef struct GC_Heap_Statistics_t { - size_t bytes; -} GC_Heap_Statistics; - -static struct GC_Heap_t { - RAW_PTR* blocks; - size_t cap; - size_t len; - GC_Heap_Statistics statistics; -} GC_GLOBAL_HEAP; - -static void __GC_teardown(void) { - INFO("Used %ld bytes in total", GC_GLOBAL_HEAP.statistics.bytes); - - if (GC_GLOBAL_HEAP.blocks == NULL) { - return; - } - - for (size_t i = 0; i < GC_GLOBAL_HEAP.len; i++) { - free(GC_GLOBAL_HEAP.blocks[i]); - } - - free(GC_GLOBAL_HEAP.blocks); - - GC_GLOBAL_HEAP.blocks = NULL; -} - -static void __GC_check_init(void) { - if (GC_GLOBAL_HEAP.blocks == NULL) { - GC_GLOBAL_HEAP.cap = GC_HEAP_PREALLOC_CNT; - const size_t bytes = sizeof(RAW_PTR) * GC_GLOBAL_HEAP.cap; - GC_GLOBAL_HEAP.blocks = malloc(bytes); - GC_GLOBAL_HEAP.len = 0; - } -} - -void GC_init(void) { - atexit(__GC_teardown); -} - -static void __GC_check_overflow(void) { - if (GC_GLOBAL_HEAP.len >= GC_GLOBAL_HEAP.cap) { - GC_GLOBAL_HEAP.cap += GC_HEAP_PREALLOC_CNT; - const size_t bytes = sizeof(RAW_PTR) * GC_GLOBAL_HEAP.cap; - GC_GLOBAL_HEAP.blocks = realloc(GC_GLOBAL_HEAP.blocks, bytes); - } -} - -RAW_PTR GC_malloc(const size_t bytes) { - const RAW_PTR ptr = malloc(bytes); - - if (ptr == NULL) { - FATAL("unable to allocate memory"); - } - - __GC_check_init(); - __GC_check_overflow(); - - GC_GLOBAL_HEAP.blocks[GC_GLOBAL_HEAP.len++] = ptr; - - GC_GLOBAL_HEAP.statistics.bytes += bytes; - - return ptr; -} - -static void __GC_swap_ptr(RAW_PTR old, RAW_PTR new) { - for (size_t i = 0; i < GC_GLOBAL_HEAP.len; i++) { - if (GC_GLOBAL_HEAP.blocks[i] == old) { - GC_GLOBAL_HEAP.blocks[i] = new; - } - } -} - -RAW_PTR GC_realloc(RAW_PTR ptr, size_t bytes) { - const RAW_PTR new_ptr = (RAW_PTR) realloc(ptr, bytes); - - if (new_ptr == NULL) { - FATAL("unable to reallocate memory"); - } - - __GC_swap_ptr(ptr, new_ptr); - - return new_ptr; -} - -void GC_free(RAW_PTR ptr) { - DEBUG("freeing memory: %p", ptr); - - for (size_t i = 0; i < GC_GLOBAL_HEAP.len; i++) { - - if (ptr == GC_GLOBAL_HEAP.blocks[i]) { - free(GC_GLOBAL_HEAP.blocks[i]); - - GC_GLOBAL_HEAP.len--; - - memcpy(&GC_GLOBAL_HEAP.blocks[i], &GC_GLOBAL_HEAP.blocks[i + 1], GC_GLOBAL_HEAP.len - i); - } - } -} diff --git a/src/gc/gc.h b/src/gc/gc.h deleted file mode 100644 index 6fe17a3..0000000 --- a/src/gc/gc.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by servostar on 5/7/24. -// - -#ifndef GC_H -#define GC_H - -#include - -#define malloc GC_malloc -#define free GC_free -#define realloc GC_realloc - -typedef void* RAW_PTR; - -void GC_init(void); - -RAW_PTR GC_malloc(size_t bytes); - -RAW_PTR GC_realloc(RAW_PTR ptr, size_t bytes); - -void GC_free(RAW_PTR ptr); - -#endif //GC_H diff --git a/src/lex/lexer.l b/src/lex/lexer.l index 129af69..8823195 100644 --- a/src/lex/lexer.l +++ b/src/lex/lexer.l @@ -2,7 +2,6 @@ %{ #include #include - #include int yyLineNumber = 1; int yylex(); diff --git a/src/main.c b/src/main.c index 0b56d88..03ab6b9 100644 --- a/src/main.c +++ b/src/main.c @@ -1,82 +1,71 @@ +#include #include #include #include -#include -#include #define LOG_LEVEL LOG_LEVEL_DEBUG -extern FILE* yyin; +extern FILE *yyin; /** * @brief Log a debug message to inform about beginning exit procedures * */ -void notify_exit(void) -{ - DEBUG("Exiting gemstone..."); -} +void notify_exit(void) { DEBUG("Exiting gemstone..."); } /** * @brief Closes File after compiling. * */ -void close_file(void) -{ - if (NULL != yyin) - { - fclose(yyin); - } +void close_file(void) { + if (NULL != yyin) { + fclose(yyin); + } } /** * @brief Run compiler setup here * */ -void setup(void) -{ - // setup preample +void setup(void) { + // 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(); - GC_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"); - } + // Check for file input as argument + if (2 != argc) { + INFO("Usage: %s \n", argv[0]); + PANIC("No File could be found"); + } - // filename as first argument - char *filename = argv[1]; + // filename as first argument + char *filename = argv[1]; - FILE *file = fopen(filename, "r"); + FILE *file = fopen(filename, "r"); - if (NULL == file) - { - PANIC("File couldn't be opened!"); - } - yyin = file; + if (NULL == file) { + PANIC("File couldn't be opened!"); + } + yyin = file; - yyparse(); + yyparse(); - return 0; -} \ No newline at end of file + return 0; +} diff --git a/src/sys/log.h b/src/sys/log.h index 3b4f7ed..64e8d44 100644 --- a/src/sys/log.h +++ b/src/sys/log.h @@ -2,7 +2,6 @@ #define _SYS_ERR_H_ #include -#include #define LOG_DEFAULT_STREAM stderr From dd4d70829e6fd37cb94012426e86b3553f9d460e Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 10 May 2024 13:00:25 +0200 Subject: [PATCH 28/31] added more documentation to AST header --- src/ast/ast.h | 77 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 11 deletions(-) diff --git a/src/ast/ast.h b/src/ast/ast.h index 0bb9c6e..767388b 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -4,7 +4,12 @@ #include -// Syntax elements which are stored in a syntax tree +/** + * @brief The type of a AST node + * @attention The last element is not to be used in the AST + * as it is used as a lazy way to get the total number of available + * variants of this enum. + */ enum AST_SyntaxElement_t { AST_Stmt = 0, AST_Expr, @@ -57,6 +62,13 @@ enum AST_SyntaxElement_t { AST_ELEMENT_COUNT }; +/** + * @brief A single node which can be joined with other nodes like a graph. + * Every node can have one ancestor (parent) but multiple (also none) children. + * Nodes have two properties: + * - kind: The type of the node. Such as AST_Expr, AST_Add, ... + * - value: A string representing an optional value. Can be a integer literal for kind AST_int + */ struct AST_Node_t { // parent node that owns this node struct AST_Node_t *parent; @@ -73,33 +85,76 @@ struct AST_Node_t { struct AST_Node_t **children; }; +/** + * Shorthand type for a single AST node + */ typedef struct AST_Node_t* AST_NODE_PTR; -// initialize the global AST state +/** + * @brief Initalize the global state of this module. Required for some functionality to work correctly. + */ void AST_init(void); -// return a string representation of the nodes type and its value -// does not take into account its children or parent +/** + * @brief Returns the string representation of the supplied node + * @attention The retuned pointer is not to be freed as it may either be a statically stored string or + * used by the node after this function call. + * @param node to return string representation of + * @return string represenation of the node + */ const char* AST_node_to_string(struct AST_Node_t* node); -// create a new initialized (empty) node +/** + * @brief Create a new node struct on the system heap. Initializes the struct with the given values. + * All other fields are set to either NULL or 0. No allocation for children array is preformed. +* @attention parameter value can be NULL in case no value can be provided for the node + * @param kind the type of this node + * @param value an optional value for this node + * @return + */ struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value); -void AST_delete_node(struct AST_Node_t *); +/** + * @brief Deallocate this node and all of its children. + * @attention This function will detach this node from its parent if one is present + * Use of the supplied node after this call is undefined behavior + * @param node The node to deallocate + */ +void AST_delete_node(struct AST_Node_t * node); -// add a new child node +/** + * @brief Add a new child node to a parent node + * @attention This can reallocate the children array + * @param owner node to add a child to + * @param child node to be added as a child + */ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child); -// get a specific child node +/** + * @brief Return a pointer to the n-th child of a node + * @attention Pointer to childen nodes will never change. + * However, the index a node is stored within a parent can change + * if a child of lower index is removed, thus reducing the childrens index by one. + * @param owner the parent node which owns the children + * @param idx the index of the child to get a pointer to + * @return a pointer to the n-th child of the owner node + */ struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx); -// visit this and all of its child nodes calling the given function -// for every node +/** + * @brief Execute a function for every child, grandchild, ... and the supplied node as topmost ancestor + * @param root the root to recursively execute a function for + * @param for_each the function to execute + */ void AST_visit_nodes_recurse(struct AST_Node_t *root, void (*for_each)(struct AST_Node_t *node, size_t depth)); -// print a graphviz diagram of the supplied node (as root node) into stream +/** + * @brief Prints a graphviz graph of the node and all its ancestors. + * @param stream The stream to print to. Can be a file, stdout, ... + * @param node the topmost ancestor + */ void AST_fprint_graphviz(FILE* stream, struct AST_Node_t* node); #endif From 80888fdb8d67e3e974fc9368aae0e581fdae0e71 Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 10 May 2024 13:49:04 +0200 Subject: [PATCH 29/31] added function for removing and detaching a node --- src/ast/ast.c | 35 +++++++++++++++++++++++++++++++++++ src/ast/ast.h | 20 ++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/ast/ast.c b/src/ast/ast.c index 7bae47b..ca2442c 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -134,6 +134,33 @@ struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) { return child; } +struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, const size_t idx) { + + struct AST_Node_t* child = owner->children[idx]; + + child->parent = NULL; + + owner->child_count--; + + // shift back every following element by one + for (size_t i = idx; i < owner->child_count; i++) { + owner->children[i] = owner->children[i + 1]; + } + + return child; +} + +struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_Node_t* child) { + for (size_t i = 0; i < owner->child_count; i++) { + if (owner->children[i] == child) { + return AST_remove_child(owner, i); + } + } + + PANIC("Child to detach not a child of parent"); + return NULL; +} + void AST_delete_node(struct AST_Node_t *node) { DEBUG("Deleting AST node: %p", node); @@ -145,9 +172,17 @@ void AST_delete_node(struct AST_Node_t *node) { return; } + if (node->parent != NULL) { + AST_detach_child(node->parent, node); + } + for (size_t i = 0; i < node->child_count; i++) { + // prevent detach of children node + node->children[i]->parent = NULL; AST_delete_node(node->children[i]); } + + free(node); } static void __AST_visit_nodes_recurse2(struct AST_Node_t *root, diff --git a/src/ast/ast.h b/src/ast/ast.h index 767388b..40c9af2 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -130,6 +130,26 @@ void AST_delete_node(struct AST_Node_t * node); */ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child); +/** + * @brief Remove the specified child from the owner. + * @attention The parent of the removed node is set to NULL. + * The returned pointer is still valid. It must be freed at some pointer later. + * @param owner Node to remove the child from + * @param idx the index of the child to remove + * @return a pointer to the child which was removed + */ +struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, size_t idx); + +/** + * @brief Detach a child from its parent. This involves removing the child from its parent + * and marking the parent of the child as NULL. + * @attention The returned pointer is still valid. It must be freed at some pointer later. + * @param owner the owner to remove the child from + * @param child the child to detach + * @return a pointer to child detached + */ +struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_Node_t* child); + /** * @brief Return a pointer to the n-th child of a node * @attention Pointer to childen nodes will never change. From 12a522b0e69643d44d2c365b172e948a7c3125dd Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 10 May 2024 13:51:23 +0200 Subject: [PATCH 30/31] added missing free for child array --- src/ast/ast.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ast/ast.c b/src/ast/ast.c index ca2442c..ef7ce47 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -182,6 +182,7 @@ void AST_delete_node(struct AST_Node_t *node) { AST_delete_node(node->children[i]); } + free(node->children); free(node); } From 23968f7acca68e86ef3cc32f521fc0f00ae90f56 Mon Sep 17 00:00:00 2001 From: servostar Date: Fri, 10 May 2024 15:09:52 +0200 Subject: [PATCH 31/31] required C standard is C23 refactored logger function names to remove underscores added function attributes --- CMakeLists.txt | 3 ++ src/ast/ast.c | 79 ++++++++++++++++++++++++++++++++++---------------- src/ast/ast.h | 24 +++++++++++++-- src/sys/log.c | 13 +++++---- src/sys/log.h | 24 +++++++++------ 5 files changed, 102 insertions(+), 41 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 079fc47..f4dd41f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,9 @@ project(gemstone DESCRIPTION "programming language compiler" LANGUAGES C) +set(CMAKE_C_STANDARD 23) +set(CMAKE_C_STANDARD_REQUIRED TRUE) + set(GEMSTONE_TEST_DIR ${PROJECT_SOURCE_DIR}/tests) set(GEMSTONE_BINARY_DIR ${PROJECT_SOURCE_DIR}/bin) diff --git a/src/ast/ast.c b/src/ast/ast.c index ef7ce47..233e8da 100644 --- a/src/ast/ast.c +++ b/src/ast/ast.c @@ -1,12 +1,13 @@ #include #include -#include #include #include +#include struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value) { DEBUG("creating new AST node: %d \"%s\"", kind, value); + assert(kind < AST_ELEMENT_COUNT); struct AST_Node_t *node = malloc(sizeof(struct AST_Node_t)); @@ -14,6 +15,8 @@ struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value PANIC("failed to allocate AST node"); } + assert(node != NULL); + // init to discrete state node->parent = NULL; node->children = NULL; @@ -71,10 +74,11 @@ void AST_init() { lookup_table[AST_Condition] = "condition"; } -const char* AST_node_to_string(struct AST_Node_t* node) { +const char* AST_node_to_string(const struct AST_Node_t* node) { DEBUG("converting AST node to string: %p", node); + assert(node != NULL); - const char* string = "unknown"; + const char* string; switch(node->kind) { case AST_Int: @@ -90,11 +94,15 @@ const char* AST_node_to_string(struct AST_Node_t* node) { string = lookup_table[node->kind]; } + assert(string != NULL); + return string; } 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); + assert(child != NULL); // if there are no children for now if (owner->child_count == 0) { @@ -111,15 +119,16 @@ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) { PANIC("failed to allocate children array of AST node"); } + assert(owner->children != NULL); + owner->children[owner->child_count++] = child; } -struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) { +struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, const size_t idx) { DEBUG("retrvieng node %d from %p", idx, owner); - - if (owner == NULL) { - PANIC("AST owner node is NULL"); - } + assert(owner != NULL); + assert(owner->children != NULL); + assert(idx < owner->child_count); if (owner->children == NULL) { PANIC("AST owner node has no children"); @@ -135,6 +144,9 @@ struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) { } struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, const size_t idx) { + assert(owner != NULL); + assert(owner->children != NULL); + assert(idx < owner->child_count); struct AST_Node_t* child = owner->children[idx]; @@ -151,6 +163,10 @@ struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, const size_t idx) } struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_Node_t* child) { + assert(owner != NULL); + assert(child != NULL); + assert(owner->children != NULL); + for (size_t i = 0; i < owner->child_count; i++) { if (owner->children[i] == child) { return AST_remove_child(owner, i); @@ -158,22 +174,20 @@ struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_N } PANIC("Child to detach not a child of parent"); - return NULL; } void AST_delete_node(struct AST_Node_t *node) { - DEBUG("Deleting AST node: %p", node); + assert(node != NULL); - if (node == NULL) { - PANIC("Node to free is NULL"); - } + DEBUG("Deleting AST node: %p", node); if (node->children == NULL) { return; } if (node->parent != NULL) { - AST_detach_child(node->parent, node); + const struct AST_Node_t* child = AST_detach_child(node->parent, node); + assert(child == node); } for (size_t i = 0; i < node->child_count; i++) { @@ -186,16 +200,18 @@ void AST_delete_node(struct AST_Node_t *node) { free(node); } -static void __AST_visit_nodes_recurse2(struct AST_Node_t *root, +static void AST_visit_nodes_recurse2(struct AST_Node_t *root, void (*for_each)(struct AST_Node_t *node, size_t depth), - size_t depth) { + const size_t depth) { DEBUG("Recursive visit of %p at %d with %p", root, depth, for_each); + assert(root != NULL); + (for_each)(root, depth); for (size_t i = 0; i < root->child_count; i++) { - __AST_visit_nodes_recurse2(root->children[i], for_each, depth + 1); + AST_visit_nodes_recurse2(root->children[i], for_each, depth + 1); } } @@ -203,12 +219,19 @@ void AST_visit_nodes_recurse(struct AST_Node_t *root, void (*for_each)(struct AST_Node_t *node, size_t depth)) { DEBUG("Starting recursive visit of %p with %p", root, for_each); - __AST_visit_nodes_recurse2(root, for_each, 0); + + assert(root != NULL); + assert(for_each != NULL); + + AST_visit_nodes_recurse2(root, for_each, 0); } -static void __AST_fprint_graphviz_node_definition(FILE* stream, struct AST_Node_t* node) { +static void AST_fprint_graphviz_node_definition(FILE* stream, const struct AST_Node_t* node) { DEBUG("Printing graphviz definition of %p", node); + assert(stream != NULL); + assert(node != NULL); + fprintf(stream, "\tnode%p [label=\"%s\"]\n", (void*) node, AST_node_to_string(node)); if (node->children == NULL) { @@ -216,30 +239,36 @@ static void __AST_fprint_graphviz_node_definition(FILE* stream, struct AST_Node_ } for (size_t i = 0; i < node->child_count; i++) { - __AST_fprint_graphviz_node_definition(stream, node->children[i]); + AST_fprint_graphviz_node_definition(stream, node->children[i]); } } -static void __AST_fprint_graphviz_node_connection(FILE* stream, struct AST_Node_t* node) { +static void AST_fprint_graphviz_node_connection(FILE* stream, const struct AST_Node_t* node) { DEBUG("Printing graphviz connection of %p", node); + assert(stream != NULL); + assert(node != NULL); + if (node->children == NULL) { return; } for (size_t i = 0; i < node->child_count; i++) { fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) node->children[i]); - __AST_fprint_graphviz_node_connection(stream, node->children[i]); + AST_fprint_graphviz_node_connection(stream, node->children[i]); } } -void AST_fprint_graphviz(FILE* stream, struct AST_Node_t* root) { +void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* root) { DEBUG("Starting print of graphviz graph of %p", root); + assert(stream != NULL); + assert(root != NULL); + fprintf(stream, "graph {\n"); - __AST_fprint_graphviz_node_definition(stream, root); - __AST_fprint_graphviz_node_connection(stream, root); + AST_fprint_graphviz_node_definition(stream, root); + AST_fprint_graphviz_node_connection(stream, root); fprintf(stream, "}\n"); } diff --git a/src/ast/ast.h b/src/ast/ast.h index 40c9af2..5129765 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -102,7 +102,9 @@ void AST_init(void); * @param node to return string representation of * @return string represenation of the node */ -const char* AST_node_to_string(struct AST_Node_t* node); +[[maybe_unused]] +[[gnu::nonnull(1)]] +const char* AST_node_to_string(const struct AST_Node_t* node); /** * @brief Create a new node struct on the system heap. Initializes the struct with the given values. @@ -112,6 +114,9 @@ const char* AST_node_to_string(struct AST_Node_t* node); * @param value an optional value for this node * @return */ +[[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); /** @@ -120,6 +125,8 @@ struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value * Use of the supplied node after this call is undefined behavior * @param node The node to deallocate */ +[[maybe_unused]] +[[gnu::nonnull(1)]] void AST_delete_node(struct AST_Node_t * node); /** @@ -128,6 +135,8 @@ void AST_delete_node(struct AST_Node_t * node); * @param owner node to add a child to * @param child node to be added as a child */ +[[maybe_unused]] +[[gnu::nonnull(1), gnu::nonnull(2)]] void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child); /** @@ -138,6 +147,9 @@ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child); * @param idx the index of the child to remove * @return a pointer to the child which was removed */ +[[maybe_unused]] +[[nodiscard("pointer must be freed")]] +[[gnu::nonnull(1)]] struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, size_t idx); /** @@ -148,6 +160,8 @@ struct AST_Node_t* AST_remove_child(struct AST_Node_t* owner, size_t idx); * @param child the child to detach * @return a pointer to child detached */ +[[nodiscard("pointer must be freed")]] +[[gnu::nonnull(1), gnu::nonnull(1)]] struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_Node_t* child); /** @@ -159,6 +173,8 @@ struct AST_Node_t* AST_detach_child(struct AST_Node_t* owner, const struct AST_N * @param idx the index of the child to get a pointer to * @return a pointer to the n-th child of the owner node */ +[[maybe_unused]] +[[gnu::nonnull(1)]] struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx); /** @@ -166,6 +182,8 @@ struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx); * @param root the root to recursively execute a function for * @param for_each the function to execute */ +[[maybe_unused]] +[[gnu::nonnull(1), gnu::nonnull(2)]] void AST_visit_nodes_recurse(struct AST_Node_t *root, void (*for_each)(struct AST_Node_t *node, size_t depth)); @@ -175,6 +193,8 @@ void AST_visit_nodes_recurse(struct AST_Node_t *root, * @param stream The stream to print to. Can be a file, stdout, ... * @param node the topmost ancestor */ -void AST_fprint_graphviz(FILE* stream, struct AST_Node_t* node); +[[maybe_unused]] +[[gnu::nonnull(1), gnu::nonnull(2)]] +void AST_fprint_graphviz(FILE* stream, const struct AST_Node_t* node); #endif diff --git a/src/sys/log.c b/src/sys/log.c index e3a7cd6..0aeab5f 100644 --- a/src/sys/log.c +++ b/src/sys/log.c @@ -3,6 +3,7 @@ #include #include #include +#include static struct Logger_t { FILE** streams; @@ -11,13 +12,15 @@ static struct Logger_t { void log_init(void) { + assert(LOG_DEFAULT_STREAM != NULL); log_register_stream(LOG_DEFAULT_STREAM); } void log_register_stream(FILE* restrict stream) { - if (stream == NULL) - PANIC("stream to register is NULL"); + // replace runtime check with assertion + // only to be used in debug target + assert(stream != NULL); if (GlobalLogger.stream_count == 0) { @@ -57,7 +60,7 @@ static void vflogf( vfprintf(stream, format, args); } -void __logf( +void syslog_logf( const char* restrict level, const char* restrict file, const unsigned long line, @@ -78,7 +81,7 @@ void __logf( va_end(args); } -void __panicf( +void syslog_panicf( const char* restrict file, const unsigned long line, const char* restrict func, @@ -95,7 +98,7 @@ void __panicf( exit(EXIT_FAILURE); } -void __fatalf( +void syslog_fatalf( const char* restrict file, const unsigned long line, const char* restrict func, diff --git a/src/sys/log.h b/src/sys/log.h index 64e8d44..6b8991d 100644 --- a/src/sys/log.h +++ b/src/sys/log.h @@ -34,14 +34,14 @@ * This macro will print debug information to stderr and call abort() to * performa a ungracefull exit. No clean up possible. */ -#define PANIC(format, ...) __panicf(__FILE_NAME__, __LINE__, __func__, format"\n", ##__VA_ARGS__) +#define PANIC(format, ...) syslog_panicf(__FILE_NAME__, __LINE__, __func__, format"\n", ##__VA_ARGS__) /** * @brief Panic is used in cases where the process is in an invalid or undefined state. * This macro will print debug information to stderr and call exit() to * initiate a gracefull exit, giving the process the opportunity to clean up. */ -#define FATAL(format, ...) __fatalf(__FILE_NAME__, __LINE__, __func__, format"\n", ##__VA_ARGS__) +#define FATAL(format, ...) syslog_fatalf(__FILE_NAME__, __LINE__, __func__, format"\n", ##__VA_ARGS__) /* Standard log macros. These will not terminate the application. @@ -56,7 +56,7 @@ will not print. #define __LOG(level, priority, format, ...) \ do { \ if (LOG_LEVEL <= priority) \ - __logf(level, __FILE_NAME__, __LINE__, __func__, format, ##__VA_ARGS__); \ + syslog_logf(level, __FILE_NAME__, __LINE__, __func__, format, ##__VA_ARGS__); \ } while(0) /** @@ -69,10 +69,11 @@ will not print. * @param format the format to print following args in * @param ... */ -void __logf( +[[gnu::nonnull(1), gnu::nonnull(2), gnu::nonnull(4)]] +void syslog_logf( const char* restrict level, const char* restrict file, - const unsigned long line, + unsigned long line, const char* restrict func, const char* restrict format, ...); @@ -86,9 +87,11 @@ void __logf( * @param format the format to print following args in * @param ... */ -void __panicf( +[[noreturn]] +[[gnu::nonnull(1), gnu::nonnull(3), gnu::nonnull(4)]] +void syslog_panicf( const char* restrict file, - const unsigned long line, + unsigned long line, const char* restrict func, const char* restrict format, ...); @@ -102,9 +105,11 @@ void __panicf( * @param format the format to print following args in * @param ... */ -void __fatalf( +[[noreturn]] +[[gnu::nonnull(1), gnu::nonnull(3), gnu::nonnull(4)]] +void syslog_fatalf( const char* restrict file, - const unsigned long line, + unsigned long line, const char* restrict func, const char* restrict format, ...); @@ -120,6 +125,7 @@ void log_init(void); * * @param stream */ +[[gnu::nonnull(1)]] void log_register_stream(FILE* restrict stream); #endif /* _SYS_ERR_H_ */