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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
266
105291
            if lhs_normal != rhs_normal && c.equality || lhs_normal == rhs_normal && !c.equality {
267
25890
                return false;
268
79401
            }
269
        }
270

            
271
985303
        true
272
1011193
    }
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
}