1
#![forbid(unsafe_code)]
2

            
3
use log::info;
4

            
5
use merc_aterm::storage::THREAD_TERM_POOL;
6
use merc_aterm::storage::ThreadTermPool;
7
use merc_data::DataApplication;
8
use merc_data::DataExpression;
9
use merc_data::DataExpressionRef;
10

            
11
use crate::RewriteEngine;
12
use crate::RewriteSpecification;
13
use crate::RewritingStatistics;
14
use crate::Rule;
15
use crate::matching::conditions::EMACondition;
16
use crate::matching::conditions::extend_conditions;
17
use crate::matching::nonlinear::EquivalenceClass;
18
use crate::matching::nonlinear::check_equivalence_classes;
19
use crate::matching::nonlinear::derive_equivalence_classes;
20
use crate::set_automaton::MatchAnnouncement;
21
use crate::set_automaton::SetAutomaton;
22
use crate::utilities::Config;
23
use crate::utilities::DataPositionIndexed;
24
use crate::utilities::InnermostStack;
25
use crate::utilities::TermStack;
26
use crate::utilities::TermStackBuilder;
27
use merc_utilities::debug_trace;
28

            
29
impl RewriteEngine for InnermostRewriter {
30
42
    fn rewrite(&mut self, t: &DataExpression) -> DataExpression {
31
42
        let mut stats = RewritingStatistics::default();
32

            
33
42
        debug_trace!("input: {}", t);
34

            
35
42
        let result = THREAD_TERM_POOL.with_borrow(|tp| {
36
42
            InnermostRewriter::rewrite_aux(tp, &mut self.stack, &mut self.builder, &mut stats, &self.apma, t)
37
42
        });
38

            
39
42
        info!(
40
            "{} rewrites, {} single steps and {} symbol comparisons",
41
            stats.recursions, stats.rewrite_steps, stats.symbol_comparisons
42
        );
43
42
        result
44
42
    }
45
}
46

            
47
impl InnermostRewriter {
48
    /// Creates a new InnermostRewriter from the given rewrite specification.
49
26
    pub fn new(spec: &RewriteSpecification) -> InnermostRewriter {
50
26
        let apma = SetAutomaton::new(spec, AnnouncementInnermost::new, true);
51

            
52
26
        InnermostRewriter {
53
26
            apma,
54
26
            stack: InnermostStack::default(),
55
26
            builder: TermStackBuilder::new(),
56
26
        }
57
26
    }
58

            
59
    /// Function to rewrite a term 't'. The elements of the automaton 'states'
60
    /// and 'tp' are passed as separate parameters to satisfy the borrow
61
    /// checker.
62
    ///
63
    /// # Details
64
    ///
65
    /// Uses a stack of terms and configurations to avoid recursions and to keep
66
    /// track of terms in normal forms without explicit tagging. The configuration
67
    /// stack consists of three different possible values with the following semantics
68
    ///     - Return(): Returns the top of the stack.
69
    ///     - Rewrite(index): Updates the configuration to rewrite the top of the term stack
70
    ///                       and places the result on the given index.
71
    ///     - Construct(arity, index, result):
72
    ///
73
209978
    pub(crate) fn rewrite_aux(
74
209978
        tp: &ThreadTermPool,
75
209978
        stack: &mut InnermostStack,
76
209978
        builder: &mut TermStackBuilder,
77
209978
        stats: &mut RewritingStatistics,
78
209978
        automaton: &SetAutomaton<AnnouncementInnermost>,
79
209978
        input_term: &DataExpression,
80
209978
    ) -> DataExpression {
81
209978
        stats.recursions += 1;
82
209978
        {
83
209978
            let mut write_terms = stack.terms.write();
84
209978
            let mut write_configs = stack.configs.write();
85
209978

            
86
209978
            // Push the result term to the stack.
87
209978
            let top_of_stack = write_terms.len();
88
209978
            write_configs.push(Config::Return());
89
209978
            write_terms.push(None);
90
209978
            InnermostStack::add_rewrite(&mut write_configs, &mut write_terms, input_term.copy(), top_of_stack);
91
209978
        }
92

            
93
        loop {
94
5884947
            debug_trace!("{}", stack);
95

            
96
5884947
            let mut write_configs = stack.configs.write();
97
5884947
            if let Some(config) = write_configs.pop() {
98
5884947
                match config {
99
2235656
                    Config::Rewrite(result) => {
100
2235656
                        let mut write_terms = stack.terms.write();
101
2235656
                        let term = write_terms.pop().unwrap().unwrap();
102

            
103
2235656
                        let symbol = term.data_function_symbol();
104
2235656
                        let arguments = term.data_arguments();
105

            
106
                        // For all the argument we reserve space on the stack.
107
2235656
                        let top_of_stack = write_terms.len();
108
2235656
                        for _ in 0..arguments.len() {
109
2025678
                            write_terms.push(Default::default());
110
2025678
                        }
111

            
112
2235656
                        let symbol = write_configs.protect(&symbol);
113
2235656
                        InnermostStack::add_result(&mut write_configs, symbol.into(), arguments.len(), result);
114
2235656
                        for (offset, arg) in arguments.into_iter().enumerate() {
115
2025678
                            InnermostStack::add_rewrite(
116
2025678
                                &mut write_configs,
117
2025678
                                &mut write_terms,
118
2025678
                                arg,
119
2025678
                                top_of_stack + offset,
120
2025678
                            );
121
2025678
                        }
122
2235656
                        drop(write_configs);
123
                    }
124
3439313
                    Config::Construct(symbol, arity, index) => {
125
                        // Take the last arity arguments.
126
3439313
                        let mut write_terms = stack.terms.write();
127
3439313
                        let length = write_terms.len();
128

            
129
3439313
                        let arguments = &write_terms[length - arity..];
130

            
131
3439313
                        let term: DataExpression = if arguments.is_empty() {
132
567714
                            symbol.protect().into()
133
                        } else {
134
2871599
                            DataApplication::with_iter(&symbol, arguments.len(), arguments.iter().flatten()).into()
135
                        };
136

            
137
                        // Remove the arguments from the stack.
138
3439313
                        write_terms.drain(length - arity..);
139
3439313
                        drop(write_terms);
140
3439313
                        drop(write_configs);
141

            
142
3439313
                        match InnermostRewriter::find_match(tp, stack, builder, stats, automaton, &term.copy()) {
143
984840
                            Some((_announcement, annotation)) => {
144
984840
                                debug_trace!(
145
984840
                                    "rewrite {} => {} using rule {}",
146
984840
                                    term,
147
984840
                                    annotation.rhs_stack.evaluate(&term),
148
984840
                                    _announcement.rule
149
984840
                                );
150
984840

            
151
984840
                                // Reacquire the write access and add the matching RHSStack.
152
984840
                                let mut write_terms = stack.terms.write();
153
984840
                                let mut write_configs = stack.configs.write();
154
984840
                                InnermostStack::integrate(
155
984840
                                    &mut write_configs,
156
984840
                                    &mut write_terms,
157
984840
                                    &annotation.rhs_stack,
158
984840
                                    &term.copy(),
159
984840
                                    index,
160
984840
                                );
161
984840
                                stats.rewrite_steps += 1;
162
984840
                            }
163
2454473
                            None => {
164
2454473
                                // Add the term on the stack.
165
2454473
                                let mut write_terms = stack.terms.write();
166
2454473
                                write_terms[index] = Some(write_terms.protect(&term).into());
167
2454473
                            }
168
                        }
169
                    }
170
                    Config::Term(_, _) => {
171
                        unreachable!("This case should not happen");
172
                    }
173
                    Config::Return() => {
174
209978
                        let mut write_terms = stack.terms.write();
175

            
176
209978
                        return write_terms
177
209978
                            .pop()
178
209978
                            .expect("The result should be the last element on the stack")
179
209978
                            .expect("The result should be Some")
180
209978
                            .protect();
181
                    }
182
                }
183

            
184
5674969
                if cfg!(debug_assertions) {
185
5674969
                    let read_configs = stack.configs.read();
186
539814051
                    for (index, term) in stack.terms.read().iter().enumerate() {
187
539814051
                        if term.is_none() {
188
216562143
                            debug_assert!(
189
216562143
                                read_configs.iter().any(|x| {
190
216562143
                                    match x {
191
                                        Config::Construct(_, _, result) => index == *result,
192
                                        Config::Rewrite(result) => index == *result,
193
                                        Config::Term(_, result) => index == *result,
194
216562143
                                        Config::Return() => true,
195
                                    }
196
216562143
                                }),
197
                                "The default term at index {index} is not a result of any operation."
198
                            );
199
323251908
                        }
200
                    }
201
                }
202
            }
203
        }
204
209978
    }
205

            
206
    /// Use the APMA to find a match for the given term.
207
3439313
    fn find_match<'a>(
208
3439313
        tp: &ThreadTermPool,
209
3439313
        stack: &mut InnermostStack,
210
3439313
        builder: &mut TermStackBuilder,
211
3439313
        stats: &mut RewritingStatistics,
212
3439313
        automaton: &'a SetAutomaton<AnnouncementInnermost>,
213
3439313
        t: &DataExpressionRef<'_>,
214
3439313
    ) -> Option<(&'a MatchAnnouncement, &'a AnnouncementInnermost)> {
215
        // Start at the initial state
216
3439313
        let mut state_index = 0;
217
        loop {
218
5232013
            let state = &automaton.states()[state_index];
219

            
220
            // Get the symbol at the position state.label
221
5232013
            stats.symbol_comparisons += 1;
222
5232013
            let pos = t.get_data_position(state.label());
223
5232013
            let symbol = pos.data_function_symbol();
224

            
225
            // Get the transition for the label and check if there is a pattern match
226
5232013
            if let Some(transition) = automaton.transitions().get(&(state_index, symbol.operation_id())) {
227
5232005
                for (announcement, annotation) in &transition.announcements {
228
1010810
                    if check_equivalence_classes(t, &annotation.equivalence_classes)
229
1010810
                        && InnermostRewriter::check_conditions(tp, stack, builder, stats, automaton, annotation, t)
230
                    {
231
                        // We found a matching pattern
232
984840
                        return Some((announcement, annotation));
233
25970
                    }
234
                }
235

            
236
                // If there is no pattern match we check if the transition has a destination state
237
4247165
                if transition.destinations.is_empty() {
238
                    // If there is no destination state there is no pattern match
239
2454465
                    return None;
240
1792700
                }
241

            
242
1792700
                state_index = transition.destinations.first().unwrap().1;
243
            } else {
244
8
                return None;
245
            }
246
        }
247
3439313
    }
248

            
249
    /// Checks whether the condition holds for given match announcement.
250
1010810
    fn check_conditions(
251
1010810
        tp: &ThreadTermPool,
252
1010810
        stack: &mut InnermostStack,
253
1010810
        builder: &mut TermStackBuilder,
254
1010810
        stats: &mut RewritingStatistics,
255
1010810
        automaton: &SetAutomaton<AnnouncementInnermost>,
256
1010810
        announcement: &AnnouncementInnermost,
257
1010810
        t: &DataExpressionRef<'_>,
258
1010810
    ) -> bool {
259
1010810
        for c in &announcement.conditions {
260
104968
            let rhs: DataExpression = c.rhs_term_stack.evaluate_with(t, builder);
261
104968
            let lhs: DataExpression = c.lhs_term_stack.evaluate_with(t, builder);
262

            
263
104968
            let rhs_normal = InnermostRewriter::rewrite_aux(tp, stack, builder, stats, automaton, &rhs);
264
104968
            let lhs_normal = InnermostRewriter::rewrite_aux(tp, stack, builder, stats, automaton, &lhs);
265

            
266
104968
            if lhs_normal != rhs_normal && c.equality || lhs_normal == rhs_normal && !c.equality {
267
25970
                return false;
268
78998
            }
269
        }
270

            
271
984840
        true
272
1010810
    }
273
}
274

            
275
/// Innermost Adaptive Pattern Matching Automaton (APMA) rewrite engine.
276
pub struct InnermostRewriter {
277
    apma: SetAutomaton<AnnouncementInnermost>,
278
    stack: InnermostStack,
279
    builder: TermStackBuilder,
280
}
281

            
282
pub struct AnnouncementInnermost {
283
    /// Positions in the pattern with the same variable, for non-linear patterns
284
    pub equivalence_classes: Vec<EquivalenceClass>,
285

            
286
    /// Conditions for the left hand side.
287
    pub conditions: Vec<EMACondition>,
288

            
289
    /// The innermost stack for the right hand side of the rewrite rule.
290
    pub rhs_stack: TermStack,
291
}
292

            
293
impl AnnouncementInnermost {
294
1417
    pub fn new(rule: &Rule) -> AnnouncementInnermost {
295
1417
        AnnouncementInnermost {
296
1417
            conditions: extend_conditions(rule),
297
1417
            equivalence_classes: derive_equivalence_classes(rule),
298
1417
            rhs_stack: TermStack::new(rule),
299
1417
        }
300
1417
    }
301
}