106 lines
2.6 KiB
Rust
106 lines
2.6 KiB
Rust
|
use std::num::{ParseIntError, ParseFloatError};
|
||
|
|
||
|
use thiserror::Error;
|
||
|
|
||
|
#[derive(Clone, Debug, Error)]
|
||
|
pub enum ParseError {
|
||
|
#[error("{0}")]
|
||
|
StrEscape(#[from] StrEscapeError),
|
||
|
#[error("{0}")]
|
||
|
Integer(#[from] ParseIntError),
|
||
|
#[error("{0}")]
|
||
|
Float(#[from] ParseFloatError),
|
||
|
}
|
||
|
|
||
|
#[derive(Clone, Copy, Debug, Error)]
|
||
|
pub enum StrEscapeError {
|
||
|
#[error("EOF in string escape")]
|
||
|
Eof,
|
||
|
#[error("EOF in string escape \\x")]
|
||
|
HexEof,
|
||
|
#[error("EOF in string escape \\u")]
|
||
|
UnicodeEof,
|
||
|
#[error("Invalid string escape \\{0}")]
|
||
|
Invalid(char),
|
||
|
#[error("Invalid hex digit '{0}' in string escape")]
|
||
|
InvalidHex(char),
|
||
|
#[error("Missing brace after string escape \\u")]
|
||
|
MissingBrace,
|
||
|
#[error("Invalid codepoint in string escape: {0:x}")]
|
||
|
InvalidCodepoint(u32),
|
||
|
#[error("Codepoint in string escape too large")]
|
||
|
CodepointTooLarge,
|
||
|
}
|
||
|
|
||
|
pub fn parse_str_escapes(src: &str) -> Result<String, StrEscapeError> {
|
||
|
let mut s = String::with_capacity(src.len());
|
||
|
let mut chars = src.chars();
|
||
|
|
||
|
while let Some(c) = chars.next() {
|
||
|
if c != '\\' { s.push(c); continue }
|
||
|
let c = chars.next().ok_or(StrEscapeError::Eof)?;
|
||
|
match c {
|
||
|
'"' | '\'' | '\\' => s.push(c),
|
||
|
'0' => s.push('\0'),
|
||
|
'a' => s.push('\x07'),
|
||
|
'b' => s.push('\x08'),
|
||
|
't' => s.push('\t'),
|
||
|
'n' => s.push('\n'),
|
||
|
'v' => s.push('\x0b'),
|
||
|
'f' => s.push('\x0c'),
|
||
|
'r' => s.push('\r'),
|
||
|
'e' => s.push('\x1b'),
|
||
|
'x' => {
|
||
|
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
|
||
|
let n1 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
|
||
|
let c = chars.next().ok_or(StrEscapeError::HexEof)?;
|
||
|
let n2 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
|
||
|
// can't panic: all bytes 0..256 are valid codepoints
|
||
|
s.push(char::from_u32(n1 * 16 + n2).unwrap());
|
||
|
},
|
||
|
'u' => {
|
||
|
let Some('{') = chars.next() else {
|
||
|
return Err(StrEscapeError::MissingBrace)
|
||
|
};
|
||
|
let mut n = 0u32;
|
||
|
loop {
|
||
|
let Some(c) = chars.next() else {
|
||
|
return Err(StrEscapeError::UnicodeEof)
|
||
|
};
|
||
|
if c == '}' { break }
|
||
|
if n >= 0x1000_0000u32 {
|
||
|
return Err(StrEscapeError::CodepointTooLarge)
|
||
|
}
|
||
|
n = n * 16 + c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
|
||
|
}
|
||
|
let ch = char::from_u32(n).ok_or(StrEscapeError::InvalidCodepoint(n))?;
|
||
|
s.push(ch);
|
||
|
|
||
|
},
|
||
|
c => return Err(StrEscapeError::Invalid(c)),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Ok(s)
|
||
|
}
|
||
|
|
||
|
pub fn parse_float(f: &str) -> Result<f64, ParseFloatError> {
|
||
|
let mut s = String::new();
|
||
|
for c in f.chars() {
|
||
|
if c != '_' {
|
||
|
s.push(c);
|
||
|
}
|
||
|
}
|
||
|
s.parse()
|
||
|
}
|
||
|
|
||
|
pub fn parse_int(f: &str, radix: u32) -> Result<i64, ParseIntError> {
|
||
|
let mut s = String::new();
|
||
|
for c in f.chars() {
|
||
|
if c != '_' {
|
||
|
s.push(c);
|
||
|
}
|
||
|
}
|
||
|
i64::from_str_radix(&s, radix)
|
||
|
}
|