1use std::fmt;
4use std::sync::Arc;
5
6#[derive(Clone, PartialEq, Eq, Hash)]
8pub struct Span {
9 pub start: usize,
11 pub end: usize,
13 pub source: Arc<str>,
15}
16
17impl Span {
18 #[must_use]
20 pub fn new(start: usize, end: usize, source: Arc<str>) -> Self {
21 Self { start, end, source }
22 }
23
24 #[must_use]
26 pub fn dummy() -> Self {
27 Self {
28 start: 0,
29 end: 0,
30 source: Arc::from(""),
31 }
32 }
33
34 #[must_use]
36 pub fn len(&self) -> usize {
37 self.end.saturating_sub(self.start)
38 }
39
40 #[must_use]
42 pub fn is_empty(&self) -> bool {
43 self.len() == 0
44 }
45
46 #[must_use]
48 pub fn text(&self) -> &str {
49 &self.source[self.start..self.end]
50 }
51
52 #[must_use]
54 pub fn merge(&self, other: &Span) -> Span {
55 Span {
56 start: self.start.min(other.start),
57 end: self.end.max(other.end),
58 source: Arc::clone(&self.source),
59 }
60 }
61
62 #[must_use]
64 pub fn line_col(&self) -> (usize, usize) {
65 let mut line = 1;
66 let mut col = 1;
67 for (i, ch) in self.source.char_indices() {
68 if i >= self.start {
69 break;
70 }
71 if ch == '\n' {
72 line += 1;
73 col = 1;
74 } else {
75 col += 1;
76 }
77 }
78 (line, col)
79 }
80}
81
82impl fmt::Debug for Span {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 let (line, col) = self.line_col();
85 let len = self.end - self.start;
86 write!(f, "{line}:{col}..{len}")
87 }
88}
89
90impl fmt::Display for Span {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 let (line, col) = self.line_col();
93 write!(f, "{line}:{col}")
94 }
95}
96
97#[derive(Clone, PartialEq, Eq, Hash)]
99pub struct Ident {
100 pub name: String,
102 pub span: Span,
104}
105
106impl Ident {
107 #[must_use]
109 pub fn new(name: impl Into<String>, span: Span) -> Self {
110 Self {
111 name: name.into(),
112 span,
113 }
114 }
115
116 #[must_use]
118 pub fn dummy(name: impl Into<String>) -> Self {
119 Self {
120 name: name.into(),
121 span: Span::dummy(),
122 }
123 }
124}
125
126impl fmt::Debug for Ident {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 write!(f, "Ident({:?})", self.name)
129 }
130}
131
132impl fmt::Display for Ident {
133 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134 write!(f, "{}", self.name)
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141
142 #[test]
143 fn span_text() {
144 let source: Arc<str> = Arc::from("hello world");
145 let span = Span::new(0, 5, Arc::clone(&source));
146 assert_eq!(span.text(), "hello");
147 }
148
149 #[test]
150 fn span_line_col() {
151 let source: Arc<str> = Arc::from("line1\nline2\nline3");
152
153 let span = Span::new(0, 1, Arc::clone(&source));
155 assert_eq!(span.line_col(), (1, 1));
156
157 let span = Span::new(6, 7, Arc::clone(&source));
159 assert_eq!(span.line_col(), (2, 1));
160
161 let span = Span::new(14, 15, Arc::clone(&source));
163 assert_eq!(span.line_col(), (3, 3));
164 }
165
166 #[test]
167 fn span_merge() {
168 let source: Arc<str> = Arc::from("hello world");
169 let span1 = Span::new(0, 5, Arc::clone(&source));
170 let span2 = Span::new(6, 11, Arc::clone(&source));
171 let merged = span1.merge(&span2);
172 assert_eq!(merged.start, 0);
173 assert_eq!(merged.end, 11);
174 }
175
176 #[test]
177 fn ident_display() {
178 let ident = Ident::dummy("foo");
179 assert_eq!(format!("{ident}"), "foo");
180 }
181}