1use std::cmp::Ordering;
2use std::error::Error;
3use std::fmt::{Display, Formatter, Result as FmtResult};
4use std::ops::{BitAnd, BitOr, Bound, RangeBounds};
5use std::sync::atomic::{AtomicU64, Ordering as AtomicOrdering};
6
7const FIRST_SOURCE_ID: u64 = 1;
8static NEXT_SOURCE_ID: AtomicU64 = AtomicU64::new(FIRST_SOURCE_ID);
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11pub struct SourceId(pub u64);
12
13impl SourceId {
14 pub fn fresh() -> Self {
16 let id = NEXT_SOURCE_ID
17 .fetch_update(
18 AtomicOrdering::Relaxed,
19 AtomicOrdering::Relaxed,
20 |current| current.checked_add(1),
21 )
22 .expect("source id counter exhausted");
23 Self(id)
24 }
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct TextPoint {
30 pub line: u32,
31 pub column: u32,
32 pub byte: usize,
33}
34
35impl TextPoint {
36 pub fn new(line: u32, column: u32, byte: usize) -> Self {
37 Self { line, column, byte }
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
42pub enum TextRange {
43 Range {
44 start: TextPoint,
45 end: TextPoint,
46 },
47 #[default]
48 Empty,
49}
50
51impl TextRange {
52 pub fn from_point(point: TextPoint) -> Self {
53 Self::Range {
54 start: point,
55 end: point,
56 }
57 }
58
59 pub fn new(start: TextPoint, end: TextPoint) -> Self {
60 match start.cmp(&end) {
61 Ordering::Less => Self::Range { start, end },
62 Ordering::Equal => Self::from_point(start),
63 Ordering::Greater => Self::Empty,
64 }
65 }
66
67 pub fn join(self, other: Self) -> Self {
68 match (self, other) {
69 (Self::Empty, _) => other,
70 (_, Self::Empty) => self,
71 (
72 Self::Range { start, end },
73 Self::Range {
74 start: start2,
75 end: end2,
76 },
77 ) => Self::new(start.min(start2), end.max(end2)),
78 }
79 }
80
81 pub fn meet(self, other: Self) -> Self {
82 match (self, other) {
83 (Self::Empty, _) => Self::Empty,
84 (_, Self::Empty) => Self::Empty,
85 (
86 Self::Range { start, end },
87 Self::Range {
88 start: start2,
89 end: end2,
90 },
91 ) => Self::new(start.max(start2), end.min(end2)),
92 }
93 }
94
95 pub fn start(&self) -> Option<TextPoint> {
96 match self {
97 Self::Empty => None,
98 Self::Range { start, .. } => Some(*start),
99 }
100 }
101
102 pub fn end(&self) -> Option<TextPoint> {
103 match self {
104 Self::Empty => None,
105 Self::Range { end, .. } => Some(*end),
106 }
107 }
108
109 pub fn is_empty(&self) -> bool {
110 matches!(self, Self::Empty)
111 }
112
113 pub fn is_point(&self) -> bool {
114 matches!(self, Self::Range { start, end } if start == end)
115 }
116
117 pub fn size(&self) -> Option<usize> {
118 self.end()?.byte.checked_sub(self.start()?.byte)
119 }
120}
121
122impl BitOr for TextRange {
123 type Output = Self;
124
125 fn bitor(self, other: Self) -> Self {
126 self.join(other)
127 }
128}
129
130impl BitAnd for TextRange {
131 type Output = Self;
132
133 fn bitand(self, other: Self) -> Self {
134 self.meet(other)
135 }
136}
137
138impl RangeBounds<TextPoint> for TextRange {
141 fn start_bound(&self) -> Bound<&TextPoint> {
142 match self {
143 Self::Empty => Bound::Unbounded,
144 Self::Range { start, .. } => Bound::Included(start),
145 }
146 }
147
148 fn end_bound(&self) -> Bound<&TextPoint> {
149 match self {
150 Self::Empty => Bound::Unbounded,
151 Self::Range { end, .. } => Bound::Included(end),
152 }
153 }
154}
155
156#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
157pub enum Span {
158 #[default]
159 Detached,
160 FileLocated {
161 source: SourceId,
162 range: TextRange,
163 },
164 LocalRefFrame {
165 range: TextRange,
166 },
167}
168
169impl Span {
170 pub fn detached() -> Self {
171 Self::default()
172 }
173
174 pub fn new(source: SourceId, range: TextRange) -> Self {
175 Self::FileLocated { source, range }
176 }
177
178 pub fn in_tmp_file(range: TextRange) -> Self {
179 Self::LocalRefFrame { range }
180 }
181
182 pub fn source(self) -> Result<SourceId, MissingData> {
183 match self {
184 Self::FileLocated { source, .. } => Ok(source),
185 _ => Err(MissingData),
186 }
187 }
188
189 pub fn range(self) -> Result<TextRange, MissingData> {
190 match self {
191 Self::Detached => Err(MissingData),
192 Self::FileLocated { range, .. } => Ok(range),
193 Self::LocalRefFrame { range } => Ok(range),
194 }
195 }
196
197 pub fn start_point(self) -> Result<TextPoint, MissingData> {
198 self.range()?.start().ok_or(MissingData)
199 }
200
201 pub fn end_point(self) -> Result<TextPoint, MissingData> {
202 self.range()?.end().ok_or(MissingData)
203 }
204
205 pub fn byte_size(self) -> Result<usize, MissingData> {
206 self.range()?.size().ok_or(MissingData)
207 }
208
209 pub fn join(self, other: Self) -> Self {
210 match (self, other) {
211 (Self::Detached, span) | (span, Self::Detached) => span,
212 (
213 Self::FileLocated { source, range },
214 Self::FileLocated {
215 source: source2,
216 range: range2,
217 },
218 ) => {
219 if source == source2 {
220 Self::new(source, range | range2)
221 } else {
222 Self::Detached
223 }
224 }
225
226 (Self::LocalRefFrame { range }, Self::LocalRefFrame { range: range2 }) => {
227 Self::in_tmp_file(range | range2)
228 }
229 (_, _) => Self::Detached,
230 }
231 }
232
233 pub fn join_all(spans: impl IntoIterator<Item = Span>) -> Self {
234 spans.into_iter().reduce(Span::join).unwrap_or_default()
235 }
236}
237
238#[derive(Debug, Clone, Copy, PartialEq, Eq)]
239pub struct MissingData;
240
241impl Display for MissingData {
242 fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
243 formatter.write_str("syntax has no source location")
244 }
245}
246
247impl Error for MissingData {}
248
249pub trait Spanned {
250 fn span(&self) -> Span;
251}
252
253impl Spanned for Span {
254 fn span(&self) -> Span {
255 *self
256 }
257}
258
259impl<T: Spanned + ?Sized> Spanned for &T {
260 fn span(&self) -> Span {
261 (*self).span()
262 }
263}
264
265impl<T: Spanned + ?Sized> Spanned for Box<T> {
266 fn span(&self) -> Span {
267 self.as_ref().span()
268 }
269}
270
271impl<T: Spanned> Spanned for Option<T> {
272 fn span(&self) -> Span {
273 self.as_ref().map_or_else(Span::detached, Spanned::span)
274 }
275}
276
277impl<T: Spanned> Spanned for Vec<T> {
278 fn span(&self) -> Span {
279 Span::join_all(self.iter().map(Spanned::span))
280 }
281}
282
283impl<A: Spanned, B: Spanned> Spanned for (A, B) {
284 fn span(&self) -> Span {
285 self.0.span().join(self.1.span())
286 }
287}
288
289impl<T: Spanned> Spanned for &[T] {
290 fn span(&self) -> Span {
291 Span::join_all(self.iter().map(Spanned::span))
292 }
293}