1
//! Common utilities and types for rewriters.
2
use super::{
3
    Reduction,
4
    resolve_rules::{ResolveRulesError, RuleData},
5
};
6
use crate::ast::{
7
    Expression, Name, SymbolTable,
8
    pretty::{pretty_variable_declaration, pretty_vec},
9
};
10

            
11
use itertools::Itertools;
12
use serde_json::json;
13
use std::collections::BTreeMap;
14
use std::fmt::Debug;
15
use thiserror::Error;
16
use tracing::{info, trace};
17

            
18
#[derive(Debug, Clone)]
19
pub struct RuleResult<'a> {
20
    pub rule_data: RuleData<'a>,
21
    pub reduction: Reduction,
22
}
23

            
24
pub type VariableDeclarationSnapshot = BTreeMap<Name, String>;
25

            
26
6217802
pub fn snapshot_variable_declarations(symbols: &SymbolTable) -> VariableDeclarationSnapshot {
27
6217802
    symbols
28
6217802
        .clone()
29
6217802
        .into_iter_local()
30
307499558
        .filter_map(|(name, _)| {
31
307499558
            pretty_variable_declaration(symbols, &name).map(|declaration| (name, declaration))
32
307499558
        })
33
6217802
        .collect()
34
6217802
}
35

            
36
/// Logs, to the main log, and the human readable traces used by the integration tester, that the
37
/// rule has been applied to the expression
38
150468
pub fn log_rule_application(
39
150468
    result: &RuleResult,
40
150468
    initial_expression: &Expression,
41
150468
    initial_symbols: &SymbolTable,
42
150468
    variable_declaration_snapshots: Option<(
43
150468
        &VariableDeclarationSnapshot,
44
150468
        &VariableDeclarationSnapshot,
45
150468
    )>,
46
150468
) {
47
150468
    let red = &result.reduction;
48
150468
    let rule = result.rule_data.rule;
49

            
50
    // A reduction can only modify either constraints or clauses, not both. So the the same
51
    // variable is used to hold changes in both (or empty if neither are changed).
52
150468
    let new_top_string = if !red.new_top.is_empty() {
53
12924
        pretty_vec(&red.new_top)
54
    } else {
55
137544
        pretty_vec(&red.new_clauses)
56
    };
57

            
58
150468
    info!(
59
        %new_top_string,
60
        "Applying rule: {} ({:?}), to expression: {}, resulting in: {}",
61
        rule.name,
62
        rule.rule_sets,
63
        initial_expression,
64
        red.new_expression
65
    );
66

            
67
    // empty if no top level constraints
68
150468
    let new_constraints_str = if !red.new_top.is_empty() {
69
12924
        let mut exprs: Vec<String> = vec![];
70
15572
        for expr in &red.new_top {
71
15572
            exprs.push(format!("  {expr}"));
72
15572
        }
73
12924
        let exprs = exprs.iter().join("\n");
74
12924
        format!("new constraints:\n{exprs}\n")
75
137544
    } else if !red.new_clauses.is_empty() {
76
25980
        let mut exprs: Vec<String> = vec![];
77
971120
        for clause in &red.new_clauses {
78
971120
            exprs.push(format!("  {clause}"));
79
971120
        }
80
25980
        let exprs = exprs.iter().join("\n");
81
25980
        format!("new clauses:\n{exprs}\n")
82
    } else {
83
111564
        String::new()
84
    };
85

            
86
150468
    let (new_variables_str, updated_variables_str) =
87
150468
        if let Some((before, after)) = variable_declaration_snapshots {
88
49582
            let mut new_variables = Vec::new();
89
49582
            let mut updated_variables = Vec::new();
90

            
91
2489334
            for (name, declaration_after) in after {
92
2489334
                match before.get(name) {
93
17660
                    None => new_variables.push(format!("  {declaration_after}")),
94
2471674
                    Some(declaration_before) if declaration_before != declaration_after => {
95
180
                        updated_variables
96
180
                            .push(format!("  {declaration_before} ~~> {declaration_after}"));
97
180
                    }
98
2471494
                    _ => {}
99
                }
100
            }
101

            
102
49582
            let new_variables_str = if new_variables.is_empty() {
103
47174
                String::new()
104
            } else {
105
2408
                format!("new variables:\n{}\n", new_variables.join("\n"))
106
            };
107

            
108
49582
            let updated_variables_str = if updated_variables.is_empty() {
109
49442
                String::new()
110
            } else {
111
140
                format!("\nupdated variables:\n{}\n", updated_variables.join("\n"))
112
            };
113

            
114
49582
            (new_variables_str, updated_variables_str)
115
        } else {
116
            // empty if no new variables
117
100886
            let mut vars: Vec<String> = vec![];
118
396806
            for var_name in red.added_symbols(initial_symbols) {
119
396612
                #[allow(clippy::unwrap_used)]
120
396612
                vars.push(format!(
121
396612
                    "  {}",
122
396612
                    pretty_variable_declaration(&red.symbols, &var_name).unwrap()
123
396612
                ));
124
396612
            }
125
100886
            let new_variables_str = if vars.is_empty() {
126
73482
                String::new()
127
            } else {
128
27404
                format!("new variables:\n{}\n", vars.join("\n"))
129
            };
130
100886
            (new_variables_str, String::new())
131
        };
132

            
133
150468
    trace!(
134
        target: "rule_engine_human",
135
        "{}, \n   ~~> {} ({:?})\n{}\n{}{}{}\n--\n",
136
        initial_expression,
137
        rule.name,
138
        rule.rule_sets,
139
        red.new_expression,
140
        new_variables_str,
141
        updated_variables_str,
142
        new_constraints_str
143
    );
144

            
145
150468
    trace!(
146
        target: "rule_engine",
147
        "{}",
148
420
    json!({
149
420
        "rule_name": result.rule_data.rule.name,
150
420
        "rule_priority": result.rule_data.priority,
151
420
        "rule_set": {
152
420
            "name": result.rule_data.rule_set.name,
153
        },
154
420
        "initial_expression": serde_json::to_value(initial_expression).unwrap(),
155
420
        "transformed_expression": serde_json::to_value(&red.new_expression).unwrap()
156
    })
157

            
158
    )
159
150468
}
160

            
161
/// Represents errors that can occur during the model rewriting process.
162
#[derive(Debug, Error)]
163
pub enum RewriteError {
164
    #[error("Error resolving rules {0}")]
165
    ResolveRulesError(ResolveRulesError),
166
}
167

            
168
impl From<ResolveRulesError> for RewriteError {
169
    fn from(error: ResolveRulesError) -> Self {
170
        RewriteError::ResolveRulesError(error)
171
    }
172
}