Skip to main content

cabalist_parser/
span.rs

1//! Byte-offset spans and node identifiers for the CST arena.
2
3/// A byte-offset range into the source text. Both `start` and `end` are
4/// byte indices; the range is half-open: `[start, end)`.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub struct Span {
7    pub start: usize,
8    pub end: usize,
9}
10
11impl Span {
12    /// Create a new span covering `[start, end)`.
13    #[inline]
14    pub fn new(start: usize, end: usize) -> Self {
15        debug_assert!(start <= end, "Span start ({start}) > end ({end})");
16        Self { start, end }
17    }
18
19    /// A zero-length span at the given offset.
20    #[inline]
21    pub fn empty(offset: usize) -> Self {
22        Self {
23            start: offset,
24            end: offset,
25        }
26    }
27
28    /// Length in bytes.
29    #[inline]
30    pub fn len(&self) -> usize {
31        self.end - self.start
32    }
33
34    /// Whether the span covers zero bytes.
35    #[inline]
36    pub fn is_empty(&self) -> bool {
37        self.start == self.end
38    }
39
40    /// Whether this span fully contains `other`.
41    #[inline]
42    pub fn contains(&self, other: &Span) -> bool {
43        self.start <= other.start && other.end <= self.end
44    }
45
46    /// Return the smallest span that covers both `self` and `other`.
47    #[inline]
48    pub fn merge(&self, other: &Span) -> Span {
49        Span {
50            start: self.start.min(other.start),
51            end: self.end.max(other.end),
52        }
53    }
54
55    /// Slice the referenced source text.
56    #[inline]
57    pub fn slice<'a>(&self, source: &'a str) -> &'a str {
58        &source[self.start..self.end]
59    }
60}
61
62/// An index into the CST node arena (`CabalCst::nodes`).
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
64pub struct NodeId(pub usize);
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn span_basics() {
72        let s = Span::new(2, 7);
73        assert_eq!(s.len(), 5);
74        assert!(!s.is_empty());
75
76        let e = Span::empty(3);
77        assert_eq!(e.len(), 0);
78        assert!(e.is_empty());
79    }
80
81    #[test]
82    fn span_contains() {
83        let outer = Span::new(0, 10);
84        let inner = Span::new(2, 5);
85        assert!(outer.contains(&inner));
86        assert!(!inner.contains(&outer));
87    }
88
89    #[test]
90    fn span_merge() {
91        let a = Span::new(0, 5);
92        let b = Span::new(3, 10);
93        let m = a.merge(&b);
94        assert_eq!(m, Span::new(0, 10));
95    }
96
97    #[test]
98    fn span_slice() {
99        let src = "hello world";
100        let s = Span::new(6, 11);
101        assert_eq!(s.slice(src), "world");
102    }
103}