Skip to main content

ast_lang/
transform.rs

1//! Tree rewriting: the [`transform`] driver.
2
3use alloc::collections::BTreeMap;
4use alloc::vec::Vec;
5
6use arena_lang::{Arena, Id};
7
8use crate::{Node, Visitor, walk};
9
10/// Collects node handles in post-order (children before their parent) by leaving
11/// each node into a list as the walk unwinds.
12struct PostOrder<N> {
13    order: Vec<Id<N>>,
14}
15
16impl<N: Node> Visitor<N> for PostOrder<N> {
17    fn leave(&mut self, _arena: &Arena<N>, id: Id<N>, _node: &N) {
18        self.order.push(id);
19    }
20}
21
22/// Rebuilds the tree rooted at `root` from `src` into `dst`, passing every node
23/// through `f`, and returns the handle of the new root.
24///
25/// This is the rewrite (fold) operation: a copy of the subtree is built in the
26/// destination arena, and `f` gets a chance to replace each node as it is rebuilt.
27/// Nodes are processed **post-order** — every child is rebuilt and allocated into
28/// `dst` before its parent — so when `f` receives a node, that node's child handles
29/// have already been remapped to point at their new selves in `dst`. With `f` set
30/// to the identity (`|node| node`), the result is a faithful deep copy: same shape,
31/// same span on every node.
32///
33/// The traversal is the iterative [`walk`], so an arbitrarily deep tree is rebuilt
34/// without recursing on the call stack. It assumes a tree — each node reachable
35/// from `root` exactly once; a shared sub-node would be copied once per path to it.
36///
37/// # Returns
38///
39/// The handle of the rebuilt root in `dst`, or `None` if `root` names no live node
40/// in `src`, or if `dst` runs out of capacity while allocating. Either way `dst`
41/// is left holding whatever was allocated before the stop, and never panics.
42///
43/// # Examples
44///
45/// Deep-copy a tree, doubling every literal on the way:
46///
47/// ```
48/// use ast_lang::{transform, walk, Arena, Flow, Id, Node, Span, Visitor};
49///
50/// #[derive(Clone)]
51/// enum Expr {
52///     Lit(i64, Span),
53///     Add(Id<Expr>, Id<Expr>, Span),
54/// }
55///
56/// impl Node for Expr {
57///     fn span(&self) -> Span {
58///         match self {
59///             Expr::Lit(_, s) | Expr::Add(_, _, s) => *s,
60///         }
61///     }
62///     fn each_child(&self, f: &mut dyn FnMut(Id<Self>)) {
63///         if let Expr::Add(a, b, _) = self {
64///             f(*a);
65///             f(*b);
66///         }
67///     }
68///     fn map_children(&self, f: &mut dyn FnMut(Id<Self>) -> Id<Self>) -> Self {
69///         match self {
70///             Expr::Lit(v, s) => Expr::Lit(*v, *s),
71///             Expr::Add(a, b, s) => Expr::Add(f(*a), f(*b), *s),
72///         }
73///     }
74/// }
75///
76/// let mut src = Arena::new();
77/// let one = src.alloc(Expr::Lit(1, Span::new(0, 1)));
78/// let two = src.alloc(Expr::Lit(2, Span::new(4, 5)));
79/// let add = src.alloc(Expr::Add(one, two, Span::new(0, 5)));
80///
81/// let mut dst = Arena::new();
82/// let new_root = transform(&src, add, &mut dst, |node| match node {
83///     Expr::Lit(v, s) => Expr::Lit(v * 2, s),
84///     other => other,
85/// })
86/// .expect("root is live");
87///
88/// // The rebuilt tree sums to 2*(1) + 2*(2) = 6.
89/// struct Sum(i64);
90/// impl Visitor<Expr> for Sum {
91///     fn enter(&mut self, _: &Arena<Expr>, _: Id<Expr>, node: &Expr) -> Flow {
92///         if let Expr::Lit(v, _) = node {
93///             self.0 += *v;
94///         }
95///         Flow::Continue
96///     }
97/// }
98/// let mut sum = Sum(0);
99/// walk(&dst, new_root, &mut sum);
100/// assert_eq!(sum.0, 6);
101/// ```
102pub fn transform<N, F>(src: &Arena<N>, root: Id<N>, dst: &mut Arena<N>, mut f: F) -> Option<Id<N>>
103where
104    N: Node,
105    F: FnMut(N) -> N,
106{
107    // Visit the source post-order so every child is rebuilt before its parent.
108    let mut order = PostOrder { order: Vec::new() };
109    walk(src, root, &mut order);
110
111    // Map each source handle to its rebuilt handle in `dst`. Because the order is
112    // post-order, a parent's children are always already in the map when it is
113    // rebuilt, so `map_children` can substitute their new handles.
114    let mut remap: BTreeMap<Id<N>, Id<N>> = BTreeMap::new();
115    for id in order.order {
116        let Some(node) = src.get(id) else {
117            continue;
118        };
119        let rebuilt = node.map_children(&mut |child| remap.get(&child).copied().unwrap_or(child));
120        let new_id = dst.try_alloc(f(rebuilt)).ok()?;
121        let _ = remap.insert(id, new_id);
122    }
123
124    remap.get(&root).copied()
125}
126
127#[cfg(test)]
128mod tests {
129    use crate::Span;
130
131    use super::*;
132
133    #[derive(Clone, PartialEq, Eq, Debug)]
134    enum E {
135        Leaf(i64, Span),
136        Neg(Id<E>, Span),
137    }
138
139    impl Node for E {
140        fn span(&self) -> Span {
141            match self {
142                E::Leaf(_, s) | E::Neg(_, s) => *s,
143            }
144        }
145        fn each_child(&self, f: &mut dyn FnMut(Id<Self>)) {
146            if let E::Neg(a, _) = self {
147                f(*a);
148            }
149        }
150        fn map_children(&self, f: &mut dyn FnMut(Id<Self>) -> Id<Self>) -> Self {
151            match self {
152                E::Leaf(v, s) => E::Leaf(*v, *s),
153                E::Neg(a, s) => E::Neg(f(*a), *s),
154            }
155        }
156    }
157
158    fn neg_leaf() -> (Arena<E>, Id<E>) {
159        let mut arena = Arena::new();
160        let leaf = arena.alloc(E::Leaf(7, Span::new(0, 1)));
161        let neg = arena.alloc(E::Neg(leaf, Span::new(0, 2)));
162        (arena, neg)
163    }
164
165    #[test]
166    fn test_identity_copies_into_destination() {
167        let (src, root) = neg_leaf();
168        let mut dst = Arena::new();
169        let new_root = transform(&src, root, &mut dst, |n| n).expect("live root");
170        assert_eq!(dst.len(), 2);
171        match dst.get(new_root) {
172            Some(E::Neg(child, s)) => {
173                assert_eq!(*s, Span::new(0, 2));
174                assert_eq!(dst.get(*child), Some(&E::Leaf(7, Span::new(0, 1))));
175            }
176            other => panic!("expected Neg, got {other:?}"),
177        }
178    }
179
180    #[test]
181    fn test_transform_applies_to_each_node() {
182        let (src, root) = neg_leaf();
183        let mut dst = Arena::new();
184        let new_root = transform(&src, root, &mut dst, |n| match n {
185            E::Leaf(v, s) => E::Leaf(v + 1, s),
186            other => other,
187        })
188        .expect("live root");
189        // The negated leaf's value was bumped 7 -> 8.
190        match dst.get(new_root) {
191            Some(E::Neg(child, _)) => {
192                assert_eq!(dst.get(*child), Some(&E::Leaf(8, Span::new(0, 1))));
193            }
194            other => panic!("expected Neg, got {other:?}"),
195        }
196    }
197
198    #[test]
199    fn test_invalid_root_returns_none_and_leaves_dst_empty() {
200        let (src, _root) = neg_leaf();
201        // A handle from a larger arena names a slot `src` lacks.
202        let mut big = Arena::new();
203        let mut far = big.alloc(E::Leaf(0, Span::new(0, 1)));
204        for _ in 0..10 {
205            far = big.alloc(E::Leaf(0, Span::new(0, 1)));
206        }
207        let mut dst = Arena::new();
208        assert_eq!(transform(&src, far, &mut dst, |n| n), None);
209        assert!(dst.is_empty());
210    }
211}