pub trait Rule<T: Uniplate, M> {
// Required method
fn apply(
&self,
commands: &mut Commands<T, M>,
subtree: &T,
meta: &M,
) -> Option<T>;
}Expand description
Trait implemented by rules to transform parts of a tree.
Rules contain a method apply which accepts a Commands instance, a subtree, and
global metadata. If the rule is applicable to the subtree, it should return Some(<new_tree>),
otherwise it should return None.
§Rule Application
As the engine traverses the tree (in left-most, outer-most order), it will apply rules to each
node. The subtree argument passed to the rule is the current node being visited.
If a rule is applicable to the given node/subtree (i.e. can transform it), then it should return the resulting new subtree, which will be inserted into the tree in place of the original node.
§Side-Effects
The Commands instance passed to the rule can be used to apply side-effects after the rule
has been applied. This can be used to update global metadata, or to apply transformations to the
entire tree.
§Global Metadata
In contrast to the subtree argument given to rules, the meta argument is a
reference to a global value which is available to all rules regardless of where in
the tree they are applied. This user-defined value can be used to store information
such as a symbol table, or the number of times a specific rule has been applied.
The global metadata may be mutated as a side-effect of applying a rule, using the
Commands::mut_meta method.
§Provided Implementations
This trait is automatically implemented by all types which implement
Fn(&mut Commands<T, M>, &T, &M) -> Option<T> for types T: Uniplate and M. This allows
function pointers and closures with the correct signatures to be used as rules directly.
§Example
use tree_morph::prelude::*;
use uniplate::Uniplate;
#[derive(Debug, Clone, PartialEq, Eq, Uniplate)]
#[uniplate()]
enum Term {
A,
B,
}
// Functions and closures automatically implement the Rule trait
fn my_rule_fn(_: &mut Commands<Term, ()>, _: &Term, _: &()) -> Option<Term> {
None // Never applicable
}
let engine = EngineBuilder::new()
.add_rule_group(rule_fns![my_rule_fn])
.build();
let (result, _) = engine.morph(Term::A, ());
assert_eq!(result, Term::A);
// Custom types can implement the Rule trait to allow more complex behaviour
// Here a rule can be "toggled" to change whether it is applicable
struct CustomRule(bool);
impl Rule<Term, ()> for CustomRule {
fn apply(&self, _: &mut Commands<Term, ()>, t: &Term, _: &()) -> Option<Term> {
if self.0 && matches!(t, Term::A) {
Some(Term::B)
} else {
None
}
}
}
let engine = EngineBuilder::new()
.add_rule(CustomRule(false))
.build();
let (result, _) = engine.morph(Term::A, ());
assert_eq!(result, Term::A);
let engine = EngineBuilder::new()
.add_rule(CustomRule(true))
.build();
let (result, _) = engine.morph(Term::A, ());
assert_eq!(result, Term::B);