gluon_base/pos.rs
1//! Source code locations (borrowed from rustc's [libsyntax_pos])
2//!
3//! [libsyntax_pos]: https://github.com/rust-lang/rust/blob/master/src/libsyntax_pos/lib.rs
4
5use std::{cmp, cmp::Ordering, fmt};
6
7pub use codespan::{
8 ByteIndex, ByteIndex as BytePos, ByteOffset, ColumnIndex as Column, ColumnOffset, Index,
9 LineIndex as Line, LineOffset, RawIndex,
10};
11
12use crate::source::CodeMap;
13
14/// A location in a source file
15#[derive(Copy, Clone, Default, Eq, PartialEq, Debug, Hash, Ord, PartialOrd)]
16pub struct Location {
17 pub line: Line,
18 pub column: Column,
19 pub absolute: BytePos,
20}
21
22impl Location {
23 pub fn shift(&mut self, ch: u8) {
24 if ch == b'\n' {
25 self.line += LineOffset(1);
26 self.column = Column(1);
27 } else {
28 self.column += ColumnOffset(1);
29 }
30 self.absolute += ByteOffset(1);
31 }
32}
33
34impl fmt::Display for Location {
35 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
36 write!(
37 f,
38 "Line: {}, Column: {}",
39 self.line.number(),
40 self.column.number()
41 )
42 }
43}
44
45#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
46pub struct Positioned<T, Pos> {
47 pub pos: Pos,
48 pub value: T,
49}
50
51/// A region of code in a source file
52#[derive(Clone, Copy, Default, PartialEq, Eq, Ord, PartialOrd)]
53#[cfg_attr(feature = "serialization", derive(Deserialize, Serialize))]
54#[cfg_attr(feature = "memory_usage", derive(HeapSizeOf))]
55pub struct Span<I> {
56 start: I,
57 end: I,
58}
59
60impl<I: fmt::Debug> fmt::Debug for Span<I> {
61 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62 write!(f, "{:?}..{:?}", self.start, self.end)
63 }
64}
65
66impl<I: Ord> Span<I> {
67 /// Create a new span
68 ///
69 /// ```rust
70 /// use gluon_base::pos::{ByteIndex, Span};
71 ///
72 /// let span = Span::new(ByteIndex(3), ByteIndex(6));
73 /// assert_eq!(span.start(), ByteIndex(3));
74 /// assert_eq!(span.end(), ByteIndex(6));
75 /// ```
76 ///
77 /// `start` and `end` are reordered to maintain the invariant that `start <= end`
78 ///
79 /// ```rust
80 /// use gluon_base::pos::{ByteIndex, Span};
81 ///
82 /// let span = Span::new(ByteIndex(6), ByteIndex(3));
83 /// assert_eq!(span.start(), ByteIndex(3));
84 /// assert_eq!(span.end(), ByteIndex(6));
85 /// ```
86 pub fn new(start: I, end: I) -> Span<I> {
87 if start <= end {
88 Span { start, end }
89 } else {
90 Span {
91 start: end,
92 end: start,
93 }
94 }
95 }
96
97 pub fn map<F, J>(self, mut f: F) -> Span<J>
98 where
99 F: FnMut(I) -> J,
100 J: Ord,
101 {
102 Span::new(f(self.start), f(self.end))
103 }
104}
105
106impl<I> Span<I> {
107 /// Create a span like `new` but does not check that `start <= end`
108 pub const fn new_unchecked(start: I, end: I) -> Span<I> {
109 Span { start, end }
110 }
111}
112
113impl<I> Span<I> {
114 /// Get the start index
115 pub fn start(self) -> I {
116 self.start
117 }
118
119 /// Get the end index
120 pub fn end(self) -> I {
121 self.end
122 }
123}
124
125impl<I: Index> Span<I> {
126 /// Makes a span from offsets relative to the start of this span.
127 pub fn subspan(&self, begin: I::Offset, end: I::Offset) -> Span<I> {
128 assert!(end >= begin);
129 assert!(self.start() + end <= self.end());
130 Span {
131 start: self.start() + begin,
132 end: self.start() + end,
133 }
134 }
135
136 /// Create a new span from a byte start and an offset
137 pub fn from_offset(start: I, off: I::Offset) -> Span<I> {
138 Span::new(start, start + off)
139 }
140
141 /// Return a new span with the low byte position replaced with the supplied byte position
142 ///
143 /// ```rust
144 /// use gluon_base::pos::{ByteIndex, Span};
145 ///
146 /// let span = Span::new(ByteIndex(3), ByteIndex(6));
147 /// assert_eq!(span.with_start(ByteIndex(2)), Span::new(ByteIndex(2), ByteIndex(6)));
148 /// assert_eq!(span.with_start(ByteIndex(5)), Span::new(ByteIndex(5), ByteIndex(6)));
149 /// assert_eq!(span.with_start(ByteIndex(7)), Span::new(ByteIndex(6), ByteIndex(7)));
150 /// ```
151 pub fn with_start(self, start: I) -> Span<I> {
152 Span::new(start, self.end())
153 }
154
155 /// Return a new span with the high byte position replaced with the supplied byte position
156 ///
157 /// ```rust
158 /// use gluon_base::pos::{ByteIndex, Span};
159 ///
160 /// let span = Span::new(ByteIndex(3), ByteIndex(6));
161 /// assert_eq!(span.with_end(ByteIndex(7)), Span::new(ByteIndex(3), ByteIndex(7)));
162 /// assert_eq!(span.with_end(ByteIndex(5)), Span::new(ByteIndex(3), ByteIndex(5)));
163 /// assert_eq!(span.with_end(ByteIndex(2)), Span::new(ByteIndex(2), ByteIndex(3)));
164 /// ```
165 pub fn with_end(self, end: I) -> Span<I> {
166 Span::new(self.start(), end)
167 }
168
169 /// Return true if `self` fully encloses `other`.
170 ///
171 /// ```rust
172 /// use gluon_base::pos::{ByteIndex, Span};
173 ///
174 /// let a = Span::new(ByteIndex(5), ByteIndex(8));
175 ///
176 /// assert_eq!(a.contains(a), true);
177 /// assert_eq!(a.contains(Span::new(ByteIndex(6), ByteIndex(7))), true);
178 /// assert_eq!(a.contains(Span::new(ByteIndex(6), ByteIndex(10))), false);
179 /// assert_eq!(a.contains(Span::new(ByteIndex(3), ByteIndex(6))), false);
180 /// ```
181 pub fn contains(self, other: Span<I>) -> bool {
182 self.start() <= other.start() && other.end() <= self.end()
183 }
184
185 pub fn contains_pos(self, other: I) -> bool {
186 self.start() <= other && other <= self.end()
187 }
188
189 /// Return `Equal` if `self` contains `pos`, otherwise it returns `Less` if `pos` is before
190 /// `start` or `Greater` if `pos` is after or at `end`.
191 ///
192 /// ```rust
193 /// use gluon_base::pos::{ByteIndex, Span};
194 /// use std::cmp::Ordering::*;
195 ///
196 /// let a = Span::new(ByteIndex(5), ByteIndex(8));
197 ///
198 /// assert_eq!(a.containment(ByteIndex(4)), Less);
199 /// assert_eq!(a.containment(ByteIndex(5)), Equal);
200 /// assert_eq!(a.containment(ByteIndex(6)), Equal);
201 /// assert_eq!(a.containment(ByteIndex(8)), Equal);
202 /// assert_eq!(a.containment(ByteIndex(9)), Greater);
203 /// ```
204 pub fn containment(self, pos: I) -> Ordering {
205 use std::cmp::Ordering::*;
206
207 match (pos.cmp(&self.start), pos.cmp(&self.end)) {
208 (Equal, _) | (_, Equal) | (Greater, Less) => Equal,
209 (Less, _) => Less,
210 (_, Greater) => Greater,
211 }
212 }
213
214 /// Return `Equal` if `self` contains `pos`, otherwise it returns `Less` if `pos` is before
215 /// `start` or `Greater` if `pos` is *strictly* after `end`.
216 ///
217 /// ```rust
218 /// use gluon_base::pos::{ByteIndex, Span};
219 /// use std::cmp::Ordering::*;
220 ///
221 /// let a = Span::new(ByteIndex(5), ByteIndex(8));
222 ///
223 /// assert_eq!(a.containment_exclusive(ByteIndex(4)), Less);
224 /// assert_eq!(a.containment_exclusive(ByteIndex(5)), Equal);
225 /// assert_eq!(a.containment_exclusive(ByteIndex(6)), Equal);
226 /// assert_eq!(a.containment_exclusive(ByteIndex(8)), Greater);
227 /// assert_eq!(a.containment_exclusive(ByteIndex(9)), Greater);
228 /// ```
229 pub fn containment_exclusive(self, pos: I) -> Ordering {
230 if self.end == pos {
231 Ordering::Greater
232 } else {
233 self.containment(pos)
234 }
235 }
236
237 /// Return a `Span` that would enclose both `self` and `end`.
238 ///
239 /// ```plain
240 /// self ~~~~~~~
241 /// end ~~~~~~~~
242 /// returns ~~~~~~~~~~~~~~~~~~~~~~~
243 /// ```
244 ///
245 /// ```rust
246 /// use gluon_base::pos::{ByteIndex, Span};
247 ///
248 /// let a = Span::new(ByteIndex(2), ByteIndex(5));
249 /// let b = Span::new(ByteIndex(10), ByteIndex(14));
250 ///
251 /// assert_eq!(a.to(b), Span::new(ByteIndex(2), ByteIndex(14)));
252 /// ```
253 pub fn to(self, end: Span<I>) -> Span<I> {
254 Span::new(
255 cmp::min(self.start(), end.start()),
256 cmp::max(self.end(), end.end()),
257 )
258 }
259
260 /// Return a `Span` between the end of `self` to the beginning of `end`.
261 ///
262 /// ```plain
263 /// self ~~~~~~~
264 /// end ~~~~~~~~
265 /// returns ~~~~~~~~~
266 /// ```
267 ///
268 /// ```rust
269 /// use gluon_base::pos::{ByteIndex, Span};
270 ///
271 /// let a = Span::new(ByteIndex(2), ByteIndex(5));
272 /// let b = Span::new(ByteIndex(10), ByteIndex(14));
273 ///
274 /// assert_eq!(a.between(b), Span::new(ByteIndex(5), ByteIndex(10)));
275 /// ```
276 pub fn between(self, end: Span<I>) -> Span<I> {
277 Span::new(self.end(), end.start())
278 }
279
280 /// Return a `Span` between the beginning of `self` to the beginning of `end`.
281 ///
282 /// ```plain
283 /// self ~~~~~~~
284 /// end ~~~~~~~~
285 /// returns ~~~~~~~~~~~~~~~~
286 /// ```
287 ///
288 /// ```rust
289 /// use gluon_base::pos::{ByteIndex, Span};
290 ///
291 /// let a = Span::new(ByteIndex(2), ByteIndex(5));
292 /// let b = Span::new(ByteIndex(10), ByteIndex(14));
293 ///
294 /// assert_eq!(a.until(b), Span::new(ByteIndex(2), ByteIndex(10)));
295 /// ```
296 pub fn until(self, end: Span<I>) -> Span<I> {
297 Span::new(self.start(), end.start())
298 }
299}
300
301impl Span<BytePos> {
302 pub fn to_range(self, source: &CodeMap) -> Option<std::ops::Range<usize>> {
303 Some(source.to_usize(self.start())?..source.to_usize(self.end())?)
304 }
305}
306
307impl<I: fmt::Display> fmt::Display for Span<I> {
308 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
309 self.start.fmt(f)?;
310 write!(f, "..")?;
311 self.end.fmt(f)?;
312 Ok(())
313 }
314}
315
316#[derive(Copy, Clone, Debug, Eq, PartialEq, Default)]
317pub struct Spanned<T, Pos> {
318 pub span: Span<Pos>,
319 pub value: T,
320}
321
322impl<T, Pos> From<(T, Span<Pos>)> for Spanned<T, Pos> {
323 fn from((value, span): (T, Span<Pos>)) -> Self {
324 Spanned { span, value }
325 }
326}
327
328impl<T, Pos> From<T> for Spanned<T, Pos>
329where
330 Pos: Default,
331{
332 fn from(value: T) -> Self {
333 Spanned {
334 span: Span::default(),
335 value,
336 }
337 }
338}
339
340impl<T, Pos> PartialEq<T> for Spanned<T, Pos>
341where
342 T: PartialEq,
343{
344 fn eq(&self, other: &T) -> bool {
345 self.value == *other
346 }
347}
348
349impl<T, Pos> std::ops::Deref for Spanned<T, Pos> {
350 type Target = T;
351 fn deref(&self) -> &T {
352 &self.value
353 }
354}
355
356impl<T, Pos> std::ops::DerefMut for Spanned<T, Pos> {
357 fn deref_mut(&mut self) -> &mut T {
358 &mut self.value
359 }
360}
361
362impl<T, U, Pos> AsRef<U> for Spanned<T, Pos>
363where
364 T: AsRef<U>,
365 U: ?Sized,
366{
367 fn as_ref(&self) -> &U {
368 self.value.as_ref()
369 }
370}
371
372impl<T, Pos> std::hash::Hash for Spanned<T, Pos>
373where
374 T: std::hash::Hash,
375 Pos: std::hash::Hash + Copy,
376{
377 fn hash<H>(&self, state: &mut H)
378 where
379 H: std::hash::Hasher,
380 {
381 self.span.start().hash(state);
382 self.span.end().hash(state);
383 self.value.hash(state);
384 }
385}
386
387impl<T, Pos> Spanned<T, Pos> {
388 pub fn map<U, F>(self, mut f: F) -> Spanned<U, Pos>
389 where
390 F: FnMut(T) -> U,
391 {
392 Spanned {
393 span: self.span,
394 value: f(self.value),
395 }
396 }
397}
398
399impl<T: fmt::Display, Pos: fmt::Display + Copy> fmt::Display for Spanned<T, Pos> {
400 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
401 write!(f, "{}: {}", self.span.start(), self.value)
402 }
403}
404
405pub fn span<Pos>(start: Pos, end: Pos) -> Span<Pos>
406where
407 Pos: Ord,
408{
409 Span::new(start, end)
410}
411
412pub fn spanned<T, Pos>(span: Span<Pos>, value: T) -> Spanned<T, Pos> {
413 Spanned { span, value }
414}
415
416pub fn spanned2<T, Pos>(start: Pos, end: Pos, value: T) -> Spanned<T, Pos>
417where
418 Pos: Ord,
419{
420 spanned(span(start, end), value)
421}
422
423pub trait HasSpan {
424 fn span(&self) -> Span<BytePos>;
425}