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
42613
pub fn rewrite_naive<'a>(
38
42613
    model: &Model,
39
42613
    rule_sets: &Vec<&'a RuleSet<'a>>,
40
42613
    prop_multiple_equally_applicable: bool,
41
42613
) -> Result<Model, RewriteError> {
42
42613
    set_current_rewriter(Rewriter::Naive);
43

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
242
333536
    Some(())
243
376585
}
244

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

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