1use core::ops;
4
5use nom::Input;
6
7use crate::{
8 alloc::{format, String},
9 Error,
10};
11
12pub type InputSpan<'a> = nom_locate::LocatedSpan<&'a str, ()>;
14pub type NomResult<'a, T> = nom::IResult<InputSpan<'a>, T, Error>;
16
17#[derive(Debug, Clone, Copy)]
27pub struct LocatedSpan<Span, T = ()> {
28 offset: usize,
29 line: u32,
30 column: usize,
31 fragment: Span,
32
33 pub extra: T,
35}
36
37impl<Span: PartialEq, T> PartialEq for LocatedSpan<Span, T> {
38 fn eq(&self, other: &Self) -> bool {
39 self.line == other.line && self.offset == other.offset && self.fragment == other.fragment
40 }
41}
42
43impl<Span, T> LocatedSpan<Span, T> {
44 pub fn location_offset(&self) -> usize {
47 self.offset
48 }
49
50 pub fn location_line(&self) -> u32 {
52 self.line
53 }
54
55 pub fn get_column(&self) -> usize {
57 self.column
58 }
59
60 pub fn fragment(&self) -> &Span {
62 &self.fragment
63 }
64
65 pub fn map_extra<U>(self, map_fn: impl FnOnce(T) -> U) -> LocatedSpan<Span, U> {
67 LocatedSpan {
68 offset: self.offset,
69 line: self.line,
70 column: self.column,
71 fragment: self.fragment,
72 extra: map_fn(self.extra),
73 }
74 }
75
76 pub fn map_fragment<U>(self, map_fn: impl FnOnce(Span) -> U) -> LocatedSpan<U, T> {
78 LocatedSpan {
79 offset: self.offset,
80 line: self.line,
81 column: self.column,
82 fragment: map_fn(self.fragment),
83 extra: self.extra,
84 }
85 }
86}
87
88impl<Span: Copy, T> LocatedSpan<Span, T> {
89 pub fn as_ref(&self) -> LocatedSpan<Span, &T> {
91 LocatedSpan {
92 offset: self.offset,
93 line: self.line,
94 column: self.column,
95 fragment: self.fragment,
96 extra: &self.extra,
97 }
98 }
99
100 pub fn copy_with_extra<U>(&self, value: U) -> LocatedSpan<Span, U> {
102 LocatedSpan {
103 offset: self.offset,
104 line: self.line,
105 column: self.column,
106 fragment: self.fragment,
107 extra: value,
108 }
109 }
110
111 pub fn with_no_extra(&self) -> LocatedSpan<Span> {
113 self.copy_with_extra(())
114 }
115}
116
117#[allow(clippy::mismatching_type_param_order)] impl<'a, T> From<nom_locate::LocatedSpan<&'a str, T>> for LocatedSpan<&'a str, T> {
119 fn from(value: nom_locate::LocatedSpan<&'a str, T>) -> Self {
120 Self {
121 offset: value.location_offset(),
122 line: value.location_line(),
123 column: value.get_column(),
124 fragment: *value.fragment(),
125 extra: value.extra,
126 }
127 }
128}
129
130pub type Spanned<'a, T = ()> = LocatedSpan<&'a str, T>;
132
133impl<'a, T> Spanned<'a, T> {
134 pub(crate) fn new(span: InputSpan<'a>, extra: T) -> Self {
135 Self {
136 offset: span.location_offset(),
137 line: span.location_line(),
138 column: span.get_column(),
139 fragment: *span.fragment(),
140 extra,
141 }
142 }
143}
144
145impl<'a> Spanned<'a> {
146 pub fn from_str<R>(code: &'a str, range: R) -> Self
148 where
149 InputSpan<'a>: Input,
150 R: ops::RangeBounds<usize>,
151 {
152 let input = InputSpan::new(code);
153 let start = match range.start_bound() {
154 ops::Bound::Unbounded => 0,
155 ops::Bound::Included(&i) => i,
156 ops::Bound::Excluded(&i) => i + 1,
157 };
158 let mut input = input.take_from(start);
159
160 let len = match range.end_bound() {
161 ops::Bound::Unbounded => None,
162 ops::Bound::Included(&i) => Some((i + 1).saturating_sub(start)),
163 ops::Bound::Excluded(&i) => Some(i.saturating_sub(start)),
164 };
165 if let Some(len) = len {
166 input = input.take(len);
167 }
168
169 Self::new(input, ())
170 }
171}
172
173pub type Location<T = ()> = LocatedSpan<usize, T>;
176
177impl Location {
178 pub fn from_str<'a, R>(code: &'a str, range: R) -> Self
180 where
181 InputSpan<'a>: Input,
182 R: ops::RangeBounds<usize>,
183 {
184 Spanned::from_str(code, range).into()
185 }
186}
187
188impl<T> Location<T> {
189 pub fn to_string(&self, default_name: &str) -> String {
191 format!("{default_name} at {}:{}", self.line, self.column)
192 }
193
194 pub fn span<'a>(&self, code: &'a str) -> &'a str {
197 &code[self.offset..(self.offset + self.fragment)]
198 }
199}
200
201impl<T> From<Spanned<'_, T>> for Location<T> {
202 fn from(value: Spanned<'_, T>) -> Self {
203 value.map_fragment(str::len)
204 }
205}
206
207pub fn with_span<'a, O, F>(
209 mut parser: F,
210) -> impl nom::Parser<InputSpan<'a>, Output = Spanned<'a, O>, Error = F::Error>
211where
212 F: nom::Parser<InputSpan<'a>, Output = O>,
213{
214 move |input: InputSpan<'a>| {
215 parser.parse(input).map(|(rest, output)| {
216 let len = rest.location_offset() - input.location_offset();
217 let spanned = Spanned {
218 offset: input.location_offset(),
219 line: input.location_line(),
220 column: input.get_column(),
221 fragment: &input.fragment()[..len],
222 extra: output,
223 };
224 (rest, spanned)
225 })
226 }
227}
228
229pub(crate) fn unite_spans<'a, T, U>(
230 input: InputSpan<'a>,
231 start: &Spanned<'_, T>,
232 end: &Spanned<'_, U>,
233) -> Spanned<'a> {
234 debug_assert!(input.location_offset() <= start.location_offset());
235 debug_assert!(start.location_offset() <= end.location_offset());
236 debug_assert!(
237 input.location_offset() + input.fragment().len()
238 >= end.location_offset() + end.fragment().len()
239 );
240
241 let start_idx = start.location_offset() - input.location_offset();
242 let end_idx = end.location_offset() + end.fragment().len() - input.location_offset();
243 Spanned {
244 offset: start.offset,
245 line: start.line,
246 column: start.column,
247 fragment: &input.fragment()[start_idx..end_idx],
248 extra: (),
249 }
250}