(buf: &mut LexBuf)
| 279 | } |
| 280 | |
| 281 | fn lex_extended_string(buf: &mut LexBuf) -> Result<Token, LexerError> { |
| 282 | fn lex_unicode_escape(buf: &mut LexBuf, n: usize) -> Result<char, LexerError> { |
| 283 | let pos = buf.pos() - 2; |
| 284 | buf.next_n(n) |
| 285 | .and_then(|s| u32::from_str_radix(s, 16).ok()) |
| 286 | .and_then(|codepoint| char::try_from(codepoint).ok()) |
| 287 | .ok_or_else(|| LexerError::new(pos, "invalid unicode escape")) |
| 288 | } |
| 289 | |
| 290 | // We do not support octal (\o) or hexadecimal (\x) escapes, since it is |
| 291 | // possible to construct invalid UTF-8 with these escapes. We could check |
| 292 | // for and reject invalid UTF-8, of course, but it is too annoying to be |
| 293 | // worth doing right now. We still lex the escapes to produce nice error |
| 294 | // messages. |
| 295 | |
| 296 | fn lex_octal_escape(buf: &mut LexBuf) -> LexerError { |
| 297 | let pos = buf.pos() - 2; |
| 298 | buf.take_while(|ch| matches!(ch, '0'..='7')); |
| 299 | LexerError::new(pos, "octal escapes are not supported") |
| 300 | } |
| 301 | |
| 302 | fn lex_hexadecimal_escape(buf: &mut LexBuf) -> LexerError { |
| 303 | let pos = buf.pos() - 2; |
| 304 | buf.take_while(|ch| matches!(ch, '0'..='9' | 'A'..='F' | 'a'..='f')); |
| 305 | LexerError::new(pos, "hexadecimal escapes are not supported") |
| 306 | } |
| 307 | |
| 308 | let mut s = String::new(); |
| 309 | loop { |
| 310 | let pos = buf.pos() - 1; |
| 311 | loop { |
| 312 | match buf.next() { |
| 313 | Some('\'') if buf.consume('\'') => s.push('\''), |
| 314 | Some('\'') => break, |
| 315 | Some('\\') => match buf.next() { |
| 316 | Some('b') => s.push('\x08'), |
| 317 | Some('f') => s.push('\x0c'), |
| 318 | Some('n') => s.push('\n'), |
| 319 | Some('r') => s.push('\r'), |
| 320 | Some('t') => s.push('\t'), |
| 321 | Some('u') => s.push(lex_unicode_escape(buf, 4)?), |
| 322 | Some('U') => s.push(lex_unicode_escape(buf, 8)?), |
| 323 | Some('0'..='7') => return Err(lex_octal_escape(buf)), |
| 324 | Some('x') => return Err(lex_hexadecimal_escape(buf)), |
| 325 | Some(c) => s.push(c), |
| 326 | None => bail!(pos, "unterminated quoted string"), |
| 327 | }, |
| 328 | Some(c) => s.push(c), |
| 329 | None => bail!(pos, "unterminated quoted string"), |
| 330 | } |
| 331 | } |
| 332 | if !lex_to_adjacent_string(buf) { |
| 333 | return Ok(Token::String(s)); |
| 334 | } |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | fn lex_to_adjacent_string(buf: &mut LexBuf) -> bool { |
no test coverage detected