From 94efa65ed0c78ca48a70fd8cb16d4a14a21c43b4 Mon Sep 17 00:00:00 2001 From: servostar Date: Wed, 8 May 2024 13:49:06 +0200 Subject: [PATCH 1/6] 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 2/6] 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 3/6] 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 4/6] 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 5/6] 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 4322797eae10bdf3acc7cc578a8eadbdea35dbaf Mon Sep 17 00:00:00 2001 From: servostar Date: Wed, 8 May 2024 20:44:52 +0200 Subject: [PATCH 6/6] 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);