tree_morph/
rule.rs

1//! Traits and types representing a transformation rule to a tree.
2//!
3//! See the [`Rule`] trait for more information.
4
5use crate::prelude::{Commands, Update};
6use 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/// ```
92pub 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
100impl<T, M, F> Rule<T, M> for F
101where
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.
111pub(crate) fn apply_into_update<T, M, R>(rule: &R, subtree: &T, meta: &M) -> Option<Update<T, M>>
112where
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.
128pub 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]
161macro_rules! rule_fns {
162    [$($x:expr),+ $(,)?] => {
163        vec![$( $x as ::tree_morph::prelude::RuleFn<_, _>, )*]
164    };
165}