use nom::{
IResult, Parser,
branch::alt,
bytes::complete::tag,
character::complete::{char, digit1, multispace0, none_of, one_of},
combinator::{map, opt, recognize},
error::Error as NomError,
multi::many0,
sequence::pair,
};
use crate::parser::ast::Value;
use crate::parser::grammar::numbers::{parse_number, parse_unsigned_number};
pub(super) fn parse_hex_byte_with_prefix(input: &str) -> IResult<&str, u8> {
let (input, _) = tag("\\x")(input)?;
let (input, hex_str) = recognize(pair(
one_of("0123456789abcdefABCDEF"),
one_of("0123456789abcdefABCDEF"),
))
.parse(input)?;
let byte_val = u8::from_str_radix(hex_str, 16)
.map_err(|_| nom::Err::Error(NomError::new(input, nom::error::ErrorKind::MapRes)))?;
Ok((input, byte_val))
}
pub(super) fn parse_hex_bytes_with_prefix(input: &str) -> IResult<&str, Vec<u8>> {
if input.starts_with("\\x") {
many0(parse_hex_byte_with_prefix).parse(input)
} else {
Err(nom::Err::Error(NomError::new(
input,
nom::error::ErrorKind::Tag,
)))
}
}
pub(super) fn parse_mixed_hex_ascii(input: &str) -> IResult<&str, Vec<u8>> {
if !input.starts_with('\\') {
return Err(nom::Err::Error(NomError::new(
input,
nom::error::ErrorKind::Tag,
)));
}
let mut bytes = Vec::new();
let mut remaining = input;
while !remaining.is_empty() {
if let Ok((new_remaining, escaped_char)) = parse_escape_sequence(remaining) {
bytes.push(escaped_char as u8);
remaining = new_remaining;
} else if let Ok((new_remaining, hex_byte)) = parse_hex_byte_with_prefix(remaining) {
bytes.push(hex_byte);
remaining = new_remaining;
} else if let Ok((new_remaining, ascii_char)) =
none_of::<&str, &str, NomError<&str>>(" \t\n\r")(remaining)
{
bytes.push(ascii_char as u8);
remaining = new_remaining;
} else {
break;
}
}
if bytes.is_empty() {
Err(nom::Err::Error(NomError::new(
input,
nom::error::ErrorKind::Tag,
)))
} else {
Ok((remaining, bytes))
}
}
pub(super) fn parse_hex_bytes_no_prefix(input: &str) -> IResult<&str, Vec<u8>> {
if input.starts_with("0x") || input.starts_with('-') {
return Err(nom::Err::Error(NomError::new(
input,
nom::error::ErrorKind::Tag,
)));
}
let hex_chars: String = input.chars().take_while(char::is_ascii_hexdigit).collect();
if hex_chars.is_empty() || !hex_chars.len().is_multiple_of(2) {
return Err(nom::Err::Error(NomError::new(
input,
nom::error::ErrorKind::Tag,
)));
}
let has_hex_letters = hex_chars
.chars()
.any(|c| matches!(c, 'a'..='f' | 'A'..='F'));
if !has_hex_letters {
return Err(nom::Err::Error(NomError::new(
input,
nom::error::ErrorKind::Tag,
)));
}
let mut bytes = Vec::with_capacity(hex_chars.len() / 2);
let mut chars = hex_chars.chars();
while let (Some(c1), Some(c2)) = (chars.next(), chars.next()) {
let digit1 = c1
.to_digit(16)
.ok_or_else(|| nom::Err::Error(NomError::new(input, nom::error::ErrorKind::MapRes)))?;
let digit2 = c2
.to_digit(16)
.ok_or_else(|| nom::Err::Error(NomError::new(input, nom::error::ErrorKind::MapRes)))?;
let byte_val = u8::try_from((digit1 << 4) | digit2)
.map_err(|_| nom::Err::Error(NomError::new(input, nom::error::ErrorKind::MapRes)))?;
bytes.push(byte_val);
}
let remaining = &input[hex_chars.len()..];
Ok((remaining, bytes))
}
pub(super) fn parse_hex_bytes(input: &str) -> IResult<&str, Vec<u8>> {
alt((
parse_mixed_hex_ascii,
parse_hex_bytes_with_prefix,
parse_hex_bytes_no_prefix,
))
.parse(input)
}
pub(super) fn parse_escape_sequence(input: &str) -> IResult<&str, char> {
let (input, _) = char('\\')(input)?;
if let Ok((remaining, octal_str)) = recognize(pair(
one_of::<&str, &str, NomError<&str>>("0123"),
pair(
one_of::<&str, &str, NomError<&str>>("01234567"),
one_of::<&str, &str, NomError<&str>>("01234567"),
),
))
.parse(input)
&& let Ok(octal_value) = u8::from_str_radix(octal_str, 8)
{
return Ok((remaining, octal_value as char));
}
let (input, escaped_char) = one_of("nrt\\\"'0")(input)?;
let result_char = match escaped_char {
'n' => '\n',
'r' => '\r',
't' => '\t',
'\\' => '\\',
'"' => '"',
'\'' => '\'',
'0' => '\0',
_ => unreachable!("one_of constrains input to known escape characters"),
};
Ok((input, result_char))
}
pub(super) fn parse_quoted_string(input: &str) -> IResult<&str, String> {
let (input, _) = multispace0(input)?;
let (input, _) = char('"')(input)?;
let mut result = String::new();
let mut remaining = input;
loop {
if let Ok((new_remaining, escaped_char)) = parse_escape_sequence(remaining) {
result.push(escaped_char);
remaining = new_remaining;
continue;
}
if let Ok((new_remaining, regular_char)) =
none_of::<&str, &str, NomError<&str>>("\"\\")(remaining)
{
result.push(regular_char);
remaining = new_remaining;
continue;
}
break;
}
let (remaining, _) = char('"')(remaining)?;
let (remaining, _) = multispace0(remaining)?;
Ok((remaining, result))
}
pub(super) fn parse_float_value(input: &str) -> IResult<&str, f64> {
let (input, _) = multispace0(input)?;
let (remaining, float_str) = recognize((
opt(char('-')),
digit1,
char('.'),
digit1,
opt((one_of("eE"), opt(one_of("+-")), digit1)),
))
.parse(input)?;
let value: f64 = float_str
.parse()
.map_err(|_| nom::Err::Error(NomError::new(input, nom::error::ErrorKind::MapRes)))?;
if !value.is_finite() {
return Err(nom::Err::Error(NomError::new(
input,
nom::error::ErrorKind::Float,
)));
}
let (remaining, _) = multispace0(remaining)?;
Ok((remaining, value))
}
pub(super) fn parse_numeric_value(input: &str) -> IResult<&str, Value> {
let (input, _) = multispace0(input)?;
let (input, value) = if input.starts_with('-') {
let (input, number) = parse_number(input)?;
(input, Value::Int(number))
} else {
let (input, number) = parse_unsigned_number(input)?;
(input, Value::Uint(number))
};
let (input, _) = multispace0(input)?;
Ok((input, value))
}
pub fn parse_value(input: &str) -> IResult<&str, Value> {
let (input, _) = multispace0(input)?;
if input.is_empty() {
return Err(nom::Err::Error(NomError::new(
input,
nom::error::ErrorKind::Tag,
)));
}
let (input, value) = alt((
map(parse_quoted_string, Value::String),
map(parse_hex_bytes, Value::Bytes),
map(parse_float_value, Value::Float),
parse_numeric_value,
))
.parse(input)?;
Ok((input, value))
}