1
#![forbid(unsafe_code)]
2

            
3
use std::fmt;
4

            
5
use merc_aterm::Protected;
6
use merc_aterm::ProtectedWriteGuard;
7
use merc_data::DataExpressionRef;
8
use merc_data::DataFunctionSymbolRef;
9
use merc_utilities::debug_trace;
10

            
11
// Only used in debug_trace!
12
#[allow(unused_imports)]
13
use itertools::Itertools;
14

            
15
use crate::utilities::DataPositionIndexed;
16

            
17
use super::Config;
18
use super::TermStack;
19

            
20
/// This stack is used to avoid recursion and also to keep track of terms in
21
/// normal forms by explicitly representing the rewrites of a right hand
22
/// side.
23
#[derive(Default)]
24
pub struct InnermostStack {
25
    pub configs: Protected<Vec<Config<'static>>>,
26
    pub terms: Protected<Vec<Option<DataExpressionRef<'static>>>>,
27
}
28

            
29
impl InnermostStack {
30
    /// Updates the InnermostStack to integrate the rhs_stack instructions.
31
2607814
    pub fn integrate(
32
2607814
        write_configs: &mut ProtectedWriteGuard<Vec<Config<'static>>>,
33
2607814
        write_terms: &mut ProtectedWriteGuard<Vec<Option<DataExpressionRef<'static>>>>,
34
2607814
        rhs_stack: &TermStack,
35
2607814
        term: &DataExpressionRef<'_>,
36
2607814
        result_index: usize,
37
2607814
    ) {
38
        // TODO: This ignores the first element of the stack, but that is kind of difficult to deal with.
39
2607814
        let top_of_stack = write_terms.len();
40
2607814
        write_terms.reserve(rhs_stack.stack_size - 1); // We already reserved space for the result.
41
4721897
        for _ in 0..rhs_stack.stack_size - 1 {
42
4721897
            write_terms.push(None);
43
4721897
        }
44

            
45
2607814
        let mut first = true;
46
2983078
        for config in rhs_stack.innermost_stack.read().iter() {
47
2983078
            match config {
48
2983078
                Config::Construct(symbol, arity, offset) => {
49
2983078
                    if first {
50
2443950
                        // The first result must be placed on the original result index.
51
2443950
                        InnermostStack::add_result(write_configs, symbol.copy(), *arity, result_index);
52
2443953
                    } else {
53
539128
                        // Otherwise, we put it on the end of the stack.
54
539128
                        InnermostStack::add_result(write_configs, symbol.copy(), *arity, top_of_stack + offset - 1);
55
539128
                    }
56
                }
57
                Config::Term(term, index) => {
58
                    let term = write_configs.protect(term);
59
                    write_configs.push(Config::Term(term.into(), *index));
60
                }
61
                Config::Rewrite(_) => {
62
                    unreachable!("This case should not happen");
63
                }
64
                Config::Return() => {
65
                    unreachable!("This case should not happen");
66
                }
67
            }
68
2983078
            first = false;
69
        }
70
2607814
        debug_trace!(
71
            "\t applied stack size: {}, substitution: {{{}}}, stack: [{}]",
72
            rhs_stack.stack_size,
73
            rhs_stack.variables.iter().format_with(", ", |element, f| {
74
                f(&format_args!("{} -> {}", element.0, element.1))
75
            }),
76
            rhs_stack.innermost_stack.read().iter().format("\n")
77
        );
78

            
79
2607814
        debug_assert!(
80
2607814
            rhs_stack.stack_size != 1 || rhs_stack.variables.len() <= 1,
81
            "There can only be a single variable in the right hand side"
82
        );
83
2607814
        if rhs_stack.stack_size == 1 && rhs_stack.variables.len() == 1 {
84
163864
            // This is a special case where we place the result on the correct position immediately.
85
163864
            // The right hand side is only a variable
86
163864
            write_terms[result_index] = Some(
87
163864
                write_terms
88
163864
                    .protect(&term.get_data_position(&rhs_stack.variables[0].0))
89
163864
                    .into(),
90
163864
            );
91
163864
        } else {
92
4182769
            for (position, index) in &rhs_stack.variables {
93
4182769
                // Add the positions to the stack.
94
4182769
                write_terms[top_of_stack + index - 1] =
95
4182769
                    Some(write_terms.protect(&term.get_data_position(position)).into());
96
4182769
            }
97
        }
98
2607814
    }
99

            
100
    /// Indicate that the given symbol with arity can be constructed at the given index.
101
5218734
    pub fn add_result(
102
5218734
        write_configs: &mut ProtectedWriteGuard<Vec<Config<'static>>>,
103
5218734
        symbol: DataFunctionSymbolRef<'_>,
104
5218734
        arity: usize,
105
5218734
        index: usize,
106
5218734
    ) {
107
5218734
        let symbol = write_configs.protect(&symbol);
108
5218734
        write_configs.push(Config::Construct(symbol.into(), arity, index));
109
5218734
    }
110

            
111
    /// Indicate that the term must be rewritten and its result must be placed at the given index.
112
2235656
    pub fn add_rewrite(
113
2235656
        write_configs: &mut ProtectedWriteGuard<Vec<Config<'static>>>,
114
2235656
        write_terms: &mut ProtectedWriteGuard<Vec<Option<DataExpressionRef<'static>>>>,
115
2235656
        term: DataExpressionRef<'_>,
116
2235656
        index: usize,
117
2235656
    ) {
118
2235656
        let term = write_terms.protect(&term);
119
2235656
        write_configs.push(Config::Rewrite(index));
120
2235656
        write_terms.push(Some(term.into()));
121
2235656
    }
122
}
123

            
124
impl fmt::Display for InnermostStack {
125
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126
        writeln!(f, "Terms: [")?;
127
        for (i, entry) in self.terms.read().iter().enumerate() {
128
            match entry {
129
                Some(term) => writeln!(f, "{i}\t{term}")?,
130
                None => writeln!(f, "{i}\tNone")?,
131
            }
132
        }
133
        writeln!(f, "]")?;
134

            
135
        writeln!(f, "Configs: [")?;
136
        for config in self.configs.read().iter() {
137
            writeln!(f, "\t{config}")?;
138
        }
139
        write!(f, "]")
140
    }
141
}