From fd91bb71c09a6b322934d0bc14773f8279d0046b Mon Sep 17 00:00:00 2001 From: Max Wash Date: Mon, 14 Apr 2025 09:44:37 +0100 Subject: [PATCH] ast: implement postorder ast traversal --- lang/ast/iterate.c | 101 ++++++++++++++++++++++++++++++++---- lang/ast/iterate.h | 5 +- lang/include/ivy/lang/ast.h | 7 ++- 3 files changed, 99 insertions(+), 14 deletions(-) diff --git a/lang/ast/iterate.c b/lang/ast/iterate.c index 2c95801..e3895c7 100644 --- a/lang/ast/iterate.c +++ b/lang/ast/iterate.c @@ -1,18 +1,18 @@ -#include #include "node.h" -enum ivy_status ivy_ast_node_iterate( - struct ivy_ast_node *node, - struct ivy_ast_node_iterator *it, +#include + +enum ivy_status iterate_regular( + struct ivy_ast_node *node, struct ivy_ast_node_iterator *it, ivy_ast_node_iteration_callback callback) { - memset(it, 0x0, sizeof *it); b_queue_push_back(&it->it_queue, &node->n_it.it_entry); node->n_it.it_depth = 0; while (!b_queue_empty(&it->it_queue)) { b_queue_entry *entry = b_queue_first(&it->it_queue); - struct ivy_ast_node_iterator_entry *it_entry = b_unbox(struct ivy_ast_node_iterator_entry, entry, it_entry); + struct ivy_ast_node_iterator_entry *it_entry = b_unbox( + struct ivy_ast_node_iterator_entry, entry, it_entry); node = b_unbox(struct ivy_ast_node, it_entry, n_it); if (!node) { @@ -37,17 +37,98 @@ enum ivy_status ivy_ast_node_iterate( return IVY_OK; } +enum ivy_status iterate_postorder( + struct ivy_ast_node *node, struct ivy_ast_node_iterator *it, + ivy_ast_node_iteration_callback callback) +{ + /* general iteration strategy: + * 1. create a queue. + * 2. push `node` to the end of the queue. + * 3. for each node N in the queue: + * 1. collect N's children. + * 2. insert the children into the queue between N and the node + * immediately following N. + * 4. the loop in step 3 continues over all nodes added within the loop, + * and stops when all nodes have been visited. + * 5. iterate over the completed queue in reverse order, calling + * `callback` on each one. + * + * NOTES: + * - not sure how this will work for package definitions. + * - should probably use this just for the executable parts of the AST, + * not for nodes defining classes, function metadata, etc. + */ + b_queue_push_back(&it->it_queue, &node->n_it.it_entry); + node->n_it.it_depth = 0; + + b_queue_iterator q_it; + b_queue_iterator_begin(&it->it_queue, &q_it); + + while (b_queue_iterator_is_valid(&q_it)) { + struct ivy_ast_node_iterator_entry *it_entry = b_unbox( + struct ivy_ast_node_iterator_entry, q_it.entry, it_entry); + node = b_unbox(struct ivy_ast_node, it_entry, n_it); + + if (!node) { + /* this should never happen. */ + return IVY_ERR_INTERNAL_FAILURE; + } + + const struct ast_node_type *type = get_ast_node_type(node->n_type); + if (type->n_collect_children) { + it->it_insert_after = q_it.entry; + type->n_collect_children(node, it); + } + + b_queue_iterator_next(&q_it); + } + + while (!b_queue_empty(&it->it_queue)) { + b_queue_entry *entry = b_queue_pop_back(&it->it_queue); + if (!entry) { + break; + } + + node = b_unbox(struct ivy_ast_node, entry, n_it); + + if (!node) { + /* this should never happen. */ + return IVY_ERR_INTERNAL_FAILURE; + } + + callback(node, it); + } + + return IVY_OK; +} + +enum ivy_status ivy_ast_node_iterate( + struct ivy_ast_node *node, struct ivy_ast_node_iterator *it, + enum ivy_ast_iterator_mode mode, ivy_ast_node_iteration_callback callback) +{ + memset(it, 0x0, sizeof *it); + + switch (mode) { + case IVY_AST_ITERATE_REGULAR: + return iterate_regular(node, it, callback); + case IVY_AST_ITERATE_POSTORDER: + return iterate_postorder(node, it, callback); + default: + return IVY_ERR_INVALID_VALUE; + } +} + void ast_node_iterator_enqueue_node( - struct ivy_ast_node_iterator *it, - struct ivy_ast_node *parent, + struct ivy_ast_node_iterator *it, struct ivy_ast_node *parent, struct ivy_ast_node *node) { if (it->it_insert_after) { - b_queue_insert_after(&it->it_queue, &node->n_it.it_entry, it->it_insert_after); + b_queue_insert_after( + &it->it_queue, &node->n_it.it_entry, it->it_insert_after); } else { b_queue_push_back(&it->it_queue, &node->n_it.it_entry); } node->n_it.it_depth = parent->n_it.it_depth + 1; it->it_insert_after = &node->n_it.it_entry; -} \ No newline at end of file +} diff --git a/lang/ast/iterate.h b/lang/ast/iterate.h index 44d5cb0..5d5f70f 100644 --- a/lang/ast/iterate.h +++ b/lang/ast/iterate.h @@ -2,8 +2,7 @@ #define _AST_ITERATE_H_ extern void ast_node_iterator_enqueue_node( - struct ivy_ast_node_iterator *it, - struct ivy_ast_node *parent, + struct ivy_ast_node_iterator *it, struct ivy_ast_node *parent, struct ivy_ast_node *node); -#endif \ No newline at end of file +#endif diff --git a/lang/include/ivy/lang/ast.h b/lang/include/ivy/lang/ast.h index 95a5a25..e273c27 100644 --- a/lang/include/ivy/lang/ast.h +++ b/lang/include/ivy/lang/ast.h @@ -54,6 +54,11 @@ enum ivy_ast_node_type { IVY_AST_TYPE_COUNT, }; +enum ivy_ast_iterator_mode { + IVY_AST_ITERATE_REGULAR, + IVY_AST_ITERATE_POSTORDER, +}; + struct ivy_ast_node_iterator { b_queue it_queue; b_queue_entry *it_insert_after; @@ -327,7 +332,7 @@ IVY_API enum ivy_status ivy_parser_push_token( IVY_API enum ivy_status ivy_ast_node_iterate( struct ivy_ast_node *node, struct ivy_ast_node_iterator *it, - ivy_ast_node_iteration_callback callback); + enum ivy_ast_iterator_mode mode, ivy_ast_node_iteration_callback callback); IVY_API void ivy_ast_node_to_string(struct ivy_ast_node *node, struct b_string *out); IVY_API void ivy_ast_node_destroy(struct ivy_ast_node *node);