1 | use crate::error::Needed; |
2 | use crate::stream::AsBStr; |
3 | use crate::stream::AsBytes; |
4 | use crate::stream::Checkpoint; |
5 | use crate::stream::Compare; |
6 | use crate::stream::CompareResult; |
7 | use crate::stream::FindSlice; |
8 | use crate::stream::Location; |
9 | use crate::stream::Offset; |
10 | #[cfg (feature = "unstable-recover" )] |
11 | #[cfg (feature = "std" )] |
12 | use crate::stream::Recover; |
13 | use crate::stream::SliceLen; |
14 | use crate::stream::Stream; |
15 | use crate::stream::StreamIsPartial; |
16 | use crate::stream::UpdateSlice; |
17 | |
18 | /// Allow collecting the span of a parsed token within a slice |
19 | /// |
20 | /// Converting byte offsets to line or column numbers is left up to the user, as computing column |
21 | /// numbers requires domain knowledge (are columns byte-based, codepoint-based, or grapheme-based?) |
22 | /// and O(n) iteration over the input to determine codepoint and line boundaries. |
23 | /// |
24 | /// [The `line-span` crate](https://docs.rs/line-span/latest/line_span/) can help with converting |
25 | /// byte offsets to line numbers. |
26 | /// |
27 | /// See [`Parser::span`][crate::Parser::span] and [`Parser::with_span`][crate::Parser::with_span] for more details |
28 | #[derive (Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord)] |
29 | #[doc (alias = "LocatingSliceSpan" )] |
30 | #[doc (alias = "Located" )] |
31 | pub struct LocatingSlice<I> { |
32 | initial: I, |
33 | input: I, |
34 | } |
35 | |
36 | impl<I> LocatingSlice<I> |
37 | where |
38 | I: Clone + Offset, |
39 | { |
40 | /// Wrap another Stream with span tracking |
41 | pub fn new(input: I) -> Self { |
42 | let initial: I = input.clone(); |
43 | Self { initial, input } |
44 | } |
45 | |
46 | #[inline ] |
47 | fn previous_token_end(&self) -> usize { |
48 | // Assumptions: |
49 | // - Index offsets is sufficient |
50 | // - Tokens are continuous |
51 | self.input.offset_from(&self.initial) |
52 | } |
53 | #[inline ] |
54 | fn current_token_start(&self) -> usize { |
55 | // Assumptions: |
56 | // - Index offsets is sufficient |
57 | self.input.offset_from(&self.initial) |
58 | } |
59 | } |
60 | |
61 | impl<I> LocatingSlice<I> |
62 | where |
63 | I: Clone + Stream + Offset, |
64 | { |
65 | /// Reset the stream to the start |
66 | /// |
67 | /// This is useful for formats that encode a graph with addresses relative to the start of the |
68 | /// input. |
69 | #[doc (alias = "fseek" )] |
70 | #[inline ] |
71 | pub fn reset_to_start(&mut self) { |
72 | let start: ::Checkpoint = self.initial.checkpoint(); |
73 | self.input.reset(&start); |
74 | } |
75 | } |
76 | |
77 | impl<I> AsRef<I> for LocatingSlice<I> { |
78 | #[inline (always)] |
79 | fn as_ref(&self) -> &I { |
80 | &self.input |
81 | } |
82 | } |
83 | |
84 | impl<I> crate::lib::std::ops::Deref for LocatingSlice<I> { |
85 | type Target = I; |
86 | |
87 | #[inline (always)] |
88 | fn deref(&self) -> &Self::Target { |
89 | &self.input |
90 | } |
91 | } |
92 | |
93 | impl<I: crate::lib::std::fmt::Display> crate::lib::std::fmt::Display for LocatingSlice<I> { |
94 | fn fmt(&self, f: &mut crate::lib::std::fmt::Formatter<'_>) -> crate::lib::std::fmt::Result { |
95 | self.input.fmt(f) |
96 | } |
97 | } |
98 | |
99 | impl<I> SliceLen for LocatingSlice<I> |
100 | where |
101 | I: SliceLen, |
102 | { |
103 | #[inline (always)] |
104 | fn slice_len(&self) -> usize { |
105 | self.input.slice_len() |
106 | } |
107 | } |
108 | |
109 | impl<I: Stream> Stream for LocatingSlice<I> { |
110 | type Token = <I as Stream>::Token; |
111 | type Slice = <I as Stream>::Slice; |
112 | |
113 | type IterOffsets = <I as Stream>::IterOffsets; |
114 | |
115 | type Checkpoint = Checkpoint<I::Checkpoint, Self>; |
116 | |
117 | #[inline (always)] |
118 | fn iter_offsets(&self) -> Self::IterOffsets { |
119 | self.input.iter_offsets() |
120 | } |
121 | #[inline (always)] |
122 | fn eof_offset(&self) -> usize { |
123 | self.input.eof_offset() |
124 | } |
125 | |
126 | #[inline (always)] |
127 | fn next_token(&mut self) -> Option<Self::Token> { |
128 | self.input.next_token() |
129 | } |
130 | |
131 | #[inline (always)] |
132 | fn peek_token(&self) -> Option<Self::Token> { |
133 | self.input.peek_token() |
134 | } |
135 | |
136 | #[inline (always)] |
137 | fn offset_for<P>(&self, predicate: P) -> Option<usize> |
138 | where |
139 | P: Fn(Self::Token) -> bool, |
140 | { |
141 | self.input.offset_for(predicate) |
142 | } |
143 | #[inline (always)] |
144 | fn offset_at(&self, tokens: usize) -> Result<usize, Needed> { |
145 | self.input.offset_at(tokens) |
146 | } |
147 | #[inline (always)] |
148 | fn next_slice(&mut self, offset: usize) -> Self::Slice { |
149 | self.input.next_slice(offset) |
150 | } |
151 | #[inline (always)] |
152 | unsafe fn next_slice_unchecked(&mut self, offset: usize) -> Self::Slice { |
153 | // SAFETY: Passing up invariants |
154 | unsafe { self.input.next_slice_unchecked(offset) } |
155 | } |
156 | #[inline (always)] |
157 | fn peek_slice(&self, offset: usize) -> Self::Slice { |
158 | self.input.peek_slice(offset) |
159 | } |
160 | #[inline (always)] |
161 | unsafe fn peek_slice_unchecked(&self, offset: usize) -> Self::Slice { |
162 | // SAFETY: Passing up invariants |
163 | unsafe { self.input.peek_slice_unchecked(offset) } |
164 | } |
165 | |
166 | #[inline (always)] |
167 | fn checkpoint(&self) -> Self::Checkpoint { |
168 | Checkpoint::<_, Self>::new(self.input.checkpoint()) |
169 | } |
170 | #[inline (always)] |
171 | fn reset(&mut self, checkpoint: &Self::Checkpoint) { |
172 | self.input.reset(&checkpoint.inner); |
173 | } |
174 | |
175 | #[inline (always)] |
176 | fn raw(&self) -> &dyn crate::lib::std::fmt::Debug { |
177 | &self.input |
178 | } |
179 | } |
180 | |
181 | impl<I> Location for LocatingSlice<I> |
182 | where |
183 | I: Clone + Offset, |
184 | { |
185 | #[inline (always)] |
186 | fn previous_token_end(&self) -> usize { |
187 | self.previous_token_end() |
188 | } |
189 | #[inline (always)] |
190 | fn current_token_start(&self) -> usize { |
191 | self.current_token_start() |
192 | } |
193 | } |
194 | |
195 | #[cfg (feature = "unstable-recover" )] |
196 | #[cfg (feature = "std" )] |
197 | impl<I, E> Recover<E> for LocatingSlice<I> |
198 | where |
199 | I: Recover<E>, |
200 | I: Stream, |
201 | { |
202 | #[inline (always)] |
203 | fn record_err( |
204 | &mut self, |
205 | _token_start: &Self::Checkpoint, |
206 | _err_start: &Self::Checkpoint, |
207 | err: E, |
208 | ) -> Result<(), E> { |
209 | Err(err) |
210 | } |
211 | |
212 | /// Report whether the [`Stream`] can save off errors for recovery |
213 | #[inline (always)] |
214 | fn is_recovery_supported() -> bool { |
215 | false |
216 | } |
217 | } |
218 | |
219 | impl<I> StreamIsPartial for LocatingSlice<I> |
220 | where |
221 | I: StreamIsPartial, |
222 | { |
223 | type PartialState = I::PartialState; |
224 | |
225 | #[inline ] |
226 | fn complete(&mut self) -> Self::PartialState { |
227 | self.input.complete() |
228 | } |
229 | |
230 | #[inline ] |
231 | fn restore_partial(&mut self, state: Self::PartialState) { |
232 | self.input.restore_partial(state); |
233 | } |
234 | |
235 | #[inline (always)] |
236 | fn is_partial_supported() -> bool { |
237 | I::is_partial_supported() |
238 | } |
239 | |
240 | #[inline (always)] |
241 | fn is_partial(&self) -> bool { |
242 | self.input.is_partial() |
243 | } |
244 | } |
245 | |
246 | impl<I> Offset for LocatingSlice<I> |
247 | where |
248 | I: Stream, |
249 | { |
250 | #[inline (always)] |
251 | fn offset_from(&self, other: &Self) -> usize { |
252 | self.offset_from(&other.checkpoint()) |
253 | } |
254 | } |
255 | |
256 | impl<I> Offset<<LocatingSlice<I> as Stream>::Checkpoint> for LocatingSlice<I> |
257 | where |
258 | I: Stream, |
259 | { |
260 | #[inline (always)] |
261 | fn offset_from(&self, other: &<LocatingSlice<I> as Stream>::Checkpoint) -> usize { |
262 | self.checkpoint().offset_from(start:other) |
263 | } |
264 | } |
265 | |
266 | impl<I> AsBytes for LocatingSlice<I> |
267 | where |
268 | I: AsBytes, |
269 | { |
270 | #[inline (always)] |
271 | fn as_bytes(&self) -> &[u8] { |
272 | self.input.as_bytes() |
273 | } |
274 | } |
275 | |
276 | impl<I> AsBStr for LocatingSlice<I> |
277 | where |
278 | I: AsBStr, |
279 | { |
280 | #[inline (always)] |
281 | fn as_bstr(&self) -> &[u8] { |
282 | self.input.as_bstr() |
283 | } |
284 | } |
285 | |
286 | impl<I, U> Compare<U> for LocatingSlice<I> |
287 | where |
288 | I: Compare<U>, |
289 | { |
290 | #[inline (always)] |
291 | fn compare(&self, other: U) -> CompareResult { |
292 | self.input.compare(other) |
293 | } |
294 | } |
295 | |
296 | impl<I, T> FindSlice<T> for LocatingSlice<I> |
297 | where |
298 | I: FindSlice<T>, |
299 | { |
300 | #[inline (always)] |
301 | fn find_slice(&self, substr: T) -> Option<crate::lib::std::ops::Range<usize>> { |
302 | self.input.find_slice(substr) |
303 | } |
304 | } |
305 | |
306 | impl<I> UpdateSlice for LocatingSlice<I> |
307 | where |
308 | I: UpdateSlice, |
309 | { |
310 | #[inline (always)] |
311 | fn update_slice(mut self, inner: Self::Slice) -> Self { |
312 | self.input = I::update_slice(self.input, inner); |
313 | self |
314 | } |
315 | } |
316 | |