1
use log::info;
2

            
3
use merc_aterm::storage::THREAD_TERM_POOL;
4
use merc_aterm::storage::ThreadTermPool;
5
use merc_data::DataApplication;
6
use merc_data::DataExpression;
7
use merc_data::DataExpressionRef;
8

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

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

            
31
132
        debug_trace!("input: {}", t);
32

            
33
132
        let result = THREAD_TERM_POOL.with(|tp| {
34
132
            InnermostRewriter::rewrite_aux(tp, &mut self.stack, &mut self.builder, &mut stats, &self.apma, t)
35
132
        });
36

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

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

            
50
84
        InnermostRewriter {
51
84
            apma,
52
84
            stack: InnermostStack::default(),
53
84
            builder: TermStackBuilder::new(),
54
84
        }
55
84
    }
56

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

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

            
91
        loop {
92
17714106
            debug_trace!("{}", stack);
93

            
94
17714106
            let mut write_configs = stack.configs.write();
95
17714106
            if let Some(config) = write_configs.pop() {
96
17714106
                match config {
97
6734511
                    Config::Rewrite(result) => {
98
6734511
                        let mut write_terms = stack.terms.write();
99
6734511
                        let term = write_terms.pop().unwrap().unwrap();
100

            
101
6734511
                        let symbol = term.data_function_symbol();
102
6734511
                        let arguments = term.data_arguments();
103

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

            
110
                        // Safety: symbol is stored in the container on the next line.
111
6734511
                        let symbol = unsafe { write_configs.protect(&symbol) };
112
6734511
                        InnermostStack::add_result(&mut write_configs, symbol.into(), arguments.len(), result);
113
6734511
                        for (offset, arg) in arguments.into_iter().enumerate() {
114
6102273
                            InnermostStack::add_rewrite(
115
6102273
                                &mut write_configs,
116
6102273
                                &mut write_terms,
117
6102273
                                arg,
118
6102273
                                top_of_stack + offset,
119
6102273
                            );
120
6102273
                        }
121
6734511
                        drop(write_configs);
122
                    }
123
10347357
                    Config::Construct(symbol, arity, index) => {
124
                        // Take the last arity arguments.
125
10347357
                        let mut write_terms = stack.terms.write();
126
10347357
                        let length = write_terms.len();
127

            
128
10347357
                        let arguments = &write_terms[length - arity..];
129

            
130
10347357
                        let term: DataExpression = if arguments.is_empty() {
131
1714824
                            symbol.protect().into()
132
                        } else {
133
8632533
                            DataApplication::with_iter(&symbol, arguments.len(), arguments.iter().flatten()).into()
134
                        };
135

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

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

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

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

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

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

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

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

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

            
242
5379747
                state_index = transition.destinations.first().unwrap().1;
243
            } else {
244
36
                return None;
245
            }
246
        }
247
10347357
    }
248

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

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

            
266
316053
            if (lhs_normal != rhs_normal && c.equality) || (lhs_normal == rhs_normal && !c.equality) {
267
77472
                return false;
268
238581
            }
269
        }
270

            
271
2956395
        true
272
3033867
    }
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
4275
    pub fn new(rule: &Rule) -> AnnouncementInnermost {
295
4275
        AnnouncementInnermost {
296
4275
            conditions: extend_conditions(rule),
297
4275
            equivalence_classes: derive_equivalence_classes(rule),
298
4275
            rhs_stack: TermStack::new(rule),
299
4275
        }
300
4275
    }
301
}