Merge pull request #62 from Servostar/concept/ast-design
Concept/ast design
This commit is contained in:
commit
33457203f8
|
@ -2,7 +2,7 @@ name: "Build check gemstone in SDK"
|
||||||
run-name: SDK build check to ${{ inputs.deploy_target }} by @${{ github.actor }}
|
run-name: SDK build check to ${{ inputs.deploy_target }} by @${{ github.actor }}
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
env:
|
env:
|
||||||
SDK: 0.2.2-alpine-3.19.1
|
SDK: 0.2.3-alpine-3.19.1
|
||||||
jobs:
|
jobs:
|
||||||
build-check-sdk:
|
build-check-sdk:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
@ -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 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 description="docker image for setting up the build pipeline on SDK"
|
||||||
LABEL website="https://github.com/Servostar/gemstone"
|
LABEL website="https://github.com/Servostar/gemstone"
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
FROM alpine:3.19.1
|
FROM alpine:3.19.1
|
||||||
LABEL authors="servostar"
|
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 description="base image for building the gemstone programming language compiler"
|
||||||
LABEL website="https://github.com/Servostar/gemstone"
|
LABEL website="https://github.com/Servostar/gemstone"
|
||||||
|
|
||||||
# install dependencies
|
# 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
|
# create user for build
|
||||||
RUN adduser --disabled-password lorang
|
RUN adduser --disabled-password lorang
|
||||||
|
|
|
@ -3,12 +3,13 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <sys/log.h>
|
#include <sys/log.h>
|
||||||
|
#include <assert.h>
|
||||||
#include <gc/gc.h>
|
#include <gc/gc.h>
|
||||||
|
|
||||||
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);
|
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) {
|
if (node == NULL) {
|
||||||
PANIC("failed to allocate AST node");
|
PANIC("failed to allocate AST node");
|
||||||
|
@ -18,6 +19,7 @@ struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value
|
||||||
node->parent = NULL;
|
node->parent = NULL;
|
||||||
node->children = NULL;
|
node->children = NULL;
|
||||||
node->child_count = 0;
|
node->child_count = 0;
|
||||||
|
node->child_cap = 0;
|
||||||
node->kind = kind;
|
node->kind = kind;
|
||||||
node->value = value;
|
node->value = value;
|
||||||
|
|
||||||
|
@ -26,11 +28,12 @@ struct AST_Node_t *AST_new_node(enum AST_SyntaxElement_t kind, const char* value
|
||||||
|
|
||||||
static const char* lookup_table[AST_ELEMENT_COUNT] = { "__UNINIT__" };
|
static const char* lookup_table[AST_ELEMENT_COUNT] = { "__UNINIT__" };
|
||||||
|
|
||||||
void AST_init() {
|
void AST_init(void) {
|
||||||
DEBUG("initializing global syntax tree...");
|
DEBUG("initializing global syntax tree...");
|
||||||
|
|
||||||
INFO("filling lookup table...");
|
INFO("filling lookup table...");
|
||||||
|
|
||||||
|
lookup_table[AST_Stmt] = "stmt";
|
||||||
lookup_table[AST_Expr] = "expr";
|
lookup_table[AST_Expr] = "expr";
|
||||||
|
|
||||||
lookup_table[AST_Add] = "+";
|
lookup_table[AST_Add] = "+";
|
||||||
|
@ -64,6 +67,10 @@ void AST_init() {
|
||||||
lookup_table[AST_Typedef] = "typedef";
|
lookup_table[AST_Typedef] = "typedef";
|
||||||
lookup_table[AST_Box] = "box";
|
lookup_table[AST_Box] = "box";
|
||||||
lookup_table[AST_Fun] = "fun";
|
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) {
|
const char* AST_node_to_string(struct AST_Node_t* node) {
|
||||||
|
@ -78,6 +85,7 @@ const char* AST_node_to_string(struct AST_Node_t* node) {
|
||||||
case AST_Ident:
|
case AST_Ident:
|
||||||
case AST_Macro:
|
case AST_Macro:
|
||||||
case AST_Import:
|
case AST_Import:
|
||||||
|
case AST_Call:
|
||||||
string = node->value;
|
string = node->value;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -87,17 +95,24 @@ const char* AST_node_to_string(struct AST_Node_t* node) {
|
||||||
return string;
|
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);
|
DEBUG("Adding new node %p to %p", child, owner);
|
||||||
|
|
||||||
|
assert(PRE_ALLOCATION_CNT >= 1);
|
||||||
|
|
||||||
// if there are no children for now
|
// 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");
|
DEBUG("Allocating new children array");
|
||||||
owner->children = malloc(sizeof(struct AST_Node_t *));
|
owner->children = malloc(sizeof(struct AST_Node_t *));
|
||||||
|
owner->child_cap = 1;
|
||||||
|
|
||||||
} else {
|
} else if (owner->child_count >= owner->child_cap) {
|
||||||
DEBUG("Rellocating old children array");
|
DEBUG("Reallocating old children array");
|
||||||
const size_t size = sizeof(struct AST_Node_t *) * (owner->child_count + 1);
|
owner->child_cap += PRE_ALLOCATION_CNT;
|
||||||
|
const size_t size = sizeof(struct AST_Node_t *) * owner->child_cap;
|
||||||
owner->children = realloc(owner->children, size);
|
owner->children = realloc(owner->children, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,8 +123,8 @@ void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) {
|
||||||
owner->children[owner->child_count++] = child;
|
owner->children[owner->child_count++] = child;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) {
|
AST_NODE_PTR AST_get_node(AST_NODE_PTR owner, size_t idx) {
|
||||||
DEBUG("retrvieng node %d from %p", idx, owner);
|
DEBUG("retrieving node %d from %p", idx, owner);
|
||||||
|
|
||||||
if (owner == NULL) {
|
if (owner == NULL) {
|
||||||
PANIC("AST owner node is NULL");
|
PANIC("AST owner node is NULL");
|
||||||
|
@ -119,7 +134,7 @@ struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) {
|
||||||
PANIC("AST owner node has no children");
|
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) {
|
if (child == NULL) {
|
||||||
PANIC("child node is NULL");
|
PANIC("child node is NULL");
|
||||||
|
@ -128,20 +143,48 @@ struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, size_t idx) {
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AST_delete_node(struct AST_Node_t *node) {
|
void AST_delete_node(AST_NODE_PTR node) {
|
||||||
DEBUG("Deleting AST node: %p", node);
|
DEBUG("Deleting AST node: %p", node);
|
||||||
|
|
||||||
if (node == NULL) {
|
if (node == NULL) {
|
||||||
PANIC("Node to free is NULL");
|
PANIC("Node to free is NULL");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node->parent != NULL) {
|
||||||
|
AST_detach_node(node->parent, node);
|
||||||
|
}
|
||||||
|
|
||||||
if (node->children == NULL) {
|
if (node->children == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < node->child_count; i++) {
|
for (size_t i = 0; i < node->child_count; i++) {
|
||||||
|
if (node->children[i] != node) {
|
||||||
AST_delete_node(node->children[i]);
|
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,
|
static void __AST_visit_nodes_recurse2(struct AST_Node_t *root,
|
||||||
|
|
|
@ -17,6 +17,7 @@ enum AST_SyntaxElement_t {
|
||||||
AST_If,
|
AST_If,
|
||||||
AST_IfElse,
|
AST_IfElse,
|
||||||
AST_Else,
|
AST_Else,
|
||||||
|
AST_Condition,
|
||||||
// Variable management
|
// Variable management
|
||||||
AST_Decl,
|
AST_Decl,
|
||||||
AST_Assign,
|
AST_Assign,
|
||||||
|
@ -68,6 +69,7 @@ struct AST_Node_t {
|
||||||
// number of child nodes ownd by this node
|
// number of child nodes ownd by this node
|
||||||
// length of children array
|
// length of children array
|
||||||
size_t child_count;
|
size_t child_count;
|
||||||
|
size_t child_cap;
|
||||||
// variable amount of child nodes
|
// variable amount of child nodes
|
||||||
struct AST_Node_t **children;
|
struct AST_Node_t **children;
|
||||||
};
|
};
|
||||||
|
@ -89,6 +91,8 @@ void AST_delete_node(struct AST_Node_t *);
|
||||||
// add a new child node
|
// add a new child node
|
||||||
void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child);
|
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
|
// get a specific child node
|
||||||
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, size_t idx);
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,28 @@ add_test(NAME ast_build_tree
|
||||||
|
|
||||||
add_executable(ast_print_node
|
add_executable(ast_print_node
|
||||||
${PROJECT_SOURCE_DIR}/src/ast/ast.c
|
${PROJECT_SOURCE_DIR}/src/ast/ast.c
|
||||||
|
${PROJECT_SOURCE_DIR}/src/sys/log.c
|
||||||
print_node.c)
|
print_node.c)
|
||||||
set_target_properties(ast_build_tree
|
set_target_properties(ast_print_node
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
OUTPUT_NAME "build_tree"
|
OUTPUT_NAME "print_node"
|
||||||
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast)
|
RUNTIME_OUTPUT_DIRECTORY ${GEMSTONE_BINARY_DIR}/tests/ast)
|
||||||
add_test(NAME ast_print_node
|
add_test(NAME ast_print_node
|
||||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
|
||||||
COMMAND python ${GEMSTONE_TEST_DIR}/ast/test_ast.py check_print_node)
|
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)
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
//
|
||||||
|
// Created by servostar on 5/8/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <ast/ast.h>
|
||||||
|
#include <sys/log.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|
||||||
|
AST_init();
|
||||||
|
|
||||||
const AST_NODE_PTR node = AST_new_node(0, "value");
|
const AST_NODE_PTR node = AST_new_node(0, "value");
|
||||||
|
|
||||||
for (size_t i = 0; i < AST_ELEMENT_COUNT; i++) {
|
for (size_t i = 0; i < AST_ELEMENT_COUNT; i++) {
|
||||||
|
@ -16,4 +18,6 @@ int main(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
AST_delete_node(node);
|
AST_delete_node(node);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,84 @@ def run_check_print_node():
|
||||||
# check exit code
|
# check exit code
|
||||||
assert p.returncode == 0
|
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...")
|
||||||
|
|
||||||
|
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...")
|
||||||
|
|
||||||
|
# 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__":
|
if __name__ == "__main__":
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
@ -41,6 +119,8 @@ if __name__ == "__main__":
|
||||||
run_check_build_tree()
|
run_check_build_tree()
|
||||||
case "check_print_node":
|
case "check_print_node":
|
||||||
run_check_print_node()
|
run_check_print_node()
|
||||||
|
case "check_print_graphviz":
|
||||||
|
run_check_print_graphviz()
|
||||||
case _:
|
case _:
|
||||||
error(f"unknown target: {target}")
|
error(f"unknown target: {target}")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
Loading…
Reference in New Issue