1use std::borrow::Cow;
2
3pub struct ParseStringError {
4 pub byte_index: usize,
5 pub kind: ParseStringErrorKind,
6}
7
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9pub enum ParseStringErrorKind {
10 InvalidEscapeInSingleQuoteString,
11 InvalidEscapeInDoubleQuoteString,
12 ExpectedFourHexDigits,
13 ExpectedTwoHexDigits,
14 InvalidUnicodeEscapeSequence(String),
15 InvalidEscape,
16 UnterminatedStringLiteral,
17}
18
19impl std::error::Error for ParseStringErrorKind {}
20
21impl std::fmt::Display for ParseStringErrorKind {
22 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23 match self {
24 ParseStringErrorKind::InvalidEscapeInSingleQuoteString => {
25 write!(f, "Invalid escape in single quote string")
26 }
27 ParseStringErrorKind::InvalidEscapeInDoubleQuoteString => {
28 write!(f, "Invalid escape in double quote string")
29 }
30 ParseStringErrorKind::ExpectedFourHexDigits => {
31 write!(f, "Expected four hex digits")
32 }
33 ParseStringErrorKind::ExpectedTwoHexDigits => {
34 write!(f, "Expected two hex digits")
35 }
36 ParseStringErrorKind::InvalidUnicodeEscapeSequence(value) => {
37 write!(
38 f,
39 "Invalid unicode escape sequence. '{}' is not a valid UTF8 character",
40 value
41 )
42 }
43 ParseStringErrorKind::InvalidEscape => {
44 write!(f, "Invalid escape")
45 }
46 ParseStringErrorKind::UnterminatedStringLiteral => {
47 write!(f, "Unterminated string literal")
48 }
49 }
50 }
51}
52
53pub trait CharProvider<'a> {
54 fn current_char(&mut self) -> Option<char>;
55 fn byte_index(&self) -> usize;
56 fn move_next_char(&mut self) -> Option<char>;
57 fn text(&self) -> &'a str;
58}
59
60#[cfg(feature = "cst")]
61pub fn parse_string(text: &str) -> Result<Cow<'_, str>, ParseStringError> {
62 struct StringCharProvider<'a> {
63 text: &'a str,
64 byte_index: usize,
65 current_char: Option<char>,
66 chars: std::str::Chars<'a>,
67 }
68
69 impl<'a> CharProvider<'a> for StringCharProvider<'a> {
70 fn current_char(&mut self) -> Option<char> {
71 self.current_char
72 }
73
74 fn byte_index(&self) -> usize {
75 self.byte_index
76 }
77
78 fn move_next_char(&mut self) -> Option<char> {
79 if let Some(current_char) = self.current_char {
80 self.byte_index += current_char.len_utf8();
81 }
82 self.current_char = self.chars.next();
83 self.current_char
84 }
85
86 fn text(&self) -> &'a str {
87 self.text
88 }
89 }
90
91 let mut chars = text.chars();
92 let mut provider = StringCharProvider {
93 text,
94 byte_index: 0,
95 current_char: chars.next(),
96 chars,
97 };
98
99 parse_string_with_char_provider(&mut provider, true)
101}
102
103pub fn parse_string_with_char_provider<'a, T: CharProvider<'a>>(
104 chars: &mut T,
105 allow_extended_escapes: bool,
106) -> Result<Cow<'a, str>, ParseStringError> {
107 debug_assert!(
108 chars.current_char() == Some('\'') || chars.current_char() == Some('"'),
109 "Expected \", was {:?}",
110 chars.current_char()
111 );
112 let is_double_quote = chars.current_char() == Some('"');
113 let mut last_start_byte_index = chars.byte_index() + 1;
114 let mut text: Option<String> = None;
115 let mut last_was_backslash = false;
116 let mut found_end_string = false;
117 let token_start = chars.byte_index();
118
119 while let Some(current_char) = chars.move_next_char() {
120 if last_was_backslash {
121 let escape_start = chars.byte_index() - 1; if allow_extended_escapes && let Some(decoded) = parse_extended_escape(chars, current_char, escape_start)? {
123 let previous_text = &chars.text()[last_start_byte_index..escape_start];
124 let text = text.get_or_insert_with(String::new);
125 text.push_str(previous_text);
126 if let Some(decoded) = decoded {
127 text.push(decoded);
128 }
129 last_start_byte_index = chars.byte_index() + chars.current_char().map(|c| c.len_utf8()).unwrap_or(0);
130 last_was_backslash = false;
131 continue;
132 }
133 match current_char {
134 '"' | '\'' | '\\' | '/' | 'b' | 'f' | 'u' | 'r' | 'n' | 't' => {
135 if current_char == '"' {
136 if !is_double_quote {
137 return Err(ParseStringError {
138 byte_index: escape_start,
139 kind: ParseStringErrorKind::InvalidEscapeInSingleQuoteString,
140 });
141 }
142 } else if current_char == '\'' && is_double_quote {
143 return Err(ParseStringError {
144 byte_index: escape_start,
145 kind: ParseStringErrorKind::InvalidEscapeInDoubleQuoteString,
146 });
147 }
148
149 let previous_text = &chars.text()[last_start_byte_index..escape_start];
150 if text.is_none() {
151 text = Some(String::new());
152 }
153 let text = text.as_mut().unwrap();
154 text.push_str(previous_text);
155 if current_char == 'u' {
156 let hex_char = parse_hex_char(chars).map_err(|kind| ParseStringError {
157 byte_index: escape_start,
158 kind,
159 })?;
160 text.push(hex_char);
161 last_start_byte_index = chars.byte_index() + chars.current_char().map(|c| c.len_utf8()).unwrap_or(0);
162 } else {
163 text.push(match current_char {
164 'b' => '\u{08}',
165 'f' => '\u{0C}',
166 't' => '\t',
167 'r' => '\r',
168 'n' => '\n',
169 _ => current_char,
170 });
171 last_start_byte_index = chars.byte_index() + current_char.len_utf8();
172 }
173 }
174 _ => {
175 return Err(ParseStringError {
176 byte_index: escape_start,
177 kind: ParseStringErrorKind::InvalidEscape,
178 });
179 }
180 }
181 last_was_backslash = false;
182 } else if is_double_quote && current_char == '"' || !is_double_quote && current_char == '\'' {
183 found_end_string = true;
184 break;
185 } else {
186 last_was_backslash = current_char == '\\';
187 }
188 }
189
190 if found_end_string {
191 chars.move_next_char();
192 let final_segment = &chars.text()[last_start_byte_index..chars.byte_index() - 1];
193 Ok(match text {
194 Some(mut text) => {
195 text.push_str(final_segment);
196 Cow::Owned(text)
197 }
198 None => Cow::Borrowed(final_segment),
199 })
200 } else {
201 Err(ParseStringError {
202 byte_index: token_start,
203 kind: ParseStringErrorKind::UnterminatedStringLiteral,
204 })
205 }
206}
207
208fn parse_extended_escape<'a, T: CharProvider<'a>>(
210 chars: &mut T,
211 current_char: char,
212 escape_start: usize,
213) -> Result<Option<Option<char>>, ParseStringError> {
214 let invalid_escape = || ParseStringError {
215 byte_index: escape_start,
216 kind: ParseStringErrorKind::InvalidEscape,
217 };
218 let decoded = match current_char {
219 'v' => Some('\u{0B}'),
220 '0' => {
221 let next_index = chars.byte_index() + 1;
223 if chars.text()[next_index..].starts_with(|c: char| c.is_ascii_digit()) {
224 return Err(invalid_escape());
225 }
226 Some('\0')
227 }
228 'x' => {
229 let mut value = 0;
230 for _ in 0..2 {
231 match chars.move_next_char().and_then(|c| c.to_digit(16)) {
232 Some(digit) => value = value * 16 + digit,
233 None => {
234 return Err(ParseStringError {
235 byte_index: escape_start,
236 kind: ParseStringErrorKind::ExpectedTwoHexDigits,
237 });
238 }
239 }
240 }
241 Some(char::from_u32(value).unwrap())
243 }
244 '\n' | '\u{2028}' | '\u{2029}' => None,
245 '\r' => {
246 if chars.text()[chars.byte_index() + 1..].starts_with('\n') {
247 chars.move_next_char();
248 }
249 None
250 }
251 '1'..='9' => return Err(invalid_escape()),
252 '\\' | '/' | 'b' | 'f' | 'u' | 'r' | 'n' | 't' => return Ok(None),
253 _ => Some(current_char),
254 };
255 Ok(Some(decoded))
256}
257
258fn read_four_hex_digits<'a, T: CharProvider<'a>>(
259 chars: &mut T,
260 buf: &mut [u8; 4],
261) -> Result<u32, ParseStringErrorKind> {
262 for slot in buf.iter_mut() {
263 match chars.move_next_char() {
264 Some(c) if c.is_ascii_hexdigit() => *slot = c as u8,
265 _ => return Err(ParseStringErrorKind::ExpectedFourHexDigits),
266 }
267 }
268 let hex_str = std::str::from_utf8(buf).unwrap();
270 u32::from_str_radix(hex_str, 16).map_err(|_| ParseStringErrorKind::InvalidUnicodeEscapeSequence(hex_str.to_string()))
271}
272
273fn hex_buf_to_str(buf: &[u8; 4]) -> String {
274 std::str::from_utf8(buf).unwrap().to_string()
275}
276
277fn parse_hex_char<'a, T: CharProvider<'a>>(chars: &mut T) -> Result<char, ParseStringErrorKind> {
278 let mut buf1 = [0u8; 4];
279 let hex_value = read_four_hex_digits(chars, &mut buf1)?;
280
281 let hex_char = if (0xD800..=0xDBFF).contains(&hex_value) {
283 if chars.move_next_char() != Some('\\') {
285 return Err(ParseStringErrorKind::InvalidUnicodeEscapeSequence(format!(
286 "{} (unpaired high surrogate)",
287 hex_buf_to_str(&buf1)
288 )));
289 }
290
291 if chars.move_next_char() != Some('u') {
292 return Err(ParseStringErrorKind::InvalidUnicodeEscapeSequence(format!(
293 "{} (unpaired high surrogate)",
294 hex_buf_to_str(&buf1)
295 )));
296 }
297
298 let mut buf2 = [0u8; 4];
300 let hex_value2 = read_four_hex_digits(chars, &mut buf2)?;
301
302 if !(0xDC00..=0xDFFF).contains(&hex_value2) {
304 return Err(ParseStringErrorKind::InvalidUnicodeEscapeSequence(format!(
305 "{} (high surrogate not followed by low surrogate)",
306 hex_buf_to_str(&buf1)
307 )));
308 }
309
310 let code_point = ((hex_value - 0xD800) * 0x400) + (hex_value2 - 0xDC00) + 0x10000;
312
313 match std::char::from_u32(code_point) {
314 Some(c) => c,
315 None => {
316 return Err(ParseStringErrorKind::InvalidUnicodeEscapeSequence(format!(
317 "{}\\u{} (invalid surrogate pair)",
318 hex_buf_to_str(&buf1),
319 hex_buf_to_str(&buf2)
320 )));
321 }
322 }
323 } else if (0xDC00..=0xDFFF).contains(&hex_value) {
324 return Err(ParseStringErrorKind::InvalidUnicodeEscapeSequence(format!(
326 "{} (unpaired low surrogate)",
327 hex_buf_to_str(&buf1)
328 )));
329 } else {
330 match std::char::from_u32(hex_value) {
332 Some(hex_char) => hex_char,
333 None => {
334 return Err(ParseStringErrorKind::InvalidUnicodeEscapeSequence(hex_buf_to_str(
335 &buf1,
336 )));
337 }
338 }
339 };
340 Ok(hex_char)
341}