diff --git a/assets/test_prelude.ld b/assets/test_prelude.ld index 1b4c138..fb87731 100644 --- a/assets/test_prelude.ld +++ b/assets/test_prelude.ld @@ -217,6 +217,12 @@ fn map { & } } +fn add { + () -> 0 + (x) -> x + (x, y) -> base :add (x, y) +} + #{ & type & coll? @@ -233,7 +239,8 @@ fn map { & not & tuple? & fn? - & rest + first + rest inc & dec & count @@ -242,7 +249,8 @@ fn map { & list? & list & first - & fold - & append + fold + append map + add } diff --git a/may_2025_thoughts.md b/may_2025_thoughts.md index 41c06df..fd5e2fc 100644 --- a/may_2025_thoughts.md +++ b/may_2025_thoughts.md @@ -422,3 +422,15 @@ https://craftinginterpreters.com/closures.html#flattening-upvalues. 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. 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. diff --git a/src/base.rs b/src/base.rs index a25caa5..68c1fb9 100644 --- a/src/base.rs +++ b/src/base.rs @@ -5,10 +5,10 @@ use std::rc::Rc; #[derive(Clone, Debug)] pub enum BaseFn { - Nullary(fn() -> Value), - Unary(fn(&Value) -> Value), - Binary(fn(&Value, &Value) -> Value), - Ternary(fn(&Value, &Value, &Value) -> Value), + Nullary(&'static str, fn() -> Value), + Unary(&'static str, fn(&Value) -> Value), + Binary(&'static str, fn(&Value, &Value) -> Value), + Ternary(&'static str, fn(&Value, &Value, &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 { let members = vec![ - ("add", Value::BaseFn(BaseFn::Binary(add))), - ("append", Value::BaseFn(BaseFn::Binary(append))), - ("assoc", Value::BaseFn(BaseFn::Ternary(assoc))), - ("at", Value::BaseFn(BaseFn::Binary(at))), - ("atan_2", Value::BaseFn(BaseFn::Binary(atan_2))), - ("bool", Value::BaseFn(BaseFn::Unary(r#bool))), - ("ceil", Value::BaseFn(BaseFn::Unary(ceil))), - ("chars", Value::BaseFn(BaseFn::Unary(chars))), - ("concat", Value::BaseFn(BaseFn::Binary(concat))), - ("cos", Value::BaseFn(BaseFn::Unary(cos))), - ("count", Value::BaseFn(BaseFn::Unary(count))), - ("dec", Value::BaseFn(BaseFn::Unary(dec))), - ("dissoc", Value::BaseFn(BaseFn::Binary(dissoc))), - ("div", Value::BaseFn(BaseFn::Binary(div))), - ("doc!", Value::BaseFn(BaseFn::Unary(doc))), - ("downcase", Value::BaseFn(BaseFn::Unary(downcase))), - ("eq?", Value::BaseFn(BaseFn::Binary(eq))), - ("first", Value::BaseFn(BaseFn::Unary(first))), - ("floor", Value::BaseFn(BaseFn::Unary(floor))), - ("get", Value::BaseFn(BaseFn::Binary(get))), - ("gt?", Value::BaseFn(BaseFn::Binary(gt))), - ("gte?", Value::BaseFn(BaseFn::Binary(gte))), - ("inc", Value::BaseFn(BaseFn::Unary(inc))), - ("last", Value::BaseFn(BaseFn::Unary(last))), - ("list", Value::BaseFn(BaseFn::Unary(list))), - ("lt?", Value::BaseFn(BaseFn::Binary(lt))), - ("lte?", Value::BaseFn(BaseFn::Binary(lte))), - ("mod", Value::BaseFn(BaseFn::Binary(r#mod))), - ("mult", Value::BaseFn(BaseFn::Binary(mult))), - ("number", Value::BaseFn(BaseFn::Unary(number))), + ("add", Value::BaseFn(BaseFn::Binary("add", add))), + ("append", Value::BaseFn(BaseFn::Binary("append", append))), + ("assoc", Value::BaseFn(BaseFn::Ternary("assoc", assoc))), + ("at", Value::BaseFn(BaseFn::Binary("at", at))), + ("atan_2", Value::BaseFn(BaseFn::Binary("atan_2", atan_2))), + ("bool", Value::BaseFn(BaseFn::Unary("bool", r#bool))), + ("ceil", Value::BaseFn(BaseFn::Unary("ceil", ceil))), + ("chars", Value::BaseFn(BaseFn::Unary("chars", chars))), + ("concat", Value::BaseFn(BaseFn::Binary("concat", concat))), + ("cos", Value::BaseFn(BaseFn::Unary("cos", cos))), + ("count", Value::BaseFn(BaseFn::Unary("count", count))), + ("dec", Value::BaseFn(BaseFn::Unary("dec", dec))), + ("dissoc", Value::BaseFn(BaseFn::Binary("dissoc", dissoc))), + ("div", Value::BaseFn(BaseFn::Binary("div", div))), + ("doc!", Value::BaseFn(BaseFn::Unary("doc!", doc))), + ( + "downcase", + Value::BaseFn(BaseFn::Unary("downcase", downcase)), + ), + ("eq?", Value::BaseFn(BaseFn::Binary("eq?", eq))), + ("first", Value::BaseFn(BaseFn::Unary("first", first))), + ("floor", Value::BaseFn(BaseFn::Unary("floor", floor))), + ("get", Value::BaseFn(BaseFn::Binary("get", get))), + ("gt?", Value::BaseFn(BaseFn::Binary("gt?", gt))), + ("gte?", Value::BaseFn(BaseFn::Binary("gte?", gte))), + ("inc", Value::BaseFn(BaseFn::Unary("inc", inc))), + ("last", Value::BaseFn(BaseFn::Unary("last", last))), + ("list", Value::BaseFn(BaseFn::Unary("list", list))), + ("lt?", Value::BaseFn(BaseFn::Binary("lt?", lt))), + ("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)), - ("print!", Value::BaseFn(BaseFn::Unary(print))), - ("random", Value::BaseFn(BaseFn::Nullary(random))), - ("range", Value::BaseFn(BaseFn::Binary(range))), - ("rest", Value::BaseFn(BaseFn::Unary(rest))), - ("round", Value::BaseFn(BaseFn::Unary(round))), - ("show", Value::BaseFn(BaseFn::Unary(show))), - ("sin", Value::BaseFn(BaseFn::Unary(sin))), - ("slice", Value::BaseFn(BaseFn::Ternary(slice))), - ("split", Value::BaseFn(BaseFn::Binary(split))), - ("sqrt", Value::BaseFn(BaseFn::Unary(sqrt))), + ("print!", Value::BaseFn(BaseFn::Unary("print!", print))), + ("random", Value::BaseFn(BaseFn::Nullary("random", random))), + ("range", Value::BaseFn(BaseFn::Binary("range", range))), + ("rest", Value::BaseFn(BaseFn::Unary("rest", rest))), + ("round", Value::BaseFn(BaseFn::Unary("round", round))), + ("show", Value::BaseFn(BaseFn::Unary("show", show))), + ("sin", Value::BaseFn(BaseFn::Unary("sin", sin))), + ("slice", Value::BaseFn(BaseFn::Ternary("slice", slice))), + ("split", Value::BaseFn(BaseFn::Binary("split", split))), + ("sqrt", Value::BaseFn(BaseFn::Unary("sqrt", sqrt))), ("sqrt_2", Value::Number(std::f64::consts::SQRT_2)), - ("store!", Value::BaseFn(BaseFn::Binary(store))), - ("sub", Value::BaseFn(BaseFn::Binary(sub))), - ("tan", Value::BaseFn(BaseFn::Unary(tan))), - ("trim", Value::BaseFn(BaseFn::Unary(trim))), - ("triml", Value::BaseFn(BaseFn::Unary(triml))), - ("trimr", Value::BaseFn(BaseFn::Unary(trimr))), - ("type", Value::BaseFn(BaseFn::Unary(r#type))), - ("unbox", Value::BaseFn(BaseFn::Unary(unbox))), - ("upcase", Value::BaseFn(BaseFn::Unary(upcase))), + ("store!", Value::BaseFn(BaseFn::Binary("store!", store))), + ("sub", Value::BaseFn(BaseFn::Binary("sub", sub))), + ("tan", Value::BaseFn(BaseFn::Unary("tan", tan))), + ("trim", Value::BaseFn(BaseFn::Unary("trim", trim))), + ("triml", Value::BaseFn(BaseFn::Unary("triml", triml))), + ("trimr", Value::BaseFn(BaseFn::Unary("trimr", trimr))), + ("type", Value::BaseFn(BaseFn::Unary("type", r#type))), + ("unbox", Value::BaseFn(BaseFn::Unary("unbox", unbox))), + ("upcase", Value::BaseFn(BaseFn::Unary("upcase", upcase))), ]; Value::Dict(Box::new(HashMap::from(members))) } diff --git a/src/compiler.rs b/src/compiler.rs index a9f92b3..8e3640c 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -26,7 +26,7 @@ pub enum Op { PushBinding, PushGlobal, Store, - StoreAt, + StoreN, Stash, Load, ResetMatch, @@ -96,6 +96,8 @@ pub enum Op { Print, SetUpvalue, GetUpvalue, + + Msg, // Inc, // Dec, // Gt, @@ -140,6 +142,7 @@ impl std::fmt::Display for Op { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { use Op::*; let rep = match self { + Msg => "msg", Noop => "noop", Nothing => "nothing", Nil => "nil", @@ -154,7 +157,7 @@ impl std::fmt::Display for Op { PushBinding => "push_binding", PushGlobal => "push_global", Store => "store", - StoreAt => "store_at", + StoreN => "store_n", Stash => "stash", Load => "load", Match => "match", @@ -268,7 +271,8 @@ impl Chunk { | Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq | Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString | 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}") } Constant | MatchConstant => { @@ -281,9 +285,8 @@ impl Chunk { } PushBinding | MatchTuple | MatchSplattedTuple | LoadSplattedTuple | MatchList | MatchSplattedList | LoadSplattedList | MatchDict | MatchSplattedDict - | DropDictEntry | LoadDictValue | PushTuple | PushBox | MatchDepth | PopN | StoreAt - | Call | SetUpvalue | GetUpvalue | Partial | MatchString | PushStringMatches - | TailCall => { + | DropDictEntry | LoadDictValue | PushTuple | PushBox | MatchDepth | PopN | StoreN + | Call | GetUpvalue | Partial | MatchString | PushStringMatches | TailCall => { let next = self.bytecode[*i + 1]; println!("{i:04}: {:16} {next:03}", op.to_string()); *i += 1; @@ -364,7 +367,7 @@ pub struct Compiler<'a> { pub src: &'static str, pub name: &'static str, pub enclosing: Option<&'a Compiler<'a>>, - pub upvalues: Vec, + pub upvalues: Vec<&'static str>, loop_info: Vec, tail_pos: bool, } @@ -383,12 +386,6 @@ fn has_placeholder(args: &[Spanned]) -> bool { 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> { pub fn new( ast: &'static Spanned, @@ -535,27 +532,7 @@ impl<'a> Compiler<'a> { } fn resolve_upvalue(&self, name: &'static str) -> Option { - self.upvalues.iter().position(|uv| uv.name == 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 - } - } + self.upvalues.iter().position(|uv| *uv == name) } fn resolve_binding(&mut self, name: &'static str) { @@ -576,40 +553,12 @@ impl<'a> Compiler<'a> { self.emit_op(Op::PushGlobal); return; } - let upvalue = self.get_upvalue(name); self.emit_op(Op::GetUpvalue); self.emit_byte(self.upvalues.len()); - self.upvalues.push(upvalue); + self.upvalues.push(name); 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) { self.emit_op(Op::Pop); self.stack_depth -= 1; @@ -648,6 +597,13 @@ impl<'a> Compiler<'a> { 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) { use Ast::*; match self.ast { @@ -1377,7 +1333,7 @@ impl<'a> Compiler<'a> { for idx in no_match_jumps { compiler.patch_jump(idx, compiler.len() - idx - 3); } - compiler.scope_depth -= 1; + // compiler.scope_depth -= 1; std::mem::swap(&mut compiler.upvalues, &mut upvalues); } @@ -1442,8 +1398,9 @@ impl<'a> Compiler<'a> { } for upvalue in upvalues { + self.resolve_binding(upvalue); self.emit_op(Op::SetUpvalue); - self.emit_byte(upvalue.stack_pos); + self.stack_depth -= 1; } } FnDeclaration(name) => { @@ -1483,17 +1440,21 @@ impl<'a> Compiler<'a> { let (Ast::Tuple(members), _) = value.as_ref() else { unreachable!() }; - for (i, member) in members.iter().enumerate() { + for member in members { 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 stack_depth = self.stack_depth; //then, save the beginning of the loop self.emit_op(Op::Load); self.enter_loop(); - self.stack_depth += arity; + // self.stack_depth += arity; //next, compile each clause: let mut clauses = clauses.iter(); let mut jump_idxes = vec![]; @@ -1550,15 +1511,21 @@ impl<'a> Compiler<'a> { self.leave_loop(); } 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.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_byte(self.loop_root()); + self.emit_byte(self.stack_depth - self.loop_root()); self.emit_op(Op::Load); self.jump(Op::JumpBack, self.len() - self.loop_idx()); + self.tail_pos = tail_pos; } Panic(msg) => { self.visit(msg); diff --git a/src/main.rs b/src/main.rs index 669a345..6cfdff1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -99,8 +99,8 @@ pub fn run(src: &'static str) { // in any event, the AST should live forever let parsed: &'static Spanned = Box::leak(Box::new(parse_result.unwrap())); - // let prelude = prelude(); - let prelude = imbl::HashMap::new(); + let prelude = prelude(); + // let prelude = imbl::HashMap::new(); let mut validator = Validator::new(&parsed.0, &parsed.1, "user input", src, prelude.clone()); validator.validate(); @@ -111,8 +111,6 @@ pub fn run(src: &'static str) { return; } - // let prelude = imbl::HashMap::new(); - let mut compiler = Compiler::new(parsed, "test", src, None, prelude); // let base = base::make_base(); // compiler.emit_constant(base); @@ -145,15 +143,7 @@ pub fn run(src: &'static str) { pub fn main() { env::set_var("RUST_BACKTRACE", "1"); let src = r#" -let foo = { - fn quux () -> { - let bar = :bar - let baz = :baz - fn frobulate () -> (bar, baz, baz) - } -} - -foo () () +map(inc, [1, 2, 3]) "#; run(src); } diff --git a/src/value.rs b/src/value.rs index eb61c4a..41b5b72 100644 --- a/src/value.rs +++ b/src/value.rs @@ -192,7 +192,15 @@ impl std::fmt::Display for Value { ), Box(value) => write!(f, "box {{ {} }}", value.as_ref().borrow()), 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), } } @@ -235,7 +243,7 @@ impl Value { Box(x) => format!("box {{ {} }}", x.as_ref().borrow().show()), Fn(lfn) => format!("fn {}", lfn.name()), Partial(partial) => format!("fn {}/partial", partial.name), - BaseFn(_) => "base fn".to_string(), + BaseFn(_) => format!("{self}"), Nothing => unreachable!(), } } diff --git a/src/vm.rs b/src/vm.rs index 604f81a..9de605c 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -317,9 +317,11 @@ impl Vm { self.push(Value::Nothing); self.ip += 1; } - StoreAt => { - let i = self.chunk().bytecode[self.ip + 1] as usize; - self.return_register[i] = self.pop(); + StoreN => { + let n = self.chunk().bytecode[self.ip + 1] as usize; + for i in (0..n).rev() { + self.return_register[i] = self.pop(); + } self.ip += 2; } Stash => { @@ -873,6 +875,10 @@ impl Vm { let val = self.pop(); + if crate::DEBUG_RUN { + println!("=== tail call into {val}/{arity} ==="); + } + match val { Value::Fn(_) => { if !val.as_fn().accepts(arity) { @@ -924,20 +930,18 @@ impl Vm { self.ip = 0; - if crate::DEBUG_RUN { - println!("== tail call into {} ==", self.frame.function.show()); - } + if crate::DEBUG_RUN {} } 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)) => { + (0, BaseFn::Nullary(_, f)) => f(), + (1, BaseFn::Unary(_, f)) => f(&self.pop()), + (2, BaseFn::Binary(_, f)) => { let y = &self.pop(); let x = &self.pop(); f(x, y) } - (3, BaseFn::Ternary(f)) => { + (3, BaseFn::Ternary(_, f)) => { let z = &self.pop(); let y = &self.pop(); let x = &self.pop(); @@ -977,10 +981,6 @@ impl Vm { 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())), } @@ -991,6 +991,10 @@ impl Vm { let val = self.pop(); + if crate::DEBUG_RUN { + println!("=== calling into {val}/{arity} ==="); + } + match val { Value::Fn(_) => { if !val.as_fn().accepts(arity) { @@ -1024,21 +1028,17 @@ impl Vm { self.call_stack.push(frame); self.ip = 0; - - if crate::DEBUG_RUN { - println!("== calling 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)) => { + (0, BaseFn::Nullary(_, f)) => f(), + (1, BaseFn::Unary(_, f)) => f(&self.pop()), + (2, BaseFn::Binary(_, f)) => { let y = &self.pop(); let x = &self.pop(); f(x, y) } - (3, BaseFn::Ternary(f)) => { + (3, BaseFn::Ternary(_, f)) => { let z = &self.pop(); let y = &self.pop(); let x = &self.pop(); @@ -1071,10 +1071,6 @@ impl Vm { 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())), } @@ -1095,13 +1091,12 @@ impl Vm { self.ip += 1; } SetUpvalue => { - let idx = self.chunk().bytecode[self.ip + 1]; - self.ip += 2; - let closed_idx = idx as usize + self.frame.stack_base; - let closed_over = self.stack[closed_idx].clone(); - if let Value::Fn(lfn) = self.peek() { - lfn.close(closed_over); - } + let value = self.pop(); + let Value::Fn(lfn) = self.peek() else { + panic!("expected function closing over value, got {}", self.peek()); + }; + lfn.close(value); + self.ip += 1; } GetUpvalue => { let idx = self.chunk().bytecode[self.ip + 1]; @@ -1112,6 +1107,11 @@ impl Vm { unreachable!(); } } + Msg => { + let msg = self.pop(); + println!("{msg}"); + self.ip += 1; + } } } }