use core::char;
use core::fmt;
use core::iter::Peekable;
use core::ops;
#[derive(Debug)]
pub(crate) enum ErrorKind {
BadEscapeSequence,
BadUnicodeEscapeInByteString,
BadUnicodeEscape,
BadHexEscapeChar,
BadHexEscapeByte,
BadByteEscape,
}
impl fmt::Display for ErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorKind::BadEscapeSequence => write!(f, "Bad escape sequence"),
ErrorKind::BadUnicodeEscapeInByteString => {
write!(
f,
"Unicode escapes are not supported as a byte or byte string"
)
}
ErrorKind::BadUnicodeEscape => {
write!(f, "Bad unicode escape")
}
ErrorKind::BadHexEscapeChar => {
write!(f, "This form of character escape may only be used with characters in the range [\\x00-\\x7f]")
}
ErrorKind::BadHexEscapeByte => {
write!(f,
"This form of byte escape may only be used with characters in the range [\\x00-\\xff]"
)
}
ErrorKind::BadByteEscape => {
write!(f, "Bad byte escape")
}
}
}
}
impl core::error::Error for ErrorKind {}
#[derive(Debug, Clone, Copy)]
pub(crate) struct WithTemplate(pub(super) bool);
impl ops::Deref for WithTemplate {
type Target = bool;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Clone, Copy)]
pub(super) struct WithLineCont(pub(super) bool);
impl ops::Deref for WithLineCont {
type Target = bool;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub(super) fn parse_byte_escape(
it: &mut Peekable<impl Iterator<Item = (usize, char)>>,
with_line_cont: WithLineCont,
) -> Result<Option<u8>, ErrorKind> {
let (_, c) = it.next().ok_or(ErrorKind::BadEscapeSequence)?;
Ok(Some(match c {
'\n' | '\r' if *with_line_cont => {
while let Some((_, c)) = it.peek() {
if !char::is_whitespace(*c) {
break;
}
it.next();
}
return Ok(None);
}
'\'' => b'\'',
'\"' => b'\"',
'n' => b'\n',
'r' => b'\r',
't' => b'\t',
'\\' => b'\\',
'0' => b'\0',
'x' => {
let result = parse_hex_escape(it)?;
if result > 0xff {
return Err(ErrorKind::BadHexEscapeByte);
}
result as u8
}
'u' => {
return Err(ErrorKind::BadUnicodeEscapeInByteString);
}
_ => {
return Err(ErrorKind::BadEscapeSequence);
}
}))
}
pub(super) fn parse_char_escape(
it: &mut Peekable<impl Iterator<Item = (usize, char)>>,
with_template: WithTemplate,
with_line_cont: WithLineCont,
) -> Result<Option<char>, ErrorKind> {
let (_, c) = it.next().ok_or(ErrorKind::BadEscapeSequence)?;
Ok(Some(match c {
'\n' | '\r' if *with_line_cont => {
while let Some((_, c)) = it.peek() {
if !char::is_whitespace(*c) {
break;
}
it.next();
}
return Ok(None);
}
'$' if *with_template => '$',
'`' if *with_template => '`',
'\'' => '\'',
'\"' => '\"',
'n' => '\n',
'r' => '\r',
't' => '\t',
'\\' => '\\',
'0' => '\0',
'x' => {
let result = parse_hex_escape(it)?;
if result > 0x7f {
return Err(ErrorKind::BadHexEscapeChar);
}
if let Some(c) = char::from_u32(result) {
c
} else {
return Err(ErrorKind::BadByteEscape);
}
}
'u' => parse_unicode_escape(it)?,
_ => {
return Err(ErrorKind::BadEscapeSequence);
}
}))
}
pub(super) fn parse_hex_escape(
it: &mut Peekable<impl Iterator<Item = (usize, char)>>,
) -> Result<u32, ErrorKind> {
let mut result = 0u32;
for _ in 0..2 {
let (_, c) = it.next().ok_or(ErrorKind::BadByteEscape)?;
result = result.checked_shl(4).ok_or(ErrorKind::BadByteEscape)?;
result += match c {
'0'..='9' => c as u32 - '0' as u32,
'a'..='f' => c as u32 - 'a' as u32 + 10,
'A'..='F' => c as u32 - 'A' as u32 + 10,
_ => return Err(ErrorKind::BadByteEscape),
};
}
Ok(result)
}
pub(super) fn parse_unicode_escape(
it: &mut Peekable<impl Iterator<Item = (usize, char)>>,
) -> Result<char, ErrorKind> {
match it.next() {
Some((_, '{')) => (),
_ => return Err(ErrorKind::BadUnicodeEscape),
};
let mut first = true;
let mut result = 0u32;
loop {
let (_, c) = it.next().ok_or(ErrorKind::BadUnicodeEscape)?;
match c {
'}' => {
if first {
return Err(ErrorKind::BadUnicodeEscape);
}
if let Some(c) = char::from_u32(result) {
return Ok(c);
}
return Err(ErrorKind::BadUnicodeEscape);
}
c => {
first = false;
result = match result.checked_shl(4) {
Some(result) => result,
None => {
return Err(ErrorKind::BadUnicodeEscape);
}
};
result += match c {
'0'..='9' => c as u32 - '0' as u32,
'a'..='f' => c as u32 - 'a' as u32 + 10,
'A'..='F' => c as u32 - 'A' as u32 + 10,
_ => {
return Err(ErrorKind::BadUnicodeEscape);
}
};
}
}
}
}