use alloc::vec::Vec;
use arena_lang::{Arena, Id};
use crate::Node;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Flow {
#[default]
Continue,
SkipChildren,
Stop,
}
pub trait Visitor<N: Node> {
fn enter(&mut self, arena: &Arena<N>, id: Id<N>, node: &N) -> Flow {
let _ = (arena, id, node);
Flow::Continue
}
fn leave(&mut self, arena: &Arena<N>, id: Id<N>, node: &N) {
let _ = (arena, id, node);
}
}
enum Step<N> {
Enter(Id<N>),
Leave(Id<N>),
}
pub fn walk<N, V>(arena: &Arena<N>, root: Id<N>, visitor: &mut V)
where
N: Node,
V: Visitor<N> + ?Sized,
{
let mut stack: Vec<Step<N>> = Vec::new();
stack.push(Step::Enter(root));
let mut scratch: Vec<Id<N>> = Vec::new();
while let Some(step) = stack.pop() {
match step {
Step::Enter(id) => {
let Some(node) = arena.get(id) else {
continue;
};
match visitor.enter(arena, id, node) {
Flow::Stop => return,
Flow::SkipChildren => visitor.leave(arena, id, node),
Flow::Continue => {
stack.push(Step::Leave(id));
scratch.clear();
node.each_child(&mut |child| scratch.push(child));
while let Some(child) = scratch.pop() {
stack.push(Step::Enter(child));
}
}
}
}
Step::Leave(id) => {
if let Some(node) = arena.get(id) {
visitor.leave(arena, id, node);
}
}
}
}
}
#[cfg(test)]
mod tests {
use crate::Span;
use super::*;
enum E {
Leaf(u32, Span),
Pair(Id<E>, Id<E>, Span),
}
impl Node for E {
fn span(&self) -> Span {
match self {
E::Leaf(_, s) | E::Pair(_, _, s) => *s,
}
}
fn each_child(&self, f: &mut dyn FnMut(Id<Self>)) {
if let E::Pair(a, b, _) = self {
f(*a);
f(*b);
}
}
fn map_children(&self, f: &mut dyn FnMut(Id<Self>) -> Id<Self>) -> Self {
match self {
E::Leaf(v, s) => E::Leaf(*v, *s),
E::Pair(a, b, s) => E::Pair(f(*a), f(*b), *s),
}
}
}
#[derive(Default)]
struct Tags(Vec<u32>);
impl Visitor<E> for Tags {
fn enter(&mut self, _: &Arena<E>, _: Id<E>, node: &E) -> Flow {
if let E::Leaf(v, _) = node {
self.0.push(*v);
}
Flow::Continue
}
}
fn pair_tree() -> (Arena<E>, Id<E>) {
let mut arena = Arena::new();
let a = arena.alloc(E::Leaf(1, Span::new(0, 1)));
let b = arena.alloc(E::Leaf(2, Span::new(1, 2)));
let root = arena.alloc(E::Pair(a, b, Span::new(0, 2)));
(arena, root)
}
#[test]
fn test_flow_default_is_continue() {
assert_eq!(Flow::default(), Flow::Continue);
}
#[test]
fn test_walk_enters_leaves_left_to_right() {
let (arena, root) = pair_tree();
let mut tags = Tags::default();
walk(&arena, root, &mut tags);
assert_eq!(tags.0, [1, 2]);
}
#[test]
fn test_walk_on_missing_root_is_a_noop() {
let arena: Arena<E> = Arena::new();
let (other, root) = pair_tree();
let _ = &other;
let mut tags = Tags::default();
walk(&arena, root, &mut tags);
assert!(tags.0.is_empty());
}
#[test]
fn test_skip_children_prunes_descendants() {
struct SkipRoot {
leaves: u32,
}
impl Visitor<E> for SkipRoot {
fn enter(&mut self, _: &Arena<E>, _: Id<E>, node: &E) -> Flow {
match node {
E::Pair(..) => Flow::SkipChildren,
E::Leaf(..) => {
self.leaves += 1;
Flow::Continue
}
}
}
}
let (arena, root) = pair_tree();
let mut v = SkipRoot { leaves: 0 };
walk(&arena, root, &mut v);
assert_eq!(v.leaves, 0); }
}