Compare commits

...

2 Commits

Author SHA1 Message Date
Scott Richmond
7e4ddd3dc4 improve panic traces; tail calls work for simpel calls 2025-06-19 21:13:18 -04:00
Scott Richmond
2f5dab84a7 tail calls work now? also print stack trace on panic 2025-06-19 20:52:19 -04:00
2 changed files with 118 additions and 24 deletions

View File

@ -75,14 +75,12 @@ pub fn run(src: &'static str) {
pub fn main() { pub fn main() {
env::set_var("RUST_BACKTRACE", "1"); env::set_var("RUST_BACKTRACE", "1");
let src = " let src = "
fn nope () -> nil fn recursive {
fn foo () -> { (0) -> :done
nope () (n) -> recursive (0)
:foo
} }
fn bar () -> foo ()
bar () recursive (1)
"; ";
run(src); run(src);
} }

132
src/vm.rs
View File

@ -164,11 +164,27 @@ impl Vm {
self.result.as_ref().unwrap() self.result.as_ref().unwrap()
} }
pub fn call_stack(&mut self) -> String {
let mut stack = format!(" calling {}", self.frame.function.show());
for frame in self.call_stack.iter().rev() {
let mut name = frame.function.show();
name = if name == "fn user script" {
"user script".to_string()
} else {
name
};
stack = format!("{stack}\n from {name}");
}
stack
}
pub fn panic(&mut self, msg: &'static str) { pub fn panic(&mut self, msg: &'static str) {
self.result = Some(Err(Panic::Str(msg))); let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack());
self.result = Some(Err(Panic::String(msg)));
} }
pub fn panic_with(&mut self, msg: String) { pub fn panic_with(&mut self, msg: String) {
let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack());
self.result = Some(Err(Panic::String(msg))); self.result = Some(Err(Panic::String(msg)));
} }
@ -530,21 +546,6 @@ impl Vm {
self.push(splatted); self.push(splatted);
self.ip += 2; self.ip += 2;
} }
// PushDict => {
// let dict_len = self.chunk().bytecode[self.ip + 1] as usize * 2;
// let dict_members = self.stack.split_off(self.stack.len() - dict_len);
// let mut dict = HashMap::new();
// let mut dict_iter = dict_members.iter();
// while let Some(kw) = dict_iter.next() {
// let Value::Keyword(key) = kw else {
// unreachable!()
// };
// let value = dict_iter.next().unwrap();
// dict.insert(*key, value.clone());
// }
// self.push(Value::Dict(Box::new(dict)));
// self.ip += 2;
// }
PushDict => { PushDict => {
self.push(Value::Dict(Box::new(HashMap::new()))); self.push(Value::Dict(Box::new(HashMap::new())));
self.ip += 1; self.ip += 1;
@ -648,7 +649,6 @@ impl Vm {
let high = self.chunk().bytecode[self.ip + 1]; let high = self.chunk().bytecode[self.ip + 1];
let low = self.chunk().bytecode[self.ip + 2]; let low = self.chunk().bytecode[self.ip + 2];
let jump_len = combine_bytes(high, low); let jump_len = combine_bytes(high, low);
// let jump_len = self.chunk().bytecode[self.ip + 1] as usize;
if self.matches { if self.matches {
self.ip += jump_len + 3; self.ip += jump_len + 3;
} else { } else {
@ -854,7 +854,103 @@ impl Vm {
}; };
self.push(Value::Partial(Rc::new(partial))); 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]; let arity = self.chunk().bytecode[self.ip + 1];
self.ip += 2; self.ip += 2;