use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use arena_lang::{Arena, Id};
use crate::{Node, Visitor, walk};
struct PostOrder<N> {
order: Vec<Id<N>>,
}
impl<N: Node> Visitor<N> for PostOrder<N> {
fn leave(&mut self, _arena: &Arena<N>, id: Id<N>, _node: &N) {
self.order.push(id);
}
}
pub fn transform<N, F>(src: &Arena<N>, root: Id<N>, dst: &mut Arena<N>, mut f: F) -> Option<Id<N>>
where
N: Node,
F: FnMut(N) -> N,
{
let mut order = PostOrder { order: Vec::new() };
walk(src, root, &mut order);
let mut remap: BTreeMap<Id<N>, Id<N>> = BTreeMap::new();
for id in order.order {
let Some(node) = src.get(id) else {
continue;
};
let rebuilt = node.map_children(&mut |child| remap.get(&child).copied().unwrap_or(child));
let new_id = dst.try_alloc(f(rebuilt)).ok()?;
let _ = remap.insert(id, new_id);
}
remap.get(&root).copied()
}
#[cfg(test)]
mod tests {
use crate::Span;
use super::*;
#[derive(Clone, PartialEq, Eq, Debug)]
enum E {
Leaf(i64, Span),
Neg(Id<E>, Span),
}
impl Node for E {
fn span(&self) -> Span {
match self {
E::Leaf(_, s) | E::Neg(_, s) => *s,
}
}
fn each_child(&self, f: &mut dyn FnMut(Id<Self>)) {
if let E::Neg(a, _) = self {
f(*a);
}
}
fn map_children(&self, f: &mut dyn FnMut(Id<Self>) -> Id<Self>) -> Self {
match self {
E::Leaf(v, s) => E::Leaf(*v, *s),
E::Neg(a, s) => E::Neg(f(*a), *s),
}
}
}
fn neg_leaf() -> (Arena<E>, Id<E>) {
let mut arena = Arena::new();
let leaf = arena.alloc(E::Leaf(7, Span::new(0, 1)));
let neg = arena.alloc(E::Neg(leaf, Span::new(0, 2)));
(arena, neg)
}
#[test]
fn test_identity_copies_into_destination() {
let (src, root) = neg_leaf();
let mut dst = Arena::new();
let new_root = transform(&src, root, &mut dst, |n| n).expect("live root");
assert_eq!(dst.len(), 2);
match dst.get(new_root) {
Some(E::Neg(child, s)) => {
assert_eq!(*s, Span::new(0, 2));
assert_eq!(dst.get(*child), Some(&E::Leaf(7, Span::new(0, 1))));
}
other => panic!("expected Neg, got {other:?}"),
}
}
#[test]
fn test_transform_applies_to_each_node() {
let (src, root) = neg_leaf();
let mut dst = Arena::new();
let new_root = transform(&src, root, &mut dst, |n| match n {
E::Leaf(v, s) => E::Leaf(v + 1, s),
other => other,
})
.expect("live root");
match dst.get(new_root) {
Some(E::Neg(child, _)) => {
assert_eq!(dst.get(*child), Some(&E::Leaf(8, Span::new(0, 1))));
}
other => panic!("expected Neg, got {other:?}"),
}
}
#[test]
fn test_invalid_root_returns_none_and_leaves_dst_empty() {
let (src, _root) = neg_leaf();
let mut big = Arena::new();
let mut far = big.alloc(E::Leaf(0, Span::new(0, 1)));
for _ in 0..10 {
far = big.alloc(E::Leaf(0, Span::new(0, 1)));
}
let mut dst = Arena::new();
assert_eq!(transform(&src, far, &mut dst, |n| n), None);
assert!(dst.is_empty());
}
}