@@ -33,9 +33,19 @@ fn is_vararg_expr(expr: &LuaExpr) -> bool {
3333 }
3434}
3535
36+ /// Result of parsing a Lua number literal
37+ #[ derive( Debug , Clone , Copy ) ]
38+ enum ParsedNumber {
39+ /// Successfully parsed as integer
40+ Int ( i64 ) ,
41+ /// Number is too large for i64, use float instead
42+ Float ( f64 ) ,
43+ }
44+
3645/// Parse a Lua integer literal from text, handling hex numbers that overflow i64
3746/// Lua treats 0xFFFFFFFFFFFFFFFF as -1 (two's complement interpretation)
38- fn parse_lua_int ( text : & str ) -> i64 {
47+ /// For decimal numbers that overflow i64 range, returns Float instead
48+ fn parse_lua_int ( text : & str ) -> ParsedNumber {
3949 let text = text. trim ( ) ;
4050 if text. starts_with ( "0x" ) || text. starts_with ( "0X" ) {
4151 // Hex number - parse as u64 first, then reinterpret as i64
@@ -44,11 +54,20 @@ fn parse_lua_int(text: &str) -> i64 {
4454 // Remove any trailing decimal part (e.g., 0xFF.0)
4555 let hex_part = hex_part. split ( '.' ) . next ( ) . unwrap_or ( hex_part) ;
4656 if let Ok ( val) = u64:: from_str_radix ( hex_part, 16 ) {
47- return val as i64 ; // Reinterpret bits as signed
57+ return ParsedNumber :: Int ( val as i64 ) ; // Reinterpret bits as signed
4858 }
4959 }
50- // Default case: parse as i64
51- text. parse :: < i64 > ( ) . unwrap_or ( 0 )
60+ // Decimal case: parse as i64 only, if overflow use float
61+ // (Unlike hex, decimal numbers should NOT be reinterpreted as two's complement)
62+ if let Ok ( val) = text. parse :: < i64 > ( ) {
63+ return ParsedNumber :: Int ( val) ;
64+ }
65+ // Decimal number is too large for i64, parse as float
66+ if let Ok ( val) = text. parse :: < f64 > ( ) {
67+ return ParsedNumber :: Float ( val) ;
68+ }
69+ // Default fallback
70+ ParsedNumber :: Int ( 0 )
5271}
5372
5473/// Lua left shift: x << n (returns 0 if |n| >= 64)
@@ -167,9 +186,11 @@ fn compile_literal_expr_desc(c: &mut Compiler, expr: &LuaLiteralExpr) -> Result<
167186 if ( !num. is_float ( ) && !has_decimal_or_exp) || is_hex_int {
168187 // Parse as integer - use our custom parser for hex numbers
169188 // Lua 5.4: 0xFFFFFFFFFFFFFFFF should be interpreted as -1 (two's complement)
170- let int_val = parse_lua_int ( text) ;
171- // Use VKInt for integers
172- Ok ( ExpDesc :: new_int ( int_val) )
189+ // parse_lua_int may return Float if the number overflows i64 range
190+ match parse_lua_int ( text) {
191+ ParsedNumber :: Int ( int_val) => Ok ( ExpDesc :: new_int ( int_val) ) ,
192+ ParsedNumber :: Float ( float_val) => Ok ( ExpDesc :: new_float ( float_val) ) ,
193+ }
173194 } else {
174195 let float_val = num. get_float_value ( ) ;
175196 // Use VKFlt for floats
@@ -877,14 +898,24 @@ fn compile_literal_expr(
877898 // Lua 5.4 optimization: Try LoadI for integers, LoadF for simple floats
878899 // Treat as integer only if: parser says integer AND no decimal/exponent AND is hex int
879900 if ( !num. is_float ( ) && !has_decimal_or_exp) || is_hex_int {
880- let int_val = parse_lua_int ( text) ;
881- // Try LoadI first (fast path for small integers)
882- if let Some ( _) = emit_loadi ( c, reg, int_val) {
883- return Ok ( reg) ;
901+ match parse_lua_int ( text) {
902+ ParsedNumber :: Int ( int_val) => {
903+ // Try LoadI first (fast path for small integers)
904+ if let Some ( _) = emit_loadi ( c, reg, int_val) {
905+ return Ok ( reg) ;
906+ }
907+ // LoadI failed, add to constant table
908+ let const_idx = add_constant_dedup ( c, LuaValue :: integer ( int_val) ) ;
909+ emit_loadk ( c, reg, const_idx) ;
910+ }
911+ ParsedNumber :: Float ( float_val) => {
912+ // Number overflowed i64, treat as float
913+ if emit_loadf ( c, reg, float_val) . is_none ( ) {
914+ let const_idx = add_constant_dedup ( c, LuaValue :: float ( float_val) ) ;
915+ emit_loadk ( c, reg, const_idx) ;
916+ }
917+ }
884918 }
885- // LoadI failed, add to constant table
886- let const_idx = add_constant_dedup ( c, LuaValue :: integer ( int_val) ) ;
887- emit_loadk ( c, reg, const_idx) ;
888919 } else {
889920 let float_val = num. get_float_value ( ) ;
890921 // Try LoadF for integer-representable floats
@@ -956,8 +987,23 @@ fn try_eval_const_int(expr: &LuaExpr) -> Option<i64> {
956987 match expr {
957988 LuaExpr :: LiteralExpr ( lit) => {
958989 if let Some ( LuaLiteralToken :: Number ( num) ) = lit. get_literal ( ) {
959- if !num. is_float ( ) {
960- return Some ( num. get_int_value ( ) ) ;
990+ let text = num. get_text ( ) ;
991+
992+ // Check if this is a hex integer
993+ let is_hex_int = ( text. starts_with ( "0x" ) || text. starts_with ( "0X" ) )
994+ && !text. contains ( '.' )
995+ && !text. to_lowercase ( ) . contains ( 'p' ) ;
996+
997+ // Check if has decimal or exponent
998+ let text_lower = text. to_lowercase ( ) ;
999+ let has_decimal_or_exp = text. contains ( '.' ) ||
1000+ ( !text_lower. starts_with ( "0x" ) && text_lower. contains ( 'e' ) ) ;
1001+
1002+ if ( !num. is_float ( ) && !has_decimal_or_exp) || is_hex_int {
1003+ match parse_lua_int ( text) {
1004+ ParsedNumber :: Int ( int_val) => return Some ( int_val) ,
1005+ ParsedNumber :: Float ( _) => return None , // Overflowed, not an integer
1006+ }
9611007 }
9621008 }
9631009 None
@@ -2094,25 +2140,50 @@ fn compile_unary_expr_to(
20942140 Some ( LuaLiteralToken :: Number ( num_token) ) => {
20952141 if op_kind == UnaryOperator :: OpUnm {
20962142 // Negative number literal: emit LOADI/LOADK with negated value
2097- if !num_token. is_float ( ) {
2098- let int_val = num_token. get_int_value ( ) ;
2099- let neg_val = -int_val;
2100-
2101- // Use LOADI for small integers
2102- if let Some ( _) = emit_loadi ( c, result_reg, neg_val) {
2103- return Ok ( result_reg) ;
2143+ let text = num_token. get_text ( ) ;
2144+
2145+ // Check if this is a hex integer
2146+ let is_hex_int = ( text. starts_with ( "0x" ) || text. starts_with ( "0X" ) )
2147+ && !text. contains ( '.' )
2148+ && !text. to_lowercase ( ) . contains ( 'p' ) ;
2149+
2150+ // Check if has decimal or exponent
2151+ let text_lower = text. to_lowercase ( ) ;
2152+ let has_decimal_or_exp = text. contains ( '.' ) ||
2153+ ( !text_lower. starts_with ( "0x" ) && text_lower. contains ( 'e' ) ) ;
2154+
2155+ // Determine if should parse as integer
2156+ if ( !num_token. is_float ( ) && !has_decimal_or_exp) || is_hex_int {
2157+ match parse_lua_int ( text) {
2158+ ParsedNumber :: Int ( int_val) => {
2159+ // Successfully parsed as integer, negate it
2160+ let neg_val = int_val. wrapping_neg ( ) ;
2161+
2162+ // Use LOADI for small integers
2163+ if let Some ( _) = emit_loadi ( c, result_reg, neg_val) {
2164+ return Ok ( result_reg) ;
2165+ }
2166+ // Large integer, add to constant table
2167+ let const_idx = add_constant ( c, LuaValue :: integer ( neg_val) ) ;
2168+ emit_loadk ( c, result_reg, const_idx) ;
2169+ return Ok ( result_reg) ;
2170+ }
2171+ ParsedNumber :: Float ( float_val) => {
2172+ // Number overflowed i64, use negated float
2173+ let neg_val = -float_val;
2174+ let const_idx = add_constant ( c, LuaValue :: number ( neg_val) ) ;
2175+ emit_loadk ( c, result_reg, const_idx) ;
2176+ return Ok ( result_reg) ;
2177+ }
21042178 }
2105- }
2106-
2107- // For floats or large integers, use constant
2108- let num_val = if num_token. is_float ( ) {
2109- LuaValue :: number ( -num_token. get_float_value ( ) )
21102179 } else {
2111- LuaValue :: integer ( -num_token. get_int_value ( ) )
2112- } ;
2113- let const_idx = add_constant ( c, num_val) ;
2114- emit_loadk ( c, result_reg, const_idx) ;
2115- return Ok ( result_reg) ;
2180+ // Float literal
2181+ let float_val = num_token. get_float_value ( ) ;
2182+ let neg_val = -float_val;
2183+ let const_idx = add_constant ( c, LuaValue :: number ( neg_val) ) ;
2184+ emit_loadk ( c, result_reg, const_idx) ;
2185+ return Ok ( result_reg) ;
2186+ }
21162187 }
21172188 }
21182189 Some ( LuaLiteralToken :: Bool ( b) ) => {
0 commit comments