From 935da30257bf55aa4dc32815acce04867f191681 Mon Sep 17 00:00:00 2001 From: omicron Date: Mon, 31 Mar 2025 16:31:42 +0200 Subject: [PATCH] Add basic AST functionality --- src/ast.c | 85 ++++++++++++++++++++++++++++++++++++++++++ src/ast.h | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 src/ast.c create mode 100644 src/ast.h diff --git a/src/ast.c b/src/ast.c new file mode 100644 index 0000000..65ac611 --- /dev/null +++ b/src/ast.c @@ -0,0 +1,85 @@ +#include "ast.h" +#include "error.h" +#include + +error_t *err_node_children_cap = &(error_t){ + .message = "Failed to increase ast node children, max capacity reached"}; + +error_t *ast_node_alloc(ast_node_t **output) { + *output = nullptr; + + ast_node_t *node = calloc(1, sizeof(ast_node_t)); + if (node == nullptr) + return err_allocation_failed; + + *output = node; + return nullptr; +} + +void ast_node_free_value(ast_node_t *node) { + // TODO: decide how value ownership will work and clean it up here +} + +void ast_node_free(ast_node_t *node) { + if (node == nullptr) + return; + if (node->children) { + for (size_t i = 0; i < node->len; ++i) + ast_node_free(node->children[i]); + free(node->children); + } + + ast_node_free_value(node); + + memset(node, 0, sizeof(ast_node_t)); + free(node); +} + +/** + * @pre node->children must be nullptr + */ +error_t *ast_node_alloc_children(ast_node_t *node) { + node->children = calloc(node_default_children_cap, sizeof(ast_node_t *)); + if (node->children == nullptr) + return err_allocation_failed; + + node->cap = node_default_children_cap; + return nullptr; +} + +error_t *ast_node_grow_cap(ast_node_t *node) { + if (node->cap >= node_max_children_cap) { + return err_node_children_cap; + } + + size_t new_cap = node->cap * 2; + if (new_cap > node_max_children_cap) { + new_cap = node_max_children_cap; + } + + ast_node_t **new_children = + realloc(node->children, new_cap * sizeof(ast_node_t *)); + if (new_children == nullptr) { + return err_allocation_failed; + } + + node->children = new_children; + node->cap = new_cap; + + return nullptr; +} + +error_t *ast_node_add_child(ast_node_t *node, ast_node_t *child) { + error_t *err = nullptr; + if (node->children == nullptr) + err = ast_node_alloc_children(node); + else if (node->len >= node->cap) + err = ast_node_grow_cap(node); + if (err) + return err; + + node->children[node->len] = child; + node->len += 1; + + return nullptr; +} diff --git a/src/ast.h b/src/ast.h new file mode 100644 index 0000000..2097910 --- /dev/null +++ b/src/ast.h @@ -0,0 +1,109 @@ +#ifndef INCLUDE_SRC_AST_H_ +#define INCLUDE_SRC_AST_H_ + +#include "error.h" +#include "lexer.h" +#include "tokenlist.h" +#include +#include + +typedef enum node_id { + NODE_INVALID, + + NODE_PROGRAM, + NODE_STATEMENT, + NODE_LABEL, + NODE_DIRECTIVE, + NODE_INSTRUCTION, + NODE_OPERANDS, + NODE_OPERAND, + NODE_IMMEDIATE, + NODE_MEMORY, + NODE_NUMBER, + NODE_LABEL_REFERENCE, + NODE_MEMORY_EXPRESSION, + NODE_REGISTER_EXPRESSION, + NODE_REGISTER_INDEX, + NODE_REGISTER_OFFSET, + NODE_PLUS_OR_MINUS, + NODE_SECTION_DIRECTIVE, + + // Validated primitives + NODE_REGISTER, + NODE_SECTION, + + // Primitive nodes + NODE_IDENTIFIER, + NODE_DECIMAL, + NODE_HEXADECIMAL, + NODE_OCTAL, + NODE_BINARY, + NODE_CHAR, + NODE_STRING, + NODE_COLON, + NODE_COMMA, + NODE_LBRACKET, + NODE_RBRACKET, + NODE_PLUS, + NODE_MINUS, + NODE_ASTERISK, + NODE_DOT, +} node_id_t; + +typedef struct ast_node ast_node_t; + +constexpr size_t node_default_children_cap = 8; +/* 65K ought to be enough for anybody */ +constexpr size_t node_max_children_cap = 1 << 16; + +struct ast_node { + node_id_t id; + tokenlist_entry_t *token_entry; + size_t len; + size_t cap; + ast_node_t **children; + + union { + struct { + uint64_t value; + int size; + } integer; + char *name; + } value; +}; + +/** + * @brief Allocates a new AST node + * + * Creates and initializes a new AST node with default (zero) values. + * + * @param[out] output Pointer to store the allocated node + * @return error_t* nullptr on success, allocation error on failure + */ +error_t *ast_node_alloc(ast_node_t **node); + +/** + * @brief Frees an AST node and all its children recursively + * + * Recursively frees all children of the node, then frees the node itself. + * If node is nullptr, the function returns without doing anything. + * + * @param node The node to free + */ +void ast_node_free(ast_node_t *node); + +/** + * @brief Adds a child node to a parent node + * + * Adds the specified child node to the parent's children array. + * If this is the first child, the function allocates the children array. + * If the children array is full, the function increases its capacity. + * + * @param node The parent node to add the child to + * @param child The child node to add + * @return error_t* nullptr on success, allocation error on failure, + * or err_node_children_cap if maximum capacity is reached + */ +error_t *ast_node_add_child(ast_node_t *node, ast_node_t *child); + +#endif // INCLUDE_SRC_AST_H_