From 2f5dab84a7de9bb8f570415b40ac7a1732da36c6 Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Thu, 19 Jun 2025 20:52:19 -0400 Subject: [PATCH] tail calls work now? also print stack trace on panic --- src/main.rs | 4 +- src/vm.rs | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index ee4a7c3..b07fd09 100644 --- a/src/main.rs +++ b/src/main.rs @@ -76,11 +76,11 @@ pub fn main() { env::set_var("RUST_BACKTRACE", "1"); let src = " fn nope () -> nil -fn foo () -> { +fn foo (_, _, _) -> { nope () :foo } -fn bar () -> foo () +fn bar () -> foo (1, 2, 3) bar () "; diff --git a/src/vm.rs b/src/vm.rs index a7b4fc0..c824d50 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -164,12 +164,21 @@ impl Vm { self.result.as_ref().unwrap() } + pub fn print_call_stack(&mut self) { + println!("in {}", self.frame.function.as_fn().name()); + for frame in self.call_stack.iter() { + println!(" in {}", frame.function.as_fn().name()); + } + } + pub fn panic(&mut self, msg: &'static str) { self.result = Some(Err(Panic::Str(msg))); + self.print_call_stack(); } pub fn panic_with(&mut self, msg: String) { self.result = Some(Err(Panic::String(msg))); + self.print_call_stack(); } pub fn interpret(&mut self) { @@ -854,7 +863,103 @@ impl Vm { }; self.push(Value::Partial(Rc::new(partial))); } - Call | TailCall => { + TailCall => { + let arity = self.chunk().bytecode[self.ip + 1]; + self.ip += 2; + + let val = self.pop(); + + match val { + Value::Fn(_) => { + if !val.as_fn().accepts(arity) { + return self.panic_with(format!( + "wrong number of arguments to {} passing {arity} args", + val.show() + )); + } + // first put the arguments in the register + for i in 0..arity as usize { + self.return_register[arity as usize - i - 1] = self.pop(); + } + + // then pop everything back to the current stack frame + self.stack.truncate(self.frame.stack_base); + // then push the arguments back on the stack + let mut i = 0; + while i < 8 && self.return_register[i] != Value::Nothing { + let mut value = Value::Nothing; + swap(&mut self.return_register[i], &mut value); + self.push(value); + i += 1; + } + + let splat_arity = val.as_fn().splat_arity(); + if splat_arity > 0 && arity >= splat_arity { + let splatted_args = self.stack.split_off( + self.stack.len() - (arity - splat_arity) as usize - 1, + ); + let gathered_args = Vector::from(splatted_args); + self.push(Value::List(Box::new(gathered_args))); + } + let arity = splat_arity.min(arity); + let mut frame = CallFrame { + function: val, + arity, + stack_base: self.stack.len() - arity as usize, + ip: 0, + }; + + swap(&mut self.frame, &mut frame); + frame.ip = self.ip; + + self.ip = 0; + + if crate::DEBUG_RUN { + println!("== tail call into {} ==", self.frame.function.show()); + } + } + Value::BaseFn(base_fn) => { + let value = match (arity, base_fn) { + (0, BaseFn::Nullary(f)) => f(), + (1, BaseFn::Unary(f)) => f(&self.pop()), + (2, BaseFn::Binary(f)) => f(&self.pop(), &self.pop()), + (3, BaseFn::Ternary(f)) => f(&self.pop(), &self.pop(), &self.pop()), + _ => return self.panic("internal ludus error"), + }; + self.push(value); + } + Value::Partial(partial) => { + let last_arg = self.pop(); + let args = &partial.args; + for arg in args { + if *arg == Value::Nothing { + self.push(last_arg.clone()); + } else { + self.push(arg.clone()); + } + } + let the_fn = partial.function.clone(); + let mut frame = CallFrame { + function: the_fn, + arity: args.len() as u8, + stack_base: self.stack.len() - arity as usize - 1, + ip: 0, + }; + + swap(&mut self.frame, &mut frame); + frame.ip = self.ip; + + self.call_stack.push(frame); + self.ip = 0; + + if crate::DEBUG_RUN { + println!("== calling into {} ==", self.frame.function.show()); + } + } + _ => return self.panic_with(format!("{} is not a function", val.show())), + } + } + Call => { let arity = self.chunk().bytecode[self.ip + 1]; self.ip += 2;