1
use crate::commands::Commands;
2
use uniplate::Uniplate;
3

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

            
79
pub trait Rule<T: Uniplate, M> {
80
    /// Applies the rule to the given subtree and returns the result if applicable.
81
    ///
82
    /// See the [Rule] trait documentation for more information.
83
    fn apply(&self, commands: &mut Commands<T, M>, subtree: &T, meta: &M) -> Option<T>;
84
}
85

            
86
// Allows the user to pass closures and function pointers directly as rules
87
impl<T, M, F> Rule<T, M> for F
88
where
89
    T: Uniplate,
90
    F: Fn(&mut Commands<T, M>, &T, &M) -> Option<T>,
91
{
92
89
    fn apply(&self, commands: &mut Commands<T, M>, subtree: &T, meta: &M) -> Option<T> {
93
89
        (self)(commands, subtree, meta)
94
89
    }
95
}
96

            
97
/// A uniform type for `fn` pointers and closures, which implements the [Rule] trait.
98
///
99
/// Casting an `fn` pointer or closure to this type allows it to be passed directly to the engine.
100
/// See the [rule_fns!](crate::rule_fns) macro for a convenient way to do this.
101
pub type RuleFn<T, M> = fn(&mut Commands<T, M>, &T, &M) -> Option<T>;
102

            
103
/// A convenience macro to cast a list of `fn` pointers or closures to a uniform type which
104
/// implements [`Rule`], to allow these to be passed directly to the engine instead of defining a
105
/// custom type.
106
///
107
/// This makes simple cases less verbose. For more complex use cases with many rules it may better
108
/// to define your own type which implements [`Rule`] directly.
109
///
110
/// # Example
111
/// ```rust
112
1
/// use tree_morph::prelude::*;
113
/// use uniplate::derive::Uniplate;
114
///
115
/// #[derive(Debug, Clone, PartialEq, Eq, Uniplate)]
116
/// #[uniplate()]
117
/// struct Foo;
118
///
119
/// fn rule_a(_: &mut Commands<Foo, ()>, _: &Foo, _: &()) -> Option<Foo> {
120
2
///     None
121
2
/// }
122
2
///
123
/// fn rule_b(_: &mut Commands<Foo, ()>, _: &Foo, _: &()) -> Option<Foo> {
124
1
///     None
125
1
/// }
126
1
///
127
/// let rules = vec![
128
1
///     rule_fns![rule_a],
129
1
///     vec![rule_a as RuleFn<_, _>], // Same as above
130
1
///
131
1
///     rule_fns![rule_b, |_, _, _| None], // Closures and fn pointers can be mixed
132
1
/// ];
133
1
///
134
1
/// morph(rules, select_first, Foo, ());
135
1
/// ```
136
1

            
137
#[macro_export]
138
macro_rules! rule_fns {
139
    [$($x:expr),+ $(,)?] => {
140
        vec![$( $x as ::tree_morph::RuleFn<_, _>, )*]
141
    };
142
}