ast: implement postorder ast traversal

This commit is contained in:
2025-04-14 09:44:37 +01:00
parent ae5e438207
commit fd91bb71c0
3 changed files with 99 additions and 14 deletions

View File

@@ -1,18 +1,18 @@
#include <ivy/lang/ast.h>
#include "node.h"
enum ivy_status ivy_ast_node_iterate(
struct ivy_ast_node *node,
struct ivy_ast_node_iterator *it,
#include <ivy/lang/ast.h>
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;
}
}

View File

@@ -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
#endif