emmylua_parser/syntax/node/token/
number_analyzer.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use crate::{parser_error::LuaParseError, LuaSyntaxToken};

pub fn float_token_value(token: &LuaSyntaxToken) -> Result<f64, LuaParseError> {
    let text = token.text();
    let hex = text.starts_with("0x") || text.starts_with("0X");

    // This section handles the parsing of hexadecimal floating-point numbers.
    // Hexadecimal floating-point literals are of the form 0x1.8p3, where:
    // - "0x1.8" is the significand (integer and fractional parts in hexadecimal)
    // - "p3" is the exponent (in decimal, base 2 exponent)
    let value = if hex {
        let hex_float_text = &text[2..];
        let exponent_position = hex_float_text
            .find('p')
            .or_else(|| hex_float_text.find('P'));
        let (float_part, exponent_part) = if let Some(pos) = exponent_position {
            (&hex_float_text[..pos], &hex_float_text[(pos + 1)..])
        } else {
            (hex_float_text, "")
        };

        let (integer_part, fraction_value) = if let Some(dot_pos) = float_part.find('.') {
            let (int_part, frac_part) = float_part.split_at(dot_pos);
            let int_value = if !int_part.is_empty() {
                i64::from_str_radix(int_part, 16).unwrap_or(0)
            } else {
                0
            };
            let frac_part = &frac_part[1..];
            let frac_value = if !frac_part.is_empty() {
                let frac_part_value = i64::from_str_radix(frac_part, 16).unwrap_or(0);
                frac_part_value as f64 * 16f64.powi(-(frac_part.len() as i32))
            } else {
                0.0
            };
            (int_value, frac_value)
        } else {
            (i64::from_str_radix(float_part, 16).unwrap_or(0), 0.0)
        };

        let mut value = integer_part as f64 + fraction_value;
        if !exponent_part.is_empty() {
            if let Ok(exp) = exponent_part.parse::<i32>() {
                value *= 2f64.powi(exp);
            }
        }
        value
    } else {
        let (float_part, exponent_part) =
            if let Some(pos) = text.find('e').or_else(|| text.find('E')) {
                (&text[..pos], &text[(pos + 1)..])
            } else {
                (text, "")
            };

        let mut value = float_part.parse::<f64>().map_err(|e| {
            LuaParseError::new(
                &format!("The float literal '{}' is invalid, {}", text, e),
                token.text_range(),
            )
        })?;

        if !exponent_part.is_empty() {
            if let Ok(exp) = exponent_part.parse::<i32>() {
                value *= 10f64.powi(exp);
            }
        }
        value
    };

    Ok(value)
}

pub fn int_token_value(token: &LuaSyntaxToken) -> Result<i64, LuaParseError> {
    let text = token.text();
    let hex = text.starts_with("0x") || text.starts_with("0X");
    let text = text.trim_end_matches(|c| c == 'u' || c == 'l' || c == 'U' || c == 'L');

    let value = if hex {
        let text = &text[2..];
        i64::from_str_radix(text, 16)
    } else {
        text.parse::<i64>()
    };

    match value {
        Ok(value) => Ok(value),
        Err(e) => {
            let range = token.text_range();
            if *e.kind() == std::num::IntErrorKind::PosOverflow
                || *e.kind() == std::num::IntErrorKind::NegOverflow
            {
                Err(LuaParseError::new(
                    &format!(
                        "The integer literal '{}' is too large to be represented in type 'long'",
                        text
                    ),
                    range,
                ))
            } else {
                Err(LuaParseError::new(
                    &format!("The integer literal '{}' is invalid, {}", text, e),
                    range,
                ))
            }
        }
    }
}