emmylua_parser/syntax/node/token/
number_analyzer.rs

1use crate::{
2    LuaSyntaxToken,
3    parser_error::{LuaParseError, LuaParseErrorKind},
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
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub enum IntegerOrUnsigned {
91    Int(i64),
92    Uint(u64),
93}
94
95impl IntegerOrUnsigned {
96    pub fn is_unsigned(&self) -> bool {
97        matches!(self, IntegerOrUnsigned::Uint(_))
98    }
99
100    pub fn is_signed(&self) -> bool {
101        matches!(self, IntegerOrUnsigned::Int(_))
102    }
103
104    pub fn as_integer(&self) -> Option<i64> {
105        match self {
106            IntegerOrUnsigned::Int(value) => Some(*value),
107            IntegerOrUnsigned::Uint(_) => None,
108        }
109    }
110}
111
112pub fn int_token_value(token: &LuaSyntaxToken) -> Result<IntegerOrUnsigned, LuaParseError> {
113    let text = token.text();
114    let repr = if text.starts_with("0x") || text.starts_with("0X") {
115        IntegerRepr::Hex
116    } else if text.starts_with("0b") || text.starts_with("0B") {
117        IntegerRepr::Bin
118    } else {
119        IntegerRepr::Normal
120    };
121
122    // 检查是否有无符号后缀并去除后缀
123    let mut is_unsigned = false;
124    let mut suffix_count = 0;
125    for c in text.chars().rev() {
126        if c == 'u' || c == 'U' {
127            is_unsigned = true;
128            suffix_count += 1;
129        } else if c == 'l' || c == 'L' {
130            suffix_count += 1;
131        } else {
132            break;
133        }
134    }
135
136    let text = &text[..text.len() - suffix_count];
137
138    // 首先尝试解析为有符号整数
139    let signed_value = match repr {
140        IntegerRepr::Hex => {
141            let text = &text[2..];
142            i64::from_str_radix(text, 16)
143        }
144        IntegerRepr::Bin => {
145            let text = &text[2..];
146            i64::from_str_radix(text, 2)
147        }
148        IntegerRepr::Normal => text.parse::<i64>(),
149    };
150
151    match signed_value {
152        Ok(value) => Ok(IntegerOrUnsigned::Int(value)),
153        Err(e) => {
154            let range = token.text_range();
155
156            // 如果是溢出错误,尝试解析为无符号整数
157            if *e.kind() == std::num::IntErrorKind::PosOverflow && is_unsigned {
158                let unsigned_value = match repr {
159                    IntegerRepr::Hex => {
160                        let text = &text[2..];
161                        u64::from_str_radix(text, 16)
162                    }
163                    IntegerRepr::Bin => {
164                        let text = &text[2..];
165                        u64::from_str_radix(text, 2)
166                    }
167                    IntegerRepr::Normal => text.parse::<u64>(),
168                };
169
170                match unsigned_value {
171                    Ok(value) => Ok(IntegerOrUnsigned::Uint(value)),
172                    Err(_) => Err(LuaParseError::new(
173                        LuaParseErrorKind::SyntaxError,
174                        &t!(
175                            "The integer literal '%{text}' is too large to be represented",
176                            text = token.text()
177                        ),
178                        range,
179                    )),
180                }
181            } else if matches!(
182                *e.kind(),
183                std::num::IntErrorKind::NegOverflow | std::num::IntErrorKind::PosOverflow
184            ) {
185                Err(LuaParseError::new(
186                    LuaParseErrorKind::SyntaxError,
187                    &t!(
188                        "The integer literal '%{text}' is too large to be represented in type 'long'",
189                        text = token.text()
190                    ),
191                    range,
192                ))
193            } else {
194                Err(LuaParseError::new(
195                    LuaParseErrorKind::SyntaxError,
196                    &t!(
197                        "The integer literal '%{text}' is invalid, %{err}",
198                        text = token.text(),
199                        err = e
200                    ),
201                    range,
202                ))
203            }
204        }
205    }
206}