spore_vm/parser/
span.rs

1/// Describes the location of a substring within a string.
2#[derive(Copy, Clone, PartialEq, Debug)]
3pub struct Span {
4    /// The start of the substring.
5    pub start: u32,
6    /// The end of the substring.
7    pub end: u32,
8}
9
10#[derive(Clone, PartialEq, Debug)]
11pub struct SpanWithSource<T> {
12    /// The location within [Self::src] to refer to.
13    pub span: Span,
14    /// The entire source string.
15    pub src: T,
16}
17
18impl Span {
19    /// Create a new span.
20    pub fn new(start: u32, end: u32) -> Span {
21        Span { start, end }
22    }
23
24    /// Link the underlying span with its source.
25    pub fn with_src<T>(self, src: T) -> SpanWithSource<T> {
26        SpanWithSource { span: self, src }
27    }
28
29    /// Expand the current span to `end`. If `end` is less than the current end, then `self` is
30    /// returned
31    pub fn extend_end(self, end: u32) -> Span {
32        Span {
33            start: self.start,
34            end: self.end.max(end),
35        }
36    }
37
38    /// Get the next window. The next window is defined as starting at the end of `self` with length
39    /// `end`.
40    pub fn next_window(self, len: u32) -> Span {
41        Span {
42            start: self.end,
43            end: self.end + len,
44        }
45    }
46
47    /// Returns a span that overlaps with both `self` and `other` or `None` if there is no overlap.
48    pub fn overlap(self, other: Span) -> Option<Span> {
49        let start = self.start.max(other.start);
50        let end = self.end.min(other.end);
51        (start <= end).then_some(Span { start, end })
52    }
53}
54
55impl<'a> SpanWithSource<&'a str> {
56    /// Get the `str` for the string pointed to by [Self::span] within [Self::src].
57    pub fn as_str(&self) -> &'a str {
58        &self.src[self.span.start as usize..self.span.end as usize]
59    }
60}
61
62impl<T: AsRef<str>> SpanWithSource<T> {
63    pub fn contextual_formatter(&self) -> impl '_ + std::fmt::Display {
64        SpanWithSourceContextualFormatter(self)
65    }
66}
67
68impl<T> std::fmt::Display for SpanWithSource<T>
69where
70    T: AsRef<str>,
71{
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        let src = self.src.as_ref();
74        let s = &src[self.span.start as usize..(self.span.end as usize).clamp(0, src.len())];
75        write!(f, "{s}")
76    }
77}
78
79struct SpanWithSourceContextualFormatter<'a, T>(&'a SpanWithSource<T>);
80
81impl<'a, T> std::fmt::Display for SpanWithSourceContextualFormatter<'a, T>
82where
83    T: AsRef<str>,
84{
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        let span = self.0.span;
87        let src = self.0.src.as_ref();
88
89        let mut current_span = Span::new(0, 0);
90        writeln!(f, "Source:")?;
91        for (idx, line) in src.split('\n').enumerate() {
92            current_span = current_span.next_window(1 + line.len() as u32);
93            if current_span.overlap(span).is_some() {
94                let line_number = idx + 1;
95                let line_src = current_span.with_src(src);
96                write!(f, "{line_number:3}: {line_src}")?;
97            }
98        }
99        Ok(())
100    }
101}