1use super::Error;
4use super::Str;
5use std::{borrow::Cow, rc::Rc};
6
7pub struct Lexer<'a> {
9 s: &'a str,
11 l: usize,
13 u: usize,
15 op: Rc<[Option<ClosingParen>]>,
17 op_i: usize,
19 errs: super::ErrorAccumulator<'a>,
21}
22
23#[derive(Clone, Copy, Debug, PartialEq)]
25pub struct ClosingParen {
26 source_idx: std::num::NonZeroUsize,
31 op_i: usize,
33}
34
35impl ClosingParen {
36 fn str<'a>(&self, source: &'a str) -> Str<'a> {
37 Str {
38 value: source,
39 start: self.source_idx.get(),
40 end: self.source_idx.get() + 1,
41 }
42 }
43}
44
45impl<'a> Lexer<'a> {
46 pub fn new(source: &'a str, errs: super::ErrorAccumulator<'a>) -> Self {
48 Self {
49 s: source,
50 l: 0,
51 u: source.len(),
52 op: Self::build(source),
53 op_i: 0,
54 errs,
55 }
56 }
57
58 pub fn split_nested(&mut self, closing_paren: Option<ClosingParen>) -> Self {
60 let inner = Self {
61 s: self.s,
62 l: self.l,
63 u: match closing_paren {
64 Some(c) => c.source_idx.get(),
65 None => self.u,
66 },
67 op: self.op.clone(),
68 op_i: self.op_i,
69 errs: self.errs.clone(),
70 };
71 (self.l, self.op_i) = match closing_paren {
72 Some(c) => (c.source_idx.get() + 1, c.op_i),
73 None => (self.u, self.op.len()),
74 };
75 inner
76 }
77
78 pub fn remaining_source(&self) -> Str<'a> {
79 Str {
80 value: self.s,
81 start: self.l,
82 end: self.u,
83 }
84 }
85 fn build(source: &'a str) -> Rc<[Option<ClosingParen>]> {
86 #[derive(Clone, Copy)]
87 enum State {
88 Regular,
89 Comment,
90 String,
92 StringEscaped,
94 }
95 struct Stack {
96 i: usize,
97 }
98 let mut v: Vec<Option<ClosingParen>> = vec![];
99 let mut stack = vec![];
100 let mut state = State::Regular;
101 let mut i = 0;
102 for c in source.chars() {
103 match (c, state) {
104 ('(' | '[', State::Regular) => {
105 stack.push(Stack { i: v.len() });
106 v.push(None);
107 }
108 (')' | ']', State::Regular) => {
109 if let Some(s) = stack.pop() {
110 v[s.i] = Some(ClosingParen {
111 source_idx: i.try_into().expect("i>0 because this character is preceded by a [ or ( that pushed to the stack"),
112 op_i: v.len(),
113 });
114 }
115 }
116 ('\n', State::Comment) => {
117 state = State::Regular;
118 }
119 ('#', State::Regular) => {
120 state = State::Comment;
121 }
122 ('"', State::Regular) => {
123 state = State::String;
124 }
125 ('"', State::String) => {
126 state = State::Regular;
127 }
128 ('\\', State::String) => {
129 state = State::StringEscaped;
130 }
131 (_, State::StringEscaped) => {
132 state = State::String;
133 }
134 (_, State::Regular | State::Comment | State::String) => {}
135 }
136 i += c.len_utf8();
137 }
138 v.into()
139 }
140}
141
142#[derive(Clone, Debug)]
144pub struct Token<'a> {
145 pub value: TokenValue<'a>,
146 pub source: Str<'a>,
147}
148
149#[derive(Clone, Debug, PartialEq)]
151pub enum TokenValue<'a> {
152 SquareOpen {
153 closing: Option<ClosingParen>,
158 },
159 SquareClose,
160 RoundOpen {
162 closing: Option<ClosingParen>,
167 },
168 RoundClose,
169 Comma,
170 Equal,
171 Keyword,
172 String(Cow<'a, str>),
173 Integer(i32),
174 Scaled(common::Scaled),
175 InfiniteGlue(common::Scaled, common::GlueOrder),
176 Comment,
177}
178
179impl<'a> Iterator for Lexer<'a> {
180 type Item = Token<'a>;
181
182 fn next(&mut self) -> Option<Token<'a>> {
183 let mut comment_start: Option<usize> = None;
185 while let Some(c) = self.s[self.l..self.u].chars().next() {
186 let should_skip = match c {
187 '\n' => {
188 if let Some(comment_start) = comment_start.take() {
189 return Some(Token {
190 value: TokenValue::Comment,
191 source: Str {
192 value: self.s,
193 start: comment_start,
194 end: self.l,
195 },
196 });
197 }
198 true
199 }
200 '#' => {
201 if comment_start.is_none() {
202 comment_start = Some(self.l + 1);
203 }
204 true
205 }
206 c => comment_start.is_some() || c.is_whitespace(),
207 };
208 if !should_skip {
209 break;
210 }
211 self.l += c.len_utf8();
212 }
213 let mut iter = self.s[self.l..self.u].chars();
215 let c = iter.next()?;
216 let start = self.l;
217 self.l += c.len_utf8();
218 use TokenValue::*;
219 let value = match c {
220 '[' | '(' => {
221 let closing = self.op.get(self.op_i).cloned().flatten();
222 let open = Str {
223 value: self.s,
224 start,
225 end: start + 1,
226 };
227 match &closing {
228 Some(closing) => {
229 let close = closing.str(self.s);
230 let want = if c == '[' { "]" } else { ")" };
231 if close.str() != want {
232 self.errs.add(Error::MismatchedBraces { open, close });
233 }
234 }
235 None => {
236 self.errs.add(Error::UnmatchedOpeningBracket { open });
237 }
238 };
239 self.op_i += 1;
240 if c == '[' {
241 SquareOpen { closing }
242 } else {
243 RoundOpen { closing }
244 }
245 }
246 ']' => SquareClose,
247 ')' => RoundClose,
248 '=' => Equal,
249 ',' => Comma,
250 'a'..='z' | 'A'..='Z' => {
251 while let Some(n @ 'a'..='z' | n @ 'A'..='Z' | n @ '_') = iter.next() {
252 self.l += n.len_utf8();
253 }
254 Keyword
255 }
256 '"' => {
257 let mut buf: std::string::String = Default::default();
259 loop {
260 let Some(n) = iter.next() else {
261 return None;
263 };
264 self.l += n.len_utf8();
265 let c: char = match n {
266 '"' => {
267 break;
268 }
269 '\\' => {
274 let Some(n) = iter.next() else {
275 return None;
277 };
278 self.l += n.len_utf8();
279 match n {
280 '\"' | '\'' | '\\' => n,
281 'n' => '\n',
282 't' => '\t',
283 '0' => '\0',
284 'r' => '\r',
285 'u' => {
286 if iter.next() != Some('{') {
287 continue;
289 }
290 self.l += '{'.len_utf8();
291 let mut i = 0;
292 let mut valid = true;
293 loop {
294 let Some(n) = iter.next() else {
295 return None;
297 };
298 self.l += n.len_utf8();
299 if n == '}' {
300 break;
302 }
303 match n.to_digit(16) {
304 None => {
305 valid = false;
306 }
307 Some(d) => {
308 i = i * 16 + d;
309 }
310 }
311 }
312 if !valid {
313 continue;
315 }
316 let Some(c) = char::from_u32(i) else {
317 continue;
319 };
320 c
321 }
322 _ => {
323 self.errs.add(Error::UnknownEscapeSequence {
324 sequence: Str {
325 value: self.s,
326 start: self.l - n.len_utf8() - 1,
327 end: self.l,
328 },
329 });
330 continue;
331 }
332 }
333 }
334 _ => n,
335 };
336 buf.push(c);
337 }
338 let source = &self.s[start + 1..self.l - 1];
341 String(if buf.len() == source.len() {
342 Cow::Borrowed(source)
343 } else {
344 Cow::Owned(buf)
345 })
346 }
347 '0'..='9' => {
348 let initial_value = (c as i32) - ('0' as i32);
349 self.parse_number(false, initial_value, start)
350 }
351 '-' => self.parse_number(true, 0, start),
352 _ => {
353 self.errs.add(Error::InvalidCharacter {
354 char: Str {
355 value: self.s,
356 start,
357 end: self.l,
358 },
359 });
360 return self.next();
361 }
362 };
363 Some(Token {
364 value,
365 source: Str {
366 value: self.s,
367 start,
368 end: self.l,
369 },
370 })
371 }
372}
373
374impl<'a> Lexer<'a> {
375 fn parse_number(
376 &mut self,
377 negative: bool,
378 initial_value: i32,
379 start_idx: usize,
380 ) -> TokenValue<'a> {
381 let mut iter = self.s[self.l..self.u].chars();
382 let mut n = initial_value;
383 let mut parsing_n = true;
384 let mut d = [0_u8; 17];
385 let mut next_d = 0_usize;
386 loop {
387 match iter.next() {
388 Some(c @ '0'..='9') => {
389 let i = (c as i32) - ('0' as i32);
390 if parsing_n {
391 n = n.checked_mul(10).unwrap();
392 n = n.checked_add(i).unwrap();
393 } else {
394 if let Some(d) = d.get_mut(next_d) {
395 *d = i.try_into().expect("i in [0,9]")
396 }
397 next_d += 1;
398 }
399 self.l += c.len_utf8();
400 }
401 Some(d @ '.') => {
402 if !parsing_n {
403 self.errs.add(Error::MultipleDecimalPoints {
404 point: Str {
405 value: self.s,
406 start: self.l,
407 end: self.l + d.len_utf8(),
408 },
409 });
410 }
411 parsing_n = false;
412 self.l += d.len_utf8();
413 }
414 Some(c @ 'a'..='z' | c @ 'A'..='Z') => {
415 let u = self.l;
416 self.l += c.len_utf8();
417 while let Some(n @ 'a'..='z' | n @ 'A'..='Z' | n @ '_') = iter.next() {
418 self.l += n.len_utf8();
419 }
420
421 let mut s = common::Scaled::from_decimal_digits(&d) + common::Scaled::ONE * n;
422 if negative {
423 s.0 *= -1;
424 }
425 let raw_unit = &self.s[u..self.l];
426 if let Some(unit) = common::ScaledUnit::parse(raw_unit) {
427 let mut s =
428 common::Scaled::new(n, common::Scaled::from_decimal_digits(&d), unit)
429 .unwrap();
430 if negative {
431 s = -s;
432 }
433 return TokenValue::Scaled(s);
434 }
435 if let Some(glue_order) = common::GlueOrder::parse(raw_unit) {
436 return TokenValue::InfiniteGlue(s, glue_order);
437 }
438 self.errs.add(Error::InvalidDimensionUnit {
439 dimension: Str {
440 value: self.s,
441 start: start_idx,
442 end: self.l,
443 },
444 unit: Str {
445 value: self.s,
446 start: u,
447 end: self.l,
448 },
449 });
450 return TokenValue::Scaled(common::Scaled::ZERO);
451 }
452 d => {
453 if !parsing_n {
454 self.errs.add(Error::NumberWithoutUnits {
455 number: Str {
456 value: self.s,
457 start: self.l,
458 end: self.l + d.map(|c| c.len_utf8()).unwrap_or(0),
459 },
460 });
461 return TokenValue::Scaled(common::Scaled::ZERO);
462 }
463 if negative {
464 n *= -1;
465 }
466 return TokenValue::Integer(n);
467 }
468 }
469 }
470 }
471}
472
473#[cfg(test)]
474mod tests {
475 use super::super::ErrorAccumulator;
476
477 use super::*;
478 fn run_lexer_test(input: &str, want: Vec<TokenValue>) {
479 let errs: ErrorAccumulator = Default::default();
480 let lexer = Lexer::new(&input, errs);
481
482 let got: Vec<TokenValue> = lexer.into_iter().map(|t| t.value).collect();
483
484 assert_eq!(got, want);
485 }
486
487 macro_rules! lexer_tests {
488 ( $( ($name: ident, $input: expr, $want: expr, ), )+ ) => {
489 $(
490 #[test]
491 fn $name() {
492 let input = $input;
493 let want = $want;
494 run_lexer_test(input, want);
495 }
496 )+
497 };
498 }
499
500 lexer_tests!(
501 (
502 string_simple,
503 r#" "string" "#,
504 vec![TokenValue::String("string".into())],
505 ),
506 (
507 string_with_special_char_1,
508 r#" "\"" "#,
509 vec![TokenValue::String("\"".into())],
510 ),
511 (
512 string_with_special_char_2,
513 r#" "\\" "#,
514 vec![TokenValue::String("\\".into())],
515 ),
516 (
517 string_with_invalid_special_char,
518 r#" "\a" "#,
519 vec![TokenValue::String("".into())],
520 ),
521 (
522 string_with_unicode,
523 r#" "\u{100}", "second" "#,
524 vec![
525 TokenValue::String("\u{100}".into()),
526 TokenValue::Comma,
527 TokenValue::String("second".into()),
528 ],
529 ),
530 );
531}