use crate::LuaValue;
pub fn parse_lua_number(s: &str) -> LuaValue {
let s = s.trim();
if s.is_empty() || s.contains('\0') {
return LuaValue::nil();
}
let (sign, rest) = if let Some(rest) = s.strip_prefix('-') {
(-1i64, rest)
} else if let Some(rest) = s.strip_prefix('+') {
(1i64, rest)
} else {
(1i64, s)
};
if rest.starts_with("0x") || rest.starts_with("0X") {
let hex_part = &rest[2..];
if hex_part.contains('.') || hex_part.to_lowercase().contains('p') {
if let Some(f) = parse_hex_float(hex_part) {
return LuaValue::float(sign as f64 * f);
}
return LuaValue::nil();
}
let mut result: u64 = 0;
let mut has_digits = false;
for c in hex_part.chars() {
if c == '_' {
continue; }
if let Some(d) = c.to_digit(16) {
result = result.wrapping_mul(16).wrapping_add(d as u64);
has_digits = true;
} else {
return LuaValue::nil();
}
}
if has_digits {
let i = result as i64;
return LuaValue::integer(sign * i);
}
return LuaValue::nil();
}
let has_dot = rest.contains('.');
let has_exponent = rest.to_lowercase().contains('e');
if !has_dot && !has_exponent {
if let Ok(i) = s.parse::<i64>() {
return LuaValue::integer(i);
}
}
if let Ok(f) = s.parse::<f64>()
&& (f.is_finite() || s.contains('.') || has_exponent)
{
let lower = s.to_lowercase();
let stripped = lower.trim_start_matches(['+', '-']);
if stripped.starts_with("inf") || stripped.starts_with("nan") {
return LuaValue::nil();
}
return LuaValue::float(f);
}
LuaValue::nil()
}
fn parse_hex_float(s: &str) -> Option<f64> {
let (mantissa_str, exp_str) = if let Some(pos) = s.to_lowercase().find('p') {
(&s[..pos], &s[pos + 1..])
} else {
(s, "0")
};
let mut mantissa: u64 = 0;
let mut found_dot = false;
let mut has_digit = false;
let mut sig_digits = 0; let mut exp_adjust: i64 = 0; const MAX_SIG: usize = 15;
for ch in mantissa_str.chars() {
if ch == '.' {
if found_dot {
return None; }
found_dot = true;
} else if let Some(digit) = ch.to_digit(16) {
has_digit = true;
if digit != 0 && sig_digits == 0 {
mantissa = digit as u64;
sig_digits = 1;
if found_dot {
exp_adjust -= 4;
}
} else if sig_digits > 0 && sig_digits < MAX_SIG {
mantissa = mantissa * 16 + digit as u64;
sig_digits += 1;
if found_dot {
exp_adjust -= 4;
}
} else if sig_digits >= MAX_SIG {
if !found_dot {
exp_adjust += 4;
}
} else {
if found_dot {
exp_adjust -= 4;
}
}
} else if !ch.is_whitespace() {
return None; }
}
if !has_digit {
return None;
}
let exp_str = exp_str.trim();
if exp_str.is_empty() && s.to_lowercase().contains('p') {
return None; }
let exp: i64 = if exp_str == "0" || exp_str.is_empty() {
0
} else {
exp_str.parse::<i64>().ok()?
};
let total_exp = exp + exp_adjust;
let result = ldexp(mantissa as f64, total_exp);
Some(result)
}
fn ldexp(mut x: f64, mut exp: i64) -> f64 {
if x == 0.0 || exp == 0 {
return x;
}
while exp > 1023 {
x *= 2.0f64.powi(1023);
exp -= 1023;
if x.is_infinite() {
return x;
}
}
while exp < -1074 {
x *= 2.0f64.powi(-1074);
exp += 1074;
if x == 0.0 {
return x;
}
}
x * (2.0f64).powi(exp as i32)
}