1use std::fmt;
2
3use derive_more::with_trait::{Display, Error};
4
5#[derive(Clone, Copy, Debug, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
7#[display("{line}:{col}")]
8pub struct SourcePosition {
9 index: usize,
10 line: usize,
11 col: usize,
12}
13
14#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
17pub struct Span {
18 pub start: SourcePosition,
20
21 pub end: SourcePosition,
25}
26
27impl Span {
28 #[doc(hidden)]
29 #[inline]
30 pub fn zero_width(pos: SourcePosition) -> Self {
31 Self {
32 start: pos,
33 end: pos,
34 }
35 }
36
37 #[doc(hidden)]
38 #[inline]
39 pub fn single_width(pos: SourcePosition) -> Self {
40 let mut end = pos;
41 end.advance_col();
42
43 Self { start: pos, end }
44 }
45
46 #[doc(hidden)]
47 #[inline]
48 pub fn unlocated() -> Self {
49 Self {
50 start: SourcePosition::new_origin(),
51 end: SourcePosition::new_origin(),
52 }
53 }
54}
55
56#[derive(Clone, Copy, Debug, Eq, Error, Hash, PartialEq)]
58pub struct Spanning<T, Sp = Span> {
59 #[error(source)]
61 pub item: T,
62
63 pub span: Sp,
65}
66
67impl<T> Spanning<T, Span> {
68 #[doc(hidden)]
69 pub fn new(span: Span, item: T) -> Self {
70 Self { item, span }
71 }
72
73 #[doc(hidden)]
74 pub fn zero_width(&pos: &SourcePosition, item: T) -> Spanning<T> {
75 Self::new(Span::zero_width(pos), item)
76 }
77
78 #[doc(hidden)]
79 pub fn single_width(&pos: &SourcePosition, item: T) -> Spanning<T> {
80 Self::new(Span::single_width(pos), item)
81 }
82
83 #[doc(hidden)]
84 pub fn start_end(&start: &SourcePosition, &end: &SourcePosition, item: T) -> Spanning<T> {
85 Self::new(Span { start, end }, item)
86 }
87
88 #[expect(clippy::self_named_constructors, reason = "intended")]
89 #[doc(hidden)]
90 pub fn spanning(v: Vec<Spanning<T>>) -> Option<Spanning<Vec<Spanning<T>>>> {
91 if let (Some(start), Some(end)) = (v.first().map(|s| s.span), v.last().map(|s| s.span)) {
92 Some(Spanning::new(
93 Span {
94 start: start.start,
95 end: end.end,
96 },
97 v,
98 ))
99 } else {
100 None
101 }
102 }
103
104 #[doc(hidden)]
105 pub fn unlocated(item: T) -> Spanning<T> {
106 Self::new(Span::unlocated(), item)
107 }
108
109 #[inline]
111 pub fn start(&self) -> SourcePosition {
112 self.span.start
113 }
114
115 #[inline]
119 pub fn end(&self) -> SourcePosition {
120 self.span.end
121 }
122
123 pub fn map<O, F: Fn(T) -> O>(self, f: F) -> Spanning<O> {
125 Spanning::new(self.span, f(self.item))
126 }
127
128 pub fn and_then<O, F: Fn(T) -> Option<O>>(self, f: F) -> Option<Spanning<O>> {
131 f(self.item).map(|item| Spanning::new(self.span, item))
132 }
133
134 pub(crate) fn as_ref(&self) -> Spanning<&'_ T, &'_ Span> {
136 Spanning {
137 item: &self.item,
138 span: &self.span,
139 }
140 }
141}
142
143impl<T: Display> Display for Spanning<T> {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 write!(f, "{}. At {}", self.item, self.span.start)
146 }
147}
148
149impl SourcePosition {
150 #[doc(hidden)]
151 pub fn new(index: usize, line: usize, col: usize) -> SourcePosition {
152 assert!(index >= line + col);
153
154 SourcePosition { index, line, col }
155 }
156
157 #[doc(hidden)]
158 pub fn new_origin() -> SourcePosition {
159 SourcePosition {
160 index: 0,
161 line: 0,
162 col: 0,
163 }
164 }
165
166 #[doc(hidden)]
167 pub fn advance_col(&mut self) {
168 self.index += 1;
169 self.col += 1;
170 }
171
172 #[doc(hidden)]
173 pub fn advance_line(&mut self) {
174 self.index += 1;
175 self.line += 1;
176 self.col = 0;
177 }
178
179 pub fn index(&self) -> usize {
184 self.index
185 }
186
187 pub fn line(&self) -> usize {
191 self.line
192 }
193
194 pub fn column(&self) -> usize {
198 self.col
199 }
200}