1
#![forbid(unsafe_code)]
2

            
3
use std::collections::VecDeque;
4
use std::fmt;
5

            
6
use merc_data::DataExpression;
7
use merc_data::DataExpressionRef;
8

            
9
use super::ExplicitPosition;
10

            
11
/// A newtype wrapper around [ExplicitPosition] specifically for data expressions
12
/// This provides type safety and clarity when dealing with positions in data expressions
13
#[repr(transparent)]
14
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
15
pub struct DataPosition(ExplicitPosition);
16

            
17
impl DataPosition {
18
    /// Creates a new empty position
19
230671
    pub fn empty() -> Self {
20
230671
        Self(ExplicitPosition::empty())
21
230671
    }
22

            
23
    /// Creates a new position from a slice of indices
24
15808411
    pub fn new(indices: &[usize]) -> Self {
25
15808411
        Self(ExplicitPosition::new(indices))
26
15808411
    }
27

            
28
    /// Returns the underlying indices
29
124562862
    pub fn indices(&self) -> &[usize] {
30
124562862
        self.0.indices()
31
124562862
    }
32

            
33
    /// Returns the length of the position indices
34
16926045
    pub fn len(&self) -> usize {
35
16926045
        self.0.len()
36
16926045
    }
37

            
38
    /// Returns true if the position is empty
39
7950132
    pub fn is_empty(&self) -> bool {
40
7950132
        self.0.is_empty()
41
7950132
    }
42

            
43
    /// Adds the index to the position
44
565407
    pub fn push(&mut self, index: usize) {
45
565407
        self.0.push(index);
46
565407
    }
47
}
48

            
49
impl fmt::Display for DataPosition {
50
30
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51
30
        write!(f, "{}", self.0)
52
30
    }
53
}
54

            
55
impl fmt::Debug for DataPosition {
56
36
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57
36
        write!(f, "{}", self.0)
58
36
    }
59
}
60

            
61
/// A specialisation of the [super::PositionIndexed] trait for [DataExpression]. This is used to keep the indexing consistent.
62
pub trait DataPositionIndexed<'b> {
63
    type Target<'a>
64
    where
65
        Self: 'a,
66
        Self: 'b;
67

            
68
    /// Returns the Target at the given position.
69
    fn get_data_position(&'b self, position: &DataPosition) -> Self::Target<'b>;
70
}
71

            
72
impl<'b> DataPositionIndexed<'b> for DataExpression {
73
    type Target<'a>
74
        = DataExpressionRef<'a>
75
    where
76
        Self: 'a;
77

            
78
13682
    fn get_data_position(&'b self, position: &DataPosition) -> Self::Target<'b> {
79
13682
        let mut result = self.copy();
80

            
81
13685
        for index in position.indices() {
82
3704
            result = result.data_arg(*index - 1); // Note that positions are 1 indexed.
83
3704
        }
84

            
85
13682
        result
86
13682
    }
87
}
88

            
89
impl<'b> DataPositionIndexed<'b> for DataExpressionRef<'b> {
90
    type Target<'a>
91
        = DataExpressionRef<'a>
92
    where
93
        Self: 'a;
94

            
95
72785574
    fn get_data_position(&'b self, position: &DataPosition) -> Self::Target<'b> {
96
72785574
        let mut result = self.copy();
97

            
98
72785576
        for index in position.indices() {
99
49069586
            result = result.data_arg(*index - 1); // Note that positions are 1 indexed.
100
49069586
        }
101

            
102
72785574
        result
103
72785574
    }
104
}
105

            
106
/// An iterator over all (term, position) pairs of the given [DataExpression].
107
pub struct DataPositionIterator<'a> {
108
    queue: VecDeque<(DataExpressionRef<'a>, DataPosition)>,
109
}
110

            
111
impl<'a> DataPositionIterator<'a> {
112
218650
    pub fn new(t: DataExpressionRef<'a>) -> Self {
113
218650
        Self {
114
218650
            queue: VecDeque::from([(t, DataPosition::empty())]),
115
218650
        }
116
218650
    }
117
}
118

            
119
impl<'a> Iterator for DataPositionIterator<'a> {
120
    type Item = (DataExpressionRef<'a>, DataPosition);
121

            
122
788519
    fn next(&mut self) -> Option<Self::Item> {
123
788519
        if self.queue.is_empty() {
124
218650
            None
125
        } else {
126
            // Get a subterm to inspect
127
569869
            let (term, pos) = self.queue.pop_front().unwrap();
128

            
129
            // Put subterms in the queue
130
569869
            for (i, argument) in term.data_arguments().enumerate() {
131
351219
                let mut new_position = pos.clone();
132
351219
                new_position.push(i + 1);
133
351219
                self.queue.push_back((argument, new_position));
134
351219
            }
135

            
136
569869
            Some((term, pos))
137
        }
138
788519
    }
139
}
140

            
141
#[cfg(test)]
142
mod tests {
143
    use merc_data::DataExpression;
144

            
145
    use super::DataPosition;
146
    use super::DataPositionIndexed;
147
    use super::DataPositionIterator;
148

            
149
    #[test]
150
1
    fn test_get_data_position() {
151
1
        let t = DataExpression::from_string("f(g(a),b)").unwrap();
152
1
        let expected = DataExpression::from_string("a").unwrap();
153

            
154
1
        assert_eq!(t.get_data_position(&DataPosition::new(&[1, 1])), expected.copy());
155
1
    }
156

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

            
161
4
        for (term, pos) in DataPositionIterator::new(t.copy()) {
162
4
            assert_eq!(
163
4
                t.get_data_position(&pos),
164
                term,
165
                "The resulting (subterm, position) pair doesn't match the get_data_position implementation"
166
            );
167
        }
168

            
169
1
        assert_eq!(
170
1
            DataPositionIterator::new(t.copy()).count(),
171
            4,
172
            "The number of subterms doesn't match the expected value"
173
        );
174
1
    }
175
}