use crate::{
ast::NumberKind,
error::{Error, ErrorKind, Result},
};
#[derive(Debug, Clone, Copy)]
pub struct ParsedInt {
pub magnitude: u128,
pub negative: bool,
}
fn determine_base_and_digits(s: &str) -> (u32, &str) {
if let Some(d) = s.strip_prefix("0x").or_else(|| s.strip_prefix("0X")) {
(16, d)
} else if let Some(d) = s.strip_prefix("0b").or_else(|| s.strip_prefix("0B")) {
(2, d)
} else if let Some(d) = s.strip_prefix("0o").or_else(|| s.strip_prefix("0O")) {
(8, d)
} else {
(10, s)
}
}
pub fn parse_int_raw(raw: &str) -> Result<ParsedInt> {
let raw = raw.trim();
let (negative, unsigned_raw) = match raw.strip_prefix('-') {
Some(r) => (true, r),
None => (false, raw),
};
let magnitude = if unsigned_raw.contains('_') {
let cleaned: alloc::string::String = unsigned_raw.chars().filter(|&c| c != '_').collect();
let (base, digits) = determine_base_and_digits(&cleaned);
u128::from_str_radix(digits, base)
} else {
let (base, digits) = determine_base_and_digits(unsigned_raw);
u128::from_str_radix(digits, base)
}
.map_err(|_| {
Error::new(ErrorKind::IntegerOutOfBounds {
value: raw.to_string().into(),
target_type: "u128",
})
})?;
Ok(ParsedInt {
magnitude,
negative,
})
}
pub(crate) fn parse_integer_from_raw<T>(
raw: &str,
kind: &NumberKind,
target_type: &'static str,
) -> Result<T>
where
T: TryFrom<i128> + TryFrom<u128>,
{
let raw_trimmed = raw.trim();
match kind {
NumberKind::Integer => {
let parsed = parse_int_raw(raw)?;
T::try_from(parsed.magnitude).map_err(|_| {
Error::new(ErrorKind::IntegerOutOfBounds {
value: raw_trimmed.to_string().into(),
target_type,
})
})
}
NumberKind::NegativeInteger => {
let parsed = parse_int_raw(raw)?;
let val = i128::try_from(parsed.magnitude).map_err(|_| {
Error::new(ErrorKind::IntegerOutOfBounds {
value: raw_trimmed.to_string().into(),
target_type,
})
})?;
let val = -val;
T::try_from(val).map_err(|_| {
Error::new(ErrorKind::IntegerOutOfBounds {
value: raw_trimmed.to_string().into(),
target_type,
})
})
}
NumberKind::Float | NumberKind::SpecialFloat => Err(Error::new(ErrorKind::TypeMismatch {
expected: "integer".into(),
found: "float".into(),
})),
}
}
pub(crate) fn parse_float_from_raw(raw: &str, kind: &NumberKind) -> Result<f64> {
let raw = raw.trim();
match kind {
NumberKind::SpecialFloat => match raw {
"inf" => Ok(f64::INFINITY),
"-inf" => Ok(f64::NEG_INFINITY),
"NaN" => Ok(f64::NAN),
_ => Err(Error::new(ErrorKind::TypeMismatch {
expected: "float".into(),
found: raw.into(),
})),
},
NumberKind::Float | NumberKind::Integer | NumberKind::NegativeInteger => {
if raw.contains('_') {
let cleaned: alloc::string::String = raw.chars().filter(|&c| c != '_').collect();
cleaned.parse().map_err(|_| {
Error::new(ErrorKind::TypeMismatch {
expected: "float".into(),
found: raw.into(),
})
})
} else {
raw.parse().map_err(|_| {
Error::new(ErrorKind::TypeMismatch {
expected: "float".into(),
found: raw.into(),
})
})
}
}
}
}