1
//! Module for storing positions of terms
2
#![forbid(unsafe_code)]
3

            
4
use core::fmt;
5
use std::collections::VecDeque;
6

            
7
use merc_aterm::ATermRef;
8

            
9
use merc_aterm::Term;
10
use smallvec::SmallVec;
11
use smallvec::smallvec;
12

            
13
/// An ExplicitPosition stores a list of position indices. The index starts at 1.
14
/// The subterm of term s(s(0)) at position 1.1 is 0.
15
/// The empty position, aka the root term, is represented by the symbol ε.
16
/// Indices are stored in a SmallVec, which is configured to store 4 elements.
17
/// If the position contains a maximum of 4 elements it is stored on the stack.
18
/// If the position is longer a heap allocation is made.
19
#[repr(transparent)]
20
#[derive(Hash, Clone, Default, Eq, PartialEq, Ord, PartialOrd)]
21
pub struct ExplicitPosition {
22
    indices: SmallVec<[usize; 4]>,
23
}
24

            
25
impl ExplicitPosition {
26
    /// Creates a new ExplicitPosition from a slice of indices.
27
5269477
    pub fn new(indices: &[usize]) -> Self {
28
5269477
        Self {
29
5269477
            indices: SmallVec::from(indices),
30
5269477
        }
31
5269477
    }
32

            
33
    /// Create the empty position.
34
76827
    pub fn empty() -> Self {
35
76827
        Self { indices: smallvec![] }
36
76827
    }
37

            
38
    /// Returns the length of the position indices
39
5642081
    pub fn len(&self) -> usize {
40
5642081
        self.indices.len()
41
5642081
    }
42

            
43
    /// Returns the indices of the position.
44
41543318
    pub fn indices(&self) -> &[usize] {
45
41543318
        &self.indices
46
41543318
    }
47

            
48
    /// Returns true iff the position is empty.
49
2653196
    pub fn is_empty(&self) -> bool {
50
2653196
        self.indices.len() == 0
51
2653196
    }
52

            
53
    /// Add the index to the position.
54
188407
    pub fn push(&mut self, index: usize) {
55
188407
        self.indices.push(index);
56
188407
    }
57
}
58

            
59
impl<'a, 'b, T: Term<'a, 'b>> PositionIndexed<'b> for T
60
where
61
    'a: 'b,
62
{
63
    type Target<'c>
64
        = ATermRef<'c>
65
    where
66
        Self: 'c,
67
        Self: 'b;
68

            
69
6
    fn get_position(&'b self, position: &ExplicitPosition) -> Self::Target<'b> {
70
6
        let mut result = self.copy();
71

            
72
8
        for index in &position.indices {
73
8
            result = result.arg(index - 1); // Note that positions are 1 indexed.
74
8
        }
75

            
76
6
        result
77
6
    }
78
}
79

            
80
pub trait PositionIndexed<'b> {
81
    type Target<'a>
82
    where
83
        Self: 'a,
84
        Self: 'b;
85

            
86
    /// Returns the Target at the given position.
87
    fn get_position(&'b self, position: &ExplicitPosition) -> Self::Target<'b>;
88
}
89

            
90
impl fmt::Display for ExplicitPosition {
91
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92
        write!(f, "{self:?}")
93
    }
94
}
95

            
96
impl fmt::Debug for ExplicitPosition {
97
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98
        if self.indices.is_empty() {
99
            write!(f, "ε")?;
100
        } else {
101
            let mut first = true;
102
            for p in &self.indices {
103
                if first {
104
                    write!(f, "{p}")?;
105
                    first = false;
106
                } else {
107
                    write!(f, ".{p}")?;
108
                }
109
            }
110
        }
111

            
112
        Ok(())
113
    }
114
}
115

            
116
/// An iterator over all (term, position) pairs of the given ATerm.
117
pub struct PositionIterator<'a> {
118
    queue: VecDeque<(ATermRef<'a>, ExplicitPosition)>,
119
}
120

            
121
impl<'a> PositionIterator<'a> {
122
2
    pub fn new(t: ATermRef<'a>) -> PositionIterator<'a> {
123
2
        PositionIterator {
124
2
            queue: VecDeque::from([(t, ExplicitPosition::empty())]),
125
2
        }
126
2
    }
127
}
128

            
129
impl<'a> Iterator for PositionIterator<'a> {
130
    type Item = (ATermRef<'a>, ExplicitPosition);
131

            
132
10
    fn next(&mut self) -> Option<Self::Item> {
133
10
        if self.queue.is_empty() {
134
2
            None
135
        } else {
136
            // Get a subterm to inspect
137
8
            let (term, pos) = self.queue.pop_front().unwrap();
138

            
139
            // Put subterms in the queue
140
8
            for (i, argument) in term.arguments().enumerate() {
141
6
                let mut new_position = pos.clone();
142
6
                new_position.indices.push(i + 1);
143
6
                self.queue.push_back((argument, new_position));
144
6
            }
145

            
146
8
            Some((term, pos))
147
        }
148
10
    }
149
}
150

            
151
#[cfg(test)]
152
mod tests {
153
    use merc_aterm::ATerm;
154

            
155
    use super::*;
156

            
157
    #[test]
158
1
    fn test_get_position() {
159
1
        let t = ATerm::from_string("f(g(a),b)").unwrap();
160
1
        let expected = ATerm::from_string("a").unwrap();
161

            
162
1
        assert_eq!(t.get_position(&ExplicitPosition::new(&[1, 1])), expected.copy());
163
1
    }
164

            
165
    #[test]
166
1
    fn test_position_iterator() {
167
1
        let t = ATerm::from_string("f(g(a),b)").unwrap();
168

            
169
4
        for (term, pos) in PositionIterator::new(t.copy()) {
170
4
            assert_eq!(
171
4
                t.get_position(&pos),
172
                term,
173
                "The resulting (subterm, position) pair doesn't match the get_position implementation"
174
            );
175
        }
176

            
177
1
        assert_eq!(
178
1
            PositionIterator::new(t.copy()).count(),
179
            4,
180
            "The number of subterms doesn't match the expected value"
181
        );
182
1
    }
183
}