Skip to main content

luaur_ast/functions/
parse_double.rs

1use crate::enums::constant_number_parse_result::ConstantNumberParseResult;
2use crate::functions::parse_integer::parse_integer;
3
4pub fn parse_double(result: &mut f64, data: &str) -> ConstantNumberParseResult {
5    let bytes = data.as_bytes();
6
7    // binary literal
8    if bytes.len() >= 3 && bytes[0] == b'0' && (bytes[1] == b'b' || bytes[1] == b'B') {
9        return parse_integer(result, &data[2..], 2);
10    }
11
12    // hexadecimal literal
13    if bytes.len() >= 3 && bytes[0] == b'0' && (bytes[1] == b'x' || bytes[1] == b'X') {
14        // pass in '0x' prefix, it's handled by 'strtoull' in C++ / parse_integer in Rust
15        return parse_integer(result, data, 16);
16    }
17
18    // Rust's f64::from_str is the equivalent of strtod.
19    // However, strtod stops at the first invalid character and returns the end pointer.
20    // f64::from_str requires the entire string to be valid.
21    let value = match data.parse::<f64>() {
22        Ok(v) => v,
23        Err(_) => return ConstantNumberParseResult::Malformed,
24    };
25
26    *result = value;
27
28    // for linting, we detect integer constants that are parsed imprecisely
29    // since the check is expensive we only perform it when the number is larger than the precise integer range
30    if value >= (1u64 << 53) as f64 && data.chars().all(|c| c.is_ascii_digit()) {
31        // Equivalent to snprintf(repr, sizeof(repr), "%.0f", value);
32        let repr = format!("{:.0}", value);
33
34        if repr != data {
35            return ConstantNumberParseResult::Imprecise;
36        }
37    }
38
39    ConstantNumberParseResult::Ok
40}