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}