Skip to main content

tree_morph/
engine_zipper.rs

1//! Define the EngineNodes and the EngineZipper
2
3use tracing::{instrument, trace};
4use uniplate::{Uniplate, tagged_zipper::TaggedZipper, zipper::Zipper};
5
6use crate::{cache::RewriteCache, events::EventHandlers, rule::Rule};
7
8#[derive(Debug, Clone)]
9pub(crate) struct EngineNodeState {
10    /// Rule groups with lower indices have already been applied without change.
11    /// For a level `n`, a state is 'dirty' if and only if `n >= dirty_from`.
12    dirty_from: usize,
13    pass_through_until: Option<usize>,
14}
15
16impl EngineNodeState {
17    /// Marks the state as dirty for anything >= `level`.
18    fn set_dirty_from(&mut self, level: usize) {
19        self.dirty_from = level;
20    }
21
22    /// For a level `n`, a state is "dirty" if and only if `n >= dirty_from`.
23    /// That is, all rules groups before `n` have been applied without change.
24    fn is_dirty(&self, level: usize) -> bool {
25        level >= self.dirty_from
26    }
27}
28
29impl EngineNodeState {
30    fn new<T: Uniplate>(_: &T) -> Self {
31        Self {
32            dirty_from: 0,
33            pass_through_until: None,
34        }
35    }
36}
37
38/// A Zipper with optimisations for tree transformation.
39pub(crate) struct EngineZipper<'a, T, M, R, C>
40where
41    T: Uniplate,
42    R: Rule<T, M>,
43    C: RewriteCache<T>,
44{
45    inner: TaggedZipper<T, EngineNodeState, fn(&T) -> EngineNodeState>,
46    event_handlers: &'a EventHandlers<T, M, R>,
47    down_predicate: fn(&T) -> bool,
48    pub(crate) cache: &'a mut C,
49    pub(crate) meta: M,
50}
51
52impl<'a, T, M, R, C> EngineZipper<'a, T, M, R, C>
53where
54    T: Uniplate,
55    R: Rule<T, M>,
56    C: RewriteCache<T>,
57{
58    pub fn new(
59        tree: T,
60        meta: M,
61        down_predicate: fn(&T) -> bool,
62        event_handlers: &'a EventHandlers<T, M, R>,
63        cache: &'a mut C,
64    ) -> Self {
65        EngineZipper {
66            inner: TaggedZipper::new(tree, EngineNodeState::new),
67            event_handlers,
68            down_predicate,
69            cache,
70            meta,
71        }
72    }
73
74    /// Returns a reference to the currently focused node.
75    pub fn focus(&self) -> &T {
76        self.inner.focus()
77    }
78
79    /// Returns a reference to the focused node and a mutable reference to the metadata.
80    /// This avoids borrow conflicts when both are needed simultaneously.
81    pub fn focus_and_meta(&mut self) -> (&T, &mut M) {
82        (self.inner.focus(), &mut self.meta)
83    }
84
85    /// Replaces the currently focused node with `replacement`.
86    pub fn replace_focus(&mut self, replacement: T) {
87        self.inner.replace_focus(replacement);
88    }
89
90    /// Go to the next node in the tree which is dirty for the given level.
91    /// That node may be the current one if it is dirty.
92    /// If no such node exists, go to the root and return `None`.
93    #[instrument(skip(self))]
94    pub fn go_next_dirty(&mut self, level: usize) -> Option<()> {
95        if self.inner.tag().is_dirty(level) {
96            let pass_through = self.inner.tag().pass_through_until;
97            match pass_through {
98                Some(n) if level <= n => {
99                    if level == n {
100                        self.inner.tag_mut().pass_through_until = None;
101                    }
102                    // Fall through to descend past this node
103                }
104                _ => {
105                    return Some(());
106                }
107            }
108        }
109
110        self.go_down()
111            .and_then(|_| {
112                // go right until we find a dirty child, if it exists.
113                loop {
114                    if self.inner.tag().is_dirty(level) {
115                        return Some(());
116                    } else if self.go_right().is_none() {
117                        // all children are clean
118                        self.go_up();
119                        return None;
120                    }
121                }
122            })
123            .or_else(|| {
124                // Neither this node, nor any of its children are dirty
125                // Go right then up until we find a dirty node or reach the root
126                loop {
127                    if self.go_right().is_some() {
128                        if self.inner.tag().is_dirty(level) {
129                            return Some(());
130                        }
131                    } else if self.go_up().is_none() {
132                        // Reached the root without finding a dirty node
133                        return None;
134                    }
135                }
136            })
137    }
138
139    fn go_down(&mut self) -> Option<()> {
140        if !(self.down_predicate)(self.inner.focus()) {
141            return None;
142        }
143        self.cache.push_ancestor(self.inner.focus());
144        if self.inner.go_down().is_none() {
145            self.cache.pop_ancestor(); // undo speculative push
146            return None;
147        }
148        trace!("Go down");
149        self.event_handlers
150            .trigger_after_down(self.inner.focus(), &mut self.meta);
151        Some(())
152    }
153
154    fn go_up(&mut self) -> Option<()> {
155        if !self.inner.zipper().has_up() {
156            return None;
157        }
158        self.event_handlers
159            .trigger_before_up(self.inner.focus(), &mut self.meta);
160        self.inner.go_up().expect("checked above");
161        self.cache.pop_ancestor();
162        trace!("Go up");
163        self.event_handlers
164            .trigger_after_up(self.inner.focus(), &mut self.meta);
165        Some(())
166    }
167
168    fn go_right(&mut self) -> Option<()> {
169        if !self.inner.zipper().has_right() {
170            return None;
171        }
172        self.event_handlers
173            .trigger_before_right(self.inner.focus(), &mut self.meta);
174        self.inner.go_right().expect("checked above");
175        trace!("Go right");
176        self.event_handlers
177            .trigger_after_right(self.inner.focus(), &mut self.meta);
178        Some(())
179    }
180
181    /// Trigger cache hit event handlers.
182    pub fn trigger_cache_hit(&mut self) {
183        self.event_handlers
184            .trigger_on_cache_hit(self.inner.focus(), &mut self.meta);
185    }
186
187    /// Trigger cache miss event handlers.
188    pub fn trigger_cache_miss(&mut self) {
189        self.event_handlers
190            .trigger_on_cache_miss(self.inner.focus(), &mut self.meta);
191    }
192
193    /// Mark the current focus as visited at the given level.
194    /// Calling `go_next_dirty` with the same level will no longer yield this node.
195    pub fn set_dirty_from(&mut self, level: usize) {
196        trace!("Setting level = {}", level);
197        self.inner.tag_mut().set_dirty_from(level);
198    }
199
200    /// Mark this node as pass-through
201    pub fn set_pass_through(&mut self, level: usize) {
202        self.inner.tag_mut().pass_through_until = Some(level);
203    }
204
205    /// Mark ancestors as dirty for all levels, and return to the root.
206    /// Pops ancestor hashes and inserts old→new ancestor mappings into the cache.
207    pub fn mark_dirty_to_root(&mut self, level: usize) {
208        trace!("Marking Dirty to Root");
209        self.set_dirty_from(0);
210        self.cache.invalidate_node(self.inner.focus());
211        while self.inner.zipper().has_up() {
212            self.event_handlers
213                .trigger_before_up(self.inner.focus(), &mut self.meta);
214            self.inner.go_up().expect("checked above");
215            self.set_dirty_from(0);
216            self.cache.invalidate_node(self.inner.focus());
217            self.cache.pop_and_map_ancestor(self.inner.focus(), level);
218            trace!("Go up (mark dirty)");
219            self.event_handlers
220                .trigger_after_up(self.inner.focus(), &mut self.meta);
221        }
222    }
223}
224
225impl<T, M, R, C> From<EngineZipper<'_, T, M, R, C>> for (T, M)
226where
227    T: Uniplate,
228    R: Rule<T, M>,
229    C: RewriteCache<T>,
230{
231    fn from(val: EngineZipper<'_, T, M, R, C>) -> Self {
232        let meta = val.meta;
233        let tree = val.inner.rebuild_root();
234        (tree, meta)
235    }
236}
237
238/// A Naive Zipper. For testing, debugging and benching
239pub(crate) struct NaiveZipper<'a, T, M, R, C>
240where
241    T: Uniplate,
242    R: Rule<T, M>,
243    C: RewriteCache<T>,
244{
245    inner: Zipper<T>,
246    event_handlers: &'a EventHandlers<T, M, R>,
247    down_predicate: fn(&T) -> bool,
248    pub(crate) cache: &'a mut C,
249    pub(crate) meta: M,
250}
251
252impl<'a, T, M, R, C> NaiveZipper<'a, T, M, R, C>
253where
254    T: Uniplate,
255    R: Rule<T, M>,
256    C: RewriteCache<T>,
257{
258    pub fn new(
259        tree: T,
260        meta: M,
261        down_predicate: fn(&T) -> bool,
262        event_handlers: &'a EventHandlers<T, M, R>,
263        cache: &'a mut C,
264    ) -> Self {
265        NaiveZipper {
266            inner: Zipper::new(tree),
267            event_handlers,
268            down_predicate,
269            cache,
270            meta,
271        }
272    }
273
274    /// Returns a reference to the currently focused node.
275    pub fn focus(&self) -> &T {
276        self.inner.focus()
277    }
278
279    /// Returns a reference to the focused node and a mutable reference to the metadata.
280    pub fn focus_and_meta(&mut self) -> (&T, &mut M) {
281        (self.inner.focus(), &mut self.meta)
282    }
283
284    /// Replaces the currently focused node with `replacement`.
285    pub fn replace_focus(&mut self, replacement: T) {
286        self.inner.replace_focus(replacement);
287    }
288
289    /// Trigger cache hit event handlers.
290    pub fn trigger_cache_hit(&mut self) {
291        self.event_handlers
292            .trigger_on_cache_hit(self.inner.focus(), &mut self.meta);
293    }
294
295    /// Trigger cache miss event handlers.
296    pub fn trigger_cache_miss(&mut self) {
297        self.event_handlers
298            .trigger_on_cache_miss(self.inner.focus(), &mut self.meta);
299    }
300
301    /// Consumes the zipper and returns the reconstructed root and metadata.
302    pub fn into_parts(self) -> (T, M) {
303        (self.inner.rebuild_root(), self.meta)
304    }
305
306    pub fn get_next(&mut self) -> Option<()> {
307        // Try going down — speculative push, undo on failure
308        // Do not descend if the down predicate rejects this node.
309        if (self.down_predicate)(self.inner.focus()) {
310            self.cache.push_ancestor(self.inner.focus());
311            if self.inner.go_down().is_some() {
312                return Some(());
313            }
314            self.cache.pop_ancestor();
315        }
316        if self.inner.go_right().is_some() {
317            return Some(());
318        }
319        while self.inner.go_up().is_some() {
320            self.cache.pop_ancestor();
321            if self.inner.go_right().is_some() {
322                return Some(());
323            }
324        }
325        None
326    }
327
328    /// Walk back to root, inserting ancestor mappings at the given level.
329    pub fn map_ancestors_to_root(&mut self, level: usize) {
330        while self.inner.has_up() {
331            self.event_handlers
332                .trigger_before_up(self.inner.focus(), &mut self.meta);
333            self.inner.go_up().expect("checked above");
334            self.cache.invalidate_node(self.inner.focus());
335            self.cache.pop_and_map_ancestor(self.inner.focus(), level);
336            trace!("Go up (map ancestor)");
337            self.event_handlers
338                .trigger_after_up(self.inner.focus(), &mut self.meta);
339        }
340    }
341}