1
//! Traits and types representing a transformation rule to a tree.
2
//!
3
//! See the [`Rule`] trait for more information.
4

            
5
use crate::prelude::{Commands, Update};
6
use uniplate::Uniplate;
7

            
8
/// Trait implemented by rules to transform parts of a tree.
9
///
10
/// Rules contain a method `apply` which accepts a [`Commands`] instance, a subtree, and
11
/// global metadata. If the rule is applicable to the subtree, it should return `Some(<new_tree>)`,
12
/// otherwise it should return `None`.
13
///
14
/// # Rule Application
15
/// As the engine traverses the tree (in left-most, outer-most order), it will apply rules to each
16
/// node. The `subtree` argument passed to the rule is the current node being visited.
17
///
18
/// If a rule is applicable to the given node/subtree (i.e. can transform it), then it should return
19
/// the resulting new subtree, which will be inserted into the tree in place of the original node.
20
///
21
/// # Side-Effects
22
///
23
/// The [`Commands`] instance passed to the rule can be used to apply side-effects after the rule
24
/// has been applied. This can be used to update global metadata, or to apply transformations to the
25
/// entire tree.
26
///
27
/// # Global Metadata
28
/// In contrast to the `subtree` argument given to rules, the `meta` argument is a
29
/// reference to a global value which is available to all rules regardless of where in
30
/// the tree they are applied. This user-defined value can be used to store information
31
/// such as a symbol table, or the number of times a specific rule has been applied.
32
///
33
/// The global metadata may be mutated as a side-effect of applying a rule, using the
34
/// [`Commands::mut_meta`] method.
35
///
36
/// # Provided Implementations
37
/// This trait is automatically implemented by all types which implement
38
/// `Fn(&mut Commands<T, M>, &T, &M) -> Option<T>` for types `T: Uniplate` and `M`. This allows
39
/// function pointers and closures with the correct signatures to be used as rules directly.
40
///
41
/// # Example
42
/// ```rust
43
/// use tree_morph::prelude::*;
44
/// use uniplate::Uniplate;
45
///
46
///
47
/// #[derive(Debug, Clone, PartialEq, Eq, Uniplate)]
48
/// #[uniplate()]
49
/// enum Term {
50
///     A,
51
///     B,
52
/// }
53
///
54
/// // Functions and closures automatically implement the Rule trait
55
/// fn my_rule_fn(_: &mut Commands<Term, ()>, _: &Term, _: &()) -> Option<Term> {
56
///     None // Never applicable
57
/// }
58
///
59
/// let engine = EngineBuilder::new()
60
///     .add_rule_group(rule_fns![my_rule_fn])
61
///     .build();
62
/// let (result, _) = engine.morph(Term::A, ());
63
/// assert_eq!(result, Term::A);
64
///
65
///
66
/// // Custom types can implement the Rule trait to allow more complex behaviour
67
/// // Here a rule can be "toggled" to change whether it is applicable
68
/// struct CustomRule(bool);
69
///
70
/// impl Rule<Term, ()> for CustomRule {
71
///     fn apply(&self, _: &mut Commands<Term, ()>, t: &Term, _: &()) -> Option<Term> {
72
///         if self.0 && matches!(t, Term::A) {
73
///             Some(Term::B)
74
///         } else {
75
///             None
76
///         }
77
///     }
78
/// }
79
///
80
/// let engine = EngineBuilder::new()
81
///     .add_rule(CustomRule(false))
82
///     .build();
83
/// let (result, _) = engine.morph(Term::A, ());
84
/// assert_eq!(result, Term::A);
85
///
86
/// let engine = EngineBuilder::new()
87
///     .add_rule(CustomRule(true))
88
///     .build();
89
/// let (result, _) = engine.morph(Term::A, ());
90
/// assert_eq!(result, Term::B);
91
/// ```
92
pub trait Rule<T: Uniplate, M> {
93
    /// Applies the rule to the given subtree and returns the result if applicable.
94
    ///
95
    /// See the [Rule] trait documentation for more information.
96
    fn apply(&self, commands: &mut Commands<T, M>, subtree: &T, meta: &M) -> Option<T>;
97
}
98

            
99
// Allows the user to pass closures and function pointers directly as rules
100
impl<T, M, F> Rule<T, M> for F
101
where
102
    T: Uniplate,
103
    F: Fn(&mut Commands<T, M>, &T, &M) -> Option<T>,
104
{
105
    fn apply(&self, commands: &mut Commands<T, M>, subtree: &T, meta: &M) -> Option<T> {
106
        (self)(commands, subtree, meta)
107
    }
108
}
109

            
110
/// A helper method to get an [`Update`] directly from a rule.
111
pub(crate) fn apply_into_update<T, M, R>(rule: &R, subtree: &T, meta: &M) -> Option<Update<T, M>>
112
where
113
    T: Uniplate,
114
    R: Rule<T, M>,
115
{
116
    let mut commands = Commands::new();
117
    let new_subtree = rule.apply(&mut commands, subtree, meta)?;
118
    Some(Update::new(new_subtree, commands))
119
}
120

            
121
/// A uniform type for `fn` pointers and closures, which implements the [Rule] trait.
122
///
123
/// Casting an `fn` pointer or closure to this type allows it to be passed to the engine alongside
124
/// other such types. This is necessary since no two `fn` pointers or closures have the same
125
/// type, and thus cannot be stored in a single collection without casting.
126
///
127
/// See the [rule_fns!](crate::rule_fns) macro for a convenient way to do this.
128
pub type RuleFn<T, M> = fn(&mut Commands<T, M>, &T, &M) -> Option<T>;
129

            
130
/// A convenience macro to cast a list of `fn` pointers or closures to a uniform type.
131
///
132
/// Casting an `fn` pointer or closure to this type allows it to be passed to the engine alongside
133
/// other such types. This is necessary since no two `fn` pointers or closures have the same
134
/// type, and thus cannot be stored in a single collection without casting.
135
///
136
/// # Example
137
/// ```rust
138
/// use tree_morph::prelude::*;
139
/// use uniplate::Uniplate;
140
///
141
///
142
/// #[derive(Debug, Clone, PartialEq, Eq, Uniplate)]
143
/// #[uniplate()]
144
/// struct Foo;
145
///
146
/// fn rule_a(_: &mut Commands<Foo, ()>, _: &Foo, _: &()) -> Option<Foo> {
147
///     None
148
/// }
149
///
150
/// fn rule_b(_: &mut Commands<Foo, ()>, _: &Foo, _: &()) -> Option<Foo> {
151
///     None
152
/// }
153
///
154
/// let rules = vec![
155
///     rule_fns![rule_a],
156
///     vec![rule_a as RuleFn<_, _>], // Same as above
157
///     rule_fns![rule_b, |_, _, _| None], // Closures and fn pointers can be mixed
158
/// ];
159
/// ```
160
#[macro_export]
161
macro_rules! rule_fns {
162
    [$($x:expr),+ $(,)?] => {
163
        vec![$( $x as ::tree_morph::prelude::RuleFn<_, _>, )*]
164
    };
165
}