@@ -51,6 +51,34 @@ fn parse_lua_int(text: &str) -> i64 {
5151 text. parse :: < i64 > ( ) . unwrap_or ( 0 )
5252}
5353
54+ /// Lua left shift: x << n (returns 0 if |n| >= 64)
55+ /// Negative n means right shift
56+ #[ inline( always) ]
57+ fn lua_shl ( l : i64 , r : i64 ) -> i64 {
58+ if r >= 64 || r <= -64 {
59+ 0
60+ } else if r >= 0 {
61+ ( l as u64 ) . wrapping_shl ( r as u32 ) as i64
62+ } else {
63+ // Negative shift means right shift (logical)
64+ ( l as u64 ) . wrapping_shr ( ( -r) as u32 ) as i64
65+ }
66+ }
67+
68+ /// Lua right shift: x >> n (logical shift, returns 0 if |n| >= 64)
69+ /// Negative n means left shift
70+ #[ inline( always) ]
71+ fn lua_shr ( l : i64 , r : i64 ) -> i64 {
72+ if r >= 64 || r <= -64 {
73+ 0
74+ } else if r >= 0 {
75+ ( l as u64 ) . wrapping_shr ( r as u32 ) as i64
76+ } else {
77+ // Negative shift means left shift
78+ ( l as u64 ) . wrapping_shl ( ( -r) as u32 ) as i64
79+ }
80+ }
81+
5482//======================================================================================
5583// NEW API: ExpDesc-based expression compilation (Lua 5.4 compatible)
5684//======================================================================================
@@ -129,7 +157,14 @@ fn compile_literal_expr_desc(c: &mut Compiler, expr: &LuaLiteralExpr) -> Result<
129157 && !text. contains ( '.' )
130158 && !text. to_lowercase ( ) . contains ( 'p' ) ;
131159
132- if !num. is_float ( ) || is_hex_int {
160+ // Check if text has decimal point or exponent (should be treated as float)
161+ // This handles cases like 1.0e19 or 9223372036854775808.0
162+ let text_lower = text. to_lowercase ( ) ;
163+ let has_decimal_or_exp = text. contains ( '.' ) ||
164+ ( !text_lower. starts_with ( "0x" ) && text_lower. contains ( 'e' ) ) ;
165+
166+ // Treat as integer only if: no decimal/exponent OR is hex int
167+ if ( !num. is_float ( ) && !has_decimal_or_exp) || is_hex_int {
133168 // Parse as integer - use our custom parser for hex numbers
134169 // Lua 5.4: 0xFFFFFFFFFFFFFFFF should be interpreted as -1 (two's complement)
135170 let int_val = parse_lua_int ( text) ;
@@ -833,8 +868,15 @@ fn compile_literal_expr(
833868 && !text. contains ( '.' )
834869 && !text. to_lowercase ( ) . contains ( 'p' ) ;
835870
871+ // Check if text has decimal point or exponent (should be treated as float)
872+ // This handles cases like 1.0e19 or 9223372036854775808.0
873+ let text_lower = text. to_lowercase ( ) ;
874+ let has_decimal_or_exp = text. contains ( '.' ) ||
875+ ( !text_lower. starts_with ( "0x" ) && text_lower. contains ( 'e' ) ) ;
876+
836877 // Lua 5.4 optimization: Try LoadI for integers, LoadF for simple floats
837- if !num. is_float ( ) || is_hex_int {
878+ // Treat as integer only if: parser says integer AND no decimal/exponent AND is hex int
879+ if ( !num. is_float ( ) && !has_decimal_or_exp) || is_hex_int {
838880 let int_val = parse_lua_int ( text) ;
839881 // Try LoadI first (fast path for small integers)
840882 if let Some ( _) = emit_loadi ( c, reg, int_val) {
@@ -944,8 +986,8 @@ fn try_eval_const_int(expr: &LuaExpr) -> Option<i64> {
944986 BinaryOperator :: OpBAnd => Some ( left_val & right_val) ,
945987 BinaryOperator :: OpBOr => Some ( left_val | right_val) ,
946988 BinaryOperator :: OpBXor => Some ( left_val ^ right_val) ,
947- BinaryOperator :: OpShl => Some ( left_val << ( right_val & 0x3f ) ) ,
948- BinaryOperator :: OpShr => Some ( left_val >> ( right_val & 0x3f ) ) ,
989+ BinaryOperator :: OpShl => Some ( lua_shl ( left_val, right_val) ) ,
990+ BinaryOperator :: OpShr => Some ( lua_shr ( left_val, right_val) ) ,
949991 _ => None ,
950992 }
951993 }
@@ -1048,8 +1090,8 @@ fn compile_binary_expr_to(
10481090 BinaryOperator :: OpBAnd => Some ( ( left_int & right_int) as f64 ) ,
10491091 BinaryOperator :: OpBOr => Some ( ( left_int | right_int) as f64 ) ,
10501092 BinaryOperator :: OpBXor => Some ( ( left_int ^ right_int) as f64 ) ,
1051- BinaryOperator :: OpShl => Some ( ( left_int << ( right_int & 0x3f ) ) as f64 ) ,
1052- BinaryOperator :: OpShr => Some ( ( left_int >> ( right_int & 0x3f ) ) as f64 ) ,
1093+ BinaryOperator :: OpShl => Some ( lua_shl ( left_int, right_int) as f64 ) ,
1094+ BinaryOperator :: OpShr => Some ( lua_shr ( left_int, right_int) as f64 ) ,
10531095 _ => None ,
10541096 } ;
10551097
@@ -1125,8 +1167,8 @@ fn compile_binary_expr_to(
11251167 BinaryOperator :: OpBAnd => left_int & right_int,
11261168 BinaryOperator :: OpBOr => left_int | right_int,
11271169 BinaryOperator :: OpBXor => left_int ^ right_int,
1128- BinaryOperator :: OpShl => left_int << ( right_int & 0x3f ) ,
1129- BinaryOperator :: OpShr => left_int >> ( right_int & 0x3f ) ,
1170+ BinaryOperator :: OpShl => lua_shl ( left_int, right_int) ,
1171+ BinaryOperator :: OpShr => lua_shr ( left_int, right_int) ,
11301172 _ => unreachable ! ( ) ,
11311173 } ;
11321174 Some ( result_int as f64 )
@@ -1504,18 +1546,14 @@ fn compile_binary_expr_to(
15041546 alloc_register ( c)
15051547 }
15061548 } ) ;
1507- // Lua 5.4: Use ShlI for immediate left shift
1508- // Note: ShlI uses negated immediate: sC << R[B] where sC is the immediate
1509- // To shift left by N, we use -N as the immediate
1510- let neg_imm = if int_val < 0 {
1511- ( ( -int_val) + 256 ) as u32
1512- } else {
1513- ( ( -int_val) + 512 ) as u32 % 512
1514- } ;
1549+ // Lua 5.4: x << n is equivalent to x >> -n
1550+ // Use ShrI with negated immediate: R[A] = R[B] >> sC
1551+ // sC encoding: (val + 127) & 0xff, where val = -n
1552+ let neg_imm = ( ( -int_val + 127 ) & 0xff ) as u32 ;
15151553 emit (
15161554 c,
15171555 Instruction :: encode_abc (
1518- OpCode :: ShlI ,
1556+ OpCode :: ShrI ,
15191557 result_reg,
15201558 left_reg,
15211559 neg_imm,
@@ -2288,11 +2326,16 @@ pub fn compile_call_expr_with_returns_and_dest(
22882326 let nactvar = c. nactvar as u32 ;
22892327 let args_start = d + 1 ;
22902328
2291- // Check if args_start would overwrite active locals
2329+ // Check if dest or args would overwrite active locals
22922330 // Active locals occupy R[0] through R[nactvar-1]
2293- // If args_start < nactvar, arguments would overwrite locals!
2294- if args_start < nactvar {
2295- // Conflict! Arguments would overwrite active locals
2331+ // - If d < nactvar: moving function to d would overwrite a local before args are compiled!
2332+ // - If args_start < nactvar: arguments would overwrite locals!
2333+ //
2334+ // Example: `x = tonumber(x)` where x is in R[0]:
2335+ // - d = 0, nactvar = 1, args_start = 1
2336+ // - If we move tonumber to R[0] first, we overwrite x before reading it as argument!
2337+ if d < nactvar || args_start < nactvar {
2338+ // Conflict! Either dest or arguments would overwrite active locals
22962339 // Solution: Place function at freereg (after all locals) and move result back later
22972340 let new_func_reg = if c. freereg < nactvar {
22982341 // Ensure freereg is at least nactvar
0 commit comments