1
//! Perform gradual rule-based transformations on trees.
2
//!
3
//! See the [`morph`](Engine::morph) for more information.
4

            
5
use crate::events::EventHandlers;
6
use crate::helpers::{SelectorFn, one_or_select};
7
use crate::prelude::Rule;
8
use crate::rule::apply_into_update;
9

            
10
use paste::paste;
11
use uniplate::{Uniplate, tagged_zipper::TaggedZipper};
12

            
13
/// An engine for exhaustively transforming trees with user-defined rules.
14
///
15
/// See the [`morph`](Engine::morph) method for more information.
16
pub struct Engine<T, M, R>
17
where
18
    T: Uniplate,
19
    R: Rule<T, M>,
20
{
21
    pub(crate) event_handlers: EventHandlers<T, M>,
22

            
23
    /// A collection of groups of equally-prioritised rules.
24
    pub(crate) rule_groups: Vec<Vec<R>>,
25

            
26
    pub(crate) selector: SelectorFn<T, M, R>,
27
}
28

            
29
impl<T, M, R> Engine<T, M, R>
30
where
31
    T: Uniplate,
32
    R: Rule<T, M>,
33
{
34
    /// Exhaustively rewrites a tree using user-defined rule groups.
35
    ///
36
    /// Rewriting is complete when all rules have been attempted with no change. Rules may be organised
37
    /// into groups to control the order in which they are attempted.
38
    ///
39
    /// # Rule Groups
40
    /// If all rules are treated equally, those which apply higher in the tree will take precedence
41
    /// because of the left-most outer-most traversal order of the engine.
42
    ///
43
    /// This can cause problems if a rule which should ideally be applied early (e.g. evaluating
44
    /// constant expressions) is left until later.
45
    ///
46
    /// To solve this, rules are organised into different collections or "groups".
47
    /// The engine will apply rules in an earlier group to the entire tree before trying later groups.
48
    /// That is, no rule is attempted if a rule in an earlier group is applicable to any part of the
49
    /// tree.
50
    ///
51
    /// # Selector Functions
52
    ///
53
    /// If multiple rules in the same group are applicable to an expression, the user-defined
54
    /// selector function is used to choose one. This function is given an iterator over pairs of
55
    /// rules and the engine-created [`Update`] values which contain their modifications to the tree.
56
    ///
57
    /// Some useful selector functions are available in the [`helpers`](crate::helpers) module. One
58
    /// common function is [`select_first`](crate::helpers::select_first), which simply returns the
59
    /// first applicable rule.
60
    ///
61
    /// # Event Handlers
62
    ///
63
    /// The engine provides events for more fine-tuned control over rewriting behaviour. Events have mutable
64
    /// access to the current metadata.
65
    ///
66
    /// The engine will call the provided handlers for the "enter" and
67
    /// "exits" events as it enters and exits subtrees while traversing, respectively.
68
    ///
69
    /// The "enter" event is triggered first on the root, and then whenever the engine moves down
70
    /// into a subtree. As a result, when a node is passed to rules, all nodes above it will have
71
    /// been passed to handlers for this event, in ascending order of their proximity to the root.
72
    ///
73
    /// The "exit" event is triggered when the engine leaves a subtree.
74
    /// All nodes passed to "enter" event handlers (except the root) will also be passed to "exit"
75
    /// event handlers in reverse order.
76
    ///
77
    /// In effect, before a node is passed to rules, all nodes in the path from the root (including the
78
    /// current node) will have been passed to the "enter" event in order. No nodes are skipped.
79
    ///
80
    /// Event handling is useful when, for example, using a symbol table to keep track of variable definitions.
81
    /// When entering a scope where a variable is defined, one can place the variable and its value into the table.
82
    /// That stack can then be used for quick value lookup while inside the scope. When leaving the scope the
83
    /// variable can be removed from the table.
84
    ///
85
    /// # Optimizations
86
    ///
87
    /// To optimize the morph function, we use a dirty/clean approach. Since whether a rule applies
88
    /// is purely a function of a node and its children, rules should only be checked once unless a node
89
    /// or one of its children has been changed.
90
    ///
91
    /// # Example
92
    /// ```rust
93
    /// use tree_morph::{prelude::*, helpers::select_panic};
94
    /// use uniplate::Uniplate;
95
    ///
96
    ///
97
    /// // A simple language of multiplied and squared constant expressions
98
    /// #[derive(Debug, Clone, PartialEq, Eq, Uniplate)]
99
    /// #[uniplate()]
100
    /// enum Expr {
101
    ///     Val(i32),
102
    ///     Mul(Box<Expr>, Box<Expr>),
103
    ///     Sqr(Box<Expr>),
104
    /// }
105
    ///
106
    /// // a * b ~> (value of) a * b, where 'a' and 'b' are literal values
107
    /// fn rule_eval_mul(cmds: &mut Commands<Expr, i32>, subtree: &Expr, meta: &i32) -> Option<Expr> {
108
    ///     cmds.mut_meta(Box::new(|m: &mut i32| *m += 1));
109
    ///
110
    ///     if let Expr::Mul(a, b) = subtree {
111
    ///         if let (Expr::Val(a_v), Expr::Val(b_v)) = (a.as_ref(), b.as_ref()) {
112
    ///             return Some(Expr::Val(a_v * b_v));
113
    ///         }
114
    ///     }
115
    ///     None
116
    /// }
117
    ///
118
    /// // e ^ 2 ~> e * e, where e is an expression
119
    /// // If this rule is applied before the sub-expression is fully evaluated, duplicate work
120
    /// // will be done on the resulting two identical sub-expressions.
121
    /// fn rule_expand_sqr(cmds: &mut Commands<Expr, i32>, subtree: &Expr, meta: &i32) -> Option<Expr> {
122
    ///     cmds.mut_meta(Box::new(|m: &mut i32| *m += 1));
123
    ///
124
    ///     if let Expr::Sqr(expr) = subtree {
125
    ///         return Some(Expr::Mul(
126
    ///             Box::new(*expr.clone()),
127
    ///             Box::new(*expr.clone())
128
    ///         ));
129
    ///     }
130
    ///     None
131
    /// }
132
    ///
133
    /// // (1 * 2) ^ 2
134
    /// let expr = Expr::Sqr(
135
    ///     Box::new(Expr::Mul(
136
    ///         Box::new(Expr::Val(1)),
137
    ///         Box::new(Expr::Val(2))
138
    ///     ))
139
    /// );
140
    ///
141
    /// // Try with both rules in the same group, keeping track of the number of rule applications
142
    /// let engine = EngineBuilder::new()
143
    ///     .set_selector(select_panic)
144
    ///     .add_rule_group(rule_fns![rule_eval_mul, rule_expand_sqr])
145
    ///     .build();
146
    /// let (result, num_applications) = engine.morph(expr.clone(), 0);
147
    /// assert_eq!(result, Expr::Val(4));
148
    /// assert_eq!(num_applications, 4); // The `Sqr` is expanded first, causing duplicate work
149
    ///
150
    /// // Move the evaluation rule to an earlier group
151
    /// let engine = EngineBuilder::new()
152
    ///     .set_selector(select_panic)
153
    ///     .add_rule_group(rule_fns![rule_eval_mul])
154
    ///     .add_rule_group(rule_fns![rule_expand_sqr])
155
    ///     .build();
156
    /// let (result, num_applications) = engine.morph(expr.clone(), 0);
157
    /// assert_eq!(result, Expr::Val(4));
158
    /// assert_eq!(num_applications, 3); // Now the sub-expression (1 * 2) is evaluated first
159
    /// ```
160
    pub fn morph(&self, tree: T, meta: M) -> (T, M)
161
    where
162
        T: Uniplate,
163
        R: Rule<T, M>,
164
    {
165
        // Owns the tree/meta and is consumed to get them back at the end
166
        let mut zipper = EngineZipper::new(tree, meta, &self.event_handlers);
167

            
168
        'main: loop {
169
            // Return here after every successful rule application
170

            
171
            for (level, rules) in self.rule_groups.iter().enumerate() {
172
                // Try each rule group in the whole tree
173

            
174
                while zipper.go_next_dirty(level).is_some() {
175
                    let subtree = zipper.inner.focus();
176

            
177
                    // Choose one transformation from all applicable rules at this level
178
                    let selected = {
179
                        let applicable = &mut rules.iter().filter_map(|rule| {
180
                            let update = apply_into_update(rule, subtree, &zipper.meta)?;
181
                            Some((rule, update))
182
                        });
183
                        one_or_select(self.selector, subtree, applicable)
184
                    };
185

            
186
                    if let Some(mut update) = selected {
187
                        // Replace the current subtree, invalidating subtree node states
188
                        zipper.inner.replace_focus(update.new_subtree);
189

            
190
                        // Mark all ancestors as dirty and move back to the root
191
                        zipper.mark_dirty_to_root();
192

            
193
                        let (new_tree, root_transformed) = update
194
                            .commands
195
                            .apply(zipper.inner.focus().clone(), &mut zipper.meta);
196

            
197
                        if root_transformed {
198
                            // This must unfortunately throw all node states away,
199
                            // since the `transform` command may redefine the whole tree
200
                            zipper.inner.replace_focus(new_tree);
201
                        }
202

            
203
                        continue 'main;
204
                    } else {
205
                        zipper.set_dirty_from(level + 1);
206
                    }
207
                }
208
            }
209

            
210
            // All rules have been tried with no more changes
211
            break;
212
        }
213

            
214
        zipper.into()
215
    }
