conjure_cp_core/rule_engine/rewrite_morph.rs
1use itertools::Itertools;
2use tree_morph::{helpers::select_panic, prelude::*};
3
4use crate::{Model, bug};
5
6use super::{RuleSet, get_rules_grouped};
7
8/// Rewrites a `Model` by applying rule sets using an optimized, tree-morphing rewriter.
9///
10/// This function traverses the expression tree of the model and applies the given rules
11/// to transform it. It operates on the model's internal structure, replacing the root
12/// expression and updating the symbol table based on the transformations performed by the
13/// `morph` function.
14///
15/// # Parameters
16///
17/// - `model`: The `Model` to be rewritten. It is consumed and a new, transformed version is returned.
18/// - `rule_sets`: A vector of `RuleSet` references containing the rules for transformation. These rules are grouped by priority before being applied.
19/// - `prop_multiple_equally_applicable`: A boolean flag to control behavior when multiple rules of the same priority can be applied to the same expression.
20/// - If `true`, the rewriter will use a selection strategy (`select_panic`) that panics.
21/// - If `false`, the rewriter will use a selection strategy (`select_first`) that simply picks the first applicable rule it encounters.
22///
23/// # Returns
24///
25/// The rewritten `Model` after all applicable rules have been applied.
26///
27/// # Panics
28///
29/// This function will panic under two conditions:
30/// - If the internal grouping of rules by priority fails (from `get_rules_grouped`).
31/// - If `prop_multiple_equally_applicable` is set to `true` and more than one rule of the same priority can be applied to the same expression.
32pub fn rewrite_morph<'a>(
33 mut model: Model,
34 rule_sets: &Vec<&'a RuleSet<'a>>,
35 prop_multiple_equally_applicable: bool,
36) -> Model {
37 let submodel = model.as_submodel_mut();
38 let rules_grouped = get_rules_grouped(rule_sets)
39 .unwrap_or_else(|_| bug!("get_rule_priorities() failed!"))
40 .into_iter()
41 .map(|(_, rule)| rule.into_iter().map(|f| f.rule).collect_vec())
42 .collect_vec();
43 let selector = if prop_multiple_equally_applicable {
44 select_panic
45 } else {
46 select_first
47 };
48
49 let engine = EngineBuilder::new()
50 .set_selector(selector)
51 .append_rule_groups(rules_grouped)
52 .build();
53 let (expr, symbol_table) = engine.morph(submodel.root().clone(), submodel.symbols().clone());
54
55 *submodel.symbols_mut() = symbol_table;
56 submodel.replace_root(expr);
57 model
58}