jetcrab\lexer\scanners/
string.rs1use crate::lexer::{LexerError, TokenKind};
2
3pub trait StringReader {
4 fn read_string(&mut self) -> Result<TokenKind, LexerError>;
5 fn read_template_string(&mut self) -> Result<TokenKind, LexerError>;
6}
7
8impl<T> StringReader for T
9where
10 T: LexerCore,
11{
12 fn read_string(&mut self) -> Result<TokenKind, LexerError> {
13 let quote = self.source()[self.pos()];
14 self.advance_pos();
15
16 let mut string = String::new();
17 let mut found_closing_quote = false;
18
19 while self.pos() < self.source().len() {
20 let c = self.source()[self.pos()];
21
22 if c == quote {
23 self.advance_pos();
24 found_closing_quote = true;
25 break;
26 } else if c == '\\' {
27 self.advance_pos();
28 if self.pos() < self.source().len() {
29 let escaped = self.source()[self.pos()];
30 match escaped {
31 'n' => string.push('\n'),
32 't' => string.push('\t'),
33 'r' => string.push('\r'),
34 '\\' => string.push('\\'),
35 '"' => string.push('"'),
36 '\'' => string.push('\''),
37 _ => string.push(escaped),
38 }
39 self.advance_pos();
40 }
41 } else {
42 string.push(c);
43 self.advance_pos();
44 }
45 }
46
47 if !found_closing_quote {
48 return Err(LexerError::UnterminatedString);
49 }
50
51 Ok(TokenKind::String(string))
52 }
53
54 fn read_template_string(&mut self) -> Result<TokenKind, LexerError> {
55 self.advance_pos();
56
57 let mut template = String::new();
58
59 while self.pos() < self.source().len() {
60 let c = self.source()[self.pos()];
61
62 if c == '`' {
63 self.advance_pos();
64 break;
65 } else if c == '$' && self.peek_char(1) == Some('{') {
66 template.push_str("${");
67 self.advance_pos();
68 self.advance_pos();
69 } else if c == '\\' {
70 self.advance_pos();
71 if self.pos() < self.source().len() {
72 let escaped = self.source()[self.pos()];
73 match escaped {
74 'n' => template.push('\n'),
75 't' => template.push('\t'),
76 'r' => template.push('\r'),
77 '\\' => template.push('\\'),
78 '`' => template.push('`'),
79 '$' => template.push('$'),
80 _ => template.push(escaped),
81 }
82 self.advance_pos();
83 }
84 } else {
85 template.push(c);
86 self.advance_pos();
87 }
88 }
89
90 Ok(TokenKind::TemplateString(template))
91 }
92}
93
94use crate::lexer::scanners::LexerCore;
95
96pub trait LexerCoreExt {
97 fn peek_char(&self, offset: usize) -> Option<char>;
98}
99
100impl<T> LexerCoreExt for T
101where
102 T: LexerCore,
103{
104 fn peek_char(&self, offset: usize) -> Option<char> {
105 let pos = self.pos() + offset;
106 if pos < self.source().len() {
107 Some(self.source()[pos])
108 } else {
109 None
110 }
111 }
112}