chumsky/span.rs
1//! Types and traits related to spans.
2//!
3//! *“We demand rigidly defined areas of doubt and uncertainty!”*
4//!
5//! You can use the [`Span`] trait to connect up chumsky to your compiler's knowledge of the input source.
6
7use super::*;
8
9/// A trait that describes a span over a particular range of inputs.
10///
11/// Spans typically consist of some context, such as the file they originated from, and a start/end offset. Spans are
12/// permitted to overlap one-another. The end offset must always be greater than or equal to the start offset.
13///
14/// Span is automatically implemented for [`Range<T>`] and [`(C, Range<T>)`].
15pub trait Span {
16 /// Extra context used in a span.
17 ///
18 /// This is usually some way to uniquely identity the source file that a span originated in such as the file's
19 /// path, URL, etc.
20 ///
21 /// NOTE: Span contexts have no inherent meaning to Chumsky and can be anything. For example, [`Range<usize>`]'s
22 /// implementation of [`Span`] simply uses `()` as its context.
23 type Context;
24
25 /// A type representing a span's start or end offset from the start of the input.
26 ///
27 /// Typically, [`usize`] is used.
28 ///
29 /// NOTE: Offsets have no inherently meaning to Chumsky and are not used to decide how to prioritize errors. This
30 /// means that it's perfectly fine for tokens to have non-continuous spans that bear no relation to their actual
31 /// location in the input stream. This is useful for languages with an AST-level macro system that need to
32 /// correctly point to symbols in the macro input when producing errors.
33 type Offset: Clone;
34
35 /// Create a new span given a context and an offset range.
36 fn new(context: Self::Context, range: Range<Self::Offset>) -> Self;
37
38 /// Return the span's context.
39 fn context(&self) -> Self::Context;
40
41 /// Return the start offset of the span.
42 fn start(&self) -> Self::Offset;
43
44 /// Return the end offset of the span.
45 fn end(&self) -> Self::Offset;
46
47 /// Turn this span into a zero-width span that starts and ends at the end of the original.
48 ///
49 /// For example, an original span like `3..7` will result in a new span of `7..7`.
50 fn to_end(&self) -> Self
51 where
52 Self: Sized,
53 {
54 Self::new(self.context(), self.end()..self.end())
55 }
56
57 /// Combine two assumed-contiguous spans together into a larger span that encompasses both (and anything between).
58 ///
59 /// For example, spans like `3..5` and `7..8` will result in a unioned span of `3..8`.
60 ///
61 /// The spans may overlap one-another, but the start offset must come before the end offset for each span (i.e:
62 /// each span must be 'well-formed'). If this is not the case, the result is unspecified.
63 ///
64 /// # Panics
65 ///
66 /// Panics if the [`Self::Context`]s of both spans are not equal.
67 fn union(&self, other: Self) -> Self
68 where
69 Self::Context: PartialEq + fmt::Debug,
70 Self::Offset: Ord,
71 Self: Sized,
72 {
73 assert_eq!(
74 self.context(),
75 other.context(),
76 "tried to union two spans with different contexts"
77 );
78 Self::new(
79 self.context(),
80 self.start().min(other.start())..self.end().max(other.end()),
81 )
82 }
83}
84
85/// The most basic implementor of `Span` - akin to `Range`, but `Copy` since it's not also
86/// an iterator. Also has a `Display` implementation
87#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
88#[derive(Copy, Clone, PartialEq, Eq, Hash)]
89pub struct SimpleSpan<T = usize, C = ()> {
90 /// The start offset of the span.
91 pub start: T,
92 /// The end (exclusive) offset of the span.
93 pub end: T,
94 /// The context of the span (usually some ID representing the file path the span relates to).
95 pub context: C,
96}
97
98impl<T, C> SimpleSpan<T, C> {
99 /// Convert this span into a [`std::ops::Range`].
100 pub fn into_range(self) -> Range<T> {
101 self.start..self.end
102 }
103}
104
105impl<T> From<Range<T>> for SimpleSpan<T> {
106 fn from(range: Range<T>) -> Self {
107 SimpleSpan {
108 start: range.start,
109 end: range.end,
110 context: (),
111 }
112 }
113}
114
115impl<T> From<SimpleSpan<T, ()>> for Range<T> {
116 fn from(span: SimpleSpan<T>) -> Self {
117 Range {
118 start: span.start,
119 end: span.end,
120 }
121 }
122}
123
124impl<T, C> fmt::Debug for SimpleSpan<T, C>
125where
126 T: fmt::Debug,
127{
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 write!(f, "{:?}..{:?}", self.start, self.end)
130 }
131}
132
133impl<T, C> fmt::Display for SimpleSpan<T, C>
134where
135 T: fmt::Display,
136{
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 write!(f, "{}..{}", self.start, self.end)
139 }
140}
141
142impl<T, C> IntoIterator for SimpleSpan<T, C>
143where
144 Range<T>: Iterator<Item = T>,
145{
146 type IntoIter = Range<T>;
147 type Item = T;
148
149 fn into_iter(self) -> Self::IntoIter {
150 self.start..self.end
151 }
152}
153
154impl<T: Clone, C: Clone> Span for SimpleSpan<T, C> {
155 type Context = C;
156 type Offset = T;
157
158 fn new(context: Self::Context, range: Range<Self::Offset>) -> Self {
159 Self {
160 start: range.start,
161 end: range.end,
162 context,
163 }
164 }
165 fn context(&self) -> Self::Context {
166 self.context.clone()
167 }
168 fn start(&self) -> Self::Offset {
169 self.start.clone()
170 }
171 fn end(&self) -> Self::Offset {
172 self.end.clone()
173 }
174}
175
176impl<C: Clone, S: Span<Context = ()>> Span for (C, S) {
177 type Context = C;
178 type Offset = S::Offset;
179
180 fn new(context: Self::Context, range: Range<Self::Offset>) -> Self {
181 (context, S::new((), range))
182 }
183 fn context(&self) -> Self::Context {
184 self.0.clone()
185 }
186 fn start(&self) -> Self::Offset {
187 self.1.start()
188 }
189 fn end(&self) -> Self::Offset {
190 self.1.end()
191 }
192}
193
194impl<T: Clone> Span for Range<T> {
195 type Context = ();
196 type Offset = T;
197
198 fn new(_context: Self::Context, range: Range<Self::Offset>) -> Self {
199 range
200 }
201 fn context(&self) -> Self::Context {}
202 fn start(&self) -> Self::Offset {
203 self.start.clone()
204 }
205 fn end(&self) -> Self::Offset {
206 self.end.clone()
207 }
208}