arithmetic_parser/
spans.rs

1//! Types related to spanning parsed code.
2
3use nom::Slice;
4
5use crate::{
6    alloc::{format, String, ToOwned},
7    Error,
8};
9
10/// Code span.
11pub type InputSpan<'a> = nom_locate::LocatedSpan<&'a str, ()>;
12/// Parsing outcome generalized by the type returned on success.
13pub type NomResult<'a, T> = nom::IResult<InputSpan<'a>, T, Error<'a>>;
14
15/// Code span together with information related to where it is located in the code.
16///
17/// This type is similar to one from the [`nom_locate`] crate, but it has slightly different
18/// functionality. In particular, this type provides no method to access other parts of the code
19/// (which is performed in `nom_locate`'s `LocatedSpan::get_column()` among other methods).
20/// As such, this allows to safely replace [span info](#method.fragment) without worrying
21/// about undefined behavior.
22///
23/// [`nom_locate`]: https://crates.io/crates/nom_locate
24#[derive(Debug, Clone, Copy)]
25pub struct LocatedSpan<Span, T = ()> {
26    offset: usize,
27    line: u32,
28    column: usize,
29    fragment: Span,
30
31    /// Extra information that can be embedded by the user.
32    pub extra: T,
33}
34
35impl<Span: PartialEq, T> PartialEq for LocatedSpan<Span, T> {
36    fn eq(&self, other: &Self) -> bool {
37        self.line == other.line && self.offset == other.offset && self.fragment == other.fragment
38    }
39}
40
41impl<Span, T> LocatedSpan<Span, T> {
42    /// The offset represents the position of the fragment relatively to the input of the parser.
43    /// It starts at offset 0.
44    pub fn location_offset(&self) -> usize {
45        self.offset
46    }
47
48    /// The line number of the fragment relatively to the input of the parser. It starts at line 1.
49    pub fn location_line(&self) -> u32 {
50        self.line
51    }
52
53    /// The column of the fragment start.
54    pub fn get_column(&self) -> usize {
55        self.column
56    }
57
58    /// The fragment that is spanned. The fragment represents a part of the input of the parser.
59    pub fn fragment(&self) -> &Span {
60        &self.fragment
61    }
62
63    /// Maps the `extra` field of this span using the provided closure.
64    pub fn map_extra<U>(self, map_fn: impl FnOnce(T) -> U) -> LocatedSpan<Span, U> {
65        LocatedSpan {
66            offset: self.offset,
67            line: self.line,
68            column: self.column,
69            fragment: self.fragment,
70            extra: map_fn(self.extra),
71        }
72    }
73
74    /// Maps the fragment field of this span using the provided closure.
75    pub fn map_fragment<U>(self, map_fn: impl FnOnce(Span) -> U) -> LocatedSpan<U, T> {
76        LocatedSpan {
77            offset: self.offset,
78            line: self.line,
79            column: self.column,
80            fragment: map_fn(self.fragment),
81            extra: self.extra,
82        }
83    }
84}
85
86impl<Span: Copy, T> LocatedSpan<Span, T> {
87    /// Returns a copy of this span with borrowed `extra` field.
88    pub fn as_ref(&self) -> LocatedSpan<Span, &T> {
89        LocatedSpan {
90            offset: self.offset,
91            line: self.line,
92            column: self.column,
93            fragment: self.fragment,
94            extra: &self.extra,
95        }
96    }
97
98    /// Copies this span with the provided `extra` field.
99    pub fn copy_with_extra<U>(&self, value: U) -> LocatedSpan<Span, U> {
100        LocatedSpan {
101            offset: self.offset,
102            line: self.line,
103            column: self.column,
104            fragment: self.fragment,
105            extra: value,
106        }
107    }
108
109    /// Removes `extra` field from this span.
110    pub fn with_no_extra(&self) -> LocatedSpan<Span> {
111        self.copy_with_extra(())
112    }
113}
114
115impl<'a, T> From<nom_locate::LocatedSpan<&'a str, T>> for LocatedSpan<&'a str, T> {
116    fn from(value: nom_locate::LocatedSpan<&'a str, T>) -> Self {
117        Self {
118            offset: value.location_offset(),
119            line: value.location_line(),
120            column: value.get_column(),
121            fragment: *value.fragment(),
122            extra: value.extra,
123        }
124    }
125}
126
127/// Value with an associated code span.
128pub type Spanned<'a, T = ()> = LocatedSpan<&'a str, T>;
129
130impl<'a, T> Spanned<'a, T> {
131    pub(crate) fn new(span: InputSpan<'a>, extra: T) -> Self {
132        Self {
133            offset: span.location_offset(),
134            line: span.location_line(),
135            column: span.get_column(),
136            fragment: *span.fragment(),
137            extra,
138        }
139    }
140}
141
142impl<'a> Spanned<'a> {
143    /// Creates a span from a `range` in the provided `code`. This is mostly useful for testing.
144    pub fn from_str<R>(code: &'a str, range: R) -> Self
145    where
146        InputSpan<'a>: Slice<R>,
147    {
148        let input = InputSpan::new(code);
149        Self::new(input.slice(range), ())
150    }
151}
152
153/// Container for a code fragment that can be in one of the two states: either the code string
154/// is retained, or it is stripped away.
155///
156/// The stripped version allows to retain information about code location within [`LocatedSpan`]
157/// without a restriction by the code lifetime.
158#[derive(Debug, Clone, Copy, PartialEq)]
159pub enum CodeFragment<'a> {
160    /// Original code fragment: a string reference.
161    Str(&'a str),
162    /// Stripped code fragment: just the string length.
163    Stripped(usize),
164}
165
166impl PartialEq<&str> for CodeFragment<'_> {
167    fn eq(&self, &other: &&str) -> bool {
168        match self {
169            Self::Str(string) => *string == other,
170            Self::Stripped(_) => false,
171        }
172    }
173}
174
175impl CodeFragment<'_> {
176    /// Strips this code fragment, extending its lifetime beyond the lifetime of the code.
177    pub fn strip(self) -> CodeFragment<'static> {
178        match self {
179            Self::Str(string) => CodeFragment::Stripped(string.len()),
180            Self::Stripped(len) => CodeFragment::Stripped(len),
181        }
182    }
183
184    /// Gets the length of this code fragment.
185    pub fn len(self) -> usize {
186        match self {
187            Self::Str(string) => string.len(),
188            Self::Stripped(len) => len,
189        }
190    }
191
192    /// Checks if this code fragment is empty.
193    pub fn is_empty(self) -> bool {
194        self.len() == 0
195    }
196}
197
198impl<'a> From<&'a str> for CodeFragment<'a> {
199    fn from(value: &'a str) -> Self {
200        CodeFragment::Str(value)
201    }
202}
203
204/// Value with an optional associated code span.
205pub type MaybeSpanned<'a, T = ()> = LocatedSpan<CodeFragment<'a>, T>;
206
207impl<'a> MaybeSpanned<'a> {
208    /// Creates a span from a `range` in the provided `code`. This is mostly useful for testing.
209    pub fn from_str<R>(code: &'a str, range: R) -> Self
210    where
211        InputSpan<'a>: Slice<R>,
212    {
213        Spanned::from_str(code, range).into()
214    }
215}
216
217impl<T> MaybeSpanned<'_, T> {
218    /// Returns either the original code fragment (if it's retained), or a string in the form
219    /// `{default_name} at {line}:{column}`.
220    pub fn code_or_location(&self, default_name: &str) -> String {
221        match self.fragment {
222            CodeFragment::Str(code) => code.to_owned(),
223            CodeFragment::Stripped(_) => {
224                format!("{} at {}:{}", default_name, self.line, self.column)
225            }
226        }
227    }
228}
229
230impl<'a, T> From<Spanned<'a, T>> for MaybeSpanned<'a, T> {
231    fn from(value: Spanned<'a, T>) -> Self {
232        value.map_fragment(CodeFragment::from)
233    }
234}
235
236/// Encapsulates stripping references to code fragments. The result can outlive the code.
237///
238/// Implementors of this trait are usually generic by the code lifetime: `Foo<'_, ..>`,
239/// with the result of stripping being `Foo<'static, ..>`.
240pub trait StripCode {
241    /// Resulting type after code stripping.
242    type Stripped: 'static;
243
244    /// Strips references to code fragments in this type.
245    fn strip_code(self) -> Self::Stripped;
246}
247
248impl<T: Clone + 'static> StripCode for MaybeSpanned<'_, T> {
249    type Stripped = MaybeSpanned<'static, T>;
250
251    fn strip_code(self) -> Self::Stripped {
252        self.map_fragment(CodeFragment::strip)
253    }
254}
255
256/// Wrapper around parsers allowing to capture both their output and the relevant span.
257pub fn with_span<'a, O>(
258    mut parser: impl FnMut(InputSpan<'a>) -> NomResult<'a, O>,
259) -> impl FnMut(InputSpan<'a>) -> NomResult<'a, Spanned<'_, O>> {
260    move |input: InputSpan<'_>| {
261        parser(input).map(|(rest, output)| {
262            let len = rest.location_offset() - input.location_offset();
263            let spanned = Spanned {
264                offset: input.location_offset(),
265                line: input.location_line(),
266                column: input.get_column(),
267                fragment: &input.fragment()[..len],
268                extra: output,
269            };
270            (rest, spanned)
271        })
272    }
273}
274
275pub(crate) fn unite_spans<'a, T, U>(
276    input: InputSpan<'a>,
277    start: &Spanned<'_, T>,
278    end: &Spanned<'_, U>,
279) -> Spanned<'a> {
280    debug_assert!(input.location_offset() <= start.location_offset());
281    debug_assert!(start.location_offset() <= end.location_offset());
282    debug_assert!(
283        input.location_offset() + input.fragment().len()
284            >= end.location_offset() + end.fragment().len()
285    );
286
287    let start_idx = start.location_offset() - input.location_offset();
288    let end_idx = end.location_offset() + end.fragment().len() - input.location_offset();
289    Spanned {
290        offset: start.offset,
291        line: start.line,
292        column: start.column,
293        fragment: &input.fragment()[start_idx..end_idx],
294        extra: (),
295    }
296}
297
298/// Helper trait for [`Result`]s with the error component that implements [`StripCode`].
299pub trait StripResultExt {
300    /// Type wrapped by the `Result::Ok` variant.
301    type Ok;
302    /// Result of stripping code fragments from an error.
303    type StrippedErr: 'static;
304
305    /// Strips code fragments from the error variant.
306    fn strip_err(self) -> Result<Self::Ok, Self::StrippedErr>;
307}
308
309impl<T, E: StripCode> StripResultExt for Result<T, E> {
310    type Ok = T;
311    type StrippedErr = E::Stripped;
312
313    fn strip_err(self) -> Result<T, Self::StrippedErr> {
314        self.map_err(StripCode::strip_code)
315    }
316}