conjure_core/rule_engine/
rewrite_naive.rs1use super::{resolve_rules::RuleData, RewriteError, RuleSet};
2use crate::{
3 ast::{Expression as Expr, SubModel},
4 bug,
5 rule_engine::{
6 get_rules_grouped,
7 rewriter_common::{log_rule_application, RuleResult},
8 submodel_zipper::submodel_ctx,
9 },
10 stats::RewriterStats,
11 Model,
12};
13
14use itertools::Itertools;
15use std::{sync::Arc, time::Instant};
16use tracing::{span, trace, Level};
17use uniplate::Biplate;
18
19pub fn rewrite_naive<'a>(
22 model: &Model,
23 rule_sets: &Vec<&'a RuleSet<'a>>,
24 prop_multiple_equally_applicable: bool,
25) -> Result<Model, RewriteError> {
26 let rules_grouped = get_rules_grouped(rule_sets)
27 .unwrap_or_else(|_| bug!("get_rule_priorities() failed!"))
28 .into_iter()
29 .collect_vec();
30
31 let mut model = model.clone();
32 let mut done_something = true;
33
34 let mut rewriter_stats = RewriterStats::new();
35 rewriter_stats.is_optimization_enabled = Some(false);
36 let run_start = Instant::now();
37
38 trace!(
39 target: "rule_engine_human",
40 "Model before rewriting:\n\n{}\n--\n",
41 model
42 );
43
44 while done_something {
46 let mut new_model = None;
47 done_something = false;
48
49 for (mut submodel, ctx) in <_ as Biplate<SubModel>>::contexts_bi(&model) {
51 if try_rewrite_model(
52 &mut submodel,
53 &rules_grouped,
54 prop_multiple_equally_applicable,
55 &mut rewriter_stats,
56 )
57 .is_some()
58 {
59 new_model = Some(ctx(submodel));
60 done_something = true;
61 break;
62 }
63 }
64 if let Some(new_model) = new_model {
65 model = new_model;
66 }
67 }
68
69 let run_end = Instant::now();
70 rewriter_stats.rewriter_run_time = Some(run_end - run_start);
71
72 model
73 .context
74 .write()
75 .unwrap()
76 .stats
77 .add_rewriter_run(rewriter_stats);
78
79 trace!(
80 target: "rule_engine_human",
81 "Final model:\n\n{}",
82 model
83 );
84 Ok(model)
85}
86
87fn try_rewrite_model(
91 submodel: &mut SubModel,
92 rules_grouped: &Vec<(u16, Vec<RuleData<'_>>)>,
93 prop_multiple_equally_applicable: bool,
94 stats: &mut RewriterStats,
95) -> Option<()> {
96 type CtxFn = Arc<dyn Fn(Expr) -> SubModel>;
97 let mut results: Vec<(RuleResult<'_>, u16, Expr, CtxFn)> = vec![];
98
99 'top: for (priority, rules) in rules_grouped.iter() {
101 for (expr, ctx) in submodel_ctx(submodel.clone()) {
104 let expr = expr.clone();
106 let ctx = ctx.clone();
107 for rd in rules {
108 stats.rewriter_rule_application_attempts =
110 Some(stats.rewriter_rule_application_attempts.unwrap_or(0) + 1);
111
112 #[cfg(debug_assertions)]
113 let span = span!(Level::TRACE,"trying_rule_application",rule_name=rd.rule.name,rule_target_expression=%expr);
114
115 #[cfg(debug_assertions)]
116 let _guard = span.enter();
117
118 #[cfg(debug_assertions)]
119 tracing::trace!(rule_name = rd.rule.name, "Trying rule");
120
121 match (rd.rule.application)(&expr, &submodel.symbols()) {
122 Ok(red) => {
123 stats.rewriter_rule_applications =
125 Some(stats.rewriter_rule_applications.unwrap_or(0) + 1);
126
127 results.push((
129 RuleResult {
130 rule_data: rd.clone(),
131 reduction: red,
132 },
133 *priority,
134 expr.clone(),
135 ctx.clone(),
136 ));
137 }
138 Err(_) => {
139 #[cfg(debug_assertions)]
141 tracing::trace!(
142 "Rule attempted but not applied: {} (priority {}, rule set {}), to expression: {}",
143 rd.rule.name,
144 priority,
145 rd.rule_set.name,
146 expr
147 );
148 }
149 }
150 }
151 if !results.is_empty() {
154 break 'top;
155 }
156 }
157 }
158
159 match results.as_slice() {
160 [] => {
161 return None;
162 } [(result, _priority, expr, ctx), ..] => {
164 if prop_multiple_equally_applicable {
165 assert_no_multiple_equally_applicable_rules(&results, rules_grouped);
166 }
167
168 log_rule_application(result, expr, submodel);
170
171 *submodel = ctx(result.reduction.new_expression.clone());
173
174 result.reduction.clone().apply(submodel);
176 }
177 }
178
179 Some(())
180}
181
182fn assert_no_multiple_equally_applicable_rules<CtxFnType>(
184 results: &Vec<(RuleResult<'_>, u16, Expr, CtxFnType)>,
185 rules_grouped: &Vec<(u16, Vec<RuleData<'_>>)>,
186) {
187 if results.len() <= 1 {
188 return;
189 }
190
191 let names: Vec<_> = results
192 .iter()
193 .map(|(result, _, _, _)| result.rule_data.rule.name)
194 .collect();
195
196 let expr = results[0].2.clone();
198
199 let mut rules_by_priority_string = String::new();
201 rules_by_priority_string.push_str("Rules grouped by priority:\n");
202 for (priority, rules) in rules_grouped.iter() {
203 rules_by_priority_string.push_str(&format!("Priority {}:\n", priority));
204 for rd in rules {
205 rules_by_priority_string.push_str(&format!(
206 " - {} (from {})\n",
207 rd.rule.name, rd.rule_set.name
208 ));
209 }
210 }
211 bug!("Multiple equally applicable rules for {expr}: {names:#?}\n\n{rules_by_priority_string}");
212}