2023-09-24 22:40:45 +00:00
|
|
|
use std::collections::BTreeMap;
|
|
|
|
|
2023-09-25 01:40:28 +00:00
|
|
|
#[derive (Debug, PartialEq)]
|
2023-09-24 22:40:45 +00:00
|
|
|
pub enum Instruction {
|
|
|
|
Add (u8, u8, u8),
|
|
|
|
|
|
|
|
Call (u8, u8, u8),
|
|
|
|
Closure (u8, i32),
|
|
|
|
|
|
|
|
// Equals Constant?
|
|
|
|
EqK (u8, u8, u8),
|
|
|
|
|
2023-09-25 01:40:28 +00:00
|
|
|
// Get Immediate?
|
|
|
|
GetI (u8, u8, u8),
|
|
|
|
|
2023-09-24 22:40:45 +00:00
|
|
|
// Get Table, Upvalue
|
|
|
|
GetTabUp (u8, u8, u8),
|
|
|
|
|
2023-09-25 01:40:28 +00:00
|
|
|
GetUpVal (u8, u8),
|
2023-09-24 22:40:45 +00:00
|
|
|
|
|
|
|
// Jump
|
|
|
|
Jmp (i32),
|
|
|
|
|
2023-09-25 00:47:17 +00:00
|
|
|
LoadFalse (u8),
|
|
|
|
|
2023-09-24 22:40:45 +00:00
|
|
|
// Load Integer?
|
|
|
|
LoadI (u8, i32),
|
|
|
|
|
|
|
|
// Load Constant
|
|
|
|
LoadK (u8, i32),
|
|
|
|
|
2023-09-25 00:47:17 +00:00
|
|
|
LoadTrue (u8),
|
|
|
|
|
2023-09-24 22:40:45 +00:00
|
|
|
// MetaMethod, Binary
|
|
|
|
MmBin (u8, u8, u8),
|
|
|
|
|
|
|
|
Move (u8, u8),
|
|
|
|
|
2023-09-25 01:40:28 +00:00
|
|
|
Mul (u8, u8, u8),
|
|
|
|
|
2023-09-25 00:47:17 +00:00
|
|
|
Not (u8, u8),
|
|
|
|
|
2023-09-25 01:40:28 +00:00
|
|
|
// (A, B, _C, k) Return B - 1 registers starting with A
|
|
|
|
Return (u8, u8, u8, bool),
|
2023-09-24 22:40:45 +00:00
|
|
|
|
2023-09-25 00:47:17 +00:00
|
|
|
Return0,
|
|
|
|
|
|
|
|
// Return just one register
|
|
|
|
Return1 (u8),
|
|
|
|
|
2023-09-25 08:05:00 +00:00
|
|
|
SetTabUp (u8, u8, u8),
|
|
|
|
|
|
|
|
TailCall (u8, u8, u8, bool),
|
2023-09-25 01:40:28 +00:00
|
|
|
|
2023-09-25 00:47:17 +00:00
|
|
|
Test (u8, i32),
|
|
|
|
|
2023-09-24 22:40:45 +00:00
|
|
|
VarArgPrep (i32),
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive (Clone, Debug, PartialEq)]
|
|
|
|
pub enum Value {
|
|
|
|
Nil,
|
2023-09-25 00:47:17 +00:00
|
|
|
Boolean (bool),
|
2023-09-24 22:40:45 +00:00
|
|
|
Float (f64),
|
|
|
|
String (String),
|
|
|
|
|
|
|
|
// These are all bogus, I haven't figured out how to implement
|
|
|
|
// tables and function pointers yet
|
|
|
|
|
|
|
|
BogusArg (Vec <String>),
|
2023-09-25 03:49:00 +00:00
|
|
|
BogusClosure {
|
|
|
|
idx: usize,
|
|
|
|
upvalues: Vec <Value>,
|
|
|
|
},
|
2023-09-24 22:40:45 +00:00
|
|
|
BogusEnv (BTreeMap <String, Value>),
|
|
|
|
BogusPrint,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Value {
|
|
|
|
fn default () -> Self {
|
|
|
|
Self::Nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From <String> for Value {
|
|
|
|
fn from (x: String) -> Self {
|
|
|
|
Self::String (x)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From <&str> for Value {
|
|
|
|
fn from (x: &str) -> Self {
|
|
|
|
Self::from (String::from (x))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From <i32> for Value {
|
|
|
|
fn from (x: i32) -> Self {
|
|
|
|
Self::Float (f64::try_from (x).unwrap ())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From <f64> for Value {
|
|
|
|
fn from (x: f64) -> Self {
|
|
|
|
Self::Float (x)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Value {
|
|
|
|
fn as_float (&self) -> Option <f64> {
|
|
|
|
match self {
|
|
|
|
Self::Float (x) => Some (*x),
|
|
|
|
_ => None,
|
|
|
|
}
|
|
|
|
}
|
2023-09-25 00:47:17 +00:00
|
|
|
|
|
|
|
fn is_truthy (&self) -> bool {
|
|
|
|
// And this is something Lua does better than JS and Python.
|
|
|
|
|
|
|
|
match self {
|
|
|
|
Value::Nil => false,
|
|
|
|
Value::Boolean (false) => false,
|
|
|
|
_ => true,
|
|
|
|
}
|
|
|
|
}
|
2023-09-25 05:23:53 +00:00
|
|
|
|
|
|
|
fn take (&mut self) -> Self {
|
|
|
|
let mut x = Value::Nil;
|
|
|
|
std::mem::swap (self, &mut x);
|
|
|
|
x
|
|
|
|
}
|
2023-09-24 22:40:45 +00:00
|
|
|
}
|
|
|
|
|
2023-09-25 00:47:17 +00:00
|
|
|
pub struct Block {
|
2023-09-24 22:40:45 +00:00
|
|
|
pub instructions: Vec <Instruction>,
|
|
|
|
pub constants: Vec <Value>,
|
2023-09-25 01:40:28 +00:00
|
|
|
pub upvalue_count: usize,
|
2023-09-24 22:40:45 +00:00
|
|
|
}
|
|
|
|
|
2023-09-25 00:47:17 +00:00
|
|
|
pub struct Chunk {
|
|
|
|
pub blocks: Vec <Block>,
|
|
|
|
}
|
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
#[derive (Clone, Debug)]
|
2023-09-25 00:47:17 +00:00
|
|
|
struct StackFrame {
|
2023-09-24 22:40:45 +00:00
|
|
|
// i32 makes it a little easier to implement jumps
|
2023-09-25 00:47:17 +00:00
|
|
|
// Starts at 0 right after OP_CALL
|
|
|
|
|
2023-09-24 22:40:45 +00:00
|
|
|
program_counter: i32,
|
2023-09-25 00:47:17 +00:00
|
|
|
|
|
|
|
// Starts from 0 for main and 1 for the first closure
|
|
|
|
block_idx: usize,
|
|
|
|
|
|
|
|
register_offset: usize,
|
|
|
|
}
|
|
|
|
|
2023-09-25 05:23:53 +00:00
|
|
|
#[derive (Debug)]
|
|
|
|
pub struct Breakpoint {
|
|
|
|
pub block_idx: usize,
|
|
|
|
pub program_counter: i32,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive (Debug)]
|
2023-09-25 00:47:17 +00:00
|
|
|
pub struct State {
|
|
|
|
registers: Vec <Value>,
|
2023-09-25 03:49:00 +00:00
|
|
|
stack: Vec <StackFrame>,
|
2023-09-25 05:23:53 +00:00
|
|
|
|
2023-09-25 00:47:17 +00:00
|
|
|
pub debug_print: bool,
|
2023-09-25 05:23:53 +00:00
|
|
|
pub breakpoints: Vec <Breakpoint>,
|
|
|
|
step_count: u32,
|
2023-09-24 22:40:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for State {
|
|
|
|
fn default () -> Self {
|
|
|
|
Self {
|
2023-09-25 05:23:53 +00:00
|
|
|
registers: vec! [Value::Nil; 16],
|
2023-09-25 03:49:00 +00:00
|
|
|
stack: vec! [
|
|
|
|
StackFrame {
|
|
|
|
program_counter: 0,
|
|
|
|
block_idx: 0,
|
|
|
|
register_offset: 0,
|
|
|
|
},
|
|
|
|
],
|
2023-09-25 00:47:17 +00:00
|
|
|
debug_print: false,
|
2023-09-25 05:23:53 +00:00
|
|
|
breakpoints: Default::default(),
|
|
|
|
step_count: 0,
|
2023-09-24 22:40:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl State {
|
|
|
|
pub fn upvalues_from_args <I: Iterator <Item = String>> (args: I) -> Vec <Value>
|
|
|
|
{
|
|
|
|
let arg = args.map (|s| s.to_string ()).collect ();
|
|
|
|
|
|
|
|
let env = BTreeMap::from_iter ([
|
|
|
|
("arg", Value::BogusArg (arg)),
|
|
|
|
("print", Value::BogusPrint),
|
2023-09-24 22:42:56 +00:00
|
|
|
].map (|(k, v)| (k.to_string (), v)));
|
2023-09-24 22:40:45 +00:00
|
|
|
|
|
|
|
vec! [
|
|
|
|
Value::BogusEnv (env),
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
fn register_window (&self) -> &[Value] {
|
|
|
|
let frame = self.stack.last ().unwrap ();
|
|
|
|
&self.registers [frame.register_offset..]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn register_window_mut (&mut self) -> &mut [Value] {
|
|
|
|
let frame = self.stack.last_mut ().unwrap ();
|
|
|
|
&mut self.registers [frame.register_offset..]
|
|
|
|
}
|
|
|
|
|
2023-09-24 22:42:56 +00:00
|
|
|
pub fn execute_chunk (&mut self, chunk: &Chunk, upvalues: &[Value])
|
2023-09-24 22:40:45 +00:00
|
|
|
-> Vec <Value> {
|
|
|
|
let max_iters = 2000;
|
|
|
|
|
|
|
|
for _ in 0..max_iters {
|
2023-09-25 05:23:53 +00:00
|
|
|
self.step_count += 1;
|
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
let frame = self.stack.last_mut ().unwrap ().clone ();
|
2023-09-25 00:47:17 +00:00
|
|
|
let block = chunk.blocks.get (frame.block_idx).unwrap ();
|
2023-09-24 22:40:45 +00:00
|
|
|
|
2023-09-25 05:23:53 +00:00
|
|
|
for bp in &self.breakpoints {
|
|
|
|
if frame.block_idx == bp.block_idx && frame.program_counter == bp.program_counter
|
|
|
|
{
|
|
|
|
dbg! (&self);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-25 00:47:17 +00:00
|
|
|
let mut next_pc = frame.program_counter;
|
|
|
|
|
|
|
|
let pc = usize::try_from (frame.program_counter).expect ("program_counter is not a valid usize");
|
|
|
|
let instruction = match block.instructions.get (pc) {
|
|
|
|
Some (x) => x,
|
|
|
|
None => {
|
2023-09-25 03:49:00 +00:00
|
|
|
dbg! (&self.stack);
|
2023-09-25 00:47:17 +00:00
|
|
|
panic! ("program_counter went out of bounds");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
// let r = &mut self.registers [frame.register_offset..];
|
2023-09-25 00:47:17 +00:00
|
|
|
let k = &block.constants;
|
2023-09-24 22:40:45 +00:00
|
|
|
|
|
|
|
match instruction {
|
|
|
|
Instruction::Add (a, b, c) => {
|
2023-09-25 03:49:00 +00:00
|
|
|
let r = self.register_window_mut ();
|
|
|
|
|
2023-09-24 22:40:45 +00:00
|
|
|
let a = usize::try_from (*a).unwrap ();
|
|
|
|
let b = usize::try_from (*b).unwrap ();
|
|
|
|
let c = usize::try_from (*c).unwrap ();
|
|
|
|
|
|
|
|
let v_b = r [b].as_float ().unwrap ();
|
|
|
|
let v_c = r [c].as_float ().unwrap ();
|
|
|
|
|
|
|
|
r [a] = (v_b + v_c).into ();
|
|
|
|
},
|
|
|
|
Instruction::Call (a, b, c) => {
|
|
|
|
// Take arguments from registers [a + 1, a + b)
|
|
|
|
// Call the function in register [a]
|
|
|
|
// Return values in registers [a, a + c - 1)
|
|
|
|
//
|
|
|
|
// That is, call a with b - 1 arguments and expect c returns
|
|
|
|
//
|
|
|
|
// e.g. CALL 0 2 1 mean "Call 0 with 1 argument, return 1 value", like for printing a constant
|
|
|
|
|
|
|
|
// TODO: Only implement printing values for now
|
|
|
|
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
2023-09-25 05:23:53 +00:00
|
|
|
let r = self.register_window_mut ();
|
2023-09-25 00:47:17 +00:00
|
|
|
let v_a = r.get (a).unwrap ();
|
2023-09-24 22:40:45 +00:00
|
|
|
|
2023-09-25 00:47:17 +00:00
|
|
|
match v_a {
|
2023-09-25 03:49:00 +00:00
|
|
|
Value::BogusClosure {
|
|
|
|
idx,
|
|
|
|
upvalues,
|
|
|
|
}=> {
|
2023-09-25 00:47:17 +00:00
|
|
|
let block_idx = frame.block_idx;
|
2023-09-25 01:40:28 +00:00
|
|
|
let target_block = *idx;
|
2023-09-25 00:47:17 +00:00
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
let current_frame = self.stack.last ().unwrap ();
|
2023-09-25 00:47:17 +00:00
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
self.stack.push (StackFrame {
|
2023-09-25 00:47:17 +00:00
|
|
|
program_counter: 0,
|
|
|
|
block_idx: target_block,
|
|
|
|
register_offset: current_frame.register_offset + a + 1,
|
|
|
|
});
|
|
|
|
|
|
|
|
if self.debug_print {
|
|
|
|
println! ("Inst {block_idx}:{pc} calls {target_block}:0");
|
2023-09-25 03:49:00 +00:00
|
|
|
let stack_depth = self.stack.len ();
|
2023-09-25 00:47:17 +00:00
|
|
|
println! ("stack_depth: {stack_depth}");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Skip the PC increment at the bottom of the loop
|
|
|
|
continue;
|
|
|
|
},
|
|
|
|
Value::BogusPrint => {
|
|
|
|
// In real Lua, print is a function inside
|
|
|
|
// the runtime. Here it's bogus.
|
|
|
|
|
|
|
|
// assert_eq! (*b, 2);
|
|
|
|
assert_eq! (*c, 1);
|
|
|
|
|
|
|
|
println! ("{:?}", r.get (a + 1).unwrap ());
|
2023-09-25 05:23:53 +00:00
|
|
|
r [a] = r [a + 1].take ();
|
2023-09-25 00:47:17 +00:00
|
|
|
},
|
2023-09-25 03:49:00 +00:00
|
|
|
_ => {
|
|
|
|
let stack = &self.stack;
|
|
|
|
panic! ("Cannot call value {a:?}. backtrace: {stack:?}");
|
|
|
|
},
|
2023-09-25 00:47:17 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
Instruction::Closure (a, b) => {
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
|
|
|
let b = usize::try_from (*b).unwrap ();
|
2023-09-24 22:40:45 +00:00
|
|
|
|
2023-09-25 05:23:53 +00:00
|
|
|
if self.debug_print {
|
|
|
|
println! ("OP_CLOSURE {a} {b}");
|
|
|
|
}
|
|
|
|
|
|
|
|
let r = self.register_window_mut ();
|
2023-09-25 03:49:00 +00:00
|
|
|
r [a] = Value::BogusClosure {
|
|
|
|
idx: b + frame.block_idx + 1,
|
|
|
|
upvalues: vec! [],
|
|
|
|
};
|
2023-09-24 22:40:45 +00:00
|
|
|
},
|
|
|
|
Instruction::EqK (a, b, c_k) => {
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
|
|
|
let b = usize::try_from (*b).unwrap ();
|
2023-09-25 03:49:00 +00:00
|
|
|
let r = self.register_window ();
|
2023-09-24 22:40:45 +00:00
|
|
|
|
|
|
|
let equal = r [a] == k [b];
|
|
|
|
|
|
|
|
match (equal, c_k) {
|
2023-09-25 00:47:17 +00:00
|
|
|
(true, 0) => next_pc += 1,
|
|
|
|
(false, 1) => next_pc += 1,
|
2023-09-24 22:40:45 +00:00
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Instruction::GetTabUp (a, b, c) => {
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
|
|
|
let b = usize::try_from (*b).unwrap ();
|
|
|
|
let c = usize::try_from (*c).unwrap ();
|
|
|
|
|
|
|
|
let env = match upvalues.get (b).unwrap () {
|
|
|
|
Value::BogusEnv (x) => x,
|
|
|
|
_ => panic! ("Only allowed upvalue is BogusEnv"),
|
|
|
|
};
|
|
|
|
|
|
|
|
let key = match k.get (c).unwrap () {
|
|
|
|
Value::String (s) => s,
|
|
|
|
_ => panic! ("GetTabUp only supports string keys"),
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let value = env.get (key).unwrap ();
|
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
|
|
|
|
self.register_window_mut() [a] = value.clone ();
|
2023-09-24 22:40:45 +00:00
|
|
|
},
|
|
|
|
Instruction::GetI (a, b, c) => {
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
|
|
|
let b = usize::try_from (*b).unwrap ();
|
|
|
|
let c = usize::try_from (*c).unwrap ();
|
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
let r = self.register_window_mut ();
|
|
|
|
|
2023-09-24 22:40:45 +00:00
|
|
|
let table = r.get (b).unwrap ();
|
|
|
|
let value = match table {
|
|
|
|
Value::BogusArg (arg) => arg.get (c).map (|x| x.as_str().into ()).unwrap_or_default(),
|
|
|
|
_ => unimplemented!(),
|
|
|
|
};
|
|
|
|
|
|
|
|
r [a] = value;
|
|
|
|
},
|
2023-09-25 05:23:53 +00:00
|
|
|
Instruction::GetUpVal (a, b) => {
|
|
|
|
let this_func = self.stack.last ().unwrap ().register_offset - 1;
|
|
|
|
let upvalues = match &self.registers [this_func] {
|
|
|
|
Value::BogusClosure { idx, upvalues } => upvalues,
|
|
|
|
_ => panic! ("Can't do GetUpVal outside a closure"),
|
|
|
|
};
|
|
|
|
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
|
|
|
let b = usize::try_from (*b).unwrap ();
|
|
|
|
|
|
|
|
self.register_window_mut ()[a] = upvalues [b].clone ();
|
|
|
|
},
|
2023-09-25 00:47:17 +00:00
|
|
|
Instruction::Jmp (s_j) => next_pc += s_j,
|
|
|
|
Instruction::LoadFalse (a) => {
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
2023-09-25 03:49:00 +00:00
|
|
|
self.register_window_mut ()[a] = Value::Boolean (false);
|
2023-09-25 00:47:17 +00:00
|
|
|
},
|
2023-09-24 22:40:45 +00:00
|
|
|
Instruction::LoadI (a, sbx) => {
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
self.register_window_mut ()[a] = (*sbx).into ();
|
2023-09-24 22:40:45 +00:00
|
|
|
},
|
|
|
|
Instruction::LoadK (a, bx) => {
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
|
|
|
let bx = usize::try_from (*bx).unwrap ();
|
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
self.register_window_mut ()[a] = k [bx].clone ();
|
2023-09-24 22:40:45 +00:00
|
|
|
},
|
2023-09-25 00:47:17 +00:00
|
|
|
Instruction::LoadTrue (a) => {
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
2023-09-25 03:49:00 +00:00
|
|
|
self.register_window_mut ()[a] = Value::Boolean (true);
|
2023-09-25 00:47:17 +00:00
|
|
|
},
|
2023-09-24 22:40:45 +00:00
|
|
|
Instruction::MmBin (a, b, _c) => {
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
|
|
|
let b = usize::try_from (*b).unwrap ();
|
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
let r = self.register_window();
|
2023-09-24 22:40:45 +00:00
|
|
|
let a = &r [a];
|
|
|
|
let b = &r [b];
|
|
|
|
|
|
|
|
if a.as_float().is_some() && b.as_float().is_some () {
|
|
|
|
// No need for metamethods
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
panic! ("Not sure how to implememtn OP_MMBIN for these 2 values {a:?}, {b:?}");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Instruction::Move (a, b) => {
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
|
|
|
let b = usize::try_from (*b).unwrap ();
|
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
let r = self.register_window_mut();
|
2023-09-24 22:40:45 +00:00
|
|
|
r [a] = r [b].clone ();
|
|
|
|
},
|
2023-09-25 00:47:17 +00:00
|
|
|
Instruction::Not (a, b) => {
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
|
|
|
let b = usize::try_from (*b).unwrap ();
|
2023-09-25 03:49:00 +00:00
|
|
|
let r = self.register_window_mut();
|
2023-09-25 00:47:17 +00:00
|
|
|
r [a] = Value::Boolean (! r [b].is_truthy());
|
|
|
|
}
|
2023-09-25 05:23:53 +00:00
|
|
|
Instruction::Return (a, b, c, k) => {
|
2023-09-24 22:40:45 +00:00
|
|
|
let a = usize::try_from (*a).unwrap ();
|
|
|
|
let b = usize::try_from (*b).unwrap ();
|
2023-09-25 05:23:53 +00:00
|
|
|
let c = usize::try_from (*c).unwrap ();
|
2023-09-24 22:40:45 +00:00
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
let popped_frame = self.stack.pop ().unwrap ();
|
|
|
|
|
|
|
|
// Build closure if needed
|
|
|
|
if *k {
|
|
|
|
|
2023-09-25 05:23:53 +00:00
|
|
|
let closure_idx = match &self.registers [popped_frame.register_offset + a] {
|
2023-09-25 03:49:00 +00:00
|
|
|
Value::BogusClosure { idx, upvalues } => idx,
|
|
|
|
_ => panic! ("Impossible"),
|
|
|
|
};
|
|
|
|
|
|
|
|
let upvalue_count = chunk.blocks [*closure_idx].upvalue_count;
|
|
|
|
|
|
|
|
let start_reg = a + popped_frame.register_offset - upvalue_count;
|
|
|
|
|
|
|
|
let upvalues = self.registers [start_reg..start_reg+upvalue_count].iter ().cloned ().collect ();
|
|
|
|
|
|
|
|
self.registers [a + popped_frame.register_offset] = Value::BogusClosure {
|
|
|
|
idx: *closure_idx,
|
|
|
|
upvalues,
|
|
|
|
};
|
|
|
|
}
|
2023-09-25 01:40:28 +00:00
|
|
|
|
|
|
|
if self.debug_print {
|
|
|
|
let old_block = popped_frame.block_idx;
|
|
|
|
let old_pc = popped_frame.program_counter;
|
|
|
|
println! ("Inst {old_block}:{old_pc} returns");
|
2023-09-25 03:49:00 +00:00
|
|
|
let stack_depth = self.stack.len ();
|
2023-09-25 01:40:28 +00:00
|
|
|
println! ("stack_depth: {stack_depth}");
|
|
|
|
}
|
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
if let Some (new_frame) = self.stack.last() {
|
2023-09-25 01:40:28 +00:00
|
|
|
next_pc = new_frame.program_counter;
|
2023-09-25 05:23:53 +00:00
|
|
|
|
|
|
|
// Shift our output registers down so the caller
|
|
|
|
// can grab them
|
|
|
|
// idk exactly why Lua does this
|
|
|
|
|
|
|
|
// Register that our function was in before we
|
|
|
|
// called it.
|
|
|
|
|
|
|
|
let offset = popped_frame.register_offset - 1;
|
|
|
|
for i in (offset)..(offset - 1 + b) {
|
|
|
|
self.registers [i] = self.registers [i + 1 + a].take ();
|
|
|
|
}
|
2023-09-25 01:40:28 +00:00
|
|
|
}
|
|
|
|
else {
|
2023-09-25 05:23:53 +00:00
|
|
|
// Return from the entire program
|
2023-09-25 06:57:57 +00:00
|
|
|
return self.registers [a..(a + b - 1)].to_vec();
|
2023-09-25 01:40:28 +00:00
|
|
|
}
|
2023-09-25 00:47:17 +00:00
|
|
|
},
|
|
|
|
Instruction::Return1 (a) => {
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
2023-09-25 03:49:00 +00:00
|
|
|
let popped_frame = self.stack.pop ().unwrap ();
|
|
|
|
|
2023-09-25 00:47:17 +00:00
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
self.registers [popped_frame.register_offset - 1] = self.register_window ()[a].clone ();
|
2023-09-25 00:47:17 +00:00
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
let frame = self.stack.last ().unwrap ();
|
2023-09-25 00:47:17 +00:00
|
|
|
let new_block = frame.block_idx;
|
|
|
|
next_pc = frame.program_counter;
|
|
|
|
|
|
|
|
if self.debug_print {
|
|
|
|
let old_block = popped_frame.block_idx;
|
|
|
|
let old_pc = popped_frame.program_counter;
|
|
|
|
println! ("Inst {old_block}:{old_pc} returns to inst {new_block}:{next_pc}");
|
2023-09-25 03:49:00 +00:00
|
|
|
let stack_depth = self.stack.len ();
|
2023-09-25 00:47:17 +00:00
|
|
|
println! ("stack_depth: {stack_depth}");
|
|
|
|
}
|
2023-09-25 05:23:53 +00:00
|
|
|
|
|
|
|
// Shift output register down
|
|
|
|
let offset = popped_frame.register_offset;
|
|
|
|
self.registers [offset - 1] = self.registers [offset + a].take ();
|
2023-09-25 00:47:17 +00:00
|
|
|
},
|
|
|
|
Instruction::Test (a, _k) => {
|
|
|
|
let a = usize::try_from (*a).unwrap ();
|
|
|
|
|
2023-09-25 03:49:00 +00:00
|
|
|
let a = self.register_window ().get (a).unwrap ();
|
2023-09-25 00:47:17 +00:00
|
|
|
|
|
|
|
if self.debug_print {
|
|
|
|
println! ("Test {a:?}");
|
|
|
|
}
|
|
|
|
|
|
|
|
if a.is_truthy() {
|
|
|
|
next_pc += 1;
|
|
|
|
}
|
2023-09-24 22:40:45 +00:00
|
|
|
},
|
|
|
|
Instruction::VarArgPrep (_) => (),
|
|
|
|
x => panic! ("Unimplemented instruction {x:?}"),
|
|
|
|
}
|
|
|
|
|
2023-09-25 00:47:17 +00:00
|
|
|
next_pc += 1;
|
|
|
|
{
|
2023-09-25 03:49:00 +00:00
|
|
|
let frame = self.stack.last_mut ().unwrap ();
|
2023-09-25 00:47:17 +00:00
|
|
|
frame.program_counter = next_pc;
|
|
|
|
}
|
2023-09-24 22:40:45 +00:00
|
|
|
}
|
|
|
|
|
2023-09-25 00:47:17 +00:00
|
|
|
panic! ("Hit max iterations before block returned");
|
2023-09-24 22:40:45 +00:00
|
|
|
}
|
|
|
|
}
|