1use num::{BigInt, BigUint};
2use serde::{Deserialize, Serialize};
3use spade_codespan::{ByteOffset, Span};
4use spade_codespan_reporting::diagnostic::Label;
5
6pub trait AsLabel {
7 fn file_id(&self) -> usize;
8 fn span(&self) -> std::ops::Range<usize>;
9
10 fn primary_label(&self) -> Label<usize> {
11 Label::primary(self.file_id(), self.span())
12 }
13
14 fn secondary_label(&self) -> Label<usize> {
15 Label::secondary(self.file_id(), self.span())
16 }
17}
18
19pub type FullSpan = (Span, usize);
20
21impl<T> From<&Loc<T>> for FullSpan {
22 fn from(loc: &Loc<T>) -> Self {
23 (loc.span, loc.file_id)
24 }
25}
26
27impl<T> From<Loc<T>> for FullSpan {
28 fn from(loc: Loc<T>) -> Self {
29 FullSpan::from(&loc)
30 }
31}
32
33impl AsLabel for FullSpan {
34 fn span(&self) -> std::ops::Range<usize> {
35 self.0.into()
36 }
37
38 fn file_id(&self) -> usize {
39 self.1
40 }
41}
42
43pub trait HasCodespan {
44 fn codespan(&self) -> Span;
45}
46
47impl<T> HasCodespan for Loc<T> {
48 fn codespan(&self) -> Span {
49 self.span
50 }
51}
52
53impl HasCodespan for Span {
54 fn codespan(&self) -> Span {
55 *self
56 }
57}
58
59impl HasCodespan for std::ops::Range<usize> {
60 fn codespan(&self) -> Span {
61 lspan(self.clone())
62 }
63}
64
65pub trait WithLocation: Sized {
66 fn at(self, file_id: usize, span: &impl HasCodespan) -> Loc<Self>
67 where
68 Self: Sized,
69 {
70 Loc::new(self, span.codespan(), file_id)
71 }
72
73 fn at_loc<T: Sized>(self, loc: &Loc<T>) -> Loc<Self> {
75 Loc::new(self, loc.span, loc.file_id)
76 }
77
78 fn between(
79 self,
80 file_id: usize,
81 start: &impl HasCodespan,
82 end: &impl HasCodespan,
83 ) -> Loc<Self> {
84 Loc::new(self, start.codespan().merge(end.codespan()), file_id)
85 }
86
87 fn between_locs<T, Y>(self, start: &Loc<T>, end: &Loc<Y>) -> Loc<Self> {
88 assert!(start.file_id == end.file_id);
89 Loc::new(self, start.codespan().merge(end.codespan()), end.file_id())
90 }
91
92 fn nowhere(self) -> Loc<Self>
93 where
94 Self: Sized,
95 {
96 self.at(0, &Span::new(0, 0))
97 }
98}
99
100impl WithLocation for () {}
101impl WithLocation for BigInt {}
102impl WithLocation for BigUint {}
103impl WithLocation for u128 {}
104impl WithLocation for u64 {}
105impl WithLocation for i64 {}
106impl WithLocation for usize {}
107impl WithLocation for bool {}
108impl WithLocation for String {}
109impl<'a> WithLocation for &'a str {}
110impl<T> WithLocation for Vec<T> {}
111
112pub fn lspan(s: logos::Span) -> Span {
113 Span::new(s.start as u32, s.end as u32)
114}
115
116#[cfg(test)]
117pub fn dummy() -> Span {
118 Span::new(0, 0)
119}
120
121#[derive(Clone, Copy, Serialize, Deserialize)]
122pub struct Loc<T> {
123 pub inner: T,
124 pub span: Span,
125 pub file_id: usize,
126}
127
128impl<T> Loc<T> {
129 pub fn new(inner: T, span: Span, file_id: usize) -> Self {
130 Self {
131 inner,
132 span,
133 file_id,
134 }
135 }
136 pub fn nowhere(inner: T) -> Self {
137 Self::new(inner, Span::new(0, 0), 0)
138 }
139
140 pub fn strip(self) -> T {
141 self.inner
142 }
143
144 pub fn strip_ref(&self) -> &T {
145 &self.inner
146 }
147
148 pub fn separate(self) -> (Self, Span) {
149 let span = self.span;
150 (self, span)
151 }
152
153 pub fn separate_loc(self) -> (Self, Loc<()>) {
154 let loc = self.loc();
155 (self, loc)
156 }
157
158 pub fn split(self) -> (T, Span) {
159 (self.inner, self.span)
160 }
161 pub fn split_ref(&self) -> (&T, Span) {
162 (&self.inner, self.span)
163 }
164 pub fn split_loc(self) -> (T, Loc<()>) {
165 let loc = self.loc();
166 (self.inner, loc)
167 }
168 pub fn split_loc_ref(&self) -> (&T, Loc<()>) {
169 let loc = self.loc();
170 (&self.inner, loc)
171 }
172
173 pub fn is_same_loc<R>(&self, other: &Loc<R>) -> bool {
174 self.span == other.span && self.file_id == other.file_id
175 }
176
177 pub fn map<Y>(self, mut op: impl FnMut(T) -> Y) -> Loc<Y> {
178 Loc {
179 inner: op(self.inner),
180 span: self.span,
181 file_id: self.file_id,
182 }
183 }
184
185 pub fn try_map<Y, E>(self, mut op: impl FnMut(T) -> Result<Y, E>) -> Result<Loc<Y>, E> {
186 Ok(Loc {
187 inner: op(self.inner)?,
188 span: self.span,
189 file_id: self.file_id,
190 })
191 }
192
193 pub fn map_ref<Y>(&self, mut op: impl FnMut(&T) -> Y) -> Loc<Y> {
194 Loc {
195 inner: op(&self.inner),
196 span: self.span,
197 file_id: self.file_id,
198 }
199 }
200
201 pub fn try_map_ref<Y, E>(&self, mut op: impl FnMut(&T) -> Result<Y, E>) -> Result<Loc<Y>, E> {
202 Ok(Loc {
203 inner: op(&self.inner)?,
204 span: self.span,
205 file_id: self.file_id,
206 })
207 }
208
209 pub fn loc(&self) -> Loc<()> {
210 Loc {
211 inner: (),
212 span: self.span,
213 file_id: self.file_id,
214 }
215 }
216
217 pub fn contains_start<R>(&self, other: &Loc<R>) -> bool {
218 other.file_id == self.file_id
219 && other.span.start() >= self.span.start()
220 && other.span.start() < self.span.end()
221 }
222
223 pub fn shrink_left(&self, s: &str) -> Loc<()> {
235 if self.span.start() == self.span.end() {
236 self.loc()
237 } else {
238 Loc {
239 inner: (),
240 span: Span::new(
241 self.span.start() + ByteOffset::from_str_len(s),
242 self.span.end(),
243 ),
244 file_id: self.file_id,
245 }
246 }
247 }
248
249 pub fn shrink_right(&self, s: &str) -> Loc<()> {
251 if self.span.start() == self.span.end() {
252 self.loc()
253 } else {
254 Loc {
255 inner: (),
256 span: Span::new(
257 self.span.start(),
258 self.span.end() - ByteOffset::from_str_len(s),
259 ),
260 file_id: self.file_id,
261 }
262 }
263 }
264}
265
266impl<T, E> Loc<Result<T, E>> {
267 pub fn map_err<E2>(self, err_fn: impl Fn(E, Loc<()>) -> E2) -> Result<Loc<T>, E2> {
268 match self.inner {
269 Ok(inner) => Ok(Loc {
270 inner,
271 span: self.span,
272 file_id: self.file_id,
273 }),
274 Err(e) => Err(err_fn(e, ().at(self.file_id, &self.span))),
275 }
276 }
277}
278
279impl<T> PartialEq for Loc<T>
280where
281 T: PartialEq,
282{
283 fn eq(&self, other: &Self) -> bool {
284 self.inner == other.inner
285 }
286}
287
288impl<T> Eq for Loc<T> where T: Eq {}
289
290impl<T> PartialOrd for Loc<T>
291where
292 T: PartialOrd,
293{
294 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
295 self.inner.partial_cmp(&other.inner)
296 }
297}
298
299impl<T> Ord for Loc<T>
300where
301 T: Ord,
302{
303 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
304 self.inner.cmp(&other.inner)
305 }
306}
307
308impl<T> std::fmt::Display for Loc<T>
309where
310 T: std::fmt::Display,
311{
312 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
313 write!(f, "{}", self.inner)
314 }
315}
316
317impl<T> std::hash::Hash for Loc<T>
318where
319 T: std::hash::Hash,
320{
321 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
322 self.inner.hash(state)
323 }
324}
325
326impl<T> std::fmt::Debug for Loc<T>
327where
328 T: std::fmt::Debug,
329{
330 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
331 self.inner.fmt(f)
332 }
333}
334
335impl<T> std::ops::Deref for Loc<T> {
336 type Target = T;
337
338 fn deref(&self) -> &Self::Target {
339 &self.inner
340 }
341}
342impl<T> std::ops::DerefMut for Loc<T> {
343 fn deref_mut(&mut self) -> &mut Self::Target {
344 &mut self.inner
345 }
346}
347
348impl<T> AsLabel for Loc<T> {
349 fn file_id(&self) -> usize {
350 self.file_id
351 }
352
353 fn span(&self) -> std::ops::Range<usize> {
354 self.span.into()
355 }
356}