1use alloc::vec::Vec;
4
5#[derive(Clone, Debug)]
10pub enum StrPart<S, T> {
11 Str(S),
13 Term(T),
18 Char(char),
20}
21
22#[derive(Debug)]
28pub struct Token<S>(pub S, pub Tok<S>);
29
30#[derive(Debug)]
35pub enum Tok<S> {
36 Word,
38 Var,
40 Fmt,
42 Num,
44 Str(Vec<StrPart<S, Token<S>>>),
46 Sym,
48 Block(Vec<Token<S>>),
50}
51
52#[derive(Clone, Debug)]
56#[non_exhaustive]
57pub enum Expect<S> {
58 Digit,
60 Ident,
62 Delim(S),
64 Escape,
66 Unicode,
68 Token,
70}
71
72impl Expect<&str> {
73 pub fn as_str(&self) -> &'static str {
75 match self {
76 Self::Digit => "digit",
77 Self::Ident => "identifier",
78 Self::Delim("(") => "closing parenthesis",
79 Self::Delim("[") => "closing bracket",
80 Self::Delim("{") => "closing brace",
81 Self::Delim("\"") => "closing quote",
82 Self::Delim(_) => panic!(),
83 Self::Escape => "string escape sequence",
84 Self::Unicode => "4-digit hexadecimal UTF-8 code point",
85 Self::Token => "token",
86 }
87 }
88}
89
90pub type Error<S> = (Expect<S>, S);
92
93pub struct Lexer<S> {
95 i: S,
100 e: Vec<Error<S>>,
102}
103
104impl<'a> Lexer<&'a str> {
105 #[must_use]
107 pub fn new(i: &'a str) -> Self {
108 let e = Vec::new();
109 Self { i, e }
110 }
111
112 pub fn lex(mut self) -> Result<Vec<Token<&'a str>>, Vec<Error<&'a str>>> {
114 let tokens = self.tokens();
115 self.space();
116 if !self.i.is_empty() {
117 self.e.push((Expect::Token, self.i));
118 }
119
120 if self.e.is_empty() {
121 Ok(tokens)
122 } else {
123 Err(self.e)
124 }
125 }
126
127 fn next(&mut self) -> Option<char> {
128 let mut chars = self.i.chars();
129 let c = chars.next()?;
130 self.i = chars.as_str();
131 Some(c)
132 }
133
134 fn take(&mut self, len: usize) -> &'a str {
135 let (head, tail) = self.i.split_at(len);
136 self.i = tail;
137 head
138 }
139
140 fn trim(&mut self, f: impl FnMut(char) -> bool) {
141 self.i = self.i.trim_start_matches(f);
142 }
143
144 fn consumed(&mut self, skip: usize, f: impl FnOnce(&mut Self)) -> &'a str {
145 self.with_consumed(|l| {
146 l.i = &l.i[skip..];
147 f(l);
148 })
149 .0
150 }
151
152 fn with_consumed<T>(&mut self, f: impl FnOnce(&mut Self) -> T) -> (&'a str, T) {
153 let start = self.i;
154 let y = f(self);
155 (&start[..start.len() - self.i.len()], y)
156 }
157
158 fn space(&mut self) {
160 loop {
161 self.i = self.i.trim_start();
162 match self.i.strip_prefix('#') {
163 Some(comment) => self.i = comment,
164 None => break,
165 }
166 loop {
168 let no_newline = || (self.i, &self.i[self.i.len()..]);
169 let (before, after) = self.i.split_once('\n').unwrap_or_else(no_newline);
170 let before = before.strip_suffix('\r').unwrap_or(before);
171 self.i = after;
172 if before.chars().rev().take_while(|c| *c == '\\').count() % 2 == 0 {
174 break;
175 }
176 }
177 }
178 }
179
180 fn mod_then_ident(&mut self) {
181 self.ident0();
182 if let Some(rest) = self.i.strip_prefix("::") {
183 self.i = rest.strip_prefix(['@', '$']).unwrap_or(rest);
184 self.ident1();
185 }
186 }
187
188 fn ident0(&mut self) {
190 self.trim(|c: char| c.is_ascii_alphanumeric() || c == '_');
191 }
192
193 fn ident1(&mut self) {
195 let first = |c: char| c.is_ascii_alphabetic() || c == '_';
196 if let Some(rest) = self.i.strip_prefix(first) {
197 self.i = rest;
198 self.ident0();
199 } else {
200 self.e.push((Expect::Ident, self.i));
201 }
202 }
203
204 fn digits1(&mut self) {
206 if let Some(rest) = self.i.strip_prefix(|c: char| c.is_ascii_digit()) {
207 self.i = rest.trim_start_matches(|c: char| c.is_ascii_digit());
208 } else {
209 self.e.push((Expect::Digit, self.i));
210 }
211 }
212
213 fn num(&mut self) {
215 self.trim(|c| c.is_ascii_digit());
216 if let Some(i) = self.i.strip_prefix('.') {
217 self.i = i;
218 self.digits1();
219 }
220 if let Some(i) = self.i.strip_prefix(['e', 'E']) {
221 self.i = i.strip_prefix(['+', '-']).unwrap_or(i);
222 self.digits1();
223 }
224 }
225
226 fn escape(&mut self) -> Option<StrPart<&'a str, Token<&'a str>>> {
227 let mut chars = self.i.chars();
228 let part = match chars.next() {
229 Some(c @ ('\\' | '/' | '"')) => StrPart::Char(c),
230 Some('b') => StrPart::Char('\x08'),
231 Some('f') => StrPart::Char('\x0C'),
232 Some('n') => StrPart::Char('\n'),
233 Some('r') => StrPart::Char('\r'),
234 Some('t') => StrPart::Char('\t'),
235 Some('u') => {
236 let err_at = |lex: &mut Self, pos| {
237 lex.i = pos;
238 lex.e.push((Expect::Unicode, lex.i));
239 None
240 };
241 let mut hex = 0;
242 let start_i = chars.as_str();
243 for _ in 0..4 {
244 let cur_i = chars.as_str();
245 if let Some(digit) = chars.next().and_then(|c| c.to_digit(16)) {
246 hex = (hex << 4) + digit;
247 } else {
248 return err_at(self, cur_i);
249 }
250 }
251 match char::from_u32(hex) {
252 None => return err_at(self, start_i),
253 Some(c) => StrPart::Char(c),
254 }
255 }
256 Some('(') => {
257 let (full, block) = self.with_consumed(Self::block);
258 return Some(StrPart::Term(Token(full, block)));
259 }
260 Some(_) | None => {
261 self.e.push((Expect::Escape, self.i));
262 return None;
263 }
264 };
265
266 self.i = chars.as_str();
267 Some(part)
268 }
269
270 fn str(&mut self) -> Tok<&'a str> {
274 let start = self.take(1);
275 assert_eq!(start, "\"");
276 let mut parts = Vec::new();
277
278 loop {
279 let s = self.consumed(0, |lex| lex.trim(|c| c != '\\' && c != '"'));
280 if !s.is_empty() {
281 parts.push(StrPart::Str(s));
282 }
283 match self.next() {
284 Some('"') => break,
285 Some('\\') => self.escape().map(|part| parts.push(part)),
286 Some(_) => unreachable!(),
288 None => {
289 self.e.push((Expect::Delim(start), self.i));
290 break;
291 }
292 };
293 }
294 Tok::Str(parts)
295 }
296
297 fn token(&mut self) -> Option<Token<&'a str>> {
298 self.space();
299
300 let hd_op = |c| "|=!<>+-*/%".contains(c);
301 let tl_op = |c| hd_op(c) && c != '-';
302
303 let mut chars = self.i.chars();
304 let (s, tok) = match chars.next()? {
305 'a'..='z' | 'A'..='Z' | '_' => (self.consumed(1, Self::mod_then_ident), Tok::Word),
306 '$' => (self.consumed(1, Self::ident1), Tok::Var),
307 '@' => (self.consumed(1, Self::ident1), Tok::Fmt),
308 '0'..='9' => (self.consumed(1, Self::num), Tok::Num),
309 c if hd_op(c) => (self.consumed(1, |lex| lex.trim(tl_op)), Tok::Sym),
310 '.' => match chars.next() {
311 Some('.') => (self.take(2), Tok::Sym),
312 Some('a'..='z' | 'A'..='Z' | '_') => (self.consumed(2, Self::ident0), Tok::Sym),
313 _ => (self.take(1), Tok::Sym),
314 },
315 ':' | ';' | ',' | '?' => (self.take(1), Tok::Sym),
316 '"' => self.with_consumed(Self::str),
317 '(' | '[' | '{' => self.with_consumed(Self::block),
318 _ => return None,
319 };
320 Some(Token(s, tok))
321 }
322
323 fn tokens(&mut self) -> Vec<Token<&'a str>> {
324 core::iter::from_fn(|| self.token()).collect()
325 }
326
327 fn block(&mut self) -> Tok<&'a str> {
331 let open = self.take(1);
332 let close = match open {
333 "(" => ')',
334 "[" => ']',
335 "{" => '}',
336 _ => panic!(),
337 };
338 let mut tokens = self.tokens();
339
340 self.space();
341 if let Some(rest) = self.i.strip_prefix(close) {
342 tokens.push(Token(&self.i[..1], Tok::Sym));
343 self.i = rest;
344 } else {
345 self.e.push((Expect::Delim(open), self.i));
346 }
347 Tok::Block(tokens)
348 }
349}
350
351impl<'a> Token<&'a str> {
352 pub fn opt_as_str(found: Option<&Self>, code: &'a str) -> &'a str {
356 found.map_or(&code[code.len()..], |found| found.as_str())
357 }
358
359 pub fn as_str(&self) -> &'a str {
361 self.0
362 }
363}