Skip to content

Commit 0342671

Browse files
committed
update
1 parent 70046d9 commit 0342671

File tree

3 files changed

+129
-36
lines changed

3 files changed

+129
-36
lines changed

crates/luars/src/compiler/expr.rs

Lines changed: 104 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -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)) => {

crates/luars/src/lua_vm/execute/mod.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,8 +525,14 @@ pub fn luavm_execute(vm: &mut LuaVM) -> LuaResult<LuaValue> {
525525
// Read caller's state
526526
let caller_base = unsafe { (*caller_ptr).base_ptr } as usize;
527527

528-
// Only fill nil if caller expects results (rare case)
529-
if num_results > 0 && num_results != usize::MAX {
528+
// CRITICAL FIX: When caller expects variable returns (C=0, num_results=usize::MAX),
529+
// we must set top to indicate 0 values were returned.
530+
if num_results == usize::MAX {
531+
unsafe {
532+
(*caller_ptr).top = result_reg as u32;
533+
}
534+
} else if num_results > 0 {
535+
// Only fill nil if caller expects fixed results (rare case)
530536
unsafe {
531537
let reg_ptr = vm.register_stack.as_mut_ptr();
532538
let nil_val = LuaValue::nil();
@@ -572,6 +578,7 @@ pub fn luavm_execute(vm: &mut LuaVM) -> LuaResult<LuaValue> {
572578
if vm.frame_count > 1 {
573579
// Get caller info BEFORE decrementing frame_count
574580
let result_reg = unsafe { (*frame_ptr).get_result_reg() };
581+
let num_results = unsafe { (*frame_ptr).get_num_results() };
575582
let caller_ptr = unsafe { vm.frames.as_mut_ptr().add(vm.frame_count - 2) };
576583

577584
// Pop frame
@@ -588,6 +595,15 @@ pub fn luavm_execute(vm: &mut LuaVM) -> LuaResult<LuaValue> {
588595
.get_unchecked_mut(caller_base + result_reg) = return_value;
589596
}
590597

598+
// CRITICAL FIX: When caller expects variable returns (C=0, num_results=usize::MAX),
599+
// we must set top to indicate how many values were returned.
600+
// This is essential for nested calls like outer(1, inner(10))
601+
if num_results == usize::MAX {
602+
unsafe {
603+
(*caller_ptr).top = (result_reg + 1) as u32;
604+
}
605+
}
606+
591607
// Update frame_ptr and local state
592608
frame_ptr = caller_ptr;
593609
unsafe {

crates/luars/src/stdlib/basic.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,13 @@ fn lua_print(vm: &mut LuaVM) -> LuaResult<MultiValue> {
6464

6565
/// type(v) - Return the type of a value as a string
6666
fn lua_type(vm: &mut LuaVM) -> LuaResult<MultiValue> {
67-
let value = get_arg(vm, 1).unwrap_or(LuaValue::nil());
67+
// type() requires exactly one argument
68+
let value = match get_arg(vm, 1) {
69+
Some(v) => v,
70+
None => {
71+
return Err(vm.error("bad argument #1 to 'type' (value expected)".to_string()));
72+
}
73+
};
6874

6975
let type_name = match value.kind() {
7076
LuaValueKind::Nil => "nil",

0 commit comments

Comments
 (0)