216
}
217

            
218
#[derive(Debug, Clone)]
219
struct EngineNodeState {
220
    /// Rule groups with lower indices have already been applied without change.
221
    /// For a level `n`, a state is 'dirty' if and only if `n >= dirty_from`.
222
    dirty_from: usize,
223
}
224

            
225
impl EngineNodeState {
226
    /// Marks the state as dirty for anything >= `level`.
227
    fn set_dirty_from(&mut self, level: usize) {
228
        self.dirty_from = level;
229
    }
230

            
231
    /// For a level `n`, a state is "dirty" if and only if `n >= dirty_from`.
232
    /// That is, all rules groups before `n` have been applied without change.
233
    fn is_dirty(&self, level: usize) -> bool {
234
        level >= self.dirty_from
235
    }
236
}
237

            
238
impl EngineNodeState {
239
    fn new<T: Uniplate>(_: &T) -> Self {
240
        Self { dirty_from: 0 }
241
    }
242
}
243

            
244
macro_rules! movement_fns {
245
    (
246
        directions: [$($dir:ident),*]
247
    ) => {
248
        paste! {
249
            $(fn [<go_ $dir>](&mut self) -> Option<()> {
250
                self.inner.zipper().[<has_ $dir>]().then(|| {
251
                    self.event_handlers
252
                        .[<trigger_before_ $dir>](self.inner.focus(), &mut self.meta);
253
                    self.inner.[<go_ $dir>]().expect("zipper movement failed despite check");
254
                    self.event_handlers
255
                        .[<trigger_after_ $dir>](self.inner.focus(), &mut self.meta);
256
                })
257
            })*
258
        }
259
    };
260
}
261

            
262
/// A Zipper with optimisations for tree transformation.
263
struct EngineZipper<'events, T: Uniplate, M> {
264
    inner: TaggedZipper<T, EngineNodeState, fn(&T) -> EngineNodeState>,
265
    event_handlers: &'events EventHandlers<T, M>,
266
    meta: M,
267
}
268

            
269
impl<'events, T: Uniplate, M> EngineZipper<'events, T, M> {
270
    pub fn new(tree: T, meta: M, event_handlers: &'events EventHandlers<T, M>) -> Self {
271
        EngineZipper {
272
            inner: TaggedZipper::new(tree, EngineNodeState::new),
273
            event_handlers,
274
            meta,
275
        }
276
    }
277

            
278
    /// Go to the next node in the tree which is dirty for the given level.
279
    /// That node may be the current one if it is dirty.
280
    /// If no such node exists, go to the root and return `None`.
281
    pub fn go_next_dirty(&mut self, level: usize) -> Option<()> {
282
        if self.inner.tag().is_dirty(level) {
283
            return Some(());
284
        }
285

            
286
        self.go_down()
287
            .and_then(|_| {
288
                // go right until we find a dirty child, if it exists.
289
                loop {
290
                    if self.inner.tag().is_dirty(level) {
291
                        return Some(());
292
                    } else if self.go_right().is_none() {
293
                        // all children are clean
294
                        self.go_up();
295
                        return None;
296
                    }
297
                }
298
            })
299
            .or_else(|| {
300
                // Neither this node, nor any of its children are dirty
301
                // Go right then up until we find a dirty node or reach the root
302
                loop {
303
                    if self.go_right().is_some() {
304
                        if self.inner.tag().is_dirty(level) {
305
                            return Some(());
306
                        }
307
                    } else if self.go_up().is_none() {
308
                        // Reached the root without finding a dirty node
309
                        return None;
310
                    }
311
                }
312
            })
313
    }
314

            
315
    // We never move left in the tree
316
    movement_fns! { directions: [up, down, right] }
317

            
318
    /// Mark the current focus as visited at the given level.
319
    /// Calling `go_next_dirty` with the same level will no longer yield this node.
320
    pub fn set_dirty_from(&mut self, level: usize) {
321
        self.inner.tag_mut().set_dirty_from(level);
322
    }
323

            
324
    /// Mark ancestors as dirty for all levels, and return to the root
325
    pub fn mark_dirty_to_root(&mut self) {
326
        while self.go_up().is_some() {
327
            self.set_dirty_from(0);
328
        }
329
    }
330
}
331

            
332
impl<T: Uniplate, M> From<EngineZipper<'_, T, M>> for (T, M) {
333
    fn from(val: EngineZipper<'_, T, M>) -> Self {
334
        let meta = val.meta;
335
        let tree = val.inner.rebuild_root();
336
        (tree, meta)
337
    }
338
}