1
//! Commands encode side-effects that will take place after a successful rule application.
2
//!
3
//! A rule may require some changes to the whole tree as part of its transformations, or a
4
//! change to some associated data. These changes are applied after the node changed by the rule
5
//! is updated.
6
//!
7
//! It may use [`Commands::transform`] to arbitrarily change entire tree after the rule has been
8
//! applied. To change the global metadata in-place, it may use [`Commands::mut_meta`].
9

            
10
use std::collections::VecDeque;
11
use uniplate::Uniplate;
12

            
13
enum Command<T: Uniplate, M> {
14
    Transform(Box<dyn FnOnce(T) -> T + Send>),
15
    MutMeta(Box<dyn FnOnce(&mut M) + Send>),
16
}
17

            
18
/// A queue of commands (side-effects) to be applied after a successful rule application.
19
///
20
/// A rule is given a mutable reference to a [`Commands`] and can use it to register side-effects.
21
/// These side-effects are applied in order of registration **after** the rule itself updates
22
/// a part of the tree.
23
///
24
/// # Application
25
///
26
/// A rule may not be applied due to different reasons, for example:
27
/// - It does not return a new subtree (i.e. it returns `None`).
28
/// - It returns a new subtree but the resulting [`Update`](crate::update::Update) is not chosen
29
///   by the user-defined selector function. The function may select a different rule's update or
30
///   no update at all.
31
/// - It is part of a lower-priority rule group and a higher-priority rule is applied first.
32
///
33
/// In these cases, any side-effects which are registered by the rule are not applied and are
34
/// dropped by the engine.
35
///
36
/// # Example
37
/// ```rust
38
/// use tree_morph::prelude::*;
39
/// use uniplate::Uniplate;
40
///
41
/// #[derive(Debug, Clone, PartialEq, Eq, Uniplate)]
42
/// #[uniplate()]
43
/// enum Expr {
44
///     A,
45
///     B,
46
///     C,
47
/// }
48
///
49
/// fn rule(cmds: &mut Commands<Expr, bool>, subtree: &Expr, meta: &bool) -> Option<Expr> {
50
///     cmds.transform(Box::new(|t| match t { // A pure transformation (no other side-effects)
51
///         Expr::B => Expr::C,
52
///         _ => t,
53
///     }));
54
///     cmds.mut_meta(Box::new(|m: &mut bool| *m = true)); // Set the metadata to 'true'
55
///
56
///     match subtree {
57
///         Expr::A => Some(Expr::B),
58
///         _ => None,
59
///     }
60
/// }
61
///
62
/// // Start with the expression 'A' and a metadata value of 'false'
63
/// let mut engine = EngineBuilder::new()
64
///     .add_rule_group(rule_fns![rule])
65
///     .build();
66
/// let (result, meta) = engine.morph(Expr::A, false);
67
///
68
/// // After applying the rule itself, the commands are applied in order
69
/// assert_eq!(result, Expr::C);
70
/// assert_eq!(meta, true);
71
/// ```
72
pub struct Commands<T: Uniplate, M> {
73
    commands: VecDeque<Command<T, M>>,
74
    has_transform: bool,
75
}
76

            
77
impl<T: Uniplate, M> Commands<T, M> {
78
415369352
    pub(crate) fn new() -> Self {
79
415369352
        Self {
80
415369352
            commands: VecDeque::new(),
81
415369352
            has_transform: false,
82
415369352
        }
83
415369352
    }
84

            
85
    /// Registers a pure transformation of the whole tree.
86
    ///
87
    /// In this case, "pure" means that the transformation cannot register additional side-effects.
88
    /// The transformation function is given ownership of the tree and should return the updated
89
    /// tree.
90
    ///
91
    /// Side-effects are applied in order of registration after the rule is applied.
92
27980
    pub fn transform(&mut self, f: Box<dyn FnOnce(T) -> T + Send>) {
93
27980
        self.has_transform = true;
94
27980
        self.commands.push_back(Command::Transform(f));
95
27980
    }
96

            
97
    /// Updates the global metadata in-place via a mutable reference.
98
    ///
99
    /// Side-effects are applied in order of registration after the rule is applied.
100
300275
    pub fn mut_meta(&mut self, f: Box<dyn FnOnce(&mut M) + Send>) {
101
300275
        self.commands.push_back(Command::MutMeta(f));
102
300275
    }
103

            
104
    /// Removes all side-effects previously registered by the rule.
105
    pub fn clear(&mut self) {
106
        self.commands.clear();
107
    }
108

            
109
    /// Consumes and apply the side-effects currently in the queue.
110
300053
    pub(crate) fn apply(&mut self, mut tree: T, meta: &mut M) -> (T, bool) {
111
300053
        let mut transformed = false;
112
628087
        while let Some(cmd) = self.commands.pop_front() {
113
328034
            match cmd {
114
27980
                Command::Transform(f) => {
115
27980
                    transformed = true;
116
27980
                    tree = f(tree);
117
27980
                }
118
300054
                Command::MutMeta(f) => f(meta),
119
            }
120
        }
121
300053
        (tree, transformed)
122
300053
    }
123

            
124
1410
    pub(crate) fn has_transform(&self) -> bool {
125
1410
        self.has_transform
126
1410
    }
127
}