1
use super::{RewriteError, RuleSet, resolve_rules::RuleData};
2
use crate::{
3
    Model,
4
    ast::Expression as Expr,
5
    bug,
6
    rule_engine::{
7
        get_rules_grouped,
8
        rewriter_common::{
9
            RuleResult, VariableDeclarationSnapshot, log_rule_application,
10
            snapshot_variable_declarations, try_rewrite_value_letting_once,
11
        },
12
        submodel_zipper::expression_ctx,
13
    },
14
    settings::{
15
        Rewriter, default_rule_trace_enabled, rule_trace_enabled, rule_trace_verbose_enabled,
16
        set_current_rewriter,
17
    },
18
    stats::RewriterStats,
19
};
20

            
21
use itertools::Itertools;
22
use std::{sync::Arc, time::Instant};
23
use tracing::trace;
24

            
25
// debug imports
26
#[cfg(debug_assertions)]
27
use {
28
    crate::ast::assertions::debug_assert_model_well_formed,
29
    tracing::{Level, span},
30
};
31

            
32
type VariableSnapshots = Option<(VariableDeclarationSnapshot, VariableDeclarationSnapshot)>;
33
type ApplicableRule<'a, CtxFnType> = (RuleResult<'a>, u16, Expr, CtxFnType, VariableSnapshots);
34

            
35
/// A naive, exhaustive rewriter for development purposes. Applies rules in priority order,
36
/// favouring expressions found earlier during preorder traversal of the tree.
37
41095
pub fn rewrite_naive<'a>(
38
41095
    model: &Model,
39
41095
    rule_sets: &Vec<&'a RuleSet<'a>>,
40
41095
    prop_multiple_equally_applicable: bool,
41
41095
) -> Result<Model, RewriteError> {
42
41095
    set_current_rewriter(Rewriter::Naive);
43

            
44
41095
    let rules_grouped = get_rules_grouped(rule_sets)
45
        .unwrap_or_else(|_| bug!("get_rule_priorities() failed!"))
46
41095
        .into_iter()
47
41095
        .collect_vec();
48

            
49
41095
    let mut model = model.clone();
50
41095
    let mut done_something = true;
51

            
52
41095
    let mut rewriter_stats = RewriterStats::new();
53
41095
    rewriter_stats.is_optimization_enabled = Some(false);
54
41095
    let run_start = Instant::now();
55

            
56
41095
    if rule_trace_enabled() && default_rule_trace_enabled() {
57
26034
        trace!(
58
            target: "rule_engine_rule_trace",
59
            "Model before rewriting:\n\n{}\n--\n",
60
            model
61
        );
62
15061
    }
63
41095
    if rule_trace_enabled() && rule_trace_verbose_enabled() {
64
8
        trace!(
65
            target: "rule_engine_rule_trace_verbose",
66
            "elapsed_s,rule_level,rule_name,rule_set,status,expression"
67
        );
68
41087
    }
69

            
70
    // Rewrite until there are no more rules left to apply.
71
396964
    while done_something {
72
355869
        done_something = try_rewrite_model(
73
355869
            &mut model,
74
355869
            &rules_grouped,
75
355869
            prop_multiple_equally_applicable,
76
355869
            &mut rewriter_stats,
77
355869
            &run_start,
78
355869
        )
79
355869
        .is_some();
80
355869
    }
81

            
82
41095
    let run_end = Instant::now();
83
41095
    rewriter_stats.rewriter_run_time = Some(run_end - run_start);
84

            
85
41095
    model
86
41095
        .context
87
41095
        .write()
88
41095
        .unwrap()
89
41095
        .stats
90
41095
        .add_rewriter_run(rewriter_stats);
91

            
92
41095
    if rule_trace_enabled() && default_rule_trace_enabled() {
93
26034
        trace!(
94
            target: "rule_engine_rule_trace",
95
            "Final model:\n\n{}",
96
            model
97
        );
98
15061
    }
99
41095
    Ok(model)
100
41095
}
101

            
102
// Tries to do a single rewrite on the model.
103
//
104
// Returns None if no change was made.
105
355869
fn try_rewrite_model(
106
355869
    submodel: &mut Model,
107
355869
    rules_grouped: &Vec<(u16, Vec<RuleData<'_>>)>,
108
355869
    prop_multiple_equally_applicable: bool,
109
355869
    stats: &mut RewriterStats,
110
355869
    #[cfg(debug_assertions)] run_start: &Instant,
111
355869
    #[cfg(not(debug_assertions))] _: &Instant,
112
355869
) -> Option<()> {
113
418
    if let Some(result) =
114
355869
        try_rewrite_value_letting_once(submodel, rules_grouped, prop_multiple_equally_applicable)
115
    {
116
418
        return Some(result);
117
355451
    }
118

            
119
    type CtxFn = Arc<dyn Fn(Expr) -> Expr>;
120
355451
    let mut results: Vec<ApplicableRule<'_, CtxFn>> = vec![];
121

            
122
    // Iterate over rules by priority in descending order.
123
2979319
    'top: for (priority, rules) in rules_grouped.iter() {
124
        // Rewrite within the current root expression tree.
125
81150300
        for (expr, ctx) in expression_ctx(submodel.root().clone()) {
126
            // Clone expr and ctx so they can be reused
127
81150300
            let expr = expr.clone();
128
81150300
            let ctx = ctx.clone();
129
345755070
            for rd in rules {
130
                // Count rule application attempts
131
345755070
                stats.rewriter_rule_application_attempts =
132
345755070
                    Some(stats.rewriter_rule_application_attempts.unwrap_or(0) + 1);
133

            
134
                #[cfg(debug_assertions)]
135
345755070
                let span = span!(Level::TRACE,"trying_rule_application",rule_name=rd.rule.name,rule_target_expression=%expr);
136

            
137
                #[cfg(debug_assertions)]
138
345755070
                let _guard = span.enter();
139

            
140
                #[cfg(debug_assertions)]
141
345755070
                tracing::trace!(rule_name = rd.rule.name, "Trying rule");
142

            
143
345755070
                let before_variable_snapshot = matches!(expr, Expr::Root(_, _))
144
345755070
                    .then(|| snapshot_variable_declarations(&submodel.symbols()));
145

            
146
345755070
                match (rd.rule.application)(&expr, &submodel.symbols()) {
147
314436
                    Ok(red) => {
148
                        // when called a lot, this becomes very expensive!
149
                        #[cfg(debug_assertions)]
150
314436
                        if rule_trace_enabled() && rule_trace_verbose_enabled() {
151
354
                            log_verbose_rule_attempt(
152
354
                                run_start,
153
354
                                priority,
154
354
                                rd.rule.name,
155
354
                                rd.rule_set.name,
156
354
                                "success",
157
354
                                &expr,
158
354
                            );
159
314082
                        }
160

            
161
                        // Count successful rule applications
162
314436
                        stats.rewriter_rule_applications =
163
314436
                            Some(stats.rewriter_rule_applications.unwrap_or(0) + 1);
164

            
165
314436
                        let after_variable_snapshot = before_variable_snapshot
166
314436
                            .as_ref()
167
314436
                            .map(|_| snapshot_variable_declarations(&red.symbols));
168
314436
                        let variable_snapshots =
169
314436
                            before_variable_snapshot.zip(after_variable_snapshot);
170

            
171
                        // Collect applicable rules
172
314436
                        results.push((
173
314436
                            RuleResult {
174
314436
                                rule_data: rd.clone(),
175
314436
                                reduction: red,
176
314436
                            },
177
314436
                            *priority,
178
314436
                            expr.clone(),
179
314436
                            ctx.clone(),
180
314436
                            variable_snapshots,
181
314436
                        ));
182
                    }
183
                    Err(_) => {
184
                        // when called a lot, this becomes very expensive!
185
                        #[cfg(debug_assertions)]
186
345440634
                        if rule_trace_enabled() && rule_trace_verbose_enabled() {
187
1221198
                            log_verbose_rule_attempt(
188
1221198
                                run_start,
189
1221198
                                priority,
190
1221198
                                rd.rule.name,
191
1221198
                                rd.rule_set.name,
192
1221198
                                "fail",
193
1221198
                                &expr,
194
1221198
                            );
195
344219436
                        }
196
                    }
197
                }
198
            }
199
            // This expression has the highest rule priority so far, so this is what we want to
200
            // rewrite.
201
81150300
            if !results.is_empty() {
202
314356
                break 'top;
203
80835944
            }
204
        }
205
    }
206

            
207
355451
    match results.as_slice() {
208
355451
        [] => return None, // no rules are applicable.
209
314356
        [(result, _priority, expr, ctx, variable_snapshots), ..] => {
210
314356
            if prop_multiple_equally_applicable {
211
100
                assert_no_multiple_equally_applicable_rules(&results, rules_grouped);
212
314256
            }
213

            
214
            // Extract the single applicable rule and apply it
215
314356
            log_rule_application(
216
314356
                result,
217
314356
                expr,
218
314356
                &submodel.symbols(),
219
314356
                variable_snapshots
220
314356
                    .as_ref()
221
314356
                    .map(|(before, after)| (before, after)),
222
            );
223

            
224
            // Replace expr with new_expression
225
314356
            let new_root = ctx(result.reduction.new_expression.clone());
226
314356
            submodel.replace_root(new_root);
227

            
228
            // Apply new symbols and top level
229
314356
            result.reduction.clone().apply(submodel);
230

            
231
            #[cfg(debug_assertions)]
232
314356
            {
233
314356
                let assertion_context = format!(
234
314356
                    "naive rewriter after applying rule '{}'",
235
314356
                    result.rule_data.rule.name
236
314356
                );
237
314356
                debug_assert_model_well_formed(submodel, &assertion_context);
238
314356
            }
239
        }
240
    }
241

            
242
314356
    Some(())
243
355869
}
244

            
245
#[cfg(debug_assertions)]
246
3664656
fn csv_escape(field: &str) -> String {
247
3664656
    if field.contains([',', '"', '\n', '\r']) {
248
513466
        format!("\"{}\"", field.replace('"', "\"\""))
249
    } else {
250
3151190
        field.to_string()
251
    }
252
3664656
}
253

            
254
#[cfg(debug_assertions)]
255
1221552
fn log_verbose_rule_attempt(
256
1221552
    run_start: &Instant,
257
1221552
    priority: &u16,
258
1221552
    rule_name: &str,
259
1221552
    rule_set_name: &str,
260
1221552
    status: &str,
261
1221552
    expr: &Expr,
262
1221552
) {
263
1221552
    let elapsed_seconds = run_start.elapsed().as_secs_f64();
264
1221552
    let expr_str = expr.to_string();
265
1221552
    trace!(
266
        target: "rule_engine_rule_trace_verbose",
267
        "{:.3},{},{},{},{},{}",
268
        elapsed_seconds,
269
        priority,
270
1221552
        csv_escape(rule_name),
271
1221552
        csv_escape(rule_set_name),
272
        status,
273
1221552
        csv_escape(&expr_str)
274
    );
275
1221552
}
276

            
277
// Exits with a bug if there are multiple equally applicable rules for an expression.
278
100
fn assert_no_multiple_equally_applicable_rules<CtxFnType>(
279
100
    results: &Vec<ApplicableRule<'_, CtxFnType>>,
280
100
    rules_grouped: &Vec<(u16, Vec<RuleData<'_>>)>,
281
100
) {
282
100
    if results.len() <= 1 {
283
100
        return;
284
    }
285

            
286
    let names: Vec<_> = results
287
        .iter()
288
        .map(|(result, _, _, _, _)| result.rule_data.rule.name)
289
        .collect();
290

            
291
    // Extract the expression from the first result
292
    let expr = results[0].2.clone();
293

            
294
    // Construct a single string to display the names of the rules grouped by priority
295
    let mut rules_by_priority_string = String::new();
296
    rules_by_priority_string.push_str("Rules grouped by priority:\n");
297
    for (priority, rules) in rules_grouped.iter() {
298
        rules_by_priority_string.push_str(&format!("Priority {priority}:\n"));
299
        for rd in rules {
300
            rules_by_priority_string.push_str(&format!(
301
                "  - {} (from {})\n",
302
                rd.rule.name, rd.rule_set.name
303
            ));
304
        }
305
    }
306
    bug!("Multiple equally applicable rules for {expr}: {names:#?}\n\n{rules_by_priority_string}");
307
100
}