diff --git a/include/ast.h b/include/ast.h index a7a8a65..5838b49 100644 --- a/include/ast.h +++ b/include/ast.h @@ -47,4 +47,34 @@ typedef struct ASTNode { } extra; } ASTNode; + +/** + * Allocate a new AST Node, and initialize it with the given type + * + * @param type The type of the AST Node + * + * @return A pointer to a heap allocated AST Node on success, + * NULL on failure + */ +ASTNode* ast_node_create(ASTNodeType type); + + +/** + * Initialize an AST Node + * + * @param node A Pointer to the AST Node to initialize + * @param type The type of the AST Node + * + * @return 0 on success, -1 on failure + */ +int ast_node_init(ASTNode* node, ASTNodeType type); + + +/** + * Release memory allocated for the given AST Node + * + * @param node The AST Node to free + */ +void ast_node_free(ASTNode* node); + #endif // REGEX_AST diff --git a/src/ast.c b/src/ast.c new file mode 100644 index 0000000..a407121 --- /dev/null +++ b/src/ast.c @@ -0,0 +1,53 @@ +#include + +#include "ast.h" + +// Allocate a new AST Node, and initialize it with the given type +ASTNode* ast_node_create(ASTNodeType type) { + ASTNode* node = malloc(sizeof(ASTNode)); + + // Initialize the created node, this implicitly checks for NULL + if (ast_node_init(node, type) != 0) { + free(node); // free of NULL is a no-op + return NULL; + } + + return node; +} + + +// Initialize an AST Node +int ast_node_init(ASTNode* node, ASTNodeType type) { + if (node == NULL) { + return -1; + } + + *node = (ASTNode){ + .type = type, + .child1 = NULL, + .extra = {0}, + }; + + return 0; +} + + +// Release memory allocated for the given AST Node +void ast_node_free(ASTNode* node) { + if (node == NULL) { + return; + } + + if (node->type == CHAR_NODE) { + free(node); + return; + } + + ast_node_free(node->child1); + + if (node->type == OR_NODE || node->type == CONCAT_NODE) { + ast_node_free(node->extra.child2); + } + + free(node); +} diff --git a/tests/test_ast.c b/tests/test_ast.c new file mode 100644 index 0000000..eb39d5f --- /dev/null +++ b/tests/test_ast.c @@ -0,0 +1,72 @@ +#include "testlib/asserts.h" +#include "testlib/tests.h" +#include "ast.h" + +// Test creating AST dynamically +int test_ast_create() { + TEST_BEGIN; + ASTNodeType types[] = { + CHAR_NODE, STAR_NODE, PLUS_NODE, + OR_NODE, QUESTION_NODE, CONCAT_NODE, + }; + + size_t n_types = sizeof(types) / sizeof(ASTNodeType); + + for (size_t i = 0; i < n_types; i++) { + ASTNode* node = ast_node_create(types[i]); + + assert_is_not_null(node); + assert_equals_int(node->type, types[i]); + ast_node_free(node); + } + TEST_END; +} + +int test_ast_init() { + TEST_BEGIN; + ASTNodeType types[] = { + CHAR_NODE, STAR_NODE, PLUS_NODE, + OR_NODE, QUESTION_NODE, CONCAT_NODE, + }; + + size_t n_types = sizeof(types) / sizeof(ASTNodeType); + + ASTNode node; + for (size_t i = 0; i < n_types; i++) { + int ret = ast_node_init(&node, types[i]); + + assert_equals_int(ret, 0); + assert_equals_int(node.type, types[i]); + } + + int ret = ast_node_init(NULL, *types); + assert_equals_int(ret, -1); + + TEST_END; +} + + +Test tests[] = { + {.name="test_ast_create", .func=test_ast_create}, + {.name="test_ast_init", .func=test_ast_init}, + {.name=NULL}, +}; + + +int main(int argc, char* argv[]) { + TestOpts opts; + int ret = parse_test_opts(&opts, &argv[1], argc - 1); + if (ret != 0) { + printf("%s\n", str_parse_error(ret)); + exit(1); + } + + const TestSuite* suite = create_test_suite(&opts); + + int n_failures = run_test_suite(suite); + + free_opts(&opts); + free_test_suite(suite); + + return n_failures; +}