use crate::syntax::{SURROGATE_RANGE, is_whitespace};
use std::{char::REPLACEMENT_CHARACTER, str::Chars};
pub trait ParseEscape {
fn codepoint_to_char(value: u32) -> char {
if value == 0 || SURROGATE_RANGE.contains(&value) {
REPLACEMENT_CHARACTER
} else {
char::from_u32(value).unwrap_or(REPLACEMENT_CHARACTER)
}
}
fn consume_hex_escape(&mut self, first_digit: char) -> (u32, u8);
fn consume_escape_whitespace(&mut self) -> u8;
fn parse_escape_sequence(&mut self) -> (char, u8);
}
impl<'a> ParseEscape for Chars<'a> {
fn consume_hex_escape(&mut self, first_digit: char) -> (u32, u8) {
let mut value = first_digit.to_digit(16).unwrap();
let mut i = 1u8;
while i < 6 {
if let Some(next_char) = self.as_str().chars().next() {
if let Some(hex_value) = next_char.to_digit(16) {
self.next();
value = (value << 4) | hex_value;
i += 1;
} else {
break;
}
} else {
break;
}
}
(value, i)
}
fn consume_escape_whitespace(&mut self) -> u8 {
if let Some(next_char) = self.as_str().chars().next()
&& is_whitespace(next_char)
{
self.next();
if next_char == '\r' && self.as_str().starts_with('\n') {
self.next();
2
} else {
1
}
} else {
0
}
}
fn parse_escape_sequence(&mut self) -> (char, u8) {
if let Some(c) = self.next() {
if !c.is_ascii_hexdigit() {
return (c, c.len_utf8() as u8);
}
let (value, hex_len) = self.consume_hex_escape(c);
let ws_len = self.consume_escape_whitespace();
(Self::codepoint_to_char(value), hex_len + ws_len)
} else {
(REPLACEMENT_CHARACTER, 0)
}
}
}