use atm_parser_helper::{Eoi, Error, ParserHelper};
#[cfg(feature = "arbitrary")]
pub mod testing;
pub trait WhiteSpaceE : Eoi {
fn utf8_comment() -> Self;
}
pub fn spaces<E: WhiteSpaceE>(p: &mut ParserHelper) -> Result<(), Error<E>> {
loop {
match p.peek_or_end() {
Some(0x09) | Some(0x0a) | Some(0x0d) | Some(0x20) => p.advance(1),
Some(0x23) => comment(p)?,
Some(_) | None => return Ok(()),
}
}
}
fn comment<E: WhiteSpaceE>(p: &mut ParserHelper) -> Result<(), Error<E>> {
let start = p.position();
p.advance(1); loop {
match p.next_or_end() {
Some(0x0a) | None => {
match std::str::from_utf8(p.slice(start..p.position())) {
Ok(_) => return Ok(()),
Err(_) => return p.fail_at_position(E::utf8_comment(), start),
}
}
Some(_) => {}
}
}
}
pub trait IntLiteralE : Eoi {
fn int_no_digits() -> Self;
fn not_int_literal() -> Self;
}
pub fn parse_int<I, E: IntLiteralE>(
p: &mut ParserHelper,
from_decimal: fn(&str) -> Result<I, E>,
from_hex: fn(&str) -> Result<I, E>,
from_binary: fn(&str) -> Result<I, E>,
) -> Result<I, Error<E>> {
let start = p.position();
let negative = p.advance_over(b"-");
let has_sign = negative || p.advance_over(b"+");
let is_hex = !has_sign && p.advance_over(b"0x");
let is_binary = !is_hex && (!has_sign && p.advance_over(b"0b"));
if is_hex {
if !is_hex_digit(p.peek()?) {
return p.fail(E::int_no_digits());
}
let start = p.position();
p.skip(is_hex_digit_or_underscore);
let digits_with_underscores = unsafe { std::str::from_utf8_unchecked(p.slice(start..p.position())) };
let without_underscores = digits_with_underscores.replace("_", "");
match from_hex(&without_underscores) {
Ok(n) => return Ok(n),
Err(e) => return p.fail(e),
}
} else if is_binary {
if !is_binary_digit(p.peek()?) {
return p.fail(E::int_no_digits());
}
let start = p.position();
p.skip(is_binary_digit_or_underscore);
let digits_with_underscores = unsafe { std::str::from_utf8_unchecked(p.slice(start..p.position())) };
let without_underscores = digits_with_underscores.replace("_", "");
match from_binary(&without_underscores) {
Ok(n) => return Ok(n),
Err(e) => return p.fail(e),
}
} else {
if !is_digit(p.peek()?) {
if has_sign {
return p.fail(E::int_no_digits());
} else {
return p.fail(E::not_int_literal());
}
}
p.skip(is_digit_or_underscore);
let digits_with_underscores = unsafe { std::str::from_utf8_unchecked(p.slice(start..p.position())) };
let without_underscores = digits_with_underscores.replace("_", "");
match from_decimal(&without_underscores) {
Ok(n) => return Ok(n),
Err(e) => return p.fail(e),
}
}
}
pub trait FloatLiteralE : Eoi {
fn float_no_leading_digits() -> Self;
fn float_no_point() -> Self;
fn float_no_trailing_digits() -> Self;
fn float_no_exponent_digits() -> Self;
fn not_float_literal() -> Self;
}
pub fn parse_float<F, E: FloatLiteralE>(
p: &mut ParserHelper,
from_s: fn(&str) -> Result<F, E>,
neg_inf: F,
pos_inf: F,
nan: F,
) -> Result<F, Error<E>> {
let start = p.position();
let negative = p.advance_over(b"-");
let has_sign = negative || p.advance_over(b"+");
match p.peek()? {
0x49 => {
p.expect_bytes(b"Inf", E::not_float_literal())?;
return Ok(if negative { neg_inf } else { pos_inf });
}
0x4e => {
p.expect_bytes(b"NaN", E::not_float_literal())?;
return Ok(nan);
}
_ => {}
}
if !is_digit(p.peek()?) {
if has_sign {
return p.fail(E::float_no_leading_digits());
} else {
return p.fail(E::not_float_literal());
}
}
p.skip(is_digit_or_underscore);
p.expect('.' as u8, E::float_no_point())?;
if !is_digit(p.peek()?) {
return p.fail(E::float_no_trailing_digits());
}
p.skip(is_digit_or_underscore);
if let Ok(0x45 | 0x65) = p.peek::<E>() {
p.advance(1);
let negative = p.advance_over(b"-");
if !negative {
p.advance_over(b"+");
}
if !is_digit(p.peek()?) {
return p.fail(E::float_no_exponent_digits());
}
p.skip(is_digit_or_underscore);
}
let digits_with_underscores = unsafe { std::str::from_utf8_unchecked(p.slice(start..p.position())) };
let without_underscores = digits_with_underscores.replace("_", "");
match from_s(&without_underscores) {
Ok(n) => return Ok(n),
Err(_) => panic!("Prior parsing should have ensured a valid input to f64::from_str"),
}
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum Number<I, F> {
Float(F),
Integer(I),
}
pub fn parse_number<I, F, E: FloatLiteralE + IntLiteralE>(
p: &mut ParserHelper,
from_decimal: fn(&str) -> Result<I, E>,
from_hex: fn(&str) -> Result<I, E>,
from_binary: fn(&str) -> Result<I, E>,
from_s: fn(&str) -> Result<F, E>,
neg_inf: F,
pos_inf: F,
nan: F,
) -> Result<Number<I, F>, Error<E>> {
let start = p.position();
let negative = p.advance_over(b"-");
let has_sign = negative || p.advance_over(b"+");
match p.peek()? {
0x49 => {
p.expect_bytes(b"Inf", E::not_float_literal())?;
return Ok(if negative { Number::Float(neg_inf) } else { Number::Float(pos_inf) });
}
0x4e => {
p.expect_bytes(b"NaN", E::not_float_literal())?;
return Ok(Number::Float(nan));
}
_ => {}
}
let is_hex = !has_sign && p.advance_over(b"0x");
let is_binary = !is_hex && (!has_sign && p.advance_over(b"0b"));
if is_hex {
if !is_hex_digit(p.peek()?) {
return p.fail(E::int_no_digits());
}
let start = p.position();
p.skip(is_hex_digit_or_underscore);
let digits_with_underscores = unsafe { std::str::from_utf8_unchecked(p.slice(start..p.position())) };
let without_underscores = digits_with_underscores.replace("_", "");
match from_hex(&without_underscores) {
Ok(n) => return Ok(Number::Integer(n)),
Err(e) => return p.fail(e),
}
} else if is_binary {
if !is_binary_digit(p.peek()?) {
return p.fail(E::int_no_digits());
}
let start = p.position();
p.skip(is_binary_digit_or_underscore);
let digits_with_underscores = unsafe { std::str::from_utf8_unchecked(p.slice(start..p.position())) };
let without_underscores = digits_with_underscores.replace("_", "");
match from_binary(&without_underscores) {
Ok(n) => return Ok(Number::Integer(n)),
Err(e) => return p.fail(e),
}
} else {
if !is_digit(p.peek()?) {
if has_sign {
return p.fail(E::int_no_digits());
} else {
return p.fail(E::not_int_literal());
}
}
p.skip(is_digit_or_underscore);
match p.peek::<E>() {
Ok(0x2e) => {
p.advance(1);
if !is_digit(p.peek()?) {
return p.fail(E::float_no_trailing_digits());
}
p.skip(is_digit_or_underscore);
if let Ok(0x45 | 0x65) = p.peek::<E>() {
p.advance(1);
let negative = p.advance_over(b"-");
if !negative {
p.advance_over(b"+");
}
if !is_digit(p.peek()?) {
return p.fail(E::float_no_exponent_digits());
}
p.skip(is_digit_or_underscore);
}
let digits_with_underscores = unsafe { std::str::from_utf8_unchecked(p.slice(start..p.position())) };
let without_underscores = digits_with_underscores.replace("_", "");
match from_s(&without_underscores) {
Ok(n) => return Ok(Number::Float(n)),
Err(_) => panic!("Prior parsing should have ensured a valid input to f64::from_str"),
}
}
_ => {
let digits_with_underscores = unsafe { std::str::from_utf8_unchecked(p.slice(start..p.position())) };
let without_underscores = digits_with_underscores.replace("_", "");
match from_decimal(&without_underscores) {
Ok(n) => return Ok(Number::Integer(n)),
Err(e) => return p.fail(e),
}
}
}
}
}
pub trait ByteStringLiteralE : Eoi + WhiteSpaceE + IntLiteralE {
fn odd_hex_digits() -> Self;
fn number_binary_digits() -> Self;
fn expected_comma() -> Self;
fn byte_out_of_bounds() -> Self;
fn not_byte_string_literal() -> Self;
}
pub fn parse_byte_string<E: ByteStringLiteralE>(p: &mut ParserHelper) -> Result<Vec<u8>, Error<E>> {
p.expect('@' as u8, E::not_byte_string_literal())?;
match p.next()? {
0x5b => {
let mut r = Vec::new();
loop {
spaces(p)?;
if p.peek()? == (']' as u8) {
p.advance(1);
return Ok(r);
}
let b = parse_int(p, u8_from_decimal, u8_from_hex, u8_from_binary)?;
r.push(b);
spaces(p)?;
if p.peek()? == (']' as u8) {
p.advance(1);
return Ok(r);
} else if p.peek()? == (',' as u8) {
p.advance(1);
} else {
return p.fail(E::expected_comma());
}
}
}
0x78 => {
let start = p.position();
p.skip(is_hex_digit_or_underscore);
let digits_with_underscores = unsafe { std::str::from_utf8_unchecked(p.slice(start..p.position())) };
let without_underscores = digits_with_underscores.replace("_", "");
if without_underscores.len() % 2 == 0 {
let mut buf = Vec::new();
let mut i = 0;
while i < without_underscores.len() {
buf.push(u8::from_str_radix(unsafe {std::str::from_utf8_unchecked(&without_underscores.as_bytes()[i..i + 2])}, 16).unwrap());
i += 2;
}
return Ok(buf);
} else {
p.fail(E::odd_hex_digits())
}
}
0x62 => {
let start = p.position();
p.skip(is_binary_digit_or_underscore);
let digits_with_underscores = unsafe { std::str::from_utf8_unchecked(p.slice(start..p.position())) };
let without_underscores = digits_with_underscores.replace("_", "");
if without_underscores.len() % 8 == 0 {
let mut buf = Vec::new();
let mut i = 0;
while i < without_underscores.len() {
buf.push(u8::from_str_radix(unsafe {std::str::from_utf8_unchecked(&without_underscores.as_bytes()[i..i + 8])}, 2).unwrap());
i += 8;
}
return Ok(buf);
} else {
p.fail(E::number_binary_digits())
}
}
_ => p.fail(E::not_byte_string_literal()),
}
}
pub trait Utf8StringLiteralE : Eoi {
fn raw_not_utf8() -> Self;
fn raw_too_many_ats() -> Self;
fn escaping_not_utf8() -> Self;
fn invalid_escape_sequence() -> Self;
fn unicode_escape_number_digits() -> Self;
fn unicode_escape_invalid_scalar() -> Self;
fn unicode_escape_no_closing() -> Self;
fn not_utf8_string_literal() -> Self;
}
pub fn parse_utf8_string<E: Utf8StringLiteralE>(p: &mut ParserHelper) -> Result<String, Error<E>> {
let start_ats = p.position();
p.skip(is_at);
let ats = p.position() - start_ats;
p.expect('"' as u8, E::not_utf8_string_literal())?;
let start = p.position();
if ats == 0 {
let mut s = String::new();
loop {
if p.advance_over(b"\"") {
return Ok(s);
} else {
s.push(parse_char(p)?);
}
}
} else {
let mut consecutive_ats = None;
let mut end = 0;
loop {
let b = p.next()?;
match b {
0x22 => {
consecutive_ats = Some(0);
end = p.position() - 1;
}
0x40 => {
match consecutive_ats.as_mut() {
None => {}
Some(n) => {
*n += 1;
if *n > 255 {
return p.fail(E::raw_too_many_ats());
}
if *n == ats {
return std::str::from_utf8(p.slice(start..end))
.map(|s| s.to_string())
.map_err(|_| p.fail::<(), E>(E::raw_not_utf8()).unwrap_err());
}
}
}
}
_ => consecutive_ats = None,
}
}
}
}
fn parse_char<E: Utf8StringLiteralE>(p: &mut ParserHelper) -> Result<char, Error<E>> {
let start = p.position();
let fst = p.next()?;
let mut scalar;
if (fst & 0b1000_0000) == 0b0000_0000 {
scalar = fst as u32;
} else if (fst & 0b1110_0000) == 0b1100_0000 {
scalar = (fst & 0b0001_1111) as u32;
scalar <<= 6;
scalar = ((p.next()? & 0b0011_1111) as u32) | scalar;
} else if (fst & 0b1111_0000) == 0b1110_0000 {
scalar = (fst & 0b0000_1111) as u32;
scalar <<= 6;
scalar = ((p.next()? & 0b0011_1111) as u32) | scalar;
scalar <<= 6;
scalar = ((p.next()? & 0b0011_1111) as u32) | scalar;
} else if (fst & 0b1111_1000) == 0b1111_0000 {
scalar = (fst & 0b0000_0111) as u32;
scalar <<= 6;
scalar = ((p.next()? & 0b0011_1111) as u32) | scalar;
scalar <<= 6;
scalar = ((p.next()? & 0b0011_1111) as u32) | scalar;
scalar <<= 6;
scalar = ((p.next()? & 0b0011_1111) as u32) | scalar;
} else {
return p.fail(E::escaping_not_utf8())?;
}
if let Err(_) = std::str::from_utf8(p.slice(start..p.position())) {
return p.fail(E::escaping_not_utf8()); }
match core::char::from_u32(scalar) {
None => return p.fail(E::escaping_not_utf8()),
Some(c) => {
if c == '\\' {
match p.next()? {
0x22 => return Ok('\"'),
0x30 => return Ok('\0'),
0x5c => return Ok('\\'),
0x6e => return Ok('\n'),
0x74 => return Ok('\t'),
0x7b => {
let start = p.position();
p.skip(is_hex_digit);
let end = p.position();
let len = end - start;
if len < 1 || len > 6 {
return p.fail(E::unicode_escape_number_digits());
}
let raw = p.slice(start..end);
let numeric = u32::from_str_radix(unsafe { std::str::from_utf8_unchecked(raw) }, 16).unwrap();
match std::char::from_u32(numeric) {
None => return p.fail(E::unicode_escape_invalid_scalar()),
Some(c) => {
p.expect('}' as u8, E::unicode_escape_no_closing())?;
return Ok(c);
}
}
}
_ => return p.fail(E::invalid_escape_sequence()),
}
} else {
return Ok(c);
}
}
}
}
fn is_at(b: u8) -> bool {
b == ('@' as u8)
}
fn is_digit(byte: u8) -> bool {
byte.is_ascii_digit()
}
fn is_hex_digit(byte: u8) -> bool {
byte.is_ascii_hexdigit()
}
fn is_binary_digit(byte: u8) -> bool {
byte == ('0' as u8) || byte == ('1' as u8)
}
fn is_digit_or_underscore(byte: u8) -> bool {
byte == ('_' as u8) || byte.is_ascii_digit()
}
fn is_hex_digit_or_underscore(byte: u8) -> bool {
byte == ('_' as u8) || is_hex_digit(byte)
}
fn is_binary_digit_or_underscore(byte: u8) -> bool {
byte == ('_' as u8) || is_binary_digit(byte)
}
pub fn u8_from_decimal<E: ByteStringLiteralE>(s: &str) -> Result<u8, E> {
u8::from_str_radix(s, 10).map_err(|_| E::byte_out_of_bounds())
}
pub fn u8_from_hex<E: ByteStringLiteralE>(s: &str) -> Result<u8, E> {
u8::from_str_radix(s, 16).map_err(|_| E::byte_out_of_bounds())
}
pub fn u8_from_binary<E: ByteStringLiteralE>(s: &str) -> Result<u8, E> {
u8::from_str_radix(s, 2).map_err(|_| E::byte_out_of_bounds())
}