use vyre::ir::{BufferAccess, BufferDecl, DataType, Expr, Node, Program};
use vyre_primitives::graph::vast_tree_walk;
use crate::region::{tag_program, wrap_anonymous};
const OP_ID: &str = "vyre-libs::graph::ast_walk_postorder";
#[must_use]
pub fn ast_walk_postorder(out: &str, node_count: u32) -> Program {
let out_words = node_count.max(1);
let body = vec![Node::loop_for(
"i",
Expr::u32(0),
Expr::u32(node_count),
vec![Node::store(
out,
Expr::var("i"),
Expr::sub(Expr::u32(node_count.saturating_sub(1)), Expr::var("i")),
)],
)];
Program::wrapped(
vec![
BufferDecl::storage(out, 0, BufferAccess::ReadWrite, DataType::U32)
.with_count(out_words),
],
[1, 1, 1],
vec![wrap_anonymous(
"vyre-libs::graph::ast_walk_postorder_spine",
body,
)],
)
}
#[must_use]
pub fn ast_walk_postorder_nodes(nodes: &str, out: &str, node_count: u32, out_cap: u32) -> Program {
tag_program(
OP_ID,
vast_tree_walk::ast_walk_postorder(nodes, out, node_count, out_cap),
)
}
inventory::submit! {
crate::harness::OpEntry {
id: OP_ID,
build: || ast_walk_postorder_nodes("nodes", "out", 6, 8),
test_inputs: Some(|| {
vec![vec![
super::ast_walk_preorder::pack_branching_fixture(),
vec![0u8; 32],
]]
}),
expected_output: Some(|| {
let node_region = super::ast_walk_preorder::pack_branching_fixture();
let Ok(order) = vyre_foundation::vast::walk_postorder_indices(&node_region, 6, 128)
else {
return Vec::new();
};
let mut out = vec![0u8; 32];
for (i, v) in order.into_iter().enumerate() {
out[i * 4..(i + 1) * 4].copy_from_slice(&v.to_le_bytes());
}
vec![vec![out]]
}),
category: Some("graph"),
}
}
#[cfg(test)]
mod tests {
use super::super::ast_walk_preorder::{pack_branching_fixture, pack_spine_fixture};
use super::*;
#[test]
fn postorder_matches_host_reverse_of_preorder_spine() {
let (_, node_region) = pack_spine_fixture(4);
let pre = vyre_foundation::vast::walk_preorder_indices(&node_region, 4, 128).unwrap();
let post = vyre_foundation::vast::walk_postorder_indices(&node_region, 4, 128).unwrap();
let rev: Vec<u32> = pre.iter().rev().copied().collect();
assert_eq!(post, rev);
let p = ast_walk_postorder("out", 4);
assert!(
vyre::validate(&p).is_empty(),
"postorder spine program must validate"
);
}
#[test]
fn postorder_handles_branching_siblings() {
let node_region = pack_branching_fixture();
let order = vyre_foundation::vast::walk_postorder_indices(&node_region, 6, 128).unwrap();
assert_eq!(order, vec![4, 1, 2, 5, 3, 0]);
let p = ast_walk_postorder_nodes("nodes", "out", 6, 8);
assert!(vyre::validate(&p).is_empty());
}
}