From 93493ce283ee37f5b23b76d0804e9d0f1d2d9ca3 Mon Sep 17 00:00:00 2001 From: Sofia Date: Wed, 18 Mar 2026 20:52:02 +0200 Subject: [PATCH] Implement numerical for loops --- examples/test.lua | 2 +- src/compile.rs | 6 ++---- src/vm.rs | 30 ++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/examples/test.lua b/examples/test.lua index 05a27c0..7fc8bcf 100644 --- a/examples/test.lua +++ b/examples/test.lua @@ -40,6 +40,6 @@ print(true or 0) global value, table = f(10, 11, 12) print("hello") -for i=1,10 do +for i=#table,1,-1 do print(table[i]) end \ No newline at end of file diff --git a/src/compile.rs b/src/compile.rs index 1c9a6c8..cfe24d0 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -380,9 +380,7 @@ impl Statement { scope.locals.insert("_END".to_owned(), *end_reg); scope.locals.insert("_STEP".to_owned(), *step_reg); - let is_eql = scope.register_counter.next(); - instructions.push(Instruction::LessThan(is_eql, *end_reg, *init_reg)); - instructions.push(Instruction::Test(scope.register_counter.next(), is_eql, 1)); + instructions.push(Instruction::ForTest(*init_reg, *end_reg, *step_reg)); let mut inner_scope = scope.clone(); let instr = block.compile(state, &mut inner_scope); @@ -392,7 +390,7 @@ impl Statement { instructions.extend(instr); instructions.push(Instruction::Add(*init_reg, *init_reg, *step_reg)); - instructions.push(Instruction::Jmp(-(instr_len + 5))); + instructions.push(Instruction::Jmp(-(instr_len + 4))); } } diff --git a/src/vm.rs b/src/vm.rs index 35c4af3..8541b1e 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -152,6 +152,12 @@ pub enum Instruction { Jmp(i32), /// if (R(B) <=> C) then R(A) := R(B) else PC++ Test(u16, u16, u16), + /// if R(C) >= 0 + /// then if R(A) < R(B) + /// PC ++ + /// otherwise if R(A) > R(B) + /// PC ++ + ForTest(u16, u16, u16), /// [func] [params.len()] [ret_regs.len()] /// R(A), ... R(A+C-2) := R(A)(R(A+1), ... R(A+B-1)) Call(u16, u16, u16), @@ -185,6 +191,9 @@ impl Debug for Instruction { Instruction::NewTable(arg0) => write!(f, "NEWTABLE {}", arg0), Instruction::Jmp(arg0) => write!(f, "JMP {}", arg0), Instruction::Test(arg0, arg1, arg2) => write!(f, "TEST {} {} {}", arg0, arg1, arg2), + Instruction::ForTest(arg0, arg1, arg2) => { + write!(f, "FORTEST {} {} {}", arg0, arg1, arg2) + } Instruction::Call(arg0, arg1, arg2) => write!(f, "CALL {} {} {}", arg0, arg1, arg2), Instruction::Close(arg0) => write!(f, "CLOSE {}", arg0), Instruction::Closure(arg0, arg1) => write!(f, "CLOSURE {} {}", arg0, arg1), @@ -898,6 +907,27 @@ impl ClosureRunner { self.program_counter += 1; } } + Instruction::ForTest(counter_reg, end_reg, step_reg) => { + let step_gte_0 = match self.get_stack(*step_reg) { + StackValue::Value(value) => Value::Integer(LuaInteger(0)).lt(&value), + }; + let counter = match self.get_stack(*counter_reg) { + StackValue::Value(value) => value, + }; + let end = match self.get_stack(*end_reg) { + StackValue::Value(value) => value, + }; + + if step_gte_0?.is_truthy() { + if !end.lt(&counter)?.is_truthy() { + self.program_counter += 1; + } + } else { + if !counter.lt(&end)?.is_truthy() { + self.program_counter += 1; + } + } + } Instruction::Call(func_reg, param_len, ret_len) => { let param_start_func_reg = *func_reg;