use std::borrow::Cow;
use crate::bind::BoundExpr;
pub(crate) fn checked_integer_literal(
text: &[u8],
span: crate::lexer::Span,
) -> Result<BoundExpr, crate::diagnostic::ParseError> {
let digits = text.strip_prefix(b"-").unwrap_or(text);
let hex = digits
.get(..2)
.is_some_and(|prefix| prefix.eq_ignore_ascii_case(b"0x"));
if hex {
let significant = digits
.iter()
.skip(2)
.filter(|byte| **byte != b'_')
.skip_while(|byte| **byte == b'0')
.count();
if significant > 16 {
return Err(crate::bind::refused(
format!("hex literal too big: {}", String::from_utf8_lossy(text)),
span,
));
}
}
Ok(integer_literal(text))
}
pub(crate) fn real_literal(text: &[u8]) -> f64 {
let cleaned: Cow<'_, [u8]> = match text.contains(&b'_') {
true => Cow::Owned(text.iter().copied().filter(|byte| *byte != b'_').collect()),
false => Cow::Borrowed(text),
};
inillucent_value::numeric::atof(&cleaned, inillucent_value::TextEncoding::Utf8).value
}
pub(crate) fn integer_literal(text: &[u8]) -> BoundExpr {
let (negative, digits) = match text.first() {
Some(b'-') => (true, text.get(1..).unwrap_or(&[])),
_ => (false, text),
};
if digits.len() > 2
&& digits.first() == Some(&b'0')
&& digits
.get(1)
.is_some_and(|byte| byte.eq_ignore_ascii_case(&b'x'))
{
let mut value: u64 = 0;
for byte in digits
.get(2..)
.unwrap_or(&[])
.iter()
.filter(|b| **b != b'_')
{
let digit = (*byte as char).to_digit(16).unwrap_or(0) as u64;
value = value.wrapping_mul(16).wrapping_add(digit);
}
let value = value as i64;
return BoundExpr::Integer(if negative {
value.wrapping_neg()
} else {
value
});
}
let cleaned: Cow<'_, [u8]> = match text.contains(&b'_') {
true => Cow::Owned(text.iter().copied().filter(|byte| *byte != b'_').collect()),
false => Cow::Borrowed(text),
};
let (value, syntax) =
inillucent_value::numeric::atoi64(&cleaned, inillucent_value::TextEncoding::Utf8);
if syntax.is_exact() {
return BoundExpr::Integer(value);
}
let parsed = inillucent_value::numeric::atof(&cleaned, inillucent_value::TextEncoding::Utf8);
BoundExpr::Real(parsed.value)
}