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 loop {
445 let Some(byte) = self.peek(0) else {
446 break;
447 };
448 if !is_identifier_continue(byte) || self.math_operator_width().is_some() {
449 break;
450 }
451 bytes.push(self.next_or_error(LexErrorKind::InvalidUtf8, start)?);
452 }
453 let _ = bytes;
454 output.extend([TokenTree::Ident(IdentToken::from_span(
455 self.span_from(start),
456 ))]);
457 Ok(())
458 }
459
460 fn lex_number(&mut self, output: &mut TokenStream) -> Result<(), LexError> {
462 let start = self.offset;
463 let mut bytes = Vec::new();
464 let mut decimal = true;
465
466 if self.peek(0) == Some(b'0') {
467 bytes.push(self.next_or_error(LexErrorKind::InvalidNumber, start)?);
468 if let Some(radix) = self.number_radix() {
469 decimal = false;
470 bytes.push(self.next_or_error(LexErrorKind::InvalidNumber, start)?);
471 bytes.extend(self.take_while(|byte| radix.is_digit(byte)));
472 } else {
473 bytes.extend(self.take_while(|byte| byte.is_ascii_digit()));
474 }
475 } else {
476 bytes.extend(self.take_while(|byte| byte.is_ascii_digit()));
477 }
478
479 let next = self.peek(0);
480 let begins_float = decimal
481 && (next == Some(b'.') && self.byte_after_current() != Some(b'.')
482 || matches!(next, Some(b'p' | b'e' | b'E')));
483 let kind = if begins_float {
484 if self.peek(0) == Some(b'.') {
485 bytes.push(self.next_or_error(LexErrorKind::InvalidNumber, start)?);
486 bytes.extend(self.take_while(|byte| byte.is_ascii_digit()));
487 }
488 if self.peek(0) == Some(b'p') {
489 bytes.push(self.next_or_error(LexErrorKind::InvalidNumber, start)?);
490 if !self.peek(0).is_some_and(|byte| byte.is_ascii_digit()) {
491 return Err(self.error(LexErrorKind::InvalidNumber, start));
492 }
493 bytes.extend(self.take_while(|byte| byte.is_ascii_digit()));
494 }
495 if matches!(self.peek(0), Some(b'e' | b'E')) {
496 bytes.push(self.next_or_error(LexErrorKind::InvalidNumber, start)?);
497 if matches!(self.peek(0), Some(b'+' | b'-')) {
498 bytes.push(self.next_or_error(LexErrorKind::InvalidNumber, start)?);
499 }
500 if !self.peek(0).is_some_and(|byte| byte.is_ascii_digit()) {
501 return Err(self.error(LexErrorKind::InvalidNumber, start));
502 }
503 bytes.extend(self.take_while(|byte| byte.is_ascii_digit()));
504 }
505 LiteralKind::Float
506 } else {
507 LiteralKind::Integer
508 };
509
510 let _ = bytes;
511 output.extend([TokenTree::Literal(LiteralToken::from_span(
512 kind,
513 self.span_from(start),
514 ))]);
515 Ok(())
516 }
517
518 fn number_radix(&mut self) -> Option<NumberRadix> {
520 let marker = self.peek(0)?;
521 let digit = self.peek(1);
522 match marker {
523 b'b' | b'B' if digit.is_some_and(|byte| NumberRadix::Binary.is_digit(byte)) => {
524 Some(NumberRadix::Binary)
525 }
526 b'o' | b'O' if digit.is_some_and(|byte| NumberRadix::Octal.is_digit(byte)) => {
527 Some(NumberRadix::Octal)
528 }
529 b'x' | b'X' if digit.is_some_and(|byte| NumberRadix::Hexadecimal.is_digit(byte)) => {
530 Some(NumberRadix::Hexadecimal)
531 }
532 _ => None,
533 }
534 }
535
536 fn lex_string(&mut self, output: &mut TokenStream) -> Result<(), LexError> {
538 let start = self.offset;
539 let mut bytes = vec![self.next_or_error(LexErrorKind::UnterminatedString, start)?];
540
541 loop {
542 match self.peek(0) {
543 None => return Err(self.error(LexErrorKind::UnterminatedString, start)),
544 Some(b'"') => {
545 bytes.push(self.next_or_error(LexErrorKind::UnterminatedString, start)?);
546 let _ = bytes;
547 output.extend([TokenTree::Literal(LiteralToken::from_span(
548 LiteralKind::String,
549 self.span_from(start),
550 ))]);
551 return Ok(());
552 }
553 Some(b'\\') => {
554 let escape_start = self.offset;
555 bytes.push(self.next_or_error(LexErrorKind::UnterminatedString, start)?);
556 let Some(escaped) = self.peek(0) else {
557 return Err(self.error(LexErrorKind::UnterminatedString, start));
558 };
559 bytes.push(self.next_or_error(LexErrorKind::UnterminatedString, start)?);
560 match escaped {
561 b'a' | b'b' | b'e' | b'E' | b'f' | b'r' | b't' | b'v' | b'n' | b'"'
562 | b'\\' => {}
563 b'0'..=b'7' => {
564 for _ in 0..2 {
565 if !matches!(self.peek(0), Some(b'0'..=b'7')) {
566 break;
567 }
568 bytes.push(
569 self.next_or_error(LexErrorKind::InvalidEscape, escape_start)?,
570 );
571 }
572 }
573 b'x' => self.take_hex_escape(&mut bytes, 2, escape_start)?,
574 b'u' => self.take_hex_escape(&mut bytes, 4, escape_start)?,
575 _ => return Err(self.error(LexErrorKind::InvalidEscape, escape_start)),
576 }
577 }
578 Some(_) => bytes.push(self.next_or_error(LexErrorKind::UnterminatedString, start)?),
579 }
580 }
581 }
582
583 fn take_hex_escape(
585 &mut self,
586 bytes: &mut Vec<u8>,
587 digits: usize,
588 start: Offset,
589 ) -> Result<(), LexError> {
590 for _ in 0..digits {
591 if !self.peek(0).is_some_and(|byte| byte.is_ascii_hexdigit()) {
592 return Err(self.error(LexErrorKind::InvalidEscape, start));
593 }
594 bytes.push(self.next_or_error(LexErrorKind::InvalidEscape, start)?);
595 }
596 Ok(())
597 }
598
599 fn lex_raw_string(
601 &mut self,
602 opener_len: usize,
603 output: &mut TokenStream,
604 ) -> Result<(), LexError> {
605 let start = self.offset;
606 let mut bytes = self.take_exact(opener_len, start)?;
607
608 loop {
609 if self.peek(0).is_none() {
610 return Err(self.error(LexErrorKind::UnterminatedRawString, start));
611 }
612 if self.peek(0) == Some(b'/') {
613 match self.slash_run_len() {
614 3 => {
615 bytes.extend(self.take_exact(3, start)?);
616 let _ = bytes;
617 output.extend([TokenTree::Literal(LiteralToken::from_span(
618 LiteralKind::RawString,
619 self.span_from(start),
620 ))]);
621 return Ok(());
622 }
623 0 => return Err(self.error(LexErrorKind::UnterminatedRawString, start)),
624 1 => {
625 bytes.push(self.next_or_error(LexErrorKind::UnterminatedRawString, start)?);
626 }
627 _ => bytes.extend(self.take_exact(2, start)?),
628 }
629 } else {
630 bytes.push(self.next_or_error(LexErrorKind::UnterminatedRawString, start)?);
631 }
632 }
633 }
634
635 fn lex_line_comment(
637 &mut self,
638 opener_len: usize,
639 output: &mut TokenStream,
640 ) -> Result<(), LexError> {
641 let start = self.offset;
642 let mut bytes = self.take_exact(opener_len, start)?;
643 loop {
644 match self.peek(0) {
645 None | Some(b'\n') => break,
646 Some(b'\r') if self.byte_after_current() == Some(b'\n') => break,
647 Some(_) => bytes.push(self.next_or_error(LexErrorKind::InvalidCharacter, start)?),
648 }
649 }
650 let _ = bytes;
651 output.push_line_comment(Trivia::from_span(
652 TriviaKind::LineComment,
653 self.span_from(start),
654 ));
655 Ok(())
656 }
657
658 fn lex_block_comment(
660 &mut self,
661 opener_len: usize,
662 output: &mut TokenStream,
663 ) -> Result<(), LexError> {
664 let start = self.offset;
665 let mut bytes = self.take_exact(opener_len, start)?;
666 loop {
667 if self.peek(0).is_none() {
668 return Err(self.error(LexErrorKind::UnterminatedBlockComment, start));
669 }
670 if self.peek(0) == Some(b'*') && self.byte_after_current() == Some(b'-') {
671 bytes.extend(self.take_exact(2, start)?);
672 let _ = bytes;
673 output.push_block_comment(Trivia::from_span(
674 TriviaKind::BlockComment,
675 self.span_from(start),
676 ));
677 return Ok(());
678 }
679 bytes.push(self.next_or_error(LexErrorKind::UnterminatedBlockComment, start)?);
680 }
681 }
682
683 fn match_fixed(&mut self) -> Option<FixedMatch> {
685 fixed_spellings()
686 .filter(|(spelling, _)| {
687 spelling
688 .bytes()
689 .enumerate()
690 .all(|(distance, expected)| self.peek(distance) == Some(expected))
691 })
692 .max_by_key(|(spelling, _)| spelling.len())
693 .map(|(spelling, kind)| FixedMatch {
694 len: spelling.len(),
695 kind,
696 })
697 }
698
699 fn math_operator_width(&mut self) -> Option<usize> {
701 let first = self.peek(0)?;
702 if !matches!(first, 0xc2 | 0xc3 | 0xe2) {
703 return None;
704 }
705 self.peek(1)
706 .filter(|second| is_math_operator_pair(first, *second))
707 .and_then(|_| utf8_char_width(first))
708 }
709
710 fn lex_math_operator(
712 &mut self,
713 width: usize,
714 output: &mut TokenStream,
715 ) -> Result<(), LexError> {
716 let start = self.offset;
717 let mut bytes = self.take_exact(width, start)?;
718 if self.peek(0) == Some(b'=') {
719 bytes.push(self.next_or_error(LexErrorKind::InvalidUtf8, start)?);
720 }
721 self.emit_punctuation(bytes, start, output)
722 }
723
724 fn lex_punctuation(&mut self, len: usize, output: &mut TokenStream) -> Result<(), LexError> {
726 let start = self.offset;
727 let bytes = self.take_exact(len, start)?;
728 self.emit_punctuation(bytes, start, output)
729 }
730
731 fn emit_punctuation(
733 &self,
734 bytes: Vec<u8>,
735 start: Offset,
736 output: &mut TokenStream,
737 ) -> Result<(), LexError> {
738 let _ = bytes;
739 output.extend([TokenTree::Punct(Punct::from_span(self.span_from(start)))]);
740 Ok(())
741 }
742
743 fn byte_after_current(&mut self) -> Option<u8> {
745 self.peek(1)
746 }
747
748 fn slash_run_len(&mut self) -> usize {
750 let mut len = 0;
751 while len < 4 && self.peek(len) == Some(b'/') {
752 len += 1;
753 }
754 len
755 }
756
757 fn take_while(&mut self, predicate: impl Fn(u8) -> bool) -> Vec<u8> {
759 let mut bytes = Vec::new();
760 while self.peek(0).is_some_and(&predicate) {
761 let Some(byte) = self.next() else {
762 break;
763 };
764 bytes.push(byte);
765 }
766 bytes
767 }
768
769 fn take_exact(&mut self, len: usize, start: Offset) -> Result<Vec<u8>, LexError> {
771 let mut bytes = Vec::with_capacity(len);
772 for _ in 0..len {
773 let Some(byte) = self.next() else {
774 return Err(self.error(LexErrorKind::InvalidUtf8, start));
775 };
776 bytes.push(byte);
777 }
778 Ok(bytes)
779 }
780
781 fn next(&mut self) -> Option<u8> {
783 let byte = self.peek(0)?;
784 self.offset += 1;
785 Some(byte)
786 }
787
788 fn peek(&self, distance: usize) -> Option<u8> {
790 self.offset
791 .checked_add(distance)
792 .and_then(|offset| self.source.as_bytes().get(offset))
793 .copied()
794 }
795
796 fn next_or_error(&mut self, kind: LexErrorKind, start: Offset) -> Result<u8, LexError> {
798 match self.next() {
799 Some(byte) => Ok(byte),
800 None => Err(self.error(kind, start)),
801 }
802 }
803
804 fn error(&self, kind: LexErrorKind, start: Offset) -> LexError {
806 LexError {
807 kind,
808 delimiter: None,
809 span: self.span_from(start),
810 }
811 }
812
813 fn span_from(&self, start: Offset) -> Span {
815 self.source_span.subspan(start, self.offset)
816 }
817}
818
819#[derive(Debug, Clone, Copy, PartialEq, Eq)]
820enum RequiredClause {
822 Then,
824 LoopBody,
826 ExceptBody,
828}
829
830pub(crate) fn newline_ends_cell(stream: &TokenStream) -> bool {
832 let mut required_clauses = Vec::new();
833 let mut last_text = None;
834 let mut quote_next = false;
835
836 for tree in stream.iter() {
837 let text = match tree {
838 TokenTree::Ident(token) => Some(token.text()),
839 TokenTree::Punct(token) => Some(token.text()),
840 TokenTree::Literal(_) | TokenTree::Group(_) => {
841 quote_next = false;
842 last_text = None;
843 None
844 }
845 };
846
847 let Some(text) = text else {
848 continue;
849 };
850 if quote_next {
851 quote_next = false;
852 last_text = None;
853 continue;
854 }
855 let text = canonical_word_spelling(text);
856 last_text = Some(text);
857 if is_quote_specifier(text) {
858 quote_next = true;
859 continue;
860 }
861 match text {
862 "if" => required_clauses.push(RequiredClause::Then),
863 "for" | "while" => required_clauses.push(RequiredClause::LoopBody),
864 "except" => required_clauses.push(RequiredClause::ExceptBody),
865 "then" => {
866 remove_last_clause(&mut required_clauses, RequiredClause::Then);
867 }
868 "list" => {
869 remove_last_clause(&mut required_clauses, RequiredClause::LoopBody);
870 }
871 "do" => {
872 if !remove_last_clause(&mut required_clauses, RequiredClause::ExceptBody) {
873 remove_last_clause(&mut required_clauses, RequiredClause::LoopBody);
874 }
875 }
876 _ => {}
877 }
878 }
879
880 if !required_clauses.is_empty() {
881 return false;
882 }
883
884 let Some(last) = last_text else {
885 return true;
886 };
887 if keyword_requires_value(last) {
888 return false;
889 }
890 !GENERATED_OPERATOR_SPELLINGS.contains(&last)
891 || GENERATED_POSTFIX_OPERATOR_SPELLINGS.contains(&last)
892}
893
894fn remove_last_clause(clauses: &mut Vec<RequiredClause>, clause: RequiredClause) -> bool {
896 let Some(position) = clauses.iter().rposition(|candidate| *candidate == clause) else {
897 return false;
898 };
899 clauses.remove(position);
900 true
901}
902
903fn keyword_requires_value(keyword: &str) -> bool {
905 matches!(
906 keyword,
907 "if" | "then"
908 | "else"
909 | "for"
910 | "while"
911 | "in"
912 | "from"
913 | "to"
914 | "when"
915 | "list"
916 | "do"
917 | "new"
918 | "of"
919 | "try"
920 | "except"
921 | "catch"
922 | "throw"
923 | "trap"
924 | "shield"
925 | "TEST"
926 | "time"
927 | "timing"
928 | "breakpoint"
929 | "elapsedTime"
930 | "elapsedTiming"
931 | "profile"
932 | "symbol"
933 | "local"
934 | "global"
935 | "threadVariable"
936 | "threadLocal"
937 )
938}
939
940fn last_token_quotes_next(stream: &TokenStream) -> bool {
942 matches!(stream.last_innermost(), Some(TokenTree::Ident(token))
943 if is_quote_specifier(canonical_word_spelling(token.text())))
944}
945
946pub(crate) fn is_quote_specifier(keyword: &str) -> bool {
948 matches!(
949 keyword,
950 "symbol" | "local" | "global" | "threadVariable" | "threadLocal"
951 )
952}
953
954pub(crate) fn punctuation_would_merge(left: &str, right: &str) -> bool {
956 let mut combined = String::with_capacity(left.len() + right.len());
957 combined.push_str(left);
958 combined.push_str(right);
959 fixed_spellings()
960 .any(|(spelling, _)| spelling.len() > left.len() && combined.starts_with(spelling))
961 || (right.starts_with('=') && is_math_operator(left))
962}
963
964#[derive(Debug)]
965struct OpenGroup {
967 kind: DelimiterKind,
969 opening_start: Offset,
971}
972
973#[derive(Debug, Clone, Copy, PartialEq, Eq)]
974enum FixedKind {
976 Punctuation,
978 LineComment,
980 BlockComment,
982 RawString,
984 Open(DelimiterKind),
986 Close(DelimiterKind),
988}
989
990const FIXED_SPELLINGS: &[(&str, FixedKind)] = &[
992 ("///", FixedKind::RawString),
993 ("--", FixedKind::LineComment),
994 ("-*", FixedKind::BlockComment),
995 ("(", FixedKind::Open(DelimiterKind::Parenthesis)),
996 (")", FixedKind::Close(DelimiterKind::Parenthesis)),
997 ("[", FixedKind::Open(DelimiterKind::Bracket)),
998 ("]", FixedKind::Close(DelimiterKind::Bracket)),
999 ("{", FixedKind::Open(DelimiterKind::Brace)),
1000 ("}", FixedKind::Close(DelimiterKind::Brace)),
1001 ("<|", FixedKind::Open(DelimiterKind::AngleBar)),
1002 ("|>", FixedKind::Close(DelimiterKind::AngleBar)),
1003];
1004
1005fn fixed_spellings() -> impl Iterator<Item = (&'static str, FixedKind)> {
1007 FIXED_SPELLINGS.iter().copied().chain(
1008 GENERATED_PUNCTUATION_SPELLINGS
1009 .iter()
1010 .map(|spelling| (*spelling, FixedKind::Punctuation)),
1011 )
1012}
1013
1014#[derive(Debug, Clone, PartialEq, Eq)]
1015struct FixedMatch {
1017 len: usize,
1019 kind: FixedKind,
1021}
1022
1023#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1024enum NumberRadix {
1026 Binary,
1028 Octal,
1030 Hexadecimal,
1032}
1033
1034impl NumberRadix {
1035 fn is_digit(self, byte: u8) -> bool {
1037 match self {
1038 Self::Binary => matches!(byte, b'0' | b'1'),
1039 Self::Octal => matches!(byte, b'0'..=b'7'),
1040 Self::Hexadecimal => byte.is_ascii_hexdigit(),
1041 }
1042 }
1043}
1044
1045fn is_identifier_start(byte: u8) -> bool {
1047 byte.is_ascii_alphabetic() || byte >= 128
1048}
1049
1050fn is_identifier_continue(byte: u8) -> bool {
1052 byte.is_ascii_alphanumeric() || matches!(byte, b'$' | b'\'') || byte >= 128
1053}
1054
1055fn is_math_operator_pair(first: u8, second: u8) -> bool {
1057 matches!(
1058 (first, second),
1059 (0xc2, 0xa0..=0xbf)
1060 | (0xc3, 0x97 | 0xb7)
1061 | (0xe2, 0x86..=0x8f | 0x9f | 0xa4..=0xaf)
1062 )
1063}
1064
1065fn is_math_operator(spelling: &str) -> bool {
1067 let bytes = spelling.as_bytes();
1068 bytes
1069 .first()
1070 .zip(bytes.get(1))
1071 .is_some_and(|(first, second)| {
1072 is_math_operator_pair(*first, *second) && utf8_char_width(*first) == Some(bytes.len())
1073 })
1074}
1075
1076fn utf8_char_width(first: u8) -> Option<usize> {
1078 match first {
1079 0x00..=0x7f => Some(1),
1080 0xc2..=0xdf => Some(2),
1081 0xe0..=0xef => Some(3),
1082 0xf0..=0xf4 => Some(4),
1083 _ => None,
1084 }
1085}
1086
1087#[cfg(test)]
1088mod tests {
1089 use super::*;
1090
1091 fn tokens(source: &str) -> TokenStream {
1092 let stream = lex_str(source).unwrap();
1093 assert_eq!(stream.to_source(), source);
1094 stream
1095 }
1096
1097 fn span_at(start: usize, text: &str) -> Span {
1098 let source = Source::new(format!("{}{}", " ".repeat(start), text));
1099 Span::ranged(source, start..start + text.len())
1100 }
1101
1102 #[test]
1103 fn lexer_accepts_an_arbitrary_non_peekable_byte_iterator() {
1104 let bytes = vec![b'a', b'+', b'1'].into_iter();
1105 let stream = lex(bytes).unwrap();
1106 assert_eq!(stream.to_source(), "a+1");
1107 }
1108
1109 #[test]
1110 fn every_short_byte_sequence_returns_without_panicking() {
1111 fn check(bytes: &[u8]) {
1112 let outcome = std::panic::catch_unwind(|| lex(bytes.iter().copied()));
1113 assert!(outcome.is_ok(), "lexer panicked for bytes {bytes:?}");
1114 }
1115
1116 check(&[]);
1117 for first in u8::MIN..=u8::MAX {
1118 check(&[first]);
1119 for second in u8::MIN..=u8::MAX {
1120 check(&[first, second]);
1121 }
1122 }
1123 }
1124
1125 #[test]
1126 fn invalid_utf8_is_a_lex_error() {
1127 assert_eq!(
1128 lex([0xf0, 0x80, 0x80]).unwrap_err().kind(),
1129 LexErrorKind::InvalidUtf8
1130 );
1131 }
1132
1133 #[test]
1134 fn punctuation_is_returned_as_maximal_tokens() {
1135 let source = "<<|-1||>2";
1136 let stream = tokens(source);
1137 let punctuation = stream
1138 .iter()
1139 .filter_map(|tree| match tree {
1140 TokenTree::Punct(token) => Some(token.text()),
1141 _ => None,
1142 })
1143 .collect::<Vec<_>>();
1144 let integers = stream
1145 .iter()
1146 .filter_map(|tree| match tree {
1147 TokenTree::Literal(token) if token.kind == LiteralKind::Integer => {
1148 Some(token.text())
1149 }
1150 _ => None,
1151 })
1152 .collect::<Vec<_>>();
1153 assert_eq!(punctuation, ["<<", "|-", "||", ">"]);
1154 assert_eq!(integers, ["1", "2"]);
1155 }
1156
1157 #[test]
1158 fn comment_openers_only_win_at_the_current_position() {
1159 let ordinary = tokens("|--1");
1160 assert!(
1161 ordinary.iter().all(|tree| {
1162 tree.leading_trivia().is_empty() && tree.trailing_trivia().is_empty()
1163 })
1164 );
1165
1166 let source = "-- comment\n-* block *-";
1167 let comments = tokens(source);
1168 assert!(comments.is_empty());
1169 assert!(matches!(
1170 comments.suffix_trivia(),
1171 [line, block]
1172 if line.kind() == TriviaKind::LineComment
1173 && block.kind() == TriviaKind::BlockComment
1174 ));
1175 }
1176
1177 #[test]
1178 fn leading_operator_runs_split_at_maximal_known_spellings() {
1179 let source = "***1";
1180 let stream = tokens(source);
1181 let trees = stream.iter().collect::<Vec<_>>();
1182 assert!(matches!(
1183 trees.as_slice(),
1184 [
1185 first,
1186 second,
1187 TokenTree::Literal(integer),
1188 ] if first.spelling() == Some("**")
1189 && second.spelling() == Some("*")
1190 && integer.kind == LiteralKind::Integer
1191 && integer.text() == "1"
1192 ));
1193 }
1194
1195 #[test]
1196 fn identifiers_follow_the_m2_character_classes() {
1197 let source = "Core$foo'12 1not not1";
1198 let qualified = tokens(source);
1199 let trees = qualified.iter().collect::<Vec<_>>();
1200 assert!(matches!(
1201 trees.as_slice(),
1202 [
1203 TokenTree::Ident(qualified),
1204 TokenTree::Literal(one),
1205 TokenTree::Ident(not),
1206 TokenTree::Ident(not_one),
1207 ] if qualified.text() == "Core$foo'12"
1208 && one.text() == "1"
1209 && not.text() == "not"
1210 && not_one.text() == "not1"
1211 ));
1212
1213 let source = "_name";
1214 let underscored = tokens(source);
1215 let trees = underscored.iter().collect::<Vec<_>>();
1216 assert!(matches!(
1217 trees.as_slice(),
1218 [underscore, TokenTree::Ident(name)]
1219 if underscore.spelling() == Some("_") && name.text() == "name"
1220 ));
1221
1222 let source = "éλ🐻";
1223 let unicode = tokens(source);
1224 assert!(matches!(
1225 unicode.iter().next(),
1226 Some(TokenTree::Ident(identifier)) if identifier.text() == "éλ🐻"
1227 ));
1228
1229 let source = "α⊠β";
1230 let operated = tokens(source);
1231 let trees = operated.iter().collect::<Vec<_>>();
1232 assert!(matches!(
1233 trees.as_slice(),
1234 [
1235 TokenTree::Ident(alpha),
1236 operator,
1237 TokenTree::Ident(beta),
1238 ] if alpha.text() == "α"
1239 && operator.spelling() == Some("⊠")
1240 && beta.text() == "β"
1241 ));
1242 }
1243
1244 #[test]
1245 fn qualified_identifier_metadata_uses_subspans_without_splitting_the_atom() {
1246 let spelling = "Package$name$12";
1247 let stream = tokens(spelling);
1248 assert!(matches!(
1249 stream.iter().collect::<Vec<_>>().as_slice(),
1250 [TokenTree::Ident(_)]
1251 ));
1252
1253 let symbol = crate::Symbol::new(spelling, span_at(100, spelling));
1254 assert_eq!(
1255 symbol.package_qualifier.as_ref().and_then(Span::bounds),
1256 Some((100, 107))
1257 );
1258 assert_eq!(
1259 symbol.index_qualifier.as_ref().and_then(Span::bounds),
1260 Some((113, 115))
1261 );
1262 assert!(matches!(
1263 crate::Lex::to_token_stream(&symbol)
1264 .iter()
1265 .collect::<Vec<_>>()
1266 .as_slice(),
1267 [TokenTree::Ident(identifier)] if identifier.text() == spelling
1268 ));
1269
1270 let package = crate::Symbol::new("Package$name", span_at(20, "Package$name"));
1271 assert_eq!(
1272 package.package_qualifier.as_ref().and_then(Span::bounds),
1273 Some((20, 27))
1274 );
1275 assert_eq!(package.index_qualifier, None);
1276
1277 let indexed = crate::Symbol::new("name$12", span_at(30, "name$12"));
1278 assert_eq!(indexed.package_qualifier, None);
1279 assert_eq!(
1280 indexed.index_qualifier.as_ref().and_then(Span::bounds),
1281 Some((35, 37))
1282 );
1283
1284 for unqualified in ["name", "A$B$C", "A$$B", "A$", "A$1B", "Core$Core$not"] {
1285 let symbol = crate::Symbol::new(unqualified, Span::for_text(unqualified));
1286 assert_eq!(symbol.package_qualifier, None, "`{unqualified}`");
1287 assert_eq!(symbol.index_qualifier, None, "`{unqualified}`");
1288 }
1289 }
1290
1291 #[test]
1292 fn radix_literals_stop_before_the_first_invalid_digit() {
1293 let source = "0b010201";
1294 let binary = tokens(source);
1295 let trees = binary.iter().collect::<Vec<_>>();
1296 assert!(matches!(
1297 trees.as_slice(),
1298 [
1299 TokenTree::Literal(binary),
1300 TokenTree::Literal(decimal),
1301 ]
1302 if binary.text() == "0b010" && decimal.text() == "201"
1303 ));
1304
1305 let source = "0o70789";
1306 let octal = tokens(source);
1307 let trees = octal.iter().collect::<Vec<_>>();
1308 assert!(matches!(
1309 trees.as_slice(),
1310 [
1311 TokenTree::Literal(octal),
1312 TokenTree::Literal(decimal),
1313 ]
1314 if octal.text() == "0o707" && decimal.text() == "89"
1315 ));
1316
1317 let source = "0b1E2";
1318 let binary_then_identifier = tokens(source);
1319 let trees = binary_then_identifier.iter().collect::<Vec<_>>();
1320 assert!(matches!(
1321 trees.as_slice(),
1322 [
1323 TokenTree::Literal(binary),
1324 TokenTree::Ident(identifier),
1325 ]
1326 if binary.text() == "0b1" && identifier.text() == "E2"
1327 ));
1328 }
1329
1330 #[test]
1331 fn spans_preserve_physical_separation_between_punctuation_tokens() {
1332 let source = "++x +\n+";
1333 let stream = tokens(source);
1334 let trees = stream.iter().collect::<Vec<_>>();
1335 assert!(matches!(
1336 trees.as_slice(),
1337 [
1338 increment,
1339 TokenTree::Ident(identifier),
1340 first_plus,
1341 second_plus,
1342 ] if increment.spelling() == Some("++")
1343 && identifier.text() == "x"
1344 && first_plus.spelling() == Some("+")
1345 && second_plus.spelling() == Some("+")
1346 ));
1347 let first_plus = trees[2].expect_span();
1348 let second_plus = trees[3].expect_span();
1349 assert_eq!(
1350 &first_plus.source().text()[first_plus.end().unwrap()..second_plus.start().unwrap()],
1351 "\n"
1352 );
1353 }
1354
1355 #[test]
1356 fn strings_and_raw_strings_are_literal_token_trees() {
1357 for (source, kind) in [
1358 (r#""a\n\u0101""#, LiteralKind::String),
1359 ("///left////right///", LiteralKind::RawString),
1360 ("///left//right/single///", LiteralKind::RawString),
1361 ] {
1362 let stream = tokens(source);
1363 assert!(matches!(
1364 stream.iter().next(),
1365 Some(TokenTree::Literal(literal))
1366 if literal.kind == kind && literal.text() == source
1367 ));
1368 }
1369 }
1370
1371 #[test]
1372 fn structural_delimiters_form_nested_groups_with_exact_spans() {
1373 let source = "(1, [2, <|3|>])";
1374 let stream = tokens(source);
1375 let mut trees = stream.into_iter();
1376 let Some(TokenTree::Group(parenthesized)) = trees.next() else {
1377 panic!("the source should be one parenthesized group");
1378 };
1379 assert!(trees.next().is_none());
1380 assert_eq!(parenthesized.delim_kind(), DelimiterKind::Parenthesis);
1381
1382 let span = parenthesized.expect_span();
1383 assert_eq!(span.start().unwrap(), 0);
1384 assert_eq!(span.end().unwrap(), 15);
1385 assert_eq!(parenthesized.delimiter().opening_span().end().unwrap(), 1);
1386 assert_eq!(
1387 parenthesized
1388 .delimiter()
1389 .closing_span()
1390 .unwrap()
1391 .start()
1392 .unwrap(),
1393 14
1394 );
1395
1396 let bracketed = parenthesized
1397 .stream()
1398 .iter()
1399 .find_map(|tree| match tree {
1400 TokenTree::Group(group) => Some(group),
1401 _ => None,
1402 })
1403 .unwrap();
1404 assert_eq!(bracketed.delim_kind(), DelimiterKind::Bracket);
1405 assert_eq!(bracketed.delimiter().opening_span().start().unwrap(), 4);
1406 assert_eq!(
1407 bracketed
1408 .delimiter()
1409 .closing_span()
1410 .unwrap()
1411 .start()
1412 .unwrap(),
1413 13
1414 );
1415
1416 let angle_bar = bracketed
1417 .stream()
1418 .iter()
1419 .find_map(|tree| match tree {
1420 TokenTree::Group(group) => Some(group),
1421 _ => None,
1422 })
1423 .unwrap();
1424 assert_eq!(angle_bar.delim_kind(), DelimiterKind::AngleBar);
1425 let opening = angle_bar.delimiter().opening_span();
1426 let closing = angle_bar.delimiter().closing_span().unwrap();
1427 assert_eq!(opening.start().unwrap(), 8);
1428 assert_eq!(opening.end().unwrap(), 10);
1429 assert_eq!(closing.start().unwrap(), 11);
1430 assert_eq!(closing.end().unwrap(), 13);
1431 }
1432
1433 #[test]
1434 fn graded_operator_wins_before_parenthesis_grouping() {
1435 let source = "(*)";
1436 let graded = tokens(source);
1437 let trees = graded.iter().collect::<Vec<_>>();
1438 assert!(matches!(
1439 trees.as_slice(),
1440 [graded] if graded.spelling() == Some("(*)")
1441 ));
1442
1443 let grouped = tokens("(*x)");
1444 assert!(matches!(grouped.iter().next(), Some(TokenTree::Group(_))));
1445 }
1446
1447 #[test]
1448 fn malformed_source_returns_lex_errors() {
1449 for (source, kind) in [
1450 ("\"\\q\"", LexErrorKind::InvalidEscape),
1451 ("(1", LexErrorKind::UnterminatedGroup),
1452 (")", LexErrorKind::UnexpectedClosingDelimiter),
1453 ("(]", LexErrorKind::UnexpectedClosingDelimiter),
1454 ("///", LexErrorKind::UnterminatedRawString),
1455 ("-* unterminated", LexErrorKind::UnterminatedBlockComment),
1456 ] {
1457 assert_eq!(lex_str(source).unwrap_err().kind(), kind);
1458 }
1459
1460 let nested = "(".repeat(MAX_GROUP_DEPTH + 1);
1461 assert_eq!(
1462 lex_str(&nested).unwrap_err().kind(),
1463 LexErrorKind::NestingLimitExceeded
1464 );
1465 }
1466
1467 #[test]
1468 fn a_crlf_line_break_counts_as_the_two_bytes_it_is() {
1469 let source = "x\r\ny";
1470 let stream = tokens(source);
1471 let range = stream
1472 .iter()
1473 .find_map(|tree| match tree {
1474 TokenTree::Ident(identifier) if identifier.text() == "y" => {
1475 Some(identifier.expect_span())
1476 }
1477 _ => None,
1478 })
1479 .unwrap();
1480 assert_eq!(range.bounds(), Some((3, 4)));
1482
1483 let error = lex_str("-* x\r\n").unwrap_err();
1484 assert_eq!(error.expect_span().end(), Some("-* x\r\n".len()));
1485 }
1486
1487 #[test]
1488 fn identifier_classes_match_m2_byte_ctype() {
1489 for byte in [b'a', b'Z', 0x80, 0xff] {
1490 assert!(is_identifier_start(byte));
1491 assert!(is_identifier_continue(byte));
1492 }
1493 for byte in [b'0', b'9', b'$', b'\''] {
1494 assert!(!is_identifier_start(byte));
1495 assert!(is_identifier_continue(byte));
1496 }
1497 for byte in [b'_', b' ', b'+'] {
1498 assert!(!is_identifier_start(byte));
1499 assert!(!is_identifier_continue(byte));
1500 }
1501 }
1502
1503 #[test]
1504 fn math_operator_prefixes_match_m2_ranges() {
1505 for pair in [
1506 (0xc2, 0xa0),
1507 (0xc2, 0xbf),
1508 (0xc3, 0x97),
1509 (0xc3, 0xb7),
1510 (0xe2, 0x86),
1511 (0xe2, 0x87),
1512 (0xe2, 0x88),
1513 (0xe2, 0x8f),
1514 (0xe2, 0x9f),
1515 (0xe2, 0xa4),
1516 (0xe2, 0xa7),
1517 (0xe2, 0xa8),
1518 (0xe2, 0xaf),
1519 ] {
1520 assert!(is_math_operator_pair(pair.0, pair.1));
1521 }
1522 for pair in [
1523 (0xc2, 0x9f),
1524 (0xc3, 0x96),
1525 (0xc3, 0x98),
1526 (0xe2, 0x85),
1527 (0xe2, 0x90),
1528 (0xe2, 0x9e),
1529 (0xe2, 0xa3),
1530 (0xe2, 0xb0),
1531 ] {
1532 assert!(!is_math_operator_pair(pair.0, pair.1));
1533 }
1534 }
1535
1536 #[test]
1537 fn utf8_width_rejects_continuations_and_invalid_leads() {
1538 assert_eq!(utf8_char_width(b'a'), Some(1));
1539 assert_eq!(utf8_char_width(0xc2), Some(2));
1540 assert_eq!(utf8_char_width(0xe2), Some(3));
1541 assert_eq!(utf8_char_width(0xf0), Some(4));
1542 assert_eq!(utf8_char_width(0x80), None);
1543 assert_eq!(utf8_char_width(0xc0), None);
1544 assert_eq!(utf8_char_width(0xf5), None);
1545 }
1546
1547 #[test]
1548 fn ordinary_strings_accept_exact_m2_escape_widths() {
1549 for source in [r#""\u0101""#, r#""\x4f""#, r#""\1\12\123""#, r#""\n\"\\""#] {
1550 let stream = tokens(source);
1551 assert!(matches!(
1552 stream.iter().next(),
1553 Some(TokenTree::Literal(literal))
1554 if literal.kind == LiteralKind::String && literal.text() == source
1555 ));
1556 }
1557 for source in [r#""\u101""#, r#""\u010g""#, r#""\x4""#] {
1558 assert_eq!(
1559 lex_str(source).unwrap_err().kind(),
1560 LexErrorKind::InvalidEscape
1561 );
1562 }
1563 }
1564}