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