From 700b273a117751c04d8a0d54b422a6ee2762361f Mon Sep 17 00:00:00 2001 From: _ <_@_> Date: Sun, 1 Oct 2023 19:50:50 -0500 Subject: [PATCH] :white_check_mark: test: fix up some bugs to support an embedding example --- Cargo.toml | 1 + examples/embedding.rs | 5 -- lunar_wave_cli/Cargo.toml | 2 +- lunar_wave_vm/Cargo.toml | 2 +- lunar_wave_vm/src/lib.rs | 2 + lunar_wave_vm/src/loader.rs | 2 +- lunar_wave_vm/src/state.rs | 116 ++++++++++++++++++++++--------- lunar_wave_vm/src/tests.rs | 19 +++++ lunar_wave_vm/tests/embedding.rs | 42 +++++++++++ 9 files changed, 150 insertions(+), 41 deletions(-) delete mode 100644 examples/embedding.rs create mode 100644 lunar_wave_vm/tests/embedding.rs diff --git a/Cargo.toml b/Cargo.toml index e7d464b..7de75aa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" members = [ "lunar_wave_cli", "lunar_wave_vm", diff --git a/examples/embedding.rs b/examples/embedding.rs deleted file mode 100644 index 4f1f8e2..0000000 --- a/examples/embedding.rs +++ /dev/null @@ -1,5 +0,0 @@ -fn main () -> Result <(), ()> { - println! ("Embedding"); - - Ok (()) -} diff --git a/lunar_wave_cli/Cargo.toml b/lunar_wave_cli/Cargo.toml index d071eb1..03087ab 100644 --- a/lunar_wave_cli/Cargo.toml +++ b/lunar_wave_cli/Cargo.toml @@ -3,7 +3,7 @@ name = "lunar_wave_cli" description = "A Lua CLI implementation" version = "0.1.0" edition = "2021" -author = "ReactorScram" +authors = ["ReactorScram"] [dependencies] lunar_wave_vm = { path = "../lunar_wave_vm" } diff --git a/lunar_wave_vm/Cargo.toml b/lunar_wave_vm/Cargo.toml index cbc609a..f53ce0d 100644 --- a/lunar_wave_vm/Cargo.toml +++ b/lunar_wave_vm/Cargo.toml @@ -3,4 +3,4 @@ name = "lunar_wave_vm" description = "A Lua virtual machine implementation" version = "0.1.0" edition = "2021" -author = "ReactorScram" +authors = ["ReactorScram"] diff --git a/lunar_wave_vm/src/lib.rs b/lunar_wave_vm/src/lib.rs index b180ef1..4ff2145 100644 --- a/lunar_wave_vm/src/lib.rs +++ b/lunar_wave_vm/src/lib.rs @@ -4,11 +4,13 @@ mod state; mod value; pub use loader::compile_bytecode_from_file as compile_bytecode_from_file; +pub use loader::compile_bytecode_from_stdin as compile_bytecode_from_stdin; pub use loader::parse_chunk as parse_chunk; pub use state::Breakpoint as Breakpoint; pub use state::State as State; pub use state::StepError as StepError; pub use state::StepOutput as StepOutput; +pub use value::Value as Value; #[cfg (test)] mod tests; diff --git a/lunar_wave_vm/src/loader.rs b/lunar_wave_vm/src/loader.rs index 8a55acb..16a63fc 100644 --- a/lunar_wave_vm/src/loader.rs +++ b/lunar_wave_vm/src/loader.rs @@ -36,7 +36,7 @@ pub fn compile_bytecode_from_file (path: &str) -> Vec { /// /// `source` is a Vec because we move it to a worker thread -pub (crate) fn compile_bytecode_from_stdin (source: Vec ) -> Vec { +pub fn compile_bytecode_from_stdin (source: Vec ) -> Vec { use std::{ io::Write, process::{ diff --git a/lunar_wave_vm/src/state.rs b/lunar_wave_vm/src/state.rs index fc44dac..a551221 100644 --- a/lunar_wave_vm/src/state.rs +++ b/lunar_wave_vm/src/state.rs @@ -206,12 +206,12 @@ impl <'a> State <'a> { /// Short form to get access to a register within our window - fn reg (&self, i: u8) -> &Value { + pub fn reg (&self, i: u8) -> &Value { let frame = self.stack.last ().unwrap (); &self.registers [frame.register_offset + i as usize] } - fn reg_mut (&mut self, i: u8) -> &mut Value { + pub fn reg_mut (&mut self, i: u8) -> &mut Value { let frame = self.stack.last ().unwrap (); &mut self.registers [frame.register_offset + i as usize] } @@ -330,22 +330,24 @@ impl <'a> State <'a> { Value::RsFunc (x) => { let current_frame = self.stack.last ().unwrap (); let new_offset = current_frame.register_offset + usize::from (*a) + 1; + + // Trash the stack frame so it doesn't point to a + // valid Lua function self.stack.push (StackFrame { program_counter: 65535, // Bogus for native functions block_idx: 65535, // Bogus register_offset: new_offset, }); - // No clue what the '1' is doing here - let b = if b == 0 { + let num_args = if b == 0 { self.top - *a as usize } else { - b + b - 1 }; // Call - let num_results = x (self, b - 1); + let num_results = x (self, num_args); let popped_frame = self.stack.pop ().unwrap (); let offset = popped_frame.register_offset - 1; @@ -678,7 +680,7 @@ impl <'a> State <'a> { self.top = popped_frame.register_offset - 1 + b - 1; } else { - // Return from the entire program + // Return from the entire chunk return Ok (Some (StepOutput::ChunkReturned (self.registers [a..(a + b - 1)].to_vec()))); } }, @@ -778,35 +780,83 @@ impl <'a> State <'a> { *self.reg_mut (*a) = x; }, - Instruction::TailCall (a, b, _c, k) => { + Instruction::TailCall (a, b, c, k) => { + let a = usize::from (*a); assert! (!k, "closing over values in tail calls not implemented"); - // Shift closure and inputs into place - let a = usize::from (*a); - let b = usize::from (*b); - let offset = frame.register_offset - 1; - for i in (offset)..(offset + b) { - self.registers [i] = self.registers [i + a + 1].take (); + let offset = frame.register_offset; + let value = self.registers [offset + a].take (); + match value { + Value::BogusClosure (closure) => { + let closure = closure.borrow (); + + // Shift inputs into place + + let b = usize::from (*b); + + let num_args = if b == 0 { + self.top - a + } + else { + b - 1 + }; + + for i in (offset)..(offset + num_args) { + self.registers [i] = self.registers [i + a + 1].take (); + } + + // Jump into the other function + + let frame = self.stack.last_mut ().unwrap (); + frame.program_counter = 0; + frame.block_idx = closure.idx; + + // Skip the PC increment + return Ok (None); + }, + Value::RsFunc (x) => { + // Shift inputs into place + let b = usize::from (*b); + for i in (offset)..(offset + b) { + self.registers [i] = self.registers [i + a + 1].take (); + } + + let frame = self.stack.last_mut ().unwrap (); + // Trash the stack frame so it doesn't point + // to any valid Lua function + *frame = StackFrame { + block_idx: 65535, + program_counter: 65535, + register_offset: offset, + }; + + let num_args = if b == 0 { + self.top - a + } + else { + b - 1 + }; + + // Call + let num_results = x (self, num_args); + let popped_frame = self.stack.pop ().unwrap (); + + if self.stack.is_empty () { + // The whole chunk is exiting + return Ok (Some (StepOutput::ChunkReturned (self.registers [a..(a + num_results)].to_vec()))); + } + else { + // Set up top for the next call + if *c == 0 { + self.top = popped_frame.register_offset - 1 + num_results; + } + } + }, + _ => { + dbg! (&self.stack); + panic! ("OP_TAILCALL argument must be a function"); + }, } - - let value = &self.registers [offset]; - let closure = if let Some (x) = value.as_closure () { - x - } - else { - dbg! (self); - panic! ("OP_TAILCALL only implemented for closures"); - }; - let closure = closure.borrow (); - - // Jump into the other function - - let frame = self.stack.last_mut ().unwrap (); - frame.program_counter = 0; - frame.block_idx = closure.idx; - - // Skip the PC increment - return Ok (None); }, Instruction::Test (a, k) => { if self.reg (*a).is_truthy() != *k { diff --git a/lunar_wave_vm/src/tests.rs b/lunar_wave_vm/src/tests.rs index 6ff8e5e..5e8d17f 100644 --- a/lunar_wave_vm/src/tests.rs +++ b/lunar_wave_vm/src/tests.rs @@ -359,6 +359,25 @@ fn tables_2 () { run_source (&[], src); } +#[test] +fn tailcall () { + use crate::instruction::Instruction; + + let src = r#" + return tonumber ("5") + "#; + + let bc = loader::compile_bytecode_from_stdin (src.as_bytes ().to_vec ()); + let chunk = loader::parse_chunk_from_bytes (&bc).unwrap (); + + assert_eq! (chunk.blocks [0].instructions [3], Instruction::TailCall (0, 2, 1, false)); + + let actual = run_chunk (&[], &chunk); + let expected = vec! [Value::from (5)]; + + assert_eq! (actual, expected); +} + #[test] fn value_size () { // Per https://www.lua.org/doc/jucs05.pdf, diff --git a/lunar_wave_vm/tests/embedding.rs b/lunar_wave_vm/tests/embedding.rs new file mode 100644 index 0000000..30ed2b7 --- /dev/null +++ b/lunar_wave_vm/tests/embedding.rs @@ -0,0 +1,42 @@ +use lunar_wave_vm as lwvm; + +#[test] +fn embedding () { + use lwvm::{ + State, + Value, + }; + + let src = br#" + return host_lib.add (14, 12) + "#; + + fn host_add (l: &mut State, num_args: usize) -> usize { + assert_eq! (num_args, 2); + let a = l.reg (0).as_int ().unwrap (); + let b = l.reg (1).as_int ().unwrap (); + *l.reg_mut (0) = Value::from (a + b + 1993); + 1 + } + + let bytecode = lwvm::compile_bytecode_from_stdin (src.to_vec ()); + let mut rdr = std::io::Cursor::new (bytecode); + let chunk = lwvm::parse_chunk (&mut rdr).unwrap (); + + let host_lib = [ + ("add", Value::RsFunc (host_add)), + ].into_iter ().map (|(k, v)| (k.to_string (), v)); + + let env = [ + ("host_lib", Value::from_iter (host_lib.into_iter ())), + ].into_iter ().map (|(k, v)| (k.to_string (), v)); + + let upvalues = vec! [ + Value::from_iter (env.into_iter ()), + ]; + + let mut vm = State::new (&chunk, &upvalues); + let output = vm.execute_chunk (&vec! []).unwrap (); + + assert_eq! (output, vec! [Value::from (2019)]); +}