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
210742
    pub(crate) fn rewrite_aux(
74
210742
        tp: &ThreadTermPool,
75
210742
        stack: &mut InnermostStack,
76
210742
        builder: &mut TermStackBuilder,
77
210742
        stats: &mut RewritingStatistics,
78
210742
        automaton: &SetAutomaton<AnnouncementInnermost>,
79
210742
        input_term: &DataExpression,
80
210742
    ) -> DataExpression {
81
210742
        stats.recursions += 1;
82
210742
        {
83
210742
            let mut write_terms = stack.terms.write();
84
210742
            let mut write_configs = stack.configs.write();
85
210742

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
266
105350
            if lhs_normal != rhs_normal && c.equality || lhs_normal == rhs_normal && !c.equality {
267
25823
                return false;
268
79527
            }
269
        }
270

            
271
985465
        true
272
1011288
    }
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
}