From bebc96916b71986f63866a1e72f8358056fe30bc Mon Sep 17 00:00:00 2001 From: _ <_@_> Date: Wed, 27 Sep 2023 01:46:53 -0500 Subject: [PATCH] :star: allow Lua to call native Rust functions And Value::BogusPrint is gone. So the only Bogus remaining is closures. --- src/state.rs | 61 +++++++++++++++++++++++++++++++++--------- src/tests.rs | 21 +++++++++++++++ src/value.rs | 6 ++--- test_vectors/hello.lua | 41 +++------------------------- 4 files changed, 77 insertions(+), 52 deletions(-) diff --git a/src/state.rs b/src/state.rs index 4821b14..58f6b75 100644 --- a/src/state.rs +++ b/src/state.rs @@ -40,6 +40,8 @@ pub struct Breakpoint { #[derive (Debug)] pub struct State { registers: Vec , + // Currently only used for native function calls + top: usize, stack: Vec , pub debug_print: bool, @@ -51,6 +53,7 @@ impl Default for State { fn default () -> Self { Self { registers: vec! [Value::Nil; 16], + top: 0, stack: vec! [ StackFrame { program_counter: 0, @@ -65,6 +68,22 @@ impl Default for State { } } +fn lw_print (l: &mut State) -> i32 { + for i in 0..u8::try_from (l.get_top ()).unwrap () { + let input = l.reg (i); + + if i == 0 { + print! ("{input}"); + } + else { + print! ("\t{input}"); + } + } + println! (""); + *l.reg_mut (0) = Value::from (1993); + 1 +} + impl State { pub fn upvalues_from_args > (args: I) -> Vec { @@ -73,7 +92,7 @@ impl State { let env = [ ("arg", arg), - ("print", Value::BogusPrint), + ("print", Value::RsFunc (lw_print)), ].into_iter ().map (|(k, v)| (k.to_string (), v)); vec! [ @@ -98,6 +117,11 @@ impl State { &mut self.registers [frame.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.last ().unwrap ().register_offset + } + pub fn execute_chunk (&mut self, chunk: &Chunk, upvalues: &[Value]) -> Vec { let max_iters = 2000; @@ -136,7 +160,9 @@ impl State { *self.reg_mut (*a) = Value::from (v_b + v_c); }, - Instruction::Call (a, _b, c) => { + Instruction::Call (a, b, _c) => { + 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) @@ -145,9 +171,10 @@ impl State { // // 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 + // Do a clone here to avoid a borow problem. + // Should be fixable with more clever code. - let v_a = self.reg (*a); + let v_a = self.reg (*a).clone (); match v_a { Value::BogusClosure (rc) => { @@ -173,17 +200,27 @@ impl State { // 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. + Value::RsFunc (x) => { + let current_frame = self.stack.last ().unwrap (); + let new_offset = current_frame.register_offset + usize::from (*a) + 1; + self.stack.push (StackFrame { + program_counter: 65535, // Bogus for native functions + block_idx: 65535, // Bogus + register_offset: new_offset, + }); - // assert_eq! (*b, 2); - assert_eq! (*c, 1); + // No clue what the '1' is doing here + self.top = new_offset + b - 1; - let value = self.reg (a + 1); - println! ("{}", value); + // Call + let num_results = x (self); - *self.reg_mut (*a) = self.reg_mut (*a + 1).take (); + let popped_frame = self.stack.pop ().unwrap (); + let offset = popped_frame.register_offset - 1; + + for i in (offset)..(offset + usize::try_from (num_results).unwrap ()) { + self.registers [i] = self.registers [i + 1 + usize::from (*a)].take (); + } }, _ => { let stack = &self.stack; diff --git a/src/tests.rs b/src/tests.rs index 7634421..05a425b 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -301,6 +301,27 @@ fn is_93 () { assert_eq! (run (&["", "94"], &chunk), vec! [Value::from (1)]); } +#[test] +fn native_functions () { + fn add (l: &mut State) -> i32 { + 0 + } + + fn multiply (l: &mut State) -> i32 { + 0 + } + + fn greet (l: &mut State) -> i32 { + 0 + } + + let _funcs = [ + add, + multiply, + greet, + ]; +} + #[test] fn tables_1 () { // I don't capture stdout yet, but I can at least make sure basic table diff --git a/src/value.rs b/src/value.rs index 4f61e28..21b6a97 100644 --- a/src/value.rs +++ b/src/value.rs @@ -25,6 +25,7 @@ pub enum Value { Float (f64), Integer (i64), + RsFunc (fn (&mut crate::state::State) -> i32), String (Rc ), Table (Rc >), @@ -32,7 +33,6 @@ pub enum Value { // closures yet BogusClosure (Rc ), - BogusPrint, } impl Default for Value { @@ -49,11 +49,11 @@ impl fmt::Display for Value { Value::Boolean (true) => write! (f, "true"), Value::Float (x) => write! (f, "{:?}", x), Value::Integer (x) => write! (f, "{}", x), + Value::RsFunc (x) => write! (f, "function: {:?}", x), Value::String (s) => write! (f, "{}", s), Value::Table (t) => write! (f, "table: {:?}", std::rc::Rc::as_ptr (t)), Value::BogusClosure (_) => write! (f, "BogusClosure"), - Value::BogusPrint => write! (f, "BogusPrint"), } } } @@ -139,12 +139,12 @@ impl std::hash::Hash for Value { Self::Float (x) => x.to_ne_bytes ().hash (state), Self::Integer (x) => x.hash (state), + Self::RsFunc (x) => x.hash (state), // TODO: Implement string interning so we don't hash the whole string here Self::String (x) => x.hash (state), Self::Table (x) => Rc::as_ptr (&x).hash (state), Self::BogusClosure (_) => panic! ("can't hash Bogus values"), - Self::BogusPrint => panic! ("can't hash Bogus values"), } } } diff --git a/test_vectors/hello.lua b/test_vectors/hello.lua index 167d7aa..6d746d2 100644 --- a/test_vectors/hello.lua +++ b/test_vectors/hello.lua @@ -1,37 +1,4 @@ -print " 1" - -print (nil) -print (false) -print (true) -print (1993) -print (1993.00) -print "Hello." - -print " 2" - -local t = {} -print (t) -print (t [1]) -t [1] = "asdf" -print (t [1]) -t.x = 1993 -print (t.x) - -t.t = { 3.14159 } -print (t ["t"][1]) - -print " 3" - -local c = {} -c [1] = "ddd" - -local a = { c } -local b = { c } - -print (a [1][2]) -print (b [1][2]) - -c [2] = "eee" - -print (a [1][2]) -print (b [1][2]) +print ("Hi!", 2) +print () +print ("Hi!") +print ("You've _gotta_ admit this is **cool**!")