lots of bugs fixed--upvalues, bindings, stack manipulations, tail calls, etc.

This commit is contained in:
Scott Richmond 2025-06-21 22:25:08 -04:00
parent 583262f9e8
commit 25a0c62dcf
7 changed files with 165 additions and 177 deletions

View File

@ -217,6 +217,12 @@ fn map {
& } & }
} }
fn add {
() -> 0
(x) -> x
(x, y) -> base :add (x, y)
}
#{ #{
& type & type
& coll? & coll?
@ -233,7 +239,8 @@ fn map {
& not & not
& tuple? & tuple?
& fn? & fn?
& rest first
rest
inc inc
& dec & dec
& count & count
@ -242,7 +249,8 @@ fn map {
& list? & list?
& list & list
& first & first
& fold fold
& append append
map map
add
} }

View File

@ -422,3 +422,15 @@ https://craftinginterpreters.com/closures.html#flattening-upvalues.
I need to study and adapt this exact set of problems. I need to study and adapt this exact set of problems.
I believe I need to take the strategy he uses with closures being different from functions, etc. I believe I need to take the strategy he uses with closures being different from functions, etc.
So: rework the closures strategy here. So: rework the closures strategy here.
***
Closures strategy mostly unfucked.
Now I'm having some difficulty with bindings, again?
Current situation: still trying to get `map` and `fold` to work properly.
The bindings inside of non-trivial functions goes weird.
The scope depths are all out of whack.
And the stack positions on stuff are also totally weird.
One thing: the way we are now resolving upvalues means that nothing should ever reach back further in the stack than the stack base in the vm.
So now we'll do all bindings relative to the stack base.
UGH.

View File

@ -5,10 +5,10 @@ use std::rc::Rc;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum BaseFn { pub enum BaseFn {
Nullary(fn() -> Value), Nullary(&'static str, fn() -> Value),
Unary(fn(&Value) -> Value), Unary(&'static str, fn(&Value) -> Value),
Binary(fn(&Value, &Value) -> Value), Binary(&'static str, fn(&Value, &Value) -> Value),
Ternary(fn(&Value, &Value, &Value) -> Value), Ternary(&'static str, fn(&Value, &Value, &Value) -> Value),
} }
pub fn eq(x: &Value, y: &Value) -> Value { pub fn eq(x: &Value, y: &Value) -> Value {
@ -585,57 +585,60 @@ pub fn r#mod(x: &Value, y: &Value) -> Value {
pub fn make_base() -> Value { pub fn make_base() -> Value {
let members = vec![ let members = vec![
("add", Value::BaseFn(BaseFn::Binary(add))), ("add", Value::BaseFn(BaseFn::Binary("add", add))),
("append", Value::BaseFn(BaseFn::Binary(append))), ("append", Value::BaseFn(BaseFn::Binary("append", append))),
("assoc", Value::BaseFn(BaseFn::Ternary(assoc))), ("assoc", Value::BaseFn(BaseFn::Ternary("assoc", assoc))),
("at", Value::BaseFn(BaseFn::Binary(at))), ("at", Value::BaseFn(BaseFn::Binary("at", at))),
("atan_2", Value::BaseFn(BaseFn::Binary(atan_2))), ("atan_2", Value::BaseFn(BaseFn::Binary("atan_2", atan_2))),
("bool", Value::BaseFn(BaseFn::Unary(r#bool))), ("bool", Value::BaseFn(BaseFn::Unary("bool", r#bool))),
("ceil", Value::BaseFn(BaseFn::Unary(ceil))), ("ceil", Value::BaseFn(BaseFn::Unary("ceil", ceil))),
("chars", Value::BaseFn(BaseFn::Unary(chars))), ("chars", Value::BaseFn(BaseFn::Unary("chars", chars))),
("concat", Value::BaseFn(BaseFn::Binary(concat))), ("concat", Value::BaseFn(BaseFn::Binary("concat", concat))),
("cos", Value::BaseFn(BaseFn::Unary(cos))), ("cos", Value::BaseFn(BaseFn::Unary("cos", cos))),
("count", Value::BaseFn(BaseFn::Unary(count))), ("count", Value::BaseFn(BaseFn::Unary("count", count))),
("dec", Value::BaseFn(BaseFn::Unary(dec))), ("dec", Value::BaseFn(BaseFn::Unary("dec", dec))),
("dissoc", Value::BaseFn(BaseFn::Binary(dissoc))), ("dissoc", Value::BaseFn(BaseFn::Binary("dissoc", dissoc))),
("div", Value::BaseFn(BaseFn::Binary(div))), ("div", Value::BaseFn(BaseFn::Binary("div", div))),
("doc!", Value::BaseFn(BaseFn::Unary(doc))), ("doc!", Value::BaseFn(BaseFn::Unary("doc!", doc))),
("downcase", Value::BaseFn(BaseFn::Unary(downcase))), (
("eq?", Value::BaseFn(BaseFn::Binary(eq))), "downcase",
("first", Value::BaseFn(BaseFn::Unary(first))), Value::BaseFn(BaseFn::Unary("downcase", downcase)),
("floor", Value::BaseFn(BaseFn::Unary(floor))), ),
("get", Value::BaseFn(BaseFn::Binary(get))), ("eq?", Value::BaseFn(BaseFn::Binary("eq?", eq))),
("gt?", Value::BaseFn(BaseFn::Binary(gt))), ("first", Value::BaseFn(BaseFn::Unary("first", first))),
("gte?", Value::BaseFn(BaseFn::Binary(gte))), ("floor", Value::BaseFn(BaseFn::Unary("floor", floor))),
("inc", Value::BaseFn(BaseFn::Unary(inc))), ("get", Value::BaseFn(BaseFn::Binary("get", get))),
("last", Value::BaseFn(BaseFn::Unary(last))), ("gt?", Value::BaseFn(BaseFn::Binary("gt?", gt))),
("list", Value::BaseFn(BaseFn::Unary(list))), ("gte?", Value::BaseFn(BaseFn::Binary("gte?", gte))),
("lt?", Value::BaseFn(BaseFn::Binary(lt))), ("inc", Value::BaseFn(BaseFn::Unary("inc", inc))),
("lte?", Value::BaseFn(BaseFn::Binary(lte))), ("last", Value::BaseFn(BaseFn::Unary("last", last))),
("mod", Value::BaseFn(BaseFn::Binary(r#mod))), ("list", Value::BaseFn(BaseFn::Unary("list", list))),
("mult", Value::BaseFn(BaseFn::Binary(mult))), ("lt?", Value::BaseFn(BaseFn::Binary("lt?", lt))),
("number", Value::BaseFn(BaseFn::Unary(number))), ("lte?", Value::BaseFn(BaseFn::Binary("lte?", lte))),
("mod", Value::BaseFn(BaseFn::Binary("mod", r#mod))),
("mult", Value::BaseFn(BaseFn::Binary("mult", mult))),
("number", Value::BaseFn(BaseFn::Unary("number", number))),
("pi", Value::Number(std::f64::consts::PI)), ("pi", Value::Number(std::f64::consts::PI)),
("print!", Value::BaseFn(BaseFn::Unary(print))), ("print!", Value::BaseFn(BaseFn::Unary("print!", print))),
("random", Value::BaseFn(BaseFn::Nullary(random))), ("random", Value::BaseFn(BaseFn::Nullary("random", random))),
("range", Value::BaseFn(BaseFn::Binary(range))), ("range", Value::BaseFn(BaseFn::Binary("range", range))),
("rest", Value::BaseFn(BaseFn::Unary(rest))), ("rest", Value::BaseFn(BaseFn::Unary("rest", rest))),
("round", Value::BaseFn(BaseFn::Unary(round))), ("round", Value::BaseFn(BaseFn::Unary("round", round))),
("show", Value::BaseFn(BaseFn::Unary(show))), ("show", Value::BaseFn(BaseFn::Unary("show", show))),
("sin", Value::BaseFn(BaseFn::Unary(sin))), ("sin", Value::BaseFn(BaseFn::Unary("sin", sin))),
("slice", Value::BaseFn(BaseFn::Ternary(slice))), ("slice", Value::BaseFn(BaseFn::Ternary("slice", slice))),
("split", Value::BaseFn(BaseFn::Binary(split))), ("split", Value::BaseFn(BaseFn::Binary("split", split))),
("sqrt", Value::BaseFn(BaseFn::Unary(sqrt))), ("sqrt", Value::BaseFn(BaseFn::Unary("sqrt", sqrt))),
("sqrt_2", Value::Number(std::f64::consts::SQRT_2)), ("sqrt_2", Value::Number(std::f64::consts::SQRT_2)),
("store!", Value::BaseFn(BaseFn::Binary(store))), ("store!", Value::BaseFn(BaseFn::Binary("store!", store))),
("sub", Value::BaseFn(BaseFn::Binary(sub))), ("sub", Value::BaseFn(BaseFn::Binary("sub", sub))),
("tan", Value::BaseFn(BaseFn::Unary(tan))), ("tan", Value::BaseFn(BaseFn::Unary("tan", tan))),
("trim", Value::BaseFn(BaseFn::Unary(trim))), ("trim", Value::BaseFn(BaseFn::Unary("trim", trim))),
("triml", Value::BaseFn(BaseFn::Unary(triml))), ("triml", Value::BaseFn(BaseFn::Unary("triml", triml))),
("trimr", Value::BaseFn(BaseFn::Unary(trimr))), ("trimr", Value::BaseFn(BaseFn::Unary("trimr", trimr))),
("type", Value::BaseFn(BaseFn::Unary(r#type))), ("type", Value::BaseFn(BaseFn::Unary("type", r#type))),
("unbox", Value::BaseFn(BaseFn::Unary(unbox))), ("unbox", Value::BaseFn(BaseFn::Unary("unbox", unbox))),
("upcase", Value::BaseFn(BaseFn::Unary(upcase))), ("upcase", Value::BaseFn(BaseFn::Unary("upcase", upcase))),
]; ];
Value::Dict(Box::new(HashMap::from(members))) Value::Dict(Box::new(HashMap::from(members)))
} }

View File

@ -26,7 +26,7 @@ pub enum Op {
PushBinding, PushBinding,
PushGlobal, PushGlobal,
Store, Store,
StoreAt, StoreN,
Stash, Stash,
Load, Load,
ResetMatch, ResetMatch,
@ -96,6 +96,8 @@ pub enum Op {
Print, Print,
SetUpvalue, SetUpvalue,
GetUpvalue, GetUpvalue,
Msg,
// Inc, // Inc,
// Dec, // Dec,
// Gt, // Gt,
@ -140,6 +142,7 @@ impl std::fmt::Display for Op {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use Op::*; use Op::*;
let rep = match self { let rep = match self {
Msg => "msg",
Noop => "noop", Noop => "noop",
Nothing => "nothing", Nothing => "nothing",
Nil => "nil", Nil => "nil",
@ -154,7 +157,7 @@ impl std::fmt::Display for Op {
PushBinding => "push_binding", PushBinding => "push_binding",
PushGlobal => "push_global", PushGlobal => "push_global",
Store => "store", Store => "store",
StoreAt => "store_at", StoreN => "store_n",
Stash => "stash", Stash => "stash",
Load => "load", Load => "load",
Match => "match", Match => "match",
@ -268,7 +271,8 @@ impl Chunk {
| Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq | Add | Sub | Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq | Add | Sub
| Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString | Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString
| ConcatStrings | Stringify | MatchType | Return | Match | Print | AppendList | ConcatStrings | Stringify | MatchType | Return | Match | Print | AppendList
| ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing | PushGlobal => { | ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing | PushGlobal
| SetUpvalue | Msg => {
println!("{i:04}: {op}") println!("{i:04}: {op}")
} }
Constant | MatchConstant => { Constant | MatchConstant => {
@ -281,9 +285,8 @@ impl Chunk {
} }
PushBinding | MatchTuple | MatchSplattedTuple | LoadSplattedTuple | MatchList PushBinding | MatchTuple | MatchSplattedTuple | LoadSplattedTuple | MatchList
| MatchSplattedList | LoadSplattedList | MatchDict | MatchSplattedDict | MatchSplattedList | LoadSplattedList | MatchDict | MatchSplattedDict
| DropDictEntry | LoadDictValue | PushTuple | PushBox | MatchDepth | PopN | StoreAt | DropDictEntry | LoadDictValue | PushTuple | PushBox | MatchDepth | PopN | StoreN
| Call | SetUpvalue | GetUpvalue | Partial | MatchString | PushStringMatches | Call | GetUpvalue | Partial | MatchString | PushStringMatches | TailCall => {
| TailCall => {
let next = self.bytecode[*i + 1]; let next = self.bytecode[*i + 1];
println!("{i:04}: {:16} {next:03}", op.to_string()); println!("{i:04}: {:16} {next:03}", op.to_string());
*i += 1; *i += 1;
@ -364,7 +367,7 @@ pub struct Compiler<'a> {
pub src: &'static str, pub src: &'static str,
pub name: &'static str, pub name: &'static str,
pub enclosing: Option<&'a Compiler<'a>>, pub enclosing: Option<&'a Compiler<'a>>,
pub upvalues: Vec<Upvalue>, pub upvalues: Vec<&'static str>,
loop_info: Vec<LoopInfo>, loop_info: Vec<LoopInfo>,
tail_pos: bool, tail_pos: bool,
} }
@ -383,12 +386,6 @@ fn has_placeholder(args: &[Spanned<Ast>]) -> bool {
args.iter().any(|arg| matches!(arg, (Ast::Placeholder, _))) args.iter().any(|arg| matches!(arg, (Ast::Placeholder, _)))
} }
fn as_two_bytes(x: usize) -> (u8, u8) {
let low = x as u8;
let high = (x >> 8) as u8;
(high, low)
}
impl<'a> Compiler<'a> { impl<'a> Compiler<'a> {
pub fn new( pub fn new(
ast: &'static Spanned<Ast>, ast: &'static Spanned<Ast>,
@ -535,27 +532,7 @@ impl<'a> Compiler<'a> {
} }
fn resolve_upvalue(&self, name: &'static str) -> Option<usize> { fn resolve_upvalue(&self, name: &'static str) -> Option<usize> {
self.upvalues.iter().position(|uv| uv.name == name) self.upvalues.iter().position(|uv| *uv == name)
}
fn get_upvalue(&self, name: &'static str) -> Upvalue {
let local = self.bindings.iter().find(|b| b.name == name);
match local {
Some(binding) => {
let upvalue = Upvalue {
name,
stack_pos: binding.stack_pos,
};
println!("found upvalue {name} in {}", self.name);
upvalue
}
None => {
println!("Getting upvalue {name}");
let upvalue = self.enclosing.unwrap().get_upvalue(name);
println!("upvalue: {:?}", upvalue);
upvalue
}
}
} }
fn resolve_binding(&mut self, name: &'static str) { fn resolve_binding(&mut self, name: &'static str) {
@ -576,40 +553,12 @@ impl<'a> Compiler<'a> {
self.emit_op(Op::PushGlobal); self.emit_op(Op::PushGlobal);
return; return;
} }
let upvalue = self.get_upvalue(name);
self.emit_op(Op::GetUpvalue); self.emit_op(Op::GetUpvalue);
self.emit_byte(self.upvalues.len()); self.emit_byte(self.upvalues.len());
self.upvalues.push(upvalue); self.upvalues.push(name);
self.stack_depth += 1; self.stack_depth += 1;
} }
fn resolve_binding_old(&mut self, name: &'static str) {
match self.resolve_local(name) {
Some(position) => {
self.emit_op(Op::PushBinding);
self.emit_byte(position);
self.stack_depth += 1;
}
None => match self.resolve_upvalue(name) {
Some(position) => {
println!("resolved upvalue: {name} at {position}");
self.emit_op(Op::GetUpvalue);
self.emit_byte(position);
self.stack_depth += 1;
}
None => {
println!("setting upvalue: {name}");
let upvalue = self.get_upvalue(name);
self.emit_op(Op::GetUpvalue);
self.emit_byte(self.upvalues.len());
self.upvalues.push(upvalue);
dbg!(&self.upvalues);
self.stack_depth += 1;
}
},
}
}
fn pop(&mut self) { fn pop(&mut self) {
self.emit_op(Op::Pop); self.emit_op(Op::Pop);
self.stack_depth -= 1; self.stack_depth -= 1;
@ -648,6 +597,13 @@ impl<'a> Compiler<'a> {
self.loop_info.last().unwrap().stack_root self.loop_info.last().unwrap().stack_root
} }
fn msg(&mut self, str: String) {
let leaked = Box::leak(str.into_boxed_str());
self.emit_constant(Value::Interned(leaked));
self.emit_op(Op::Msg);
self.stack_depth -= 1;
}
pub fn compile(&mut self) { pub fn compile(&mut self) {
use Ast::*; use Ast::*;
match self.ast { match self.ast {
@ -1377,7 +1333,7 @@ impl<'a> Compiler<'a> {
for idx in no_match_jumps { for idx in no_match_jumps {
compiler.patch_jump(idx, compiler.len() - idx - 3); compiler.patch_jump(idx, compiler.len() - idx - 3);
} }
compiler.scope_depth -= 1; // compiler.scope_depth -= 1;
std::mem::swap(&mut compiler.upvalues, &mut upvalues); std::mem::swap(&mut compiler.upvalues, &mut upvalues);
} }
@ -1442,8 +1398,9 @@ impl<'a> Compiler<'a> {
} }
for upvalue in upvalues { for upvalue in upvalues {
self.resolve_binding(upvalue);
self.emit_op(Op::SetUpvalue); self.emit_op(Op::SetUpvalue);
self.emit_byte(upvalue.stack_pos); self.stack_depth -= 1;
} }
} }
FnDeclaration(name) => { FnDeclaration(name) => {
@ -1483,17 +1440,21 @@ impl<'a> Compiler<'a> {
let (Ast::Tuple(members), _) = value.as_ref() else { let (Ast::Tuple(members), _) = value.as_ref() else {
unreachable!() unreachable!()
}; };
for (i, member) in members.iter().enumerate() { for member in members {
self.visit(member); self.visit(member);
self.emit_op(Op::StoreAt);
self.emit_byte(i);
} }
self.msg(format!(
"entering loop with stack depth of {}",
self.stack_depth
));
self.emit_op(Op::StoreN);
self.emit_byte(members.len());
let arity = members.len(); let arity = members.len();
let stack_depth = self.stack_depth; let stack_depth = self.stack_depth;
//then, save the beginning of the loop //then, save the beginning of the loop
self.emit_op(Op::Load); self.emit_op(Op::Load);
self.enter_loop(); self.enter_loop();
self.stack_depth += arity; // self.stack_depth += arity;
//next, compile each clause: //next, compile each clause:
let mut clauses = clauses.iter(); let mut clauses = clauses.iter();
let mut jump_idxes = vec![]; let mut jump_idxes = vec![];
@ -1550,15 +1511,21 @@ impl<'a> Compiler<'a> {
self.leave_loop(); self.leave_loop();
} }
Recur(args) => { Recur(args) => {
for (i, arg) in args.iter().enumerate() { let tail_pos = self.tail_pos;
self.tail_pos = false;
let mut argnum = 0;
for arg in args {
self.msg(format!("recur arg: {argnum}"));
argnum += 1;
self.visit(arg); self.visit(arg);
self.emit_op(Op::StoreAt);
self.emit_byte(i);
} }
self.emit_op(Op::StoreN);
self.emit_byte(args.len());
self.emit_op(Op::PopN); self.emit_op(Op::PopN);
self.emit_byte(self.loop_root()); self.emit_byte(self.stack_depth - self.loop_root());
self.emit_op(Op::Load); self.emit_op(Op::Load);
self.jump(Op::JumpBack, self.len() - self.loop_idx()); self.jump(Op::JumpBack, self.len() - self.loop_idx());
self.tail_pos = tail_pos;
} }
Panic(msg) => { Panic(msg) => {
self.visit(msg); self.visit(msg);

View File

@ -99,8 +99,8 @@ pub fn run(src: &'static str) {
// in any event, the AST should live forever // in any event, the AST should live forever
let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap())); let parsed: &'static Spanned<Ast> = Box::leak(Box::new(parse_result.unwrap()));
// let prelude = prelude(); let prelude = prelude();
let prelude = imbl::HashMap::new(); // let prelude = imbl::HashMap::new();
let mut validator = Validator::new(&parsed.0, &parsed.1, "user input", src, prelude.clone()); let mut validator = Validator::new(&parsed.0, &parsed.1, "user input", src, prelude.clone());
validator.validate(); validator.validate();
@ -111,8 +111,6 @@ pub fn run(src: &'static str) {
return; return;
} }
// let prelude = imbl::HashMap::new();
let mut compiler = Compiler::new(parsed, "test", src, None, prelude); let mut compiler = Compiler::new(parsed, "test", src, None, prelude);
// let base = base::make_base(); // let base = base::make_base();
// compiler.emit_constant(base); // compiler.emit_constant(base);
@ -145,15 +143,7 @@ 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 = r#" let src = r#"
let foo = { map(inc, [1, 2, 3])
fn quux () -> {
let bar = :bar
let baz = :baz
fn frobulate () -> (bar, baz, baz)
}
}
foo () ()
"#; "#;
run(src); run(src);
} }

View File

@ -192,7 +192,15 @@ impl std::fmt::Display for Value {
), ),
Box(value) => write!(f, "box {{ {} }}", value.as_ref().borrow()), Box(value) => write!(f, "box {{ {} }}", value.as_ref().borrow()),
Fn(lfn) => write!(f, "fn {}", lfn.name()), Fn(lfn) => write!(f, "fn {}", lfn.name()),
BaseFn(_) => write!(f, "base fn"), BaseFn(inner) => {
let name = match inner {
crate::base::BaseFn::Nullary(name, _)
| crate::base::BaseFn::Unary(name, _)
| crate::base::BaseFn::Binary(name, _)
| crate::base::BaseFn::Ternary(name, _) => name,
};
write!(f, "fn {name}/base")
}
Partial(partial) => write!(f, "fn {}/partial", partial.name), Partial(partial) => write!(f, "fn {}/partial", partial.name),
} }
} }
@ -235,7 +243,7 @@ impl Value {
Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show()), Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show()),
Fn(lfn) => format!("fn {}", lfn.name()), Fn(lfn) => format!("fn {}", lfn.name()),
Partial(partial) => format!("fn {}/partial", partial.name), Partial(partial) => format!("fn {}/partial", partial.name),
BaseFn(_) => "base fn".to_string(), BaseFn(_) => format!("{self}"),
Nothing => unreachable!(), Nothing => unreachable!(),
} }
} }

View File

@ -317,9 +317,11 @@ impl Vm {
self.push(Value::Nothing); self.push(Value::Nothing);
self.ip += 1; self.ip += 1;
} }
StoreAt => { StoreN => {
let i = self.chunk().bytecode[self.ip + 1] as usize; let n = self.chunk().bytecode[self.ip + 1] as usize;
self.return_register[i] = self.pop(); for i in (0..n).rev() {
self.return_register[i] = self.pop();
}
self.ip += 2; self.ip += 2;
} }
Stash => { Stash => {
@ -873,6 +875,10 @@ impl Vm {
let val = self.pop(); let val = self.pop();
if crate::DEBUG_RUN {
println!("=== tail call into {val}/{arity} ===");
}
match val { match val {
Value::Fn(_) => { Value::Fn(_) => {
if !val.as_fn().accepts(arity) { if !val.as_fn().accepts(arity) {
@ -924,20 +930,18 @@ impl Vm {
self.ip = 0; self.ip = 0;
if crate::DEBUG_RUN { if crate::DEBUG_RUN {}
println!("== tail call into {} ==", self.frame.function.show());
}
} }
Value::BaseFn(base_fn) => { Value::BaseFn(base_fn) => {
let value = match (arity, base_fn) { let value = match (arity, base_fn) {
(0, BaseFn::Nullary(f)) => f(), (0, BaseFn::Nullary(_, f)) => f(),
(1, BaseFn::Unary(f)) => f(&self.pop()), (1, BaseFn::Unary(_, f)) => f(&self.pop()),
(2, BaseFn::Binary(f)) => { (2, BaseFn::Binary(_, f)) => {
let y = &self.pop(); let y = &self.pop();
let x = &self.pop(); let x = &self.pop();
f(x, y) f(x, y)
} }
(3, BaseFn::Ternary(f)) => { (3, BaseFn::Ternary(_, f)) => {
let z = &self.pop(); let z = &self.pop();
let y = &self.pop(); let y = &self.pop();
let x = &self.pop(); let x = &self.pop();
@ -977,10 +981,6 @@ impl Vm {
self.call_stack.push(frame); self.call_stack.push(frame);
self.ip = 0; 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())), _ => return self.panic_with(format!("{} is not a function", val.show())),
} }
@ -991,6 +991,10 @@ impl Vm {
let val = self.pop(); let val = self.pop();
if crate::DEBUG_RUN {
println!("=== calling into {val}/{arity} ===");
}
match val { match val {
Value::Fn(_) => { Value::Fn(_) => {
if !val.as_fn().accepts(arity) { if !val.as_fn().accepts(arity) {
@ -1024,21 +1028,17 @@ impl Vm {
self.call_stack.push(frame); self.call_stack.push(frame);
self.ip = 0; self.ip = 0;
if crate::DEBUG_RUN {
println!("== calling into {} ==", self.frame.function.show());
}
} }
Value::BaseFn(base_fn) => { Value::BaseFn(base_fn) => {
let value = match (arity, base_fn) { let value = match (arity, base_fn) {
(0, BaseFn::Nullary(f)) => f(), (0, BaseFn::Nullary(_, f)) => f(),
(1, BaseFn::Unary(f)) => f(&self.pop()), (1, BaseFn::Unary(_, f)) => f(&self.pop()),
(2, BaseFn::Binary(f)) => { (2, BaseFn::Binary(_, f)) => {
let y = &self.pop(); let y = &self.pop();
let x = &self.pop(); let x = &self.pop();
f(x, y) f(x, y)
} }
(3, BaseFn::Ternary(f)) => { (3, BaseFn::Ternary(_, f)) => {
let z = &self.pop(); let z = &self.pop();
let y = &self.pop(); let y = &self.pop();
let x = &self.pop(); let x = &self.pop();
@ -1071,10 +1071,6 @@ impl Vm {
self.call_stack.push(frame); self.call_stack.push(frame);
self.ip = 0; 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())), _ => return self.panic_with(format!("{} is not a function", val.show())),
} }
@ -1095,13 +1091,12 @@ impl Vm {
self.ip += 1; self.ip += 1;
} }
SetUpvalue => { SetUpvalue => {
let idx = self.chunk().bytecode[self.ip + 1]; let value = self.pop();
self.ip += 2; let Value::Fn(lfn) = self.peek() else {
let closed_idx = idx as usize + self.frame.stack_base; panic!("expected function closing over value, got {}", self.peek());
let closed_over = self.stack[closed_idx].clone(); };
if let Value::Fn(lfn) = self.peek() { lfn.close(value);
lfn.close(closed_over); self.ip += 1;
}
} }
GetUpvalue => { GetUpvalue => {
let idx = self.chunk().bytecode[self.ip + 1]; let idx = self.chunk().bytecode[self.ip + 1];
@ -1112,6 +1107,11 @@ impl Vm {
unreachable!(); unreachable!();
} }
} }
Msg => {
let msg = self.pop();
println!("{msg}");
self.ip += 1;
}
} }
} }
} }