1use std::error::Error;
10use std::fmt::{Display, Formatter, Result as FmtResult};
11use std::mem::take;
12use std::sync::Arc;
13
14use crate::{
15 Delimiter, DelimiterKind, DoubleSpan, GENERATED_OPERATOR_SPELLINGS,
16 GENERATED_POSTFIX_OPERATOR_SPELLINGS, GENERATED_PUNCTUATION_SPELLINGS,
17 GENERATED_WORD_TOKEN_SPELLINGS, IdentToken, LiteralKind, LiteralToken, Offset, Punct,
18 Recovered, Source, Span, Spanned, TokenGroup, TokenStream, TokenTree, Trivia, TriviaKind,
19};
20
21const MAX_GROUP_DEPTH: usize = 256;
22
23pub(crate) fn canonical_word_spelling(spelling: &str) -> &str {
26 let Some(word) = spelling.strip_prefix("Core$") else {
27 return spelling;
28 };
29 if GENERATED_WORD_TOKEN_SPELLINGS.contains(&word) {
30 word
31 } else {
32 spelling
33 }
34}
35
36#[doc(hidden)]
37pub fn __qualified_identifier_spans(spelling: &str, span: &Span) -> (Option<Span>, Option<Span>) {
38 let components = spelling.match_indices('$').collect::<Vec<_>>();
39 let subspan = |start: usize, end: usize| span.subspan(start, end);
40 match components.as_slice() {
41 [(separator, _)] => {
42 let left = &spelling[..*separator];
43 let right_start = separator + 1;
44 let right = &spelling[right_start..];
45 if is_bare_symbol_name(left) && is_bare_symbol_name(right) {
46 (Some(subspan(0, *separator)), None)
47 } else if is_bare_symbol_name(left) && is_decimal_index(right) {
48 (None, Some(subspan(right_start, spelling.len())))
49 } else {
50 (None, None)
51 }
52 }
53 [(package_separator, _), (index_separator, _)] => {
54 let package = &spelling[..*package_separator];
55 let name_start = package_separator + 1;
56 let name = &spelling[name_start..*index_separator];
57 let index_start = index_separator + 1;
58 let index = &spelling[index_start..];
59 if is_bare_symbol_name(package) && is_bare_symbol_name(name) && is_decimal_index(index)
60 {
61 (
62 Some(subspan(0, *package_separator)),
63 Some(subspan(index_start, spelling.len())),
64 )
65 } else {
66 (None, None)
67 }
68 }
69 _ => (None, None),
70 }
71}
72
73fn is_bare_symbol_name(spelling: &str) -> bool {
74 let Some(first) = spelling.as_bytes().first().copied() else {
75 return false;
76 };
77 is_identifier_start(first)
78 && spelling
79 .as_bytes()
80 .iter()
81 .copied()
82 .all(|byte| byte != b'$' && is_identifier_continue(byte))
83}
84
85fn is_decimal_index(spelling: &str) -> bool {
86 !spelling.is_empty() && spelling.bytes().all(|byte| byte.is_ascii_digit())
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
90pub enum LexErrorKind {
92 InvalidCharacter,
94 InvalidEscape,
96 InvalidNumber,
98 InvalidUtf8,
100 NestingLimitExceeded,
102 UnexpectedClosingDelimiter,
104 UnterminatedGroup,
106 UnterminatedString,
108 UnterminatedRawString,
110 UnterminatedBlockComment,
112}
113
114#[derive(Debug, Clone, PartialEq, Eq, Hash, Spanned)]
115pub struct LexError {
117 kind: LexErrorKind,
118 delimiter: Option<DelimiterKind>,
119 span: Span,
120}
121
122impl LexError {
123 pub fn kind(&self) -> LexErrorKind {
125 self.kind
126 }
127
128 pub fn unclosed_expression(&self) -> Option<&'static str> {
130 self.delimiter.map(DelimiterKind::expression_name)
131 }
132
133 pub fn expected(&self) -> Option<&'static str> {
135 self.delimiter.map(DelimiterKind::closing)
136 }
137
138 pub fn found(&self) -> Option<&'static str> {
140 self.delimiter.map(|_| "end of input")
141 }
142}
143
144impl Display for LexError {
145 fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
146 let message = match self.kind {
147 LexErrorKind::InvalidCharacter => "invalid character",
148 LexErrorKind::InvalidEscape => "invalid string escape",
149 LexErrorKind::InvalidNumber => "invalid number literal",
150 LexErrorKind::InvalidUtf8 => "source is not valid UTF-8",
151 LexErrorKind::NestingLimitExceeded => "delimiter nesting limit exceeded",
152 LexErrorKind::UnexpectedClosingDelimiter => "unexpected closing delimiter",
153 LexErrorKind::UnterminatedGroup => {
154 if let Some(delimiter) = self.delimiter {
155 return write!(
156 formatter,
157 "unclosed {}: expected `{}`, found end of input",
158 delimiter.expression_name(),
159 delimiter.closing(),
160 );
161 }
162 "unterminated delimited group"
163 }
164 LexErrorKind::UnterminatedString => "unterminated string literal",
165 LexErrorKind::UnterminatedRawString => "unterminated raw string literal",
166 LexErrorKind::UnterminatedBlockComment => "unterminated block comment",
167 };
168 formatter.write_str(message)
169 }
170}
171
172impl Error for LexError {}
173
174pub fn lex<I>(bytes: I) -> Result<TokenStream, LexError>
185where
186 I: IntoIterator<Item = u8>,
187{
188 lexer_from_bytes(bytes)?.lex()
189}
190
191pub fn lex_recovering<I>(bytes: I) -> Result<Recovered<TokenStream, LexError>, LexError>
203where
204 I: IntoIterator<Item = u8>,
205{
206 lexer_from_bytes(bytes)?.lex_recovering()
207}
208
209pub fn lex_str(source: &str) -> Result<TokenStream, LexError> {
215 NativeLexer::new(Arc::<str>::from(source)).lex()
216}
217
218pub fn lex_str_recovering(source: &str) -> Result<Recovered<TokenStream, LexError>, LexError> {
225 NativeLexer::new(Arc::<str>::from(source)).lex_recovering()
226}
227
228#[doc(hidden)]
241pub fn __quote_fragment(source: &str) -> TokenStream {
242 NativeLexer::new(Arc::<str>::from(source))
243 .lex()
244 .unwrap_or_else(|error| panic!("invalid static M2 quote fragment: {error}"))
245}
246
247fn lexer_from_bytes<I>(bytes: I) -> Result<NativeLexer, LexError>
248where
249 I: IntoIterator<Item = u8>,
250{
251 let bytes = bytes.into_iter().collect::<Vec<_>>();
252 let len = bytes.len();
253 let source = String::from_utf8(bytes).map_err(|_| LexError {
254 kind: LexErrorKind::InvalidUtf8,
255 delimiter: None,
256 span: Span::ranged(Source::new(""), 0..len),
257 })?;
258 Ok(NativeLexer::new(Arc::<str>::from(source)))
259}
260
261struct NativeLexer {
262 source: Arc<str>,
263 source_span: Span,
264 offset: Offset,
265}
266
267impl NativeLexer {
268 fn new(source: Arc<str>) -> Self {
269 let source_span = Span::ranged(Source::new(Arc::clone(&source)), 0..source.len());
270 Self {
271 source,
272 source_span,
273 offset: 0,
274 }
275 }
276
277 fn lex(self) -> Result<TokenStream, LexError> {
278 let recovered = self.lex_recovering()?;
279 match recovered.diagnostics.into_iter().next() {
280 Some(error) => Err(error),
281 None => Ok(recovered.value),
282 }
283 }
284
285 fn lex_recovering(mut self) -> Result<Recovered<TokenStream, LexError>, LexError> {
286 let mut output = TokenStream::new();
287 let mut groups = Vec::<OpenGroup>::new();
288 let mut quote_next = false;
293 loop {
294 let Some(first) = self.peek(0) else {
295 output.set_physical_eof(self.source_span.at(self.source.len()));
296 let mut diagnostics = Vec::with_capacity(groups.len());
297 while let Some(group) = groups.pop() {
298 diagnostics.push(LexError {
299 kind: LexErrorKind::UnterminatedGroup,
300 delimiter: Some(group.kind),
301 span: self.span_from(group.opening_start),
302 });
303 }
304 return Ok(Recovered::new(output, diagnostics));
305 };
306
307 if first == b'\n' || (first == b'\r' && self.byte_after_current() == Some(b'\n')) {
308 self.lex_line_break(&mut output)?;
309 } else if first == b'\r' {
310 self.lex_carriage_return(&mut output)?;
311 } else if matches!(first, b' ' | b'\t') {
312 self.lex_whitespace(&mut output)?;
313 } else if let Some(width) = self.math_operator_width() {
314 self.lex_math_operator(width, &mut output)?;
315 quote_next = false;
316 } else if is_identifier_start(first) {
317 let quoted = take(&mut quote_next);
318 self.lex_identifier(&mut output)?;
319 quote_next = !quoted && last_token_quotes_next(&output);
320 } else if first.is_ascii_digit()
321 || (first == b'.'
322 && self
323 .byte_after_current()
324 .is_some_and(|byte| byte.is_ascii_digit()))
325 {
326 self.lex_number(&mut output)?;
327 quote_next = false;
328 } else if first == b'"' {
329 self.lex_string(&mut output)?;
330 quote_next = false;
331 } else if let Some(fixed) = self.match_fixed() {
332 let quoted = take(&mut quote_next);
333 match fixed.kind {
334 FixedKind::Open(_) | FixedKind::Close(_) if quoted => {
337 self.lex_punctuation(fixed.len, &mut output)?;
338 }
339 FixedKind::Punctuation => {
340 self.lex_punctuation(fixed.len, &mut output)?;
341 }
342 FixedKind::LineComment => {
345 self.lex_line_comment(fixed.len, &mut output)?;
346 quote_next = quoted;
347 }
348 FixedKind::BlockComment => {
349 self.lex_block_comment(fixed.len, &mut output)?;
350 quote_next = quoted;
351 }
352 FixedKind::RawString => {
353 self.lex_raw_string(fixed.len, &mut output)?;
354 }
355 FixedKind::Open(kind) => {
356 if groups.len() == MAX_GROUP_DEPTH {
357 return Err(self.error(LexErrorKind::NestingLimitExceeded, self.offset));
358 }
359 let opening_start = self.offset;
360 self.take_exact(fixed.len, opening_start)?;
361 let opening = self.span_from(opening_start);
362 groups.push(OpenGroup {
363 kind,
364 opening_start,
365 });
366 output.extend([TokenGroup::new(
367 Delimiter::new(kind, DoubleSpan::unclosed(opening)),
368 TokenStream::new(),
369 )]);
370 }
371 FixedKind::Close(kind) => {
372 let closing_start = self.offset;
373 self.take_exact(fixed.len, closing_start)?;
374 let closing = self.span_from(closing_start);
375 let Some(group) = groups.pop() else {
376 return Err(
377 self.error(LexErrorKind::UnexpectedClosingDelimiter, closing_start)
378 );
379 };
380 if group.kind != kind {
381 return Err(
382 self.error(LexErrorKind::UnexpectedClosingDelimiter, closing_start)
383 );
384 }
385 assert!(
386 output.complete_last_group(kind, closing),
387 "the open-group stack and token stream must agree"
388 );
389 }
390 }
391 } else {
392 let start = self.offset;
393 self.next();
394 return Err(self.error(LexErrorKind::InvalidCharacter, start));
395 }
396 }
397 }
398
399 fn lex_line_break(&mut self, _output: &mut TokenStream) -> Result<(), LexError> {
400 let start = self.offset;
401 let first = self.next_or_error(LexErrorKind::InvalidCharacter, start)?;
402 let mut bytes = vec![first];
403 if first == b'\r' && self.peek(0) == Some(b'\n') {
404 bytes.push(self.next_or_error(LexErrorKind::InvalidCharacter, start)?);
405 }
406 let _ = bytes;
407 Ok(())
408 }
409
410 fn lex_carriage_return(&mut self, _output: &mut TokenStream) -> Result<(), LexError> {
411 let start = self.offset;
412 let byte = self.next_or_error(LexErrorKind::InvalidCharacter, start)?;
413 let _ = byte;
414 Ok(())
415 }
416
417 fn lex_whitespace(&mut self, _output: &mut TokenStream) -> Result<(), LexError> {
418 let bytes = self.take_while(|byte| matches!(byte, b' ' | b'\t'));
419 let _ = bytes;
420 Ok(())
421 }
422
423 fn lex_identifier(&mut self, output: &mut TokenStream) -> Result<(), LexError> {
424 let start = self.offset;
425 let mut bytes = vec![self.next_or_error(LexErrorKind::InvalidUtf8, start)?];
426 loop {
427 let Some(byte) = self.peek(0) else {
428 break;
429 };
430 if !is_identifier_continue(byte) || self.math_operator_width().is_some() {
431 break;
432 }
433 bytes.push(self.next_or_error(LexErrorKind::InvalidUtf8, start)?);
434 }
435 let _ = bytes;
436 output.extend([TokenTree::Ident(IdentToken::from_span(
437 self.span_from(start),
438 ))]);
439 Ok(())
440 }
441
442 fn lex_number(&mut self, output: &mut TokenStream) -> Result<(), LexError> {
443 let start = self.offset;
444 let mut bytes = Vec::new();
445 let mut decimal = true;
446
447 if self.peek(0) == Some(b'0') {
448 bytes.push(self.next_or_error(LexErrorKind::InvalidNumber, start)?);
449 if let Some(radix) = self.number_radix() {
450 decimal = false;
451 bytes.push(self.next_or_error(LexErrorKind::InvalidNumber, start)?);
452 bytes.extend(self.take_while(|byte| radix.is_digit(byte)));
453 } else {
454 bytes.extend(self.take_while(|byte| byte.is_ascii_digit()));
455 }
456 } else {
457 bytes.extend(self.take_while(|byte| byte.is_ascii_digit()));
458 }
459
460 let next = self.peek(0);
461 let begins_float = decimal
462 && (next == Some(b'.') && self.byte_after_current() != Some(b'.')
463 || matches!(next, Some(b'p' | b'e' | b'E')));
464 let kind = if begins_float {
465 if self.peek(0) == Some(b'.') {
466 bytes.push(self.next_or_error(LexErrorKind::InvalidNumber, start)?);
467 bytes.extend(self.take_while(|byte| byte.is_ascii_digit()));
468 }
469 if self.peek(0) == Some(b'p') {
470 bytes.push(self.next_or_error(LexErrorKind::InvalidNumber, start)?);
471 if !self.peek(0).is_some_and(|byte| byte.is_ascii_digit()) {
472 return Err(self.error(LexErrorKind::InvalidNumber, start));
473 }
474 bytes.extend(self.take_while(|byte| byte.is_ascii_digit()));
475 }
476 if matches!(self.peek(0), Some(b'e' | b'E')) {
477 bytes.push(self.next_or_error(LexErrorKind::InvalidNumber, start)?);
478 if matches!(self.peek(0), Some(b'+' | b'-')) {
479 bytes.push(self.next_or_error(LexErrorKind::InvalidNumber, start)?);
480 }
481 if !self.peek(0).is_some_and(|byte| byte.is_ascii_digit()) {
482 return Err(self.error(LexErrorKind::InvalidNumber, start));
483 }
484 bytes.extend(self.take_while(|byte| byte.is_ascii_digit()));
485 }
486 LiteralKind::Float
487 } else {
488 LiteralKind::Integer
489 };
490
491 let _ = bytes;
492 output.extend([TokenTree::Literal(LiteralToken::from_span(
493 kind,
494 self.span_from(start),
495 ))]);
496 Ok(())
497 }
498
499 fn number_radix(&mut self) -> Option<NumberRadix> {
500 let marker = self.peek(0)?;
501 let digit = self.peek(1);
502 match marker {
503 b'b' | b'B' if digit.is_some_and(|byte| NumberRadix::Binary.is_digit(byte)) => {
504 Some(NumberRadix::Binary)
505 }
506 b'o' | b'O' if digit.is_some_and(|byte| NumberRadix::Octal.is_digit(byte)) => {
507 Some(NumberRadix::Octal)
508 }
509 b'x' | b'X' if digit.is_some_and(|byte| NumberRadix::Hexadecimal.is_digit(byte)) => {
510 Some(NumberRadix::Hexadecimal)
511 }
512 _ => None,
513 }
514 }
515
516 fn lex_string(&mut self, output: &mut TokenStream) -> Result<(), LexError> {
517 let start = self.offset;
518 let mut bytes = vec![self.next_or_error(LexErrorKind::UnterminatedString, start)?];
519
520 loop {
521 match self.peek(0) {
522 None => return Err(self.error(LexErrorKind::UnterminatedString, start)),
523 Some(b'"') => {
524 bytes.push(self.next_or_error(LexErrorKind::UnterminatedString, start)?);
525 let _ = bytes;
526 output.extend([TokenTree::Literal(LiteralToken::from_span(
527 LiteralKind::String,
528 self.span_from(start),
529 ))]);
530 return Ok(());
531 }
532 Some(b'\\') => {
533 let escape_start = self.offset;
534 bytes.push(self.next_or_error(LexErrorKind::UnterminatedString, start)?);
535 let Some(escaped) = self.peek(0) else {
536 return Err(self.error(LexErrorKind::UnterminatedString, start));
537 };
538 bytes.push(self.next_or_error(LexErrorKind::UnterminatedString, start)?);
539 match escaped {
540 b'a' | b'b' | b'e' | b'E' | b'f' | b'r' | b't' | b'v' | b'n' | b'"'
541 | b'\\' => {}
542 b'0'..=b'7' => {
543 for _ in 0..2 {
544 if !matches!(self.peek(0), Some(b'0'..=b'7')) {
545 break;
546 }
547 bytes.push(
548 self.next_or_error(LexErrorKind::InvalidEscape, escape_start)?,
549 );
550 }
551 }
552 b'x' => self.take_hex_escape(&mut bytes, 2, escape_start)?,
553 b'u' => self.take_hex_escape(&mut bytes, 4, escape_start)?,
554 _ => return Err(self.error(LexErrorKind::InvalidEscape, escape_start)),
555 }
556 }
557 Some(_) => bytes.push(self.next_or_error(LexErrorKind::UnterminatedString, start)?),
558 }
559 }
560 }
561
562 fn take_hex_escape(
563 &mut self,
564 bytes: &mut Vec<u8>,
565 digits: usize,
566 start: Offset,
567 ) -> Result<(), LexError> {
568 for _ in 0..digits {
569 if !self.peek(0).is_some_and(|byte| byte.is_ascii_hexdigit()) {
570 return Err(self.error(LexErrorKind::InvalidEscape, start));
571 }
572 bytes.push(self.next_or_error(LexErrorKind::InvalidEscape, start)?);
573 }
574 Ok(())
575 }
576
577 fn lex_raw_string(
578 &mut self,
579 opener_len: usize,
580 output: &mut TokenStream,
581 ) -> Result<(), LexError> {
582 let start = self.offset;
583 let mut bytes = self.take_exact(opener_len, start)?;
584
585 loop {
586 if self.peek(0).is_none() {
587 return Err(self.error(LexErrorKind::UnterminatedRawString, start));
588 }
589 if self.peek(0) == Some(b'/') {
590 match self.slash_run_len() {
591 3 => {
592 bytes.extend(self.take_exact(3, start)?);
593 let _ = bytes;
594 output.extend([TokenTree::Literal(LiteralToken::from_span(
595 LiteralKind::RawString,
596 self.span_from(start),
597 ))]);
598 return Ok(());
599 }
600 0 => return Err(self.error(LexErrorKind::UnterminatedRawString, start)),
601 1 => {
602 bytes.push(self.next_or_error(LexErrorKind::UnterminatedRawString, start)?);
603 }
604 _ => bytes.extend(self.take_exact(2, start)?),
605 }
606 } else {
607 bytes.push(self.next_or_error(LexErrorKind::UnterminatedRawString, start)?);
608 }
609 }
610 }
611
612 fn lex_line_comment(
613 &mut self,
614 opener_len: usize,
615 output: &mut TokenStream,
616 ) -> Result<(), LexError> {
617 let start = self.offset;
618 let mut bytes = self.take_exact(opener_len, start)?;
619 loop {
620 match self.peek(0) {
621 None | Some(b'\n') => break,
622 Some(b'\r') if self.byte_after_current() == Some(b'\n') => break,
623 Some(_) => bytes.push(self.next_or_error(LexErrorKind::InvalidCharacter, start)?),
624 }
625 }
626 let _ = bytes;
627 output.push_line_comment(Trivia::from_span(
628 TriviaKind::LineComment,
629 self.span_from(start),
630 ));
631 Ok(())
632 }
633
634 fn lex_block_comment(
635 &mut self,
636 opener_len: usize,
637 output: &mut TokenStream,
638 ) -> Result<(), LexError> {
639 let start = self.offset;
640 let mut bytes = self.take_exact(opener_len, start)?;
641 loop {
642 if self.peek(0).is_none() {
643 return Err(self.error(LexErrorKind::UnterminatedBlockComment, start));
644 }
645 if self.peek(0) == Some(b'*') && self.byte_after_current() == Some(b'-') {
646 bytes.extend(self.take_exact(2, start)?);
647 let _ = bytes;
648 output.push_block_comment(Trivia::from_span(
649 TriviaKind::BlockComment,
650 self.span_from(start),
651 ));
652 return Ok(());
653 }
654 bytes.push(self.next_or_error(LexErrorKind::UnterminatedBlockComment, start)?);
655 }
656 }
657
658 fn match_fixed(&mut self) -> Option<FixedMatch> {
659 fixed_spellings()
660 .filter(|(spelling, _)| {
661 spelling
662 .bytes()
663 .enumerate()
664 .all(|(distance, expected)| self.peek(distance) == Some(expected))
665 })
666 .max_by_key(|(spelling, _)| spelling.len())
667 .map(|(spelling, kind)| FixedMatch {
668 len: spelling.len(),
669 kind,
670 })
671 }
672
673 fn math_operator_width(&mut self) -> Option<usize> {
674 let first = self.peek(0)?;
675 if !matches!(first, 0xc2 | 0xc3 | 0xe2) {
676 return None;
677 }
678 self.peek(1)
679 .filter(|second| is_math_operator_pair(first, *second))
680 .and_then(|_| utf8_char_width(first))
681 }
682
683 fn lex_math_operator(
684 &mut self,
685 width: usize,
686 output: &mut TokenStream,
687 ) -> Result<(), LexError> {
688 let start = self.offset;
689 let mut bytes = self.take_exact(width, start)?;
690 if self.peek(0) == Some(b'=') {
691 bytes.push(self.next_or_error(LexErrorKind::InvalidUtf8, start)?);
692 }
693 self.emit_punctuation(bytes, start, output)
694 }
695
696 fn lex_punctuation(&mut self, len: usize, output: &mut TokenStream) -> Result<(), LexError> {
697 let start = self.offset;
698 let bytes = self.take_exact(len, start)?;
699 self.emit_punctuation(bytes, start, output)
700 }
701
702 fn emit_punctuation(
703 &self,
704 bytes: Vec<u8>,
705 start: Offset,
706 output: &mut TokenStream,
707 ) -> Result<(), LexError> {
708 let _ = bytes;
709 output.extend([TokenTree::Punct(Punct::from_span(self.span_from(start)))]);
710 Ok(())
711 }
712
713 fn byte_after_current(&mut self) -> Option<u8> {
714 self.peek(1)
715 }
716
717 fn slash_run_len(&mut self) -> usize {
718 let mut len = 0;
719 while len < 4 && self.peek(len) == Some(b'/') {
720 len += 1;
721 }
722 len
723 }
724
725 fn take_while(&mut self, predicate: impl Fn(u8) -> bool) -> Vec<u8> {
726 let mut bytes = Vec::new();
727 while self.peek(0).is_some_and(&predicate) {
728 let Some(byte) = self.next() else {
729 break;
730 };
731 bytes.push(byte);
732 }
733 bytes
734 }
735
736 fn take_exact(&mut self, len: usize, start: Offset) -> Result<Vec<u8>, LexError> {
737 let mut bytes = Vec::with_capacity(len);
738 for _ in 0..len {
739 let Some(byte) = self.next() else {
740 return Err(self.error(LexErrorKind::InvalidUtf8, start));
741 };
742 bytes.push(byte);
743 }
744 Ok(bytes)
745 }
746
747 fn next(&mut self) -> Option<u8> {
748 let byte = self.peek(0)?;
749 self.offset += 1;
750 Some(byte)
751 }
752
753 fn peek(&self, distance: usize) -> Option<u8> {
754 self.offset
755 .checked_add(distance)
756 .and_then(|offset| self.source.as_bytes().get(offset))
757 .copied()
758 }
759
760 fn next_or_error(&mut self, kind: LexErrorKind, start: Offset) -> Result<u8, LexError> {
761 match self.next() {
762 Some(byte) => Ok(byte),
763 None => Err(self.error(kind, start)),
764 }
765 }
766
767 fn error(&self, kind: LexErrorKind, start: Offset) -> LexError {
768 LexError {
769 kind,
770 delimiter: None,
771 span: self.span_from(start),
772 }
773 }
774
775 fn span_from(&self, start: Offset) -> Span {
777 self.source_span.subspan(start, self.offset)
778 }
779}
780
781#[derive(Debug, Clone, Copy, PartialEq, Eq)]
782enum RequiredClause {
783 Then,
784 LoopBody,
785 ExceptBody,
786}
787
788pub(crate) fn newline_ends_cell(stream: &TokenStream) -> bool {
789 let mut required_clauses = Vec::new();
790 let mut last_text = None;
791 let mut quote_next = false;
792
793 for tree in stream.iter() {
794 let text = match tree {
795 TokenTree::Ident(token) => Some(token.text()),
796 TokenTree::Punct(token) => Some(token.text()),
797 TokenTree::Literal(_) | TokenTree::Group(_) => {
798 quote_next = false;
799 last_text = None;
800 None
801 }
802 };
803
804 let Some(text) = text else {
805 continue;
806 };
807 if quote_next {
808 quote_next = false;
809 last_text = None;
810 continue;
811 }
812 let text = canonical_word_spelling(text);
813 last_text = Some(text);
814 if is_quote_specifier(text) {
815 quote_next = true;
816 continue;
817 }
818 match text {
819 "if" => required_clauses.push(RequiredClause::Then),
820 "for" | "while" => required_clauses.push(RequiredClause::LoopBody),
821 "except" => required_clauses.push(RequiredClause::ExceptBody),
822 "then" => {
823 remove_last_clause(&mut required_clauses, RequiredClause::Then);
824 }
825 "list" => {
826 remove_last_clause(&mut required_clauses, RequiredClause::LoopBody);
827 }
828 "do" => {
829 if !remove_last_clause(&mut required_clauses, RequiredClause::ExceptBody) {
830 remove_last_clause(&mut required_clauses, RequiredClause::LoopBody);
831 }
832 }
833 _ => {}
834 }
835 }
836
837 if !required_clauses.is_empty() {
838 return false;
839 }
840
841 let Some(last) = last_text else {
842 return true;
843 };
844 if keyword_requires_value(last) {
845 return false;
846 }
847 !GENERATED_OPERATOR_SPELLINGS.contains(&last)
848 || GENERATED_POSTFIX_OPERATOR_SPELLINGS.contains(&last)
849}
850
851fn remove_last_clause(clauses: &mut Vec<RequiredClause>, clause: RequiredClause) -> bool {
852 let Some(position) = clauses.iter().rposition(|candidate| *candidate == clause) else {
853 return false;
854 };
855 clauses.remove(position);
856 true
857}
858
859fn keyword_requires_value(keyword: &str) -> bool {
860 matches!(
861 keyword,
862 "if" | "then"
863 | "else"
864 | "for"
865 | "while"
866 | "in"
867 | "from"
868 | "to"
869 | "when"
870 | "list"
871 | "do"
872 | "new"
873 | "of"
874 | "try"
875 | "except"
876 | "catch"
877 | "throw"
878 | "trap"
879 | "shield"
880 | "TEST"
881 | "time"
882 | "timing"
883 | "breakpoint"
884 | "elapsedTime"
885 | "elapsedTiming"
886 | "profile"
887 | "symbol"
888 | "local"
889 | "global"
890 | "threadVariable"
891 | "threadLocal"
892 )
893}
894
895fn last_token_quotes_next(stream: &TokenStream) -> bool {
897 matches!(stream.last_innermost(), Some(TokenTree::Ident(token))
898 if is_quote_specifier(canonical_word_spelling(token.text())))
899}
900
901pub(crate) fn is_quote_specifier(keyword: &str) -> bool {
902 matches!(
903 keyword,
904 "symbol" | "local" | "global" | "threadVariable" | "threadLocal"
905 )
906}
907
908pub(crate) fn punctuation_would_merge(left: &str, right: &str) -> bool {
909 let mut combined = String::with_capacity(left.len() + right.len());
910 combined.push_str(left);
911 combined.push_str(right);
912 fixed_spellings()
913 .any(|(spelling, _)| spelling.len() > left.len() && combined.starts_with(spelling))
914 || (right.starts_with('=') && is_math_operator(left))
915}
916
917#[derive(Debug)]
918struct OpenGroup {
919 kind: DelimiterKind,
920 opening_start: Offset,
921}
922
923#[derive(Debug, Clone, Copy, PartialEq, Eq)]
924enum FixedKind {
925 Punctuation,
926 LineComment,
927 BlockComment,
928 RawString,
929 Open(DelimiterKind),
930 Close(DelimiterKind),
931}
932
933const FIXED_SPELLINGS: &[(&str, FixedKind)] = &[
934 ("///", FixedKind::RawString),
935 ("--", FixedKind::LineComment),
936 ("-*", FixedKind::BlockComment),
937 ("(", FixedKind::Open(DelimiterKind::Parenthesis)),
938 (")", FixedKind::Close(DelimiterKind::Parenthesis)),
939 ("[", FixedKind::Open(DelimiterKind::Bracket)),
940 ("]", FixedKind::Close(DelimiterKind::Bracket)),
941 ("{", FixedKind::Open(DelimiterKind::Brace)),
942 ("}", FixedKind::Close(DelimiterKind::Brace)),
943 ("<|", FixedKind::Open(DelimiterKind::AngleBar)),
944 ("|>", FixedKind::Close(DelimiterKind::AngleBar)),
945];
946
947fn fixed_spellings() -> impl Iterator<Item = (&'static str, FixedKind)> {
948 FIXED_SPELLINGS.iter().copied().chain(
949 GENERATED_PUNCTUATION_SPELLINGS
950 .iter()
951 .map(|spelling| (*spelling, FixedKind::Punctuation)),
952 )
953}
954
955#[derive(Debug, Clone, PartialEq, Eq)]
956struct FixedMatch {
957 len: usize,
958 kind: FixedKind,
959}
960
961#[derive(Debug, Clone, Copy, PartialEq, Eq)]
962enum NumberRadix {
963 Binary,
964 Octal,
965 Hexadecimal,
966}
967
968impl NumberRadix {
969 fn is_digit(self, byte: u8) -> bool {
970 match self {
971 Self::Binary => matches!(byte, b'0' | b'1'),
972 Self::Octal => matches!(byte, b'0'..=b'7'),
973 Self::Hexadecimal => byte.is_ascii_hexdigit(),
974 }
975 }
976}
977
978fn is_identifier_start(byte: u8) -> bool {
979 byte.is_ascii_alphabetic() || byte >= 128
980}
981
982fn is_identifier_continue(byte: u8) -> bool {
983 byte.is_ascii_alphanumeric() || matches!(byte, b'$' | b'\'') || byte >= 128
984}
985
986fn is_math_operator_pair(first: u8, second: u8) -> bool {
987 matches!(
988 (first, second),
989 (0xc2, 0xa0..=0xbf)
990 | (0xc3, 0x97 | 0xb7)
991 | (0xe2, 0x86..=0x8f | 0x9f | 0xa4..=0xaf)
992 )
993}
994
995fn is_math_operator(spelling: &str) -> bool {
996 let bytes = spelling.as_bytes();
997 bytes
998 .first()
999 .zip(bytes.get(1))
1000 .is_some_and(|(first, second)| {
1001 is_math_operator_pair(*first, *second) && utf8_char_width(*first) == Some(bytes.len())
1002 })
1003}
1004
1005fn utf8_char_width(first: u8) -> Option<usize> {
1006 match first {
1007 0x00..=0x7f => Some(1),
1008 0xc2..=0xdf => Some(2),
1009 0xe0..=0xef => Some(3),
1010 0xf0..=0xf4 => Some(4),
1011 _ => None,
1012 }
1013}
1014
1015#[cfg(test)]
1016mod tests {
1017 use super::*;
1018
1019 fn tokens(source: &str) -> TokenStream {
1020 let stream = lex_str(source).unwrap();
1021 assert_eq!(stream.to_source(), source);
1022 stream
1023 }
1024
1025 fn span_at(start: usize, text: &str) -> Span {
1026 let source = Source::new(format!("{}{}", " ".repeat(start), text));
1027 Span::ranged(source, start..start + text.len())
1028 }
1029
1030 #[test]
1031 fn lexer_accepts_an_arbitrary_non_peekable_byte_iterator() {
1032 let bytes = vec![b'a', b'+', b'1'].into_iter();
1033 let stream = lex(bytes).unwrap();
1034 assert_eq!(stream.to_source(), "a+1");
1035 }
1036
1037 #[test]
1038 fn every_short_byte_sequence_returns_without_panicking() {
1039 fn check(bytes: &[u8]) {
1040 let outcome = std::panic::catch_unwind(|| lex(bytes.iter().copied()));
1041 assert!(outcome.is_ok(), "lexer panicked for bytes {bytes:?}");
1042 }
1043
1044 check(&[]);
1045 for first in u8::MIN..=u8::MAX {
1046 check(&[first]);
1047 for second in u8::MIN..=u8::MAX {
1048 check(&[first, second]);
1049 }
1050 }
1051 }
1052
1053 #[test]
1054 fn invalid_utf8_is_a_lex_error() {
1055 assert_eq!(
1056 lex([0xf0, 0x80, 0x80]).unwrap_err().kind(),
1057 LexErrorKind::InvalidUtf8
1058 );
1059 }
1060
1061 #[test]
1062 fn punctuation_is_returned_as_maximal_tokens() {
1063 let source = "<<|-1||>2";
1064 let stream = tokens(source);
1065 let punctuation = stream
1066 .iter()
1067 .filter_map(|tree| match tree {
1068 TokenTree::Punct(token) => Some(token.text()),
1069 _ => None,
1070 })
1071 .collect::<Vec<_>>();
1072 let integers = stream
1073 .iter()
1074 .filter_map(|tree| match tree {
1075 TokenTree::Literal(token) if token.kind == LiteralKind::Integer => {
1076 Some(token.text())
1077 }
1078 _ => None,
1079 })
1080 .collect::<Vec<_>>();
1081 assert_eq!(punctuation, ["<<", "|-", "||", ">"]);
1082 assert_eq!(integers, ["1", "2"]);
1083 }
1084
1085 #[test]
1086 fn comment_openers_only_win_at_the_current_position() {
1087 let ordinary = tokens("|--1");
1088 assert!(
1089 ordinary.iter().all(|tree| {
1090 tree.leading_trivia().is_empty() && tree.trailing_trivia().is_empty()
1091 })
1092 );
1093
1094 let source = "-- comment\n-* block *-";
1095 let comments = tokens(source);
1096 assert!(comments.is_empty());
1097 assert!(matches!(
1098 comments.suffix_trivia(),
1099 [line, block]
1100 if line.kind() == TriviaKind::LineComment
1101 && block.kind() == TriviaKind::BlockComment
1102 ));
1103 }
1104
1105 #[test]
1106 fn leading_operator_runs_split_at_maximal_known_spellings() {
1107 let source = "***1";
1108 let stream = tokens(source);
1109 let trees = stream.iter().collect::<Vec<_>>();
1110 assert!(matches!(
1111 trees.as_slice(),
1112 [
1113 first,
1114 second,
1115 TokenTree::Literal(integer),
1116 ] if first.spelling() == Some("**")
1117 && second.spelling() == Some("*")
1118 && integer.kind == LiteralKind::Integer
1119 && integer.text() == "1"
1120 ));
1121 }
1122
1123 #[test]
1124 fn identifiers_follow_the_m2_character_classes() {
1125 let source = "Core$foo'12 1not not1";
1126 let qualified = tokens(source);
1127 let trees = qualified.iter().collect::<Vec<_>>();
1128 assert!(matches!(
1129 trees.as_slice(),
1130 [
1131 TokenTree::Ident(qualified),
1132 TokenTree::Literal(one),
1133 TokenTree::Ident(not),
1134 TokenTree::Ident(not_one),
1135 ] if qualified.text() == "Core$foo'12"
1136 && one.text() == "1"
1137 && not.text() == "not"
1138 && not_one.text() == "not1"
1139 ));
1140
1141 let source = "_name";
1142 let underscored = tokens(source);
1143 let trees = underscored.iter().collect::<Vec<_>>();
1144 assert!(matches!(
1145 trees.as_slice(),
1146 [underscore, TokenTree::Ident(name)]
1147 if underscore.spelling() == Some("_") && name.text() == "name"
1148 ));
1149
1150 let source = "éλ🐻";
1151 let unicode = tokens(source);
1152 assert!(matches!(
1153 unicode.iter().next(),
1154 Some(TokenTree::Ident(identifier)) if identifier.text() == "éλ🐻"
1155 ));
1156
1157 let source = "α⊠β";
1158 let operated = tokens(source);
1159 let trees = operated.iter().collect::<Vec<_>>();
1160 assert!(matches!(
1161 trees.as_slice(),
1162 [
1163 TokenTree::Ident(alpha),
1164 operator,
1165 TokenTree::Ident(beta),
1166 ] if alpha.text() == "α"
1167 && operator.spelling() == Some("⊠")
1168 && beta.text() == "β"
1169 ));
1170 }
1171
1172 #[test]
1173 fn qualified_identifier_metadata_uses_subspans_without_splitting_the_atom() {
1174 let spelling = "Package$name$12";
1175 let stream = tokens(spelling);
1176 assert!(matches!(
1177 stream.iter().collect::<Vec<_>>().as_slice(),
1178 [TokenTree::Ident(_)]
1179 ));
1180
1181 let symbol = crate::Symbol::new(spelling, span_at(100, spelling));
1182 assert_eq!(
1183 symbol.package_qualifier.as_ref().and_then(Span::bounds),
1184 Some((100, 107))
1185 );
1186 assert_eq!(
1187 symbol.index_qualifier.as_ref().and_then(Span::bounds),
1188 Some((113, 115))
1189 );
1190 assert!(matches!(
1191 crate::Lex::to_token_stream(&symbol)
1192 .iter()
1193 .collect::<Vec<_>>()
1194 .as_slice(),
1195 [TokenTree::Ident(identifier)] if identifier.text() == spelling
1196 ));
1197
1198 let package = crate::Symbol::new("Package$name", span_at(20, "Package$name"));
1199 assert_eq!(
1200 package.package_qualifier.as_ref().and_then(Span::bounds),
1201 Some((20, 27))
1202 );
1203 assert_eq!(package.index_qualifier, None);
1204
1205 let indexed = crate::Symbol::new("name$12", span_at(30, "name$12"));
1206 assert_eq!(indexed.package_qualifier, None);
1207 assert_eq!(
1208 indexed.index_qualifier.as_ref().and_then(Span::bounds),
1209 Some((35, 37))
1210 );
1211
1212 for unqualified in ["name", "A$B$C", "A$$B", "A$", "A$1B", "Core$Core$not"] {
1213 let symbol = crate::Symbol::new(unqualified, Span::for_text(unqualified));
1214 assert_eq!(symbol.package_qualifier, None, "`{unqualified}`");
1215 assert_eq!(symbol.index_qualifier, None, "`{unqualified}`");
1216 }
1217 }
1218
1219 #[test]
1220 fn radix_literals_stop_before_the_first_invalid_digit() {
1221 let source = "0b010201";
1222 let binary = tokens(source);
1223 let trees = binary.iter().collect::<Vec<_>>();
1224 assert!(matches!(
1225 trees.as_slice(),
1226 [
1227 TokenTree::Literal(binary),
1228 TokenTree::Literal(decimal),
1229 ]
1230 if binary.text() == "0b010" && decimal.text() == "201"
1231 ));
1232
1233 let source = "0o70789";
1234 let octal = tokens(source);
1235 let trees = octal.iter().collect::<Vec<_>>();
1236 assert!(matches!(
1237 trees.as_slice(),
1238 [
1239 TokenTree::Literal(octal),
1240 TokenTree::Literal(decimal),
1241 ]
1242 if octal.text() == "0o707" && decimal.text() == "89"
1243 ));
1244
1245 let source = "0b1E2";
1246 let binary_then_identifier = tokens(source);
1247 let trees = binary_then_identifier.iter().collect::<Vec<_>>();
1248 assert!(matches!(
1249 trees.as_slice(),
1250 [
1251 TokenTree::Literal(binary),
1252 TokenTree::Ident(identifier),
1253 ]
1254 if binary.text() == "0b1" && identifier.text() == "E2"
1255 ));
1256 }
1257
1258 #[test]
1259 fn spans_preserve_physical_separation_between_punctuation_tokens() {
1260 let source = "++x +\n+";
1261 let stream = tokens(source);
1262 let trees = stream.iter().collect::<Vec<_>>();
1263 assert!(matches!(
1264 trees.as_slice(),
1265 [
1266 increment,
1267 TokenTree::Ident(identifier),
1268 first_plus,
1269 second_plus,
1270 ] if increment.spelling() == Some("++")
1271 && identifier.text() == "x"
1272 && first_plus.spelling() == Some("+")
1273 && second_plus.spelling() == Some("+")
1274 ));
1275 let first_plus = trees[2].expect_span();
1276 let second_plus = trees[3].expect_span();
1277 assert_eq!(
1278 &first_plus.source().text()[first_plus.end().unwrap()..second_plus.start().unwrap()],
1279 "\n"
1280 );
1281 }
1282
1283 #[test]
1284 fn strings_and_raw_strings_are_literal_token_trees() {
1285 for (source, kind) in [
1286 (r#""a\n\u0101""#, LiteralKind::String),
1287 ("///left////right///", LiteralKind::RawString),
1288 ("///left//right/single///", LiteralKind::RawString),
1289 ] {
1290 let stream = tokens(source);
1291 assert!(matches!(
1292 stream.iter().next(),
1293 Some(TokenTree::Literal(literal))
1294 if literal.kind == kind && literal.text() == source
1295 ));
1296 }
1297 }
1298
1299 #[test]
1300 fn structural_delimiters_form_nested_groups_with_exact_spans() {
1301 let source = "(1, [2, <|3|>])";
1302 let stream = tokens(source);
1303 let mut trees = stream.into_iter();
1304 let Some(TokenTree::Group(parenthesized)) = trees.next() else {
1305 panic!("the source should be one parenthesized group");
1306 };
1307 assert!(trees.next().is_none());
1308 assert_eq!(parenthesized.delim_kind(), DelimiterKind::Parenthesis);
1309
1310 let span = parenthesized.expect_span();
1311 assert_eq!(span.start().unwrap(), 0);
1312 assert_eq!(span.end().unwrap(), 15);
1313 assert_eq!(parenthesized.delimiter().opening_span().end().unwrap(), 1);
1314 assert_eq!(
1315 parenthesized
1316 .delimiter()
1317 .closing_span()
1318 .unwrap()
1319 .start()
1320 .unwrap(),
1321 14
1322 );
1323
1324 let bracketed = parenthesized
1325 .stream()
1326 .iter()
1327 .find_map(|tree| match tree {
1328 TokenTree::Group(group) => Some(group),
1329 _ => None,
1330 })
1331 .unwrap();
1332 assert_eq!(bracketed.delim_kind(), DelimiterKind::Bracket);
1333 assert_eq!(bracketed.delimiter().opening_span().start().unwrap(), 4);
1334 assert_eq!(
1335 bracketed
1336 .delimiter()
1337 .closing_span()
1338 .unwrap()
1339 .start()
1340 .unwrap(),
1341 13
1342 );
1343
1344 let angle_bar = bracketed
1345 .stream()
1346 .iter()
1347 .find_map(|tree| match tree {
1348 TokenTree::Group(group) => Some(group),
1349 _ => None,
1350 })
1351 .unwrap();
1352 assert_eq!(angle_bar.delim_kind(), DelimiterKind::AngleBar);
1353 let opening = angle_bar.delimiter().opening_span();
1354 let closing = angle_bar.delimiter().closing_span().unwrap();
1355 assert_eq!(opening.start().unwrap(), 8);
1356 assert_eq!(opening.end().unwrap(), 10);
1357 assert_eq!(closing.start().unwrap(), 11);
1358 assert_eq!(closing.end().unwrap(), 13);
1359 }
1360
1361 #[test]
1362 fn graded_operator_wins_before_parenthesis_grouping() {
1363 let source = "(*)";
1364 let graded = tokens(source);
1365 let trees = graded.iter().collect::<Vec<_>>();
1366 assert!(matches!(
1367 trees.as_slice(),
1368 [graded] if graded.spelling() == Some("(*)")
1369 ));
1370
1371 let grouped = tokens("(*x)");
1372 assert!(matches!(grouped.iter().next(), Some(TokenTree::Group(_))));
1373 }
1374
1375 #[test]
1376 fn malformed_source_returns_lex_errors() {
1377 for (source, kind) in [
1378 ("\"\\q\"", LexErrorKind::InvalidEscape),
1379 ("(1", LexErrorKind::UnterminatedGroup),
1380 (")", LexErrorKind::UnexpectedClosingDelimiter),
1381 ("(]", LexErrorKind::UnexpectedClosingDelimiter),
1382 ("///", LexErrorKind::UnterminatedRawString),
1383 ("-* unterminated", LexErrorKind::UnterminatedBlockComment),
1384 ] {
1385 assert_eq!(lex_str(source).unwrap_err().kind(), kind);
1386 }
1387
1388 let nested = "(".repeat(MAX_GROUP_DEPTH + 1);
1389 assert_eq!(
1390 lex_str(&nested).unwrap_err().kind(),
1391 LexErrorKind::NestingLimitExceeded
1392 );
1393 }
1394
1395 #[test]
1396 fn a_crlf_line_break_counts_as_the_two_bytes_it_is() {
1397 let source = "x\r\ny";
1398 let stream = tokens(source);
1399 let range = stream
1400 .iter()
1401 .find_map(|tree| match tree {
1402 TokenTree::Ident(identifier) if identifier.text() == "y" => {
1403 Some(identifier.expect_span())
1404 }
1405 _ => None,
1406 })
1407 .unwrap();
1408 assert_eq!(range.bounds(), Some((3, 4)));
1410
1411 let error = lex_str("-* x\r\n").unwrap_err();
1412 assert_eq!(error.expect_span().end(), Some("-* x\r\n".len()));
1413 }
1414
1415 #[test]
1416 fn identifier_classes_match_m2_byte_ctype() {
1417 for byte in [b'a', b'Z', 0x80, 0xff] {
1418 assert!(is_identifier_start(byte));
1419 assert!(is_identifier_continue(byte));
1420 }
1421 for byte in [b'0', b'9', b'$', b'\''] {
1422 assert!(!is_identifier_start(byte));
1423 assert!(is_identifier_continue(byte));
1424 }
1425 for byte in [b'_', b' ', b'+'] {
1426 assert!(!is_identifier_start(byte));
1427 assert!(!is_identifier_continue(byte));
1428 }
1429 }
1430
1431 #[test]
1432 fn math_operator_prefixes_match_m2_ranges() {
1433 for pair in [
1434 (0xc2, 0xa0),
1435 (0xc2, 0xbf),
1436 (0xc3, 0x97),
1437 (0xc3, 0xb7),
1438 (0xe2, 0x86),
1439 (0xe2, 0x87),
1440 (0xe2, 0x88),
1441 (0xe2, 0x8f),
1442 (0xe2, 0x9f),
1443 (0xe2, 0xa4),
1444 (0xe2, 0xa7),
1445 (0xe2, 0xa8),
1446 (0xe2, 0xaf),
1447 ] {
1448 assert!(is_math_operator_pair(pair.0, pair.1));
1449 }
1450 for pair in [
1451 (0xc2, 0x9f),
1452 (0xc3, 0x96),
1453 (0xc3, 0x98),
1454 (0xe2, 0x85),
1455 (0xe2, 0x90),
1456 (0xe2, 0x9e),
1457 (0xe2, 0xa3),
1458 (0xe2, 0xb0),
1459 ] {
1460 assert!(!is_math_operator_pair(pair.0, pair.1));
1461 }
1462 }
1463
1464 #[test]
1465 fn utf8_width_rejects_continuations_and_invalid_leads() {
1466 assert_eq!(utf8_char_width(b'a'), Some(1));
1467 assert_eq!(utf8_char_width(0xc2), Some(2));
1468 assert_eq!(utf8_char_width(0xe2), Some(3));
1469 assert_eq!(utf8_char_width(0xf0), Some(4));
1470 assert_eq!(utf8_char_width(0x80), None);
1471 assert_eq!(utf8_char_width(0xc0), None);
1472 assert_eq!(utf8_char_width(0xf5), None);
1473 }
1474
1475 #[test]
1476 fn ordinary_strings_accept_exact_m2_escape_widths() {
1477 for source in [r#""\u0101""#, r#""\x4f""#, r#""\1\12\123""#, r#""\n\"\\""#] {
1478 let stream = tokens(source);
1479 assert!(matches!(
1480 stream.iter().next(),
1481 Some(TokenTree::Literal(literal))
1482 if literal.kind == LiteralKind::String && literal.text() == source
1483 ));
1484 }
1485 for source in [r#""\u101""#, r#""\u010g""#, r#""\x4""#] {
1486 assert_eq!(
1487 lex_str(source).unwrap_err().kind(),
1488 LexErrorKind::InvalidEscape
1489 );
1490 }
1491 }
1492}