emmylua_parser/syntax/node/token/
number_analyzer.rs1use 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 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 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 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 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}