(&mut self)
| 654 | } |
| 655 | |
| 656 | fn scan_fstring(&mut self) -> Token<'a> { |
| 657 | // Skip the 'f' and opening quote |
| 658 | self.advance(); // Skip the opening quote |
| 659 | |
| 660 | let start_content = self.current; // Where string content starts |
| 661 | let mut brace_depth = 0; |
| 662 | |
| 663 | while let Some((end_pos, ch)) = self.iter.peek().copied() { |
| 664 | match ch { |
| 665 | '{' => { |
| 666 | brace_depth += 1; |
| 667 | self.advance(); |
| 668 | } |
| 669 | '}' => { |
| 670 | if brace_depth > 0 { |
| 671 | brace_depth -= 1; |
| 672 | } else { |
| 673 | // Lone closing brace is treated as literal '}' |
| 674 | // This is similar to Python's f-string behavior |
| 675 | // (Could return an error here instead) |
| 676 | self.advance(); |
| 677 | } |
| 678 | } |
| 679 | '\\' => { |
| 680 | self.advance(); // consume backslash |
| 681 | self.advance(); // consume the following char |
| 682 | } |
| 683 | '"' => { |
| 684 | let content = &self.source[start_content..end_pos]; |
| 685 | self.advance(); // consume closing quote |
| 686 | return Token::new(TokenType::FString, content, self.line); |
| 687 | } |
| 688 | '\n' => { |
| 689 | self.line += 1; |
| 690 | self.advance(); |
| 691 | } |
| 692 | _ => { |
| 693 | self.advance(); |
| 694 | } |
| 695 | } |
| 696 | } |
| 697 | |
| 698 | Token::new(TokenType::Invalid, "Unterminated f-string.", self.line) |
| 699 | } |
| 700 | |
| 701 | // Scan a string literal, handling escape sequences |
| 702 | fn scan_string(&mut self) -> Token<'a> { |
no test coverage detected