Add basic AST functionality
This commit is contained in:
		
							
								
								
									
										84
									
								
								src/ast.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								src/ast.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | |||||||
|  | #include "ast.h" | ||||||
|  | #include "error.h" | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | 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]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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; | ||||||
|  | } | ||||||
							
								
								
									
										72
									
								
								src/ast.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/ast.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | #ifndef INCLUDE_SRC_AST_H_ | ||||||
|  | #define INCLUDE_SRC_AST_H_ | ||||||
|  |  | ||||||
|  | #include "error.h" | ||||||
|  | #include "lexer.h" | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <stdint.h> | ||||||
|  |  | ||||||
|  | typedef enum node_id { | ||||||
|  |     NODE_PROGRAM, | ||||||
|  |     NODE_DIRECTIVE, | ||||||
|  |     NODE_LABEL, | ||||||
|  |     NODE_INSTRUCTION | ||||||
|  | } 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; | ||||||
|  |     lexer_token_t *token; | ||||||
|  |     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_ | ||||||
		Reference in New Issue
	
	Block a user