use std::rc::Rc; use crate::{ instruction::Instruction, string_interner::Interner, value::{ BogusClosure, Value, }, }; #[derive (Clone, Debug)] pub struct Upvalue { pub in_stack: bool, pub idx: u8, pub kind: u8, } #[derive (Clone, Debug)] pub struct Block { pub instructions: Vec , pub constants: Vec , pub upvalues: Vec , } #[derive (Clone, Debug, Default)] pub struct Chunk { pub blocks: Vec >, } #[derive (Clone, Copy, Debug, Default)] pub struct StackFrame { // Starts at 0 right after OP_CALL program_counter: usize, // Starts from 0 for main and 1 for the first closure block_idx: usize, register_offset: usize, } #[derive (Debug)] pub struct Breakpoint { pub block_idx: usize, pub program_counter: usize, } #[derive (Debug)] pub struct State { pub registers: Vec , // Currently only used for native function calls top: usize, pub stack: Vec , stack_top: StackFrame, pub debug_print: bool, chunk: Chunk, current_block: Rc , pub upvalues: Vec , pub si: Interner, } fn lw_io_write (l: &mut State, num_args: usize) -> usize { for i in 0..u8::try_from (num_args).unwrap () { match l.reg (i) { Value::Float (x) => print! ("{}", x), Value::Integer (x) => print! ("{}", x), Value::String (x) => print! ("{}", l.si.get (*x)), _ => panic! ("Can't io.write this value"), } } // TODO: PUC Lua actually returns the file handle here. 0 } fn lw_print (l: &mut State, num_args: usize) -> usize { for i in 0..u8::try_from (num_args).unwrap () { let input = l.reg (i); if i == 0 { print! ("{input}"); } else { print! ("\t{input}"); } } println! (""); 1 } fn lw_sqrt (l: &mut State, num_args: usize) -> usize { assert! (num_args >= 1, "math.sqrt needs 1 argument"); let input = l.reg (0).as_float ().unwrap (); let output = input.sqrt (); *l.reg_mut (0) = Value::from (output); 1 } fn lw_string_format (l: &mut State, num_args: usize) -> usize { assert! (num_args >= 1, "string.format needs at least 1 argument"); assert_eq! (l.get_top (), 2, "string.format not fully implemented"); let f_string = l.reg (0).as_str ().unwrap (); assert_eq! (Value::String (f_string), l.to_string ("%0.9f")); let num = l.reg (1).as_float ().unwrap (); let output = format! ("{:0.9}", num); *l.reg_mut (0) = l.to_string (&output); 1 } fn lw_table_concat (l: &mut State, num_args: usize) -> usize { assert_eq! (num_args, 2); let s = { let t = l.reg (0).as_table ().unwrap ().borrow (); let joiner = l.reg (1).as_str ().unwrap (); let mut s = String::new (); for i in 0..t.length () { if i > 0 { s.push_str (l.si.get (joiner)); } let x = t.get_int (i + 1); s.push_str (&format! ("{}", x)); } s }; *l.reg_mut (0) = l.to_string (&s); 1 } fn lw_table_pack (l: &mut State, num_args: usize) -> usize { let mut v = vec! []; for i in 0..num_args { v.push (l.reg (u8::try_from (i).unwrap ()).clone ()); } *l.reg_mut (0) = Value::from_iter (v.into_iter ()); 1 } fn tonumber (l: &State, value: &Value) -> Value { match value { Value::Float (x) => Value::Float (*x), Value::Integer (x) => Value::Integer (*x), Value::String (x) => { if let Ok (x) = str::parse:: (l.si.get (*x)) { Value::from (x) } else if let Ok (x) = str::parse:: (l.si.get (*x)) { Value::from (x) } else { Value::Nil } }, _ => Value::Nil, } } fn lw_tonumber (l: &mut State, num_args: usize) -> usize { assert_eq! (num_args, 1, "tonumber only implemented for 1 argument"); let output = tonumber (&l, l.reg (0)); *l.reg_mut (0) = output; 1 } pub enum StepOutput { ChunkReturned (Vec ), } #[derive (Debug, thiserror::Error)] pub enum StepError { #[error ("generic")] Generic { frame: StackFrame, inst: Instruction, msg: &'static str, }, } impl State { pub fn new (chunk: Chunk, upvalues: Vec ) -> Self { let current_block = Rc::clone (&chunk.blocks [0]); Self { // TODO: Stack is actually supposed to grow to a limit of // idk 10,000. I thought it was fixed at 256. registers: vec! [Value::Nil; 256], top: 0, stack: vec! [], stack_top: Default::default (), debug_print: false, chunk, current_block, upvalues, si: Default::default (), } } pub fn new_with_args > (chunk: Chunk, mut si: Interner, args: I) -> Self { let upvalues = Self::upvalues_from_args (&mut si, args); let current_block = Rc::clone (&chunk.blocks [0]); Self { // TODO: Stack is actually supposed to grow to a limit of // idk 10,000. I thought it was fixed at 256. registers: vec! [Value::Nil; 256], top: 0, stack: vec! [], stack_top: Default::default (), debug_print: false, chunk, current_block, upvalues, si, } } pub fn at_breakpoint (&self, bp: &Breakpoint) -> bool { let frame = &self.stack_top; frame.block_idx == bp.block_idx && frame.program_counter == bp.program_counter } pub fn upvalues_from_args > (si: &mut Interner, args: I) -> Vec { let arg = args.map (|s| si.intern (&s)).enumerate (); let arg = Value::from_iter (arg.map (|(i, v)| (Value::from (i), Value::String (v)))); let io: Vec <_> = [ ("write", Value::RsFunc (lw_io_write)), ].into_iter ().map (|(k, v)| (si.intern (k), v)).collect (); let math: Vec <_> = [ ("sqrt", Value::RsFunc (lw_sqrt)), ].into_iter ().map (|(k, v)| (si.intern (k), v)).collect (); let string: Vec <_> = [ ("format", Value::RsFunc (lw_string_format)), ].into_iter ().map (|(k, v)| (si.intern (k), v)).collect (); let table: Vec <_> = [ ("concat", Value::RsFunc (lw_table_concat)), ("pack", Value::RsFunc (lw_table_pack)), ].into_iter ().map (|(k, v)| (si.intern (k), v)).collect (); let env = [ ("arg", arg), ("io", Value::from_iter (io.into_iter ())), ("math", Value::from_iter (math.into_iter ())), ("print", Value::RsFunc (lw_print)), ("string", Value::from_iter (string.into_iter ())), ("table", Value::from_iter (table.into_iter ())), ("tonumber", Value::RsFunc (lw_tonumber)), ].into_iter ().map (|(k, v)| (si.intern (k), v)); vec! [ Value::from_iter (env.into_iter ()), ] } fn register_window (&self) -> &[Value] { let frame = &self.stack_top; &self.registers [frame.register_offset..] } /// Short form to get access to a register within our window pub fn reg (&self, i: u8) -> &Value { let idx = self.stack_top.register_offset + i as usize; &self.registers [idx] } pub fn reg_mut (&mut self, i: u8) -> &mut Value { &mut self.registers [self.stack_top.register_offset + i as usize] } // For native functions to check how many args they got pub fn get_top (&self) -> usize { self.top - self.stack_top.register_offset } fn make_step_error (&self, msg: &'static str, inst: &Instruction) -> StepError { StepError::Generic { frame: self.stack_top.clone (), inst: inst.clone (), msg, } } fn op_add (&mut self, a: u8, b: u8, c: u8) -> bool { let v_b = self.reg (b); let v_c = self.reg (c); *self.reg_mut (a) = match (v_b, v_c) { (Value::Float (b), Value::Float (c)) => Value::from (b + c), (Value::Integer (b), Value::Integer (c)) => Value::from (b + c), (Value::Integer (b), Value::Float (c)) => Value::from (*b as f64 + c), (Value::Float (b), Value::Integer (c)) => Value::from (b + *c as f64), _ => return false, }; true } fn op_call (&mut self, a: u8, b: u8, c: u8) -> bool { let b = usize::from (b); // 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 // Do a clone here to avoid a borow problem. // Should be fixable with more clever code. let v_a = self.reg (a).clone (); match v_a { Value::BogusClosure (rc) => { let idx = rc.borrow ().idx; let target_block = idx; self.stack.push (self.stack_top); self.set_stack_top (StackFrame { program_counter: 0, block_idx: target_block, register_offset: self.stack_top.register_offset + a as usize + 1, }); // Skip the PC increment at the bottom of the loop return true; }, Value::RsFunc (x) => { let old_offset = self.stack_top.register_offset; self.stack_top.register_offset = old_offset + usize::from (a) + 1; // Trash the stack frame so it doesn't point to a // valid Lua function self.stack.push (self.stack_top); self.stack_top = StackFrame { program_counter: 65535, // Bogus for native functions block_idx: 65535, // Bogus register_offset: self.stack_top.register_offset, }; let num_args = if b == 0 { self.top - self.stack_top.register_offset } else { b - 1 }; // Call let num_results = x (self, num_args); let popped_frame = self.stack_top; let x = self.stack.pop ().unwrap (); self.set_stack_top (x); self.stack_top.register_offset = old_offset; let offset = old_offset + usize::from (a); for i in (offset)..(offset + usize::try_from (num_results).unwrap ()) { self.registers [i] = self.registers [i + 1].take (); } // Set up top for the next call if c == 0 { self.top = popped_frame.register_offset - 1 + num_results; } }, x => { panic! ("Cannot call value {x:?}"); }, } false } fn op_div (&mut self, a: u8, b: u8, c: u8) -> bool { let v_b = self.reg (b); let v_c = self.reg (c); *self.reg_mut (a) = match (v_b, v_c) { (Value::Float (b), Value::Float (c)) => Value::from (b / c), (Value::Integer (b), Value::Integer (c)) => Value::from (*b as f64 / *c as f64), (Value::Integer (b), Value::Float (c)) => Value::from (*b as f64 / c), (Value::Float (b), Value::Integer (c)) => Value::from (b / *c as f64), _ => return false, }; true } fn op_get_field (&mut self, a: u8, b: u8, c: u8) { let block = &self.current_block; let constants = &block.constants; let key = match &constants [usize::from (c)] { Value::String (s) => s, _ => panic! ("K[C] must be a string"), }; let val = match &self.registers [self.stack_top.register_offset + usize::from (b)] { Value::Nil => panic! ("R[B] must not be nil"), Value::Table (t) => t.borrow ().get_str (*key).clone (), _ => panic! ("R[B] must be a table"), }; *self.reg_mut (a) = val; } fn op_get_table (&mut self, a: u8, b: u8, c: u8) { let t = match self.reg (b) { Value::Table (t) => t, _ => panic! ("R[B] must be a table"), }; let key = self.reg (c); let val = t.borrow ().get (key.clone ()).clone (); *self.reg_mut (a) = val; } fn op_mmbin (&mut self, a: u8, b: u8, _c: u8) { let a = self.reg (a); let b = self.reg (b); if a.as_float().is_some() && b.as_float().is_some () { // No need for metamethods } else { panic! ("Not sure how to implement OP_MMBIN for these 2 values {a:?}, {b:?}"); } } fn op_mul (&mut self, a: u8, b: u8, c: u8) -> bool { let v_b = self.reg (b); let v_c = self.reg (c); *self.reg_mut (a) = match (v_b, v_c) { (Value::Float (b), Value::Float (c)) => Value::from (b * c), (Value::Integer (b), Value::Integer (c)) => Value::from (b * c), (Value::Integer (b), Value::Float (c)) => Value::from (*b as f64 * c), (Value::Float (b), Value::Integer (c)) => Value::from (b * *c as f64), _ => return false, }; true } fn op_set_field (&mut self, a: u8, b: u8, c: u8, k: bool) { let block = &self.current_block; let constants = &block.constants; let b = usize::try_from (b).unwrap (); let key = match constants.get (b).unwrap () { Value::String (s) => *s, _ => panic! ("SetField only supports string keys"), }; let value = if k { &constants [usize::from (c)] } else { self.reg_mut (c) }.clone (); let mut dst = self.reg (a).as_table () .expect ("SetField only works on tables").borrow_mut (); dst.insert_str (key, value); } fn op_sub (&mut self, a: u8, b: u8, c: u8) -> bool { let v_b = self.reg (b); let v_c = self.reg (c); *self.reg_mut (a) = match (v_b, v_c) { (Value::Float (b), Value::Float (c)) => Value::from (b - c), (Value::Integer (b), Value::Integer (c)) => Value::from (b - c), (Value::Integer (b), Value::Float (c)) => Value::from (*b as f64 - c), (Value::Float (b), Value::Integer (c)) => Value::from (b - *c as f64), _ => return false, }; true } fn constants (&self) -> &[Value] { &self.current_block.constants } fn set_block_idx (&mut self, block_idx: usize) { self.stack_top.block_idx = block_idx; self.current_block = Rc::clone (&self.chunk.blocks [block_idx]); } fn set_stack_top (&mut self, frame: StackFrame) { self.stack_top = frame; self.current_block = Rc::clone (&self.chunk.blocks [frame.block_idx]); } fn fetch (&self) -> &Instruction { match self.current_block.instructions.get (self.stack_top.program_counter) { Some (x) => x, None => { dbg! (&self.stack, &self.stack_top); panic! ("program_counter went out of bounds"); } } } fn incr_pc (&mut self) { self.stack_top.program_counter += 1; } pub fn step (&mut self) -> Result