1use std::ops::RangeInclusive;
2
3#[derive(Clone, Copy, PartialEq)]
4pub struct Input<'a> {
5 string: &'a str,
6 pub index: usize,
7 line: usize,
8}
9
10impl<'a> Input<'a> {
11 pub fn new(string: &'a str) -> Self {
12 Self {
13 string,
14 index: 0,
15 line: 1,
16 }
17 }
18
19 pub fn span_to(&self, other: Self) -> Span<'a> {
20 let string = self.string;
21 let start = self.index;
22 let end = other.index;
23 let line = self.line;
24
25 Span { string, start, end, line }
26 }
27
28 pub fn advance(mut self, offset: usize) -> Self {
29 self.index += offset;
30 self
31 }
32
33 fn curr(&self) -> &'a str {
34 self.string.get(self.index..).unwrap_or("")
35 }
36
37 pub fn exact<E: Exact>(&self, exact: E) -> Option<(Self, ())> {
38 let curr = self.curr();
39 let rest = exact.exact(curr)?;
40 let delta = curr.len() - rest.len();
41 let interim = &curr[..delta];
42 let newlines = interim.chars().fold(0, |acc, c| if c == '\n' {
43 acc + 1
44 } else {
45 acc
46 });
47 let input = Self {
48 string: self.string,
49 index: self.index + delta,
50 line: self.line + newlines,
51 };
52 Some((input, ()))
53 }
54}
55
56impl<'a> std::fmt::Debug for Input<'a> {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 let slice = self.curr();
59 let slice = &slice[..slice.len().min(15)];
60 write!(f, "Input({:?} ({}) {:?})", self.index, self.line, slice)
61 }
62}
63
64#[test]
65fn test_debug_input() {
66 let string = "word\nword\nword";
67 let index = 5;
68 let line = 2;
69 let input = Input { string, index, line };
70 assert_eq!(format!("{:?}", &input), "Input(5 (2) \"word\\nword\")".to_string());
71}
72
73#[derive(Clone, Copy, PartialEq)]
74pub struct Span<'a> {
75 string: &'a str,
76 start: usize,
77 end: usize,
78 line: usize,
79}
80
81impl<'a> Span<'a> {
82 pub fn new(string: &'a str, start: usize, end: usize) -> Self {
83 let line = string
84 .get(..start)
85 .expect("Bad span.")
86 .chars()
87 .filter(|c| *c == '\n')
88 .count() + 1;
89 Self { string, start, end, line }
90 }
91
92 pub fn slice(&self) -> &'a str {
93 self.string.get(self.start..self.end).expect("Bad span.")
94 }
95
96 pub fn column(&self) -> usize {
97 let string = &self.string[..self.start];
98 let index = string.rfind('\n').map(|i| i + 1).unwrap_or(0);
99 self.start - index + 1
100 }
101
102 pub fn error(&self, message: &str) -> String {
103 let start = self.string
104 .get(..self.start)
105 .unwrap()
106 .rfind('\n')
107 .map(|i| i + 1)
108 .unwrap_or(0);
109 let end = self.string
110 .get(self.end..)
111 .unwrap()
112 .find('\n')
113 .unwrap_or(self.string.len());
114 format!(
115 "[Error {line}:{column}] {message}\n{content}\n{leading}{carets}",
116 line = self.line,
117 column = self.column(),
118 message = message,
119 content = &self.string[start..end],
120 leading = " ".repeat(self.column() - 1),
121 carets = "^".repeat(self.end - self.start),
122 )
123 }
124}
125
126#[test]
127fn test_span_new() {
128 assert_eq!(Span::new("Hello\nthere!", 6, 9).line, 2);
129}
130
131impl<'a> std::fmt::Debug for Span<'a> {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 let slice = self.slice();
134 write!(f, "Span({:?} ({}) {:?})", self.start..self.end, self.line, slice)
135 }
136}
137
138#[test]
139fn test_debug_span() {
140 let string = "word\nword\nword";
141 let start = 5;
142 let end = 9;
143 let line = 2;
144 let span = Span { string, start, end, line };
145 assert_eq!(format!("{:?}", span), "Span(5..9 (2) \"word\")".to_string());
146}
147
148pub trait Exact {
149 fn exact<'a>(&self, input: &'a str) -> Option<&'a str>;
150}
151
152impl Exact for char {
154 fn exact<'a>(&self, input: &'a str) -> Option<&'a str> {
155 input.strip_prefix(*self)
156 }
157}
158
159impl Exact for &str {
161 fn exact<'a>(&self, input: &'a str) -> Option<&'a str> {
162 input.strip_prefix(self)
163 }
164}
165
166impl Exact for RangeInclusive<char> {
168 fn exact<'a>(&self, input: &'a str) -> Option<&'a str> {
169 let c = input.chars().next()?;
170 if self.contains(&c) {
171 Some(&input[c.len_utf8()..])
172 } else {
173 None
174 }
175 }
176}
177
178impl<F> Exact for F
180where
181 F: Fn(char) -> bool
182{
183 fn exact<'a>(&self, input: &'a str) -> Option<&'a str> {
184 input.strip_prefix(self)
185 }
186}
187
188#[test]
189fn test_exact() {
190 let input = Input::new("1234");
191 let mut out = input;
192 out.index += 1;
193 assert_eq!(input.exact('0'..='9'), Some((out, ())));
194
195 let input = Input::new("Hello");
196 let mut out = input;
197 out.index += 1;
198 assert_eq!(input.exact('H'), Some((out, ())));
199
200 let input = Input::new("Hello");
201 let mut out = input;
202 out.index += 5;
203 assert_eq!(input.exact("Hello"), Some((out, ())));
204}
205
206#[test]
207fn test_span_to() {
208 let input = Input::new("1234");
209 let mut rest = input;
210 rest.index += 2;
211 let span = input.span_to(rest);
212 assert_eq!(span, Span {
213 string: "1234",
214 start: 0,
215 end: 2,
216 line: 1,
217 });
218}
219
220#[test]
221fn test_advance() {
222 let input = Input::new("1234");
223 assert_eq!(input.advance(2).index, 2);
224}