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

            
5
use std::{collections::HashMap, marker::PhantomData};
6

            
7
use crate::prelude::{Commands, Update};
8
use uniplate::Uniplate;
9

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

            
101
    /// Return the name of the rule, will default to anonymous if not specified.
102
    fn name(&self) -> &str {
103
        "Anonymous Rule"
104
    }
105

            
106
    /// None -> Rule applies to all nodes
107
    /// Some(ids) -> Rule only applies to nodes with these discriminant ids
108
    fn applicable_to(&self) -> Option<Vec<usize>> {
109
        None
110
    }
111
}
112

            
113
// Allows the user to pass closures and function pointers directly as rules
114
impl<T, M, F> Rule<T, M> for F
115
where
116
    T: Uniplate,
117
    F: Fn(&mut Commands<T, M>, &T, &M) -> Option<T>,
118
{
119
86
    fn apply(&self, commands: &mut Commands<T, M>, subtree: &T, meta: &M) -> Option<T> {
120
86
        (self)(commands, subtree, meta)
121
86
    }
122
}
123

            
124
/// A helper method to get an [`Update`] directly from a rule.
125
403229918
pub(crate) fn apply_into_update<T, M, R>(rule: &R, subtree: &T, meta: &M) -> Option<Update<T, M>>
126
403229918
where
127
403229918
    T: Uniplate,
128
403229918
    R: Rule<T, M>,
129
{
130
403229918
    let mut commands = Commands::new();
131
403229918
    let new_subtree = rule.apply(&mut commands, subtree, meta)?;
132
290977
    Some(Update::new(new_subtree, commands))
133
403229918
}
134

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

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

            
181
/// We can create a rule using this struct and pass it into our list of rules directly,
182
/// For debugging and tracing, it is helpful to see rules by a meaningful name.
183
/// or we can make use of the `named_rule` macro (see [`tree-morph-macros`]).
184
///
185
/// This struct and macro is for the short form way of defining named rules. You can change the name
186
/// of the rule by implementing the `Rule` trait as well.
187
///
188
///  ```rust
189
/// use tree_morph::prelude::*;
190
/// use tree_morph_macros::named_rule;
191
/// use uniplate::Uniplate;
192
///
193
/// #[derive(Debug, Clone, PartialEq, Eq, Uniplate)]
194
/// #[uniplate()]
195
/// enum Expr {
196
///     # Add(Box<Expr>, Box<Expr>),
197
///    // Snip
198
/// }
199
///
200
/// struct Meta;
201
///
202
/// #[named_rule("CustomName")]
203
/// fn my_rule(_: &mut Commands<Expr, Meta>, expr: &Expr, _: &Meta) -> Option<Expr> {
204
///     /// rule implementation
205
///     # None
206
/// }
207
/// ```
208
/// This macro will return a helper function called `my_rule` which returns the NamedRule for us to
209
/// use. We can add this to our list of rules with `vec![my_rule]`.
210
///
211
/// If a name is not specified, the functions name will be it's identifier.
212
#[derive(Clone)]
213
pub struct NamedRule<F: Clone> {
214
    name: &'static str,
215
    function: F,
216
}
217

            
218
impl<F: Clone> NamedRule<F> {
219
    /// Create a Rule with a specified name.
220
    pub const fn new(name: &'static str, function: F) -> Self {
221
        Self { name, function }
222
    }
223
}
224

            
225
impl<T, M, F> Rule<T, M> for NamedRule<F>
226
where
227
    T: Uniplate,
228
    F: Fn(&mut Commands<T, M>, &T, &M) -> Option<T> + Clone,
229
{
230
152
    fn apply(&self, commands: &mut Commands<T, M>, subtree: &T, meta: &M) -> Option<T> {
231
152
        (self.function)(commands, subtree, meta)
232
152
    }
233

            
234
23
    fn name(&self) -> &str {
235
23
        self.name
236
23
    }
237
}
238

            
239
/// TODO
240
pub struct RuleGroups<T, M, R>
241
where
242
    T: Uniplate,
243
    R: Rule<T, M> + Clone,
244
{
245
    /// Priority -> Rules
246
    universal_rules: Vec<Vec<R>>,
247

            
248
    /// Priority -> discriminant id -> Rules
249
    filtered_rules: Option<Vec<HashMap<usize, Vec<R>>>>,
250

            
251
    /// Function to compute a unique usize id for a node, used for prefiltering.
252
    /// None means prefiltering is disabled.
253
    pub discriminant_fn: Option<fn(&T) -> usize>,
254

            
255
    _phantom: PhantomData<(T, M)>,
256
}
257

            
258
impl<T, M, R> RuleGroups<T, M, R>
259
where
260
    T: Uniplate,
261
    R: Rule<T, M> + Clone,
262
{
263
    /// TODO
264
39255
    pub fn new(rule_group: Vec<Vec<R>>, discriminant_fn: Option<fn(&T) -> usize>) -> Self {
265
39255
        let Some(discriminant_fn) = discriminant_fn else {
266
39255
            return Self {
267
39255
                filtered_rules: None,
268
39255
                universal_rules: rule_group,
269
39255
                discriminant_fn: None,
270
39255
                _phantom: PhantomData,
271
39255
            };
272
        };
273

            
274
        let mut filtered_rules = Vec::with_capacity(rule_group.len());
275
        let mut universal_rules = Vec::with_capacity(rule_group.len());
276

            
277
        for rules in rule_group {
278
            let mut filtered: HashMap<usize, Vec<R>> = HashMap::new();
279
            let mut universal = Vec::new();
280

            
281
            for rule in rules {
282
                match rule.applicable_to() {
283
                    None => universal.push(rule),
284
                    Some(ids) => {
285
                        for id in ids {
286
                            filtered.entry(id).or_default().push(rule.clone());
287
                        }
288
                    }
289
                };
290
            }
291

            
292
            filtered_rules.push(filtered);
293
            universal_rules.push(universal);
294
        }
295
        Self {
296
            universal_rules,
297
            filtered_rules: Some(filtered_rules),
298
            discriminant_fn: Some(discriminant_fn),
299
            _phantom: PhantomData,
300
        }
301
39255
    }
302

            
303
    /// TODO
304
330060
    pub fn levels(&self) -> usize {
305
330060
        self.universal_rules.len()
306
330060
    }
307

            
308
    /// TODO
309
96729041
    pub fn get_rules(&self, level: usize, id: Option<usize>) -> RuleSet<'_, R> {
310
96729041
        let filtered = id
311
96729041
            .and_then(|id| {
312
                self.filtered_rules
313
                    .as_ref()
314
                    .and_then(|filter_map| filter_map[level].get(&id))
315
            })
316
96729041
            .map(|f| f.as_slice())
317
96729041
            .unwrap_or(&[]);
318
96729041
        let universal = &self.universal_rules[level];
319
96729041
        RuleSet {
320
96729041
            universal,
321
96729041
            filtered,
322
96729041
        }
323
96729041
    }
324

            
325
    /// Returns the universal rule groups in priority order.
326
    pub fn get_all_rules(&self) -> &[Vec<R>] {
327
        self.universal_rules.as_slice()
328
    }
329
}
330

            
331
/// A view into the rules applicable to a specific node, split into two slices.
332
///
333
/// `universal` contains rules that apply to all nodes; `filtered` contains rules that were
334
/// prefiltered to this node's discriminant id. Both slices are empty-or-valid references into
335
/// the engine's rule storage — no allocation is required to produce this type.
336
pub struct RuleSet<'a, R> {
337
    pub(crate) universal: &'a [R],
338
    pub(crate) filtered: &'a [R],
339
}
340

            
341
impl<'a, R> RuleSet<'a, R> {
342
    /// Total number of rules in this set.
343
1215975
    pub fn len(&self) -> usize {
344
1215975
        self.universal.len() + self.filtered.len()
345
1215975
    }
346

            
347
    /// Returns true if there are no rules in this set.
348
    pub fn is_empty(&self) -> bool {
349
        self.universal.is_empty() && self.filtered.is_empty()
350
    }
351

            
352
    /// Returns a Rayon parallel iterator over the rules in this set.
353
    ///
354
    /// Chains the universal and filtered slices without any intermediate allocation.
355
    pub fn par_iter(&self) -> impl rayon::iter::ParallelIterator<Item = &'a R>
356
    where
357
        R: Sync,
358
    {
359
        use rayon::prelude::*;
360
        self.universal.par_iter().chain(self.filtered.par_iter())
361
    }
362
}
363

            
364
impl<'a, R> IntoIterator for RuleSet<'a, R> {
365
    type Item = &'a R;
366
    type IntoIter = std::iter::Chain<std::slice::Iter<'a, R>, std::slice::Iter<'a, R>>;
367

            
368
95512927
    fn into_iter(self) -> Self::IntoIter {
369
95512927
        self.universal.iter().chain(self.filtered.iter())
370
95512927
    }
371
}