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