Skip to main content

pest/
span.rs

1// pest. The Elegant Parser
2// Copyright (c) 2018 DragoČ™ Tiselice
3//
4// Licensed under the Apache License, Version 2.0
5// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT
6// license <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. All files in the project carrying such notice may not be copied,
8// modified, or distributed except according to those terms.
9
10use core::fmt;
11use core::hash::{Hash, Hasher};
12use core::ops::{Bound, RangeBounds};
13use core::ptr;
14use core::str;
15
16use crate::position;
17
18/// A span over a `&str`. It is created from either [two `Position`s] or from a [`Pair`].
19///
20/// [two `Position`s]: struct.Position.html#method.span
21/// [`Pair`]: ./iterators/struct.Pair.html#method.as_span
22#[derive(Clone, Copy)]
23pub struct Span<'i> {
24    input: &'i str,
25    start: usize,
26    end: usize,
27}
28
29impl<'i> Span<'i> {
30    /// Create a new `Span` without checking invariants. (Checked with `debug_assertions`.)
31    pub(crate) fn new_internal(input: &str, start: usize, end: usize) -> Span<'_> {
32        debug_assert!(input.get(start..end).is_some());
33        Span { input, start, end }
34    }
35
36    /// Attempts to create a new span. Will return `None` if `input[start..end]` is an invalid index
37    /// into `input`.
38    ///
39    /// # Examples
40    ///
41    /// ```
42    /// # use pest::Span;
43    /// let input = "Hello!";
44    /// assert_eq!(None, Span::new(input, 100, 0));
45    /// assert!(Span::new(input, 0, input.len()).is_some());
46    /// ```
47    pub fn new(input: &str, start: usize, end: usize) -> Option<Span<'_>> {
48        if input.get(start..end).is_some() {
49            Some(Span { input, start, end })
50        } else {
51            None
52        }
53    }
54
55    /// Attempts to create a new span based on a sub-range.
56    ///
57    /// ```
58    /// use pest::Span;
59    /// let input = "Hello World!";
60    /// let world = Span::new(input, 6, input.len()).unwrap();
61    /// let orl = world.get(1..=3);
62    /// assert!(orl.is_some());
63    /// assert_eq!(orl.unwrap().as_str(), "orl");
64    /// ```
65    ///
66    /// # Examples
67    pub fn get(&self, range: impl RangeBounds<usize>) -> Option<Span<'i>> {
68        let start = match range.start_bound() {
69            Bound::Included(offset) => *offset,
70            Bound::Excluded(offset) => *offset + 1,
71            Bound::Unbounded => 0,
72        };
73        let end = match range.end_bound() {
74            Bound::Included(offset) => *offset + 1,
75            Bound::Excluded(offset) => *offset,
76            Bound::Unbounded => self.as_str().len(),
77        };
78
79        self.as_str().get(start..end).map(|_| Span {
80            input: self.input,
81            start: self.start + start,
82            end: self.start + end,
83        })
84    }
85
86    /// Returns the `Span`'s start byte position as a `usize`.
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// # use pest::Position;
92    /// let input = "ab";
93    /// let start = Position::from_start(input);
94    /// let end = start.clone();
95    /// let span = start.span(&end);
96    ///
97    /// assert_eq!(span.start(), 0);
98    /// ```
99    #[inline]
100    pub fn start(&self) -> usize {
101        self.start
102    }
103
104    /// Returns the `Span`'s end byte position as a `usize`.
105    ///
106    /// # Examples
107    ///
108    /// ```
109    /// # use pest::Position;
110    /// let input = "ab";
111    /// let start = Position::from_start(input);
112    /// let end = start.clone();
113    /// let span = start.span(&end);
114    ///
115    /// assert_eq!(span.end(), 0);
116    /// ```
117    #[inline]
118    pub fn end(&self) -> usize {
119        self.end
120    }
121
122    /// Returns the `Span`'s start `Position`.
123    ///
124    /// # Examples
125    ///
126    /// ```
127    /// # use pest::Position;
128    /// let input = "ab";
129    /// let start = Position::from_start(input);
130    /// let end = start.clone();
131    /// let span = start.clone().span(&end);
132    ///
133    /// assert_eq!(span.start_pos(), start);
134    /// ```
135    #[inline]
136    pub fn start_pos(&self) -> position::Position<'i> {
137        position::Position::new_internal(self.input, self.start)
138    }
139
140    /// Returns the `Span`'s end `Position`.
141    ///
142    /// # Examples
143    ///
144    /// ```
145    /// # use pest::Position;
146    /// let input = "ab";
147    /// let start = Position::from_start(input);
148    /// let end = start.clone();
149    /// let span = start.span(&end);
150    ///
151    /// assert_eq!(span.end_pos(), end);
152    /// ```
153    #[inline]
154    pub fn end_pos(&self) -> position::Position<'i> {
155        position::Position::new_internal(self.input, self.end)
156    }
157
158    /// Splits the `Span` into a pair of `Position`s.
159    ///
160    /// # Examples
161    ///
162    /// ```
163    /// # use pest::Position;
164    /// let input = "ab";
165    /// let start = Position::from_start(input);
166    /// let end = start.clone();
167    /// let span = start.clone().span(&end);
168    ///
169    /// assert_eq!(span.split(), (start, end));
170    /// ```
171    #[inline]
172    pub fn split(self) -> (position::Position<'i>, position::Position<'i>) {
173        let pos1 = position::Position::new_internal(self.input, self.start);
174        let pos2 = position::Position::new_internal(self.input, self.end);
175
176        (pos1, pos2)
177    }
178
179    /// Captures a slice from the `&str` defined by the `Span`.
180    ///
181    /// # Examples
182    ///
183    /// ```
184    /// # use pest;
185    /// # #[allow(non_camel_case_types)]
186    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
187    /// enum Rule {}
188    ///
189    /// let input = "abc";
190    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input).skip(1).unwrap();
191    /// let start_pos = state.position().clone();
192    /// state = state.match_string("b").unwrap();
193    /// let span = start_pos.span(&state.position().clone());
194    /// assert_eq!(span.as_str(), "b");
195    /// ```
196    #[inline]
197    pub fn as_str(&self) -> &'i str {
198        // Span's start and end positions are always a UTF-8 borders.
199        &self.input[self.start..self.end]
200    }
201
202    /// Returns the input string of the `Span`.
203    ///
204    /// This function returns the input string of the `Span` as a `&str`. This is the source string
205    /// from which the `Span` was created. The returned `&str` can be used to examine the contents of
206    /// the `Span` or to perform further processing on the string.
207    ///
208    /// # Examples
209    ///
210    /// ```
211    /// # use pest;
212    /// # use pest::Span;
213    ///
214    /// // Example: Get input string from a span
215    /// let input = "abc\ndef\nghi";
216    /// let span = Span::new(input, 1, 7).unwrap();
217    /// assert_eq!(span.get_input(), input);
218    /// ```
219    pub fn get_input(&self) -> &'i str {
220        self.input
221    }
222
223    /// Iterates over all lines (partially) covered by this span. Yielding a `&str` for each line.
224    ///
225    /// # Examples
226    ///
227    /// ```
228    /// # use pest;
229    /// # #[allow(non_camel_case_types)]
230    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
231    /// enum Rule {}
232    ///
233    /// let input = "a\nb\nc";
234    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input).skip(2).unwrap();
235    /// let start_pos = state.position().clone();
236    /// state = state.match_string("b\nc").unwrap();
237    /// let span = start_pos.span(&state.position().clone());
238    /// assert_eq!(span.lines().collect::<Vec<_>>(), vec!["b\n", "c"]);
239    /// ```
240    #[inline]
241    pub fn lines(&self) -> Lines<'_> {
242        Lines {
243            inner: self.lines_span(),
244        }
245    }
246
247    /// Iterates over all lines (partially) covered by this span. Yielding a `Span` for each line.
248    ///
249    /// # Examples
250    ///
251    /// ```
252    /// # use pest;
253    /// # use pest::Span;
254    /// # #[allow(non_camel_case_types)]
255    /// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
256    /// enum Rule {}
257    ///
258    /// let input = "a\nb\nc";
259    /// let mut state: Box<pest::ParserState<'_, Rule>> = pest::ParserState::new(input).skip(2).unwrap();
260    /// let start_pos = state.position().clone();
261    /// state = state.match_string("b\nc").unwrap();
262    /// let span = start_pos.span(&state.position().clone());
263    /// assert_eq!(span.lines_span().collect::<Vec<_>>(), vec![Span::new(input, 2, 4).unwrap(), Span::new(input, 4, 5).unwrap()]);
264    /// ```
265    pub fn lines_span(&self) -> LinesSpan<'_> {
266        LinesSpan {
267            span: self,
268            pos: self.start,
269        }
270    }
271}
272
273impl fmt::Debug for Span<'_> {
274    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
275        let range = self.start..self.end;
276        f.debug_struct("Span")
277            .field("str", &self.as_str())
278            .field("range", &range)
279            .finish()
280    }
281}
282
283impl<'i> PartialEq for Span<'i> {
284    fn eq(&self, other: &Span<'i>) -> bool {
285        ptr::eq(self.input, other.input) && self.start == other.start && self.end == other.end
286    }
287}
288
289impl Eq for Span<'_> {}
290
291impl Hash for Span<'_> {
292    fn hash<H: Hasher>(&self, state: &mut H) {
293        (self.input as *const str).hash(state);
294        self.start.hash(state);
295        self.end.hash(state);
296    }
297}
298
299/// Merges two spans into one.
300///
301/// This function merges two spans that are contiguous or overlapping into a single span
302/// that covers the entire range of the two input spans. This is useful when you want to
303/// aggregate information from multiple spans into a single entity.
304///
305/// The function checks if the input spans are overlapping or contiguous by comparing their
306/// start and end positions. If they are, a new span is created with the minimum start position
307/// and the maximum end position of the two input spans.
308///
309/// If the input spans are neither overlapping nor contiguous, the function returns None,
310/// indicating that a merge operation was not possible.
311///
312/// # Examples
313///
314/// ```
315/// # use pest;
316/// # use pest::Span;
317/// # use pest::merge_spans;
318///
319/// // Example 1: Contiguous spans
320/// let input = "abc\ndef\nghi";
321/// let span1 = Span::new(input, 1, 7).unwrap();
322/// let span2 = Span::new(input, 7, 11).unwrap();
323/// let merged = merge_spans(&span1, &span2).unwrap();
324/// assert_eq!(merged, Span::new(input, 1, 11).unwrap());
325///
326/// // Example 2: Overlapping spans
327/// let input = "abc\ndef\nghi";
328/// let span1 = Span::new(input, 1, 7).unwrap();
329/// let span2 = Span::new(input, 5, 11).unwrap();
330/// let merged = merge_spans(&span1, &span2).unwrap();
331/// assert_eq!(merged, Span::new(input, 1, 11).unwrap());
332///
333/// // Example 3: Non-contiguous spans
334/// let input = "abc\ndef\nghi";
335/// let span1 = Span::new(input, 1, 7).unwrap();
336/// let span2 = Span::new(input, 8, 11).unwrap();
337/// let merged = merge_spans(&span1, &span2);
338/// assert!(merged.is_none());
339/// ```
340pub fn merge_spans<'i>(a: &Span<'i>, b: &Span<'i>) -> Option<Span<'i>> {
341    if a.end() >= b.start() && a.start() <= b.end() {
342        // The spans overlap or are contiguous, so they can be merged.
343        Span::new(
344            a.get_input(),
345            core::cmp::min(a.start(), b.start()),
346            core::cmp::max(a.end(), b.end()),
347        )
348    } else {
349        // The spans don't overlap and aren't contiguous, so they can't be merged.
350        None
351    }
352}
353
354/// Line iterator for Spans, created by [`Span::lines_span()`].
355///
356/// Iterates all lines that are at least _partially_ covered by the span. Yielding a `Span` for each.
357///
358/// [`Span::lines_span()`]: struct.Span.html#method.lines_span
359pub struct LinesSpan<'i> {
360    span: &'i Span<'i>,
361    pos: usize,
362}
363
364impl<'i> Iterator for LinesSpan<'i> {
365    type Item = Span<'i>;
366    fn next(&mut self) -> Option<Self::Item> {
367        // `pos` holds the start of the next line to yield, so every line the span
368        // covers has been yielded once `pos` has reached the end of the span. The
369        // exception is a zero-width span, which still sits on one line: that line is
370        // yielded by the first call, while `pos` is still the start of the span.
371        if self.pos >= self.span.end && self.pos > self.span.start {
372            return None;
373        }
374        let pos = position::Position::new(self.span.input, self.pos)?;
375        if pos.at_end() {
376            return None;
377        }
378
379        let line_start = pos.find_line_start();
380        self.pos = pos.find_line_end();
381
382        Span::new(self.span.input, line_start, self.pos)
383    }
384}
385
386/// Line iterator for Spans, created by [`Span::lines()`].
387///
388/// Iterates all lines that are at least _partially_ covered by the span. Yielding a `&str` for each.
389///
390/// [`Span::lines()`]: struct.Span.html#method.lines
391pub struct Lines<'i> {
392    inner: LinesSpan<'i>,
393}
394
395impl<'i> Iterator for Lines<'i> {
396    type Item = &'i str;
397    fn next(&mut self) -> Option<Self::Item> {
398        self.inner.next().map(|span| span.as_str())
399    }
400}
401
402#[cfg(test)]
403mod tests {
404    use super::*;
405    use alloc::borrow::ToOwned;
406    use alloc::vec::Vec;
407
408    #[test]
409    fn get() {
410        let input = "abc123abc";
411        let span = Span::new(input, 3, input.len()).unwrap();
412        assert_eq!(span.as_str(), "123abc");
413        assert_eq!(span.input, input);
414
415        let span1 = span.get(..=2);
416        assert!(span1.is_some());
417        assert_eq!(span1.unwrap().input, input);
418        assert_eq!(span1.unwrap().as_str(), "123");
419
420        let span2 = span.get(..);
421        assert!(span2.is_some());
422        assert_eq!(span2.unwrap().input, input);
423        assert_eq!(span2.unwrap().as_str(), "123abc");
424
425        let span3 = span.get(3..);
426        assert!(span3.is_some());
427        assert_eq!(span3.unwrap().input, input);
428        assert_eq!(span3.unwrap().as_str(), "abc");
429
430        let span4 = span.get(0..0);
431        assert!(span4.is_some());
432        assert_eq!(span4.unwrap().input, input);
433        assert_eq!(span4.unwrap().as_str(), "");
434    }
435
436    #[test]
437    fn get_fails() {
438        let input = "abc";
439        let span = Span::new(input, 0, input.len()).unwrap();
440
441        let span1 = span.get(0..100);
442        assert!(span1.is_none());
443
444        let span2 = span.get(100..200);
445        assert!(span2.is_none());
446    }
447
448    #[test]
449    fn span_comp() {
450        let input = "abc\ndef\nghi";
451        let span = Span::new(input, 1, 7).unwrap();
452        let span2 = Span::new(input, 50, 51);
453        assert!(span2.is_none());
454        let span3 = Span::new(input, 0, 8).unwrap();
455        assert!(span != span3);
456    }
457
458    #[test]
459    fn split() {
460        let input = "a";
461        let start = position::Position::from_start(input);
462        let mut end = start;
463
464        assert!(end.skip(1));
465
466        let span = start.clone().span(&end.clone());
467
468        assert_eq!(span.split(), (start, end));
469    }
470
471    #[test]
472    fn lines_mid() {
473        let input = "abc\ndef\nghi";
474        let span = Span::new(input, 1, 7).unwrap();
475        let lines: Vec<_> = span.lines().collect();
476        let lines_span: Vec<_> = span.lines_span().map(|span| span.as_str()).collect();
477
478        assert_eq!(lines.len(), 2);
479        assert_eq!(lines[0], "abc\n".to_owned());
480        assert_eq!(lines[1], "def\n".to_owned());
481        assert_eq!(lines, lines_span) // Verify parity with lines_span()
482    }
483
484    #[test]
485    fn lines_eof() {
486        let input = "abc\ndef\nghi";
487        let span = Span::new(input, 5, 11).unwrap();
488        assert!(span.end_pos().at_end());
489        assert_eq!(span.end(), 11);
490        let lines: Vec<_> = span.lines().collect();
491        let lines_span: Vec<_> = span.lines_span().map(|span| span.as_str()).collect();
492
493        assert_eq!(lines.len(), 2);
494        assert_eq!(lines[0], "def\n".to_owned());
495        assert_eq!(lines[1], "ghi".to_owned());
496        assert_eq!(lines, lines_span) // Verify parity with lines_span()
497    }
498
499    #[test]
500    fn lines_ending_on_line_break() {
501        let input = "abc\ndef\nghi";
502        let span = Span::new(input, 1, 4).unwrap();
503        assert_eq!(span.as_str(), "bc\n");
504        let lines: Vec<_> = span.lines().collect();
505        let lines_span: Vec<_> = span.lines_span().collect();
506
507        assert_eq!(lines.len(), 1);
508        assert_eq!(lines[0], "abc\n".to_owned());
509        assert_eq!(lines_span.len(), 1);
510        assert_eq!(lines_span[0], Span::new(input, 0, 4).unwrap());
511    }
512
513    #[test]
514    fn lines_of_empty_span() {
515        let input = "abc\ndef\nghi";
516        let span = Span::new(input, 5, 5).unwrap();
517        assert_eq!(span.as_str(), "");
518        let lines: Vec<_> = span.lines().collect();
519        let lines_span: Vec<_> = span.lines_span().collect();
520
521        assert_eq!(lines.len(), 1);
522        assert_eq!(lines[0], "def\n".to_owned());
523        assert_eq!(lines_span.len(), 1);
524        assert_eq!(lines_span[0], Span::new(input, 4, 8).unwrap());
525    }
526
527    #[test]
528    fn lines_span() {
529        let input = "abc\ndef\nghi";
530        let span = Span::new(input, 1, 7).unwrap();
531        let lines_span: Vec<_> = span.lines_span().collect();
532        let lines: Vec<_> = span.lines().collect();
533
534        assert_eq!(lines_span.len(), 2);
535        assert_eq!(lines_span[0], Span::new(input, 0, 4).unwrap());
536        assert_eq!(lines_span[1], Span::new(input, 4, 8).unwrap());
537        assert_eq!(
538            lines_span
539                .iter()
540                .map(|span| span.as_str())
541                .collect::<Vec<_>>(),
542            lines
543        );
544    }
545
546    #[test]
547    fn get_input_of_span() {
548        let input = "abc\ndef\nghi";
549        let span = Span::new(input, 1, 7).unwrap();
550
551        assert_eq!(span.get_input(), input);
552    }
553
554    #[test]
555    fn merge_contiguous() {
556        let input = "abc\ndef\nghi";
557        let span1 = Span::new(input, 1, 7).unwrap();
558        let span2 = Span::new(input, 7, 11).unwrap();
559        let merged = merge_spans(&span1, &span2).unwrap();
560
561        assert_eq!(merged, Span::new(input, 1, 11).unwrap());
562    }
563
564    #[test]
565    fn merge_overlapping() {
566        let input = "abc\ndef\nghi";
567        let span1 = Span::new(input, 1, 7).unwrap();
568        let span2 = Span::new(input, 5, 11).unwrap();
569        let merged = merge_spans(&span1, &span2).unwrap();
570
571        assert_eq!(merged, Span::new(input, 1, 11).unwrap());
572    }
573
574    #[test]
575    fn merge_non_contiguous() {
576        let input = "abc\ndef\nghi";
577        let span1 = Span::new(input, 1, 7).unwrap();
578        let span2 = Span::new(input, 8, 11).unwrap();
579        let merged = merge_spans(&span1, &span2);
580
581        assert!(merged.is_none());
582    }
583}