emmylua_parser/syntax/node/token/
number_analyzer.rs

1use crate::{
2    parser_error::{LuaParseError, LuaParseErrorKind},
3    LuaSyntaxToken,
4};
5
6pub fn float_token_value(token: &LuaSyntaxToken) -> Result<f64, LuaParseError> {
7    let text = token.text();
8    let hex = text.starts_with("0x") || text.starts_with("0X");
9
10    // This section handles the parsing of hexadecimal floating-point numbers.
11    // Hexadecimal floating-point literals are of the form 0x1.8p3, where:
12    // - "0x1.8" is the significand (integer and fractional parts in hexadecimal)
13    // - "p3" is the exponent (in decimal, base 2 exponent)
14    let value = if hex {
15        let hex_float_text = &text[2..];
16        let exponent_position = hex_float_text
17            .find('p')
18            .or_else(|| hex_float_text.find('P'));
19        let (float_part, exponent_part) = if let Some(pos) = exponent_position {
20            (&hex_float_text[..pos], &hex_float_text[(pos + 1)..])
21        } else {
22            (hex_float_text, "")
23        };
24
25        let (integer_part, fraction_value) = if let Some(dot_pos) = float_part.find('.') {
26            let (int_part, frac_part) = float_part.split_at(dot_pos);
27            let int_value = if !int_part.is_empty() {
28                i64::from_str_radix(int_part, 16).unwrap_or(0)
29            } else {
30                0
31            };
32            let frac_part = &frac_part[1..];
33            let frac_value = if !frac_part.is_empty() {
34                let frac_part_value = i64::from_str_radix(frac_part, 16).unwrap_or(0);
35                frac_part_value as f64 * 16f64.powi(-(frac_part.len() as i32))
36            } else {
37                0.0
38            };
39            (int_value, frac_value)
40        } else {
41            (i64::from_str_radix(float_part, 16).unwrap_or(0), 0.0)
42        };
43
44        let mut value = integer_part as f64 + fraction_value;
45        if !exponent_part.is_empty() {
46            if let Ok(exp) = exponent_part.parse::<i32>() {
47                value *= 2f64.powi(exp);
48            }
49        }
50        value
51    } else {
52        let (float_part, exponent_part) =
53            if let Some(pos) = text.find('e').or_else(|| text.find('E')) {
54                (&text[..pos], &text[(pos + 1)..])
55            } else {
56                (text, "")
57            };
58
59        let mut value = float_part.parse::<f64>().map_err(|e| {
60            LuaParseError::new(
61                LuaParseErrorKind::SyntaxError,
62                &t!(
63                    "The float literal '%{text}' is invalid, %{err}",
64                    text = text,
65                    err = e
66                ),
67                token.text_range(),
68            )
69        })?;
70
71        if !exponent_part.is_empty() {
72            if let Ok(exp) = exponent_part.parse::<i32>() {
73                value *= 10f64.powi(exp);
74            }
75        }
76        value
77    };
78
79    Ok(value)
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83enum IntegerRepr {
84    Normal,
85    Hex,
86    Bin,
87}
88
89pub fn int_token_value(token: &LuaSyntaxToken) -> Result<i64, LuaParseError> {
90    let text = token.text();
91    let repr = if text.starts_with("0x") || text.starts_with("0X") {
92        IntegerRepr::Hex
93    } else if text.starts_with("0b") || text.starts_with("0B") {
94        IntegerRepr::Bin
95    } else {
96        IntegerRepr::Normal
97    };
98
99    let text = text.trim_end_matches(['u', 'l', 'U', 'L']);
100
101    let value = match repr {
102        IntegerRepr::Hex => {
103            let text = &text[2..];
104            i64::from_str_radix(text, 16)
105        }
106        IntegerRepr::Bin => {
107            let text = &text[2..];
108            i64::from_str_radix(text, 2)
109        }
110        IntegerRepr::Normal => text.parse::<i64>(),
111    };
112    match value {
113        Ok(value) => Ok(value),
114        Err(e) => {
115            let range = token.text_range();
116            if *e.kind() == std::num::IntErrorKind::PosOverflow
117                || *e.kind() == std::num::IntErrorKind::NegOverflow
118            {
119                Err(LuaParseError::new(
120                    LuaParseErrorKind::SyntaxError,
121                    &t!(
122                        "The integer literal '%{text}' is too large to be represented in type 'long'",
123                        text = text
124                    ),
125                    range,
126                ))
127            } else {
128                Err(LuaParseError::new(
129                    LuaParseErrorKind::SyntaxError,
130                    &t!(
131                        "The integer literal '%{text}' is invalid, %{err}",
132                        text = text,
133                        err = e
134                    ),
135                    range,
136                ))
137            }
138        }
139    }
140}