338 lines
9.5 KiB
C
338 lines
9.5 KiB
C
|
|
#include <ast/ast.h>
|
|
#include <stdio.h>
|
|
#include <sys/log.h>
|
|
#include <assert.h>
|
|
#include <mem/cache.h>
|
|
|
|
struct AST_Node_t *AST_new_node(TokenLocation location, enum AST_SyntaxElement_t kind, const char* value) {
|
|
DEBUG("creating new AST node: %d \"%s\"", kind, value);
|
|
assert(kind < AST_ELEMENT_COUNT);
|
|
|
|
struct AST_Node_t *node = mem_alloc(MemoryNamespaceAst, sizeof(struct AST_Node_t));
|
|
|
|
if (node == NULL) {
|
|
PANIC("failed to allocate AST node");
|
|
}
|
|
|
|
assert(node != NULL);
|
|
|
|
// init to discrete state
|
|
node->parent = NULL;
|
|
node->children = mem_new_g_array(MemoryNamespaceAst, sizeof(AST_NODE_PTR));
|
|
node->kind = kind;
|
|
node->value = value;
|
|
node->location = location;
|
|
|
|
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_Stmt] = "stmt";
|
|
lookup_table[AST_Module] = "module";
|
|
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";
|
|
|
|
lookup_table[AST_Call] = "funcall";
|
|
lookup_table[AST_Typecast] = "typecast";
|
|
lookup_table[AST_Transmute] = "transmute";
|
|
lookup_table[AST_Condition] = "condition";
|
|
lookup_table[AST_List] = "list";
|
|
lookup_table[AST_ExprList] = "expr list";
|
|
lookup_table[AST_ArgList] = "arg list";
|
|
lookup_table[AST_ParamList] = "param list";
|
|
lookup_table[AST_StmtList] = "stmt list";
|
|
lookup_table[AST_IdentList] = "ident list";
|
|
lookup_table[AST_Type] = "type";
|
|
lookup_table[AST_Negate] = "-";
|
|
lookup_table[AST_Parameter] = "parameter";
|
|
lookup_table[AST_ParamDecl] = "parameter-declaration";
|
|
lookup_table[AST_AddressOf] = "address of";
|
|
lookup_table[AST_Dereference] = "deref";
|
|
lookup_table[AST_Reference] = "ref";
|
|
}
|
|
|
|
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;
|
|
|
|
switch(node->kind) {
|
|
case AST_Int:
|
|
case AST_Char:
|
|
case AST_Float:
|
|
case AST_String:
|
|
case AST_Ident:
|
|
case AST_Macro:
|
|
case AST_Import:
|
|
case AST_Include:
|
|
case AST_Storage:
|
|
case AST_Typekind:
|
|
case AST_Sign:
|
|
case AST_Scale:
|
|
case AST_Qualifyier:
|
|
string = node->value;
|
|
break;
|
|
default:
|
|
string = lookup_table[node->kind];
|
|
}
|
|
|
|
assert(string != NULL);
|
|
|
|
return string;
|
|
}
|
|
|
|
static inline unsigned long int min(unsigned long int a, unsigned long int b) {
|
|
return a > b ? b : a;
|
|
}
|
|
|
|
static inline unsigned long int max(unsigned long int a, unsigned long int b) {
|
|
return a > b ? a : b;
|
|
}
|
|
|
|
void AST_push_node(struct AST_Node_t *owner, struct AST_Node_t *child) {
|
|
DEBUG("Adding new node %p to %p", child, owner);
|
|
assert(owner != NULL);
|
|
assert(child != NULL);
|
|
|
|
if (owner->children == NULL) {
|
|
PANIC("failed to allocate children array of AST node");
|
|
}
|
|
|
|
owner->location.col_end = max(owner->location.col_end, child->location.col_end);
|
|
owner->location.line_end = max(owner->location.line_end, child->location.line_end);
|
|
|
|
owner->location.col_start = min(owner->location.col_start, child->location.col_start);
|
|
owner->location.line_start = min(owner->location.line_start, child->location.line_start);
|
|
|
|
if (owner->location.file == NULL) {
|
|
owner->location.file = child->location.file;
|
|
}
|
|
|
|
assert(owner->children != NULL);
|
|
|
|
g_array_append_val(owner->children, child);
|
|
}
|
|
|
|
struct AST_Node_t *AST_get_node(struct AST_Node_t *owner, const size_t idx) {
|
|
DEBUG("retrvieng node %d from %p", idx, owner);
|
|
assert(owner != NULL);
|
|
assert(owner->children != NULL);
|
|
assert(idx < owner->children->len);
|
|
|
|
if (owner->children == NULL) {
|
|
PANIC("AST owner node has no children");
|
|
}
|
|
|
|
AST_NODE_PTR child = g_array_index(owner->children, AST_NODE_PTR, idx);
|
|
|
|
if (child == NULL) {
|
|
PANIC("child node is NULL");
|
|
}
|
|
|
|
return child;
|
|
}
|
|
|
|
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->children->len);
|
|
|
|
AST_NODE_PTR child = g_array_index(owner->children, AST_NODE_PTR, idx);
|
|
g_array_remove_index(owner->children, idx);
|
|
|
|
child->parent = NULL;
|
|
|
|
return child;
|
|
}
|
|
|
|
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->children->len; i++) {
|
|
if (g_array_index(owner->children, AST_NODE_PTR, i) == child) {
|
|
return AST_remove_child(owner, i);
|
|
}
|
|
}
|
|
|
|
PANIC("Child to detach not a child of parent");
|
|
}
|
|
|
|
void AST_delete_node(struct AST_Node_t *node) {
|
|
assert(node != NULL);
|
|
|
|
DEBUG("Deleting AST node: %p", node);
|
|
|
|
if (node->parent != NULL) {
|
|
[[maybe_unused]]
|
|
const struct AST_Node_t* child = AST_detach_child(node->parent, node);
|
|
assert(child == node);
|
|
}
|
|
|
|
if (node->children != NULL) {
|
|
for (size_t i = 0; i < AST_get_child_count(node); i++) {
|
|
// prevent detach of children node
|
|
AST_get_node(node, i)->parent = NULL;
|
|
AST_delete_node(AST_get_node(node, i));
|
|
}
|
|
mem_free(node->children);
|
|
}
|
|
|
|
mem_free(node);
|
|
}
|
|
|
|
static void AST_visit_nodes_recurse2(struct AST_Node_t *root,
|
|
void (*for_each)(struct AST_Node_t *node,
|
|
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->children->len; i++) {
|
|
AST_visit_nodes_recurse2(g_array_index(root->children, AST_NODE_PTR, 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)) {
|
|
DEBUG("Starting recursive visit of %p with %p", root, for_each);
|
|
|
|
assert(root != NULL);
|
|
assert(for_each != NULL);
|
|
|
|
AST_visit_nodes_recurse2(root, for_each, 0);
|
|
}
|
|
|
|
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) {
|
|
return;
|
|
}
|
|
|
|
for (size_t i = 0; i < node->children->len; i++) {
|
|
AST_fprint_graphviz_node_definition(stream, g_array_index(node->children, AST_NODE_PTR, i));
|
|
}
|
|
}
|
|
|
|
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->children->len; i++) {
|
|
AST_NODE_PTR child = g_array_index(node->children, AST_NODE_PTR, i);
|
|
fprintf(stream, "\tnode%p -- node%p\n", (void*) node, (void*) child);
|
|
AST_fprint_graphviz_node_connection(stream, child);
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
fprintf(stream, "}\n");
|
|
}
|
|
|
|
AST_NODE_PTR AST_get_node_by_kind(AST_NODE_PTR owner, enum AST_SyntaxElement_t kind) {
|
|
for (size_t i = 0; i < owner->children->len; i++) {
|
|
AST_NODE_PTR child = AST_get_node(owner, i);
|
|
|
|
if (child->kind == kind) {
|
|
return child;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void AST_merge_modules(AST_NODE_PTR dst, size_t k, AST_NODE_PTR src) {
|
|
assert(dst != NULL);
|
|
assert(src != NULL);
|
|
|
|
size_t elements = src->children->len;
|
|
for (size_t i = 0; i < elements; i++) {
|
|
AST_insert_node(dst, k + i, AST_remove_child(src, 0));
|
|
}
|
|
AST_delete_node(src);
|
|
}
|
|
|
|
void AST_insert_node(AST_NODE_PTR owner, size_t idx, AST_NODE_PTR child) {
|
|
assert(owner != NULL);
|
|
assert(child != NULL);
|
|
|
|
DEBUG("Reallocating old children array");
|
|
|
|
g_array_insert_val(owner->children, idx, child);
|
|
}
|
|
|
|
size_t AST_get_child_count(AST_NODE_PTR node) {
|
|
return node->children->len;
|
|
}
|
|
|
|
AST_NODE_PTR AST_get_last_node(AST_NODE_PTR node) {
|
|
assert(node != NULL);
|
|
|
|
return g_array_index(node->children, AST_NODE_PTR, node->children->len - 1);
|
|
}
|