1pub use crate::input::{Input, Rewind, Show, ParserInfo};
2
3#[cfg(feature = "color")]
4use yansi::Paint;
5
6#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
7pub struct Span<'a> {
8 pub start: (usize, usize, usize),
10 pub end: (usize, usize, usize),
12 pub cursor: Option<char>,
14 pub snippet: Option<&'a str>,
16}
17
18const SNIPPET_LEN: usize = 30;
19
20impl<'a> Show for Span<'a> {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 let (a, b, _) = self.start;
23 let (c, d, _) = self.end;
24
25 if self.start == self.end {
26 write!(f, "{}:{}", a, b)?;
27 } else {
28 write!(f, "{}:{} to {}:{}", a, b, c, d)?;
29 }
30
31 let write_snippet = |f: &mut std::fmt::Formatter<'_>, snippet: &str| {
32 for c in snippet.escape_debug() { write!(f, "{}", c)?; }
33 Ok(())
34 };
35
36 if let Some(snippet) = self.snippet {
37 write!(f, " \"")?;
38 if snippet.len() > SNIPPET_LEN + 6 {
39 write_snippet(f, &snippet[..SNIPPET_LEN / 2])?;
40
41 #[cfg(feature = "color")]
42 write!(f, " {} ", "...".blue())?;
43
44 #[cfg(not(feature = "color"))]
45 write!(f, " ... ")?;
46
47 let end_start = snippet.len() - SNIPPET_LEN / 2;
48 write_snippet(f, &snippet[end_start..])?;
49 } else {
50 write_snippet(f, snippet)?;
51 }
52
53 if let Some(cursor) = self.cursor {
54 #[cfg(feature = "color")]
55 write!(f, "{}", cursor.escape_debug().blue())?;
56
57 #[cfg(not(feature = "color"))]
58 write!(f, "{}", cursor.escape_debug())?;
59 }
60
61 write!(f, "\"")?;
62 } else {
63 #[cfg(feature = "color")]
64 write!(f, " {}", "[EOF]".blue())?;
65
66 #[cfg(not(feature = "color"))]
67 write!(f, " [EOF]")?;
68 }
69
70 Ok(())
71 }
72}
73
74#[derive(Debug)]
75pub struct Text<'a> {
76 current: &'a str,
77 start: &'a str,
78}
79
80impl<'a> From<&'a str> for Text<'a> {
81 fn from(start: &'a str) -> Text<'a> {
82 Text { start, current: start }
83 }
84}
85
86impl Rewind for Text<'_> {
87 fn rewind_to(&mut self, marker: Self::Marker) {
88 self.current = &self.start[marker..];
89 }
90}
91
92impl<'a> Input for Text<'a> {
93 type Token = char;
94 type Slice = &'a str;
95 type Many = Self::Slice;
96
97 type Marker = usize;
98 type Context = Span<'a>;
99
100 fn token(&mut self) -> Option<Self::Token> {
102 self.current.token()
103 }
104
105 fn slice(&mut self, n: usize) -> Option<Self::Slice> {
107 self.current.slice(n)
108 }
109
110 fn peek<F>(&mut self, cond: F) -> bool
112 where F: FnMut(&Self::Token) -> bool
113 {
114 self.current.peek(cond)
115 }
116
117 fn peek_slice<F>(&mut self, n: usize, cond: F) -> bool
119 where F: FnMut(&Self::Slice) -> bool
120 {
121 self.current.peek_slice(n, cond)
122 }
123
124 fn eat<F>(&mut self, cond: F) -> Option<Self::Token>
127 where F: FnMut(&Self::Token) -> bool
128 {
129 self.current.eat(cond)
130 }
131
132 fn eat_slice<F>(&mut self, n: usize, cond: F) -> Option<Self::Slice>
135 where F: FnMut(&Self::Slice) -> bool
136 {
137 self.current.eat_slice(n, cond)
138 }
139
140 fn take<F>(&mut self, cond: F) -> Self::Many
143 where F: FnMut(&Self::Token) -> bool
144 {
145 self.current.take(cond)
146 }
147
148 fn skip<F>(&mut self, cond: F) -> usize
151 where F: FnMut(&Self::Token) -> bool
152 {
153 self.current.skip(cond)
154 }
155
156 fn has(&mut self, n: usize) -> bool {
158 self.current.has(n)
159 }
160
161 #[inline(always)]
162 fn mark(&mut self, _: &ParserInfo) -> Self::Marker {
163 self.start.len() - self.current.len()
164 }
165
166 fn context(&mut self, mark: Self::Marker) -> Self::Context {
167 let cursor = self.token();
168 let bytes_read = self.start.len() - self.current.len();
169 if bytes_read == 0 {
170 Span { start: (1, 1, 0), end: (1, 1, 0), snippet: None, cursor }
171 } else {
172 let start_offset = mark;
173 let end_offset = bytes_read;
174
175 let to_start_str = &self.start[..start_offset];
176 let (start_line, start_col) = line_col(to_start_str);
177 let start = (start_line, start_col, start_offset);
178
179 let to_current_str = &self.start[..bytes_read];
180 let (end_line, end_col) = line_col(to_current_str);
181 let end = (end_line, end_col, bytes_read);
182
183 let snippet = if end_offset <= self.start.len() {
184 Some(&self.start[start_offset..end_offset])
185 } else {
186 None
187 };
188
189 Span { start, end, cursor, snippet }
190 }
191 }
192}
193
194fn line_col(string: &str) -> (usize, usize) {
195 if string.is_empty() {
196 return (1, 1);
197 }
198
199 let (line_count, last_line) = string.lines().enumerate().last().unwrap();
200 if string.ends_with('\n') {
201 (line_count + 2, 1)
202 } else {
203 (line_count + 1, last_line.len() + 1)
204 }
205}