From 8b004b45faf48d7877c09c1af82cc0d061e0d2ca Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Thu, 19 Jun 2025 21:47:58 -0400 Subject: [PATCH] find a bug in function bindings; TCO is maybe complete?; things are in a shambles --- may_2025_thoughts.md | 15 +++++++++++++-- src/compiler.rs | 23 +++++++++++++++-------- src/main.rs | 10 +++++----- 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/may_2025_thoughts.md b/may_2025_thoughts.md index 31d2116..5b2ebff 100644 --- a/may_2025_thoughts.md +++ b/may_2025_thoughts.md @@ -250,8 +250,8 @@ To reiterate the punch list that *I would have needed for Computer Class 1*: - [ ] Identify others * [x] add guards to loop forms * [x] check loop forms against function calls: do they still work the way we want them to? -* [ ] tail call elimination -* [ ] stack traces in panics +* [x] tail call elimination +* [x] stack traces in panics * [ ] actually good error messages - [ ] parsing - [ ] my memory is that validator messages are already good? @@ -277,3 +277,14 @@ Just trying to get a sense of what needs to happen for CC2: - [ ] Makey makey for alternate input? * [ ] Saving and loading data into Ludus (perceptrons, dissociated press) * [ ] Finding corpuses for Dissociated Press + +### Final touches on semantics, or lots of bugs +#### 2025-06-19 +* Main code is fucking up bindings in functions +* Why? +* In the middle of doing TCO, looks like it works for `do` forms based on the bytecode, but I ran into these weird binding problems while trying to test that in the vm +* Once that's put to bed, I believe TCO fully works +* But then I need to test it actually works in its intended use case: recursive calls +* Which means I need to test recursive calls +* And, once that's done, I think I have a COMPLETE SEMANTICALLY CORRECT INTERPRETER. +* After that, jesus, it's time for base > prelude > test cases diff --git a/src/compiler.rs b/src/compiler.rs index 6f444b0..9b43db2 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1095,10 +1095,7 @@ impl<'a> Compiler<'a> { } self.resolve_binding(fn_name); // if we're in tail position AND there aren't any rest args, this should be a tail call (I think) - if rest.is_empty() { - self.tail_pos = tail_pos; - } - if self.tail_pos { + if rest.is_empty() && tail_pos { self.emit_op(Op::TailCall); } else { self.emit_op(Op::Call); @@ -1112,7 +1109,8 @@ impl<'a> Compiler<'a> { _ => unreachable!(), } // the last term in rest should be in tail position if we are in tail position - for (term, _) in rest { + let num_rest_terms = rest.len(); + for (i, (term, _)) in rest.iter().enumerate() { match term { Keyword(str) => { self.emit_constant(Value::Keyword(str)); @@ -1127,13 +1125,18 @@ impl<'a> Compiler<'a> { self.visit(arg); } self.emit_op(Op::Load); - self.emit_op(Op::Call); + if tail_pos && i == num_rest_terms - 1 { + self.emit_op(Op::TailCall) + } else { + self.emit_op(Op::Call); + } self.emit_byte(arity); self.stack_depth -= arity; } _ => unreachable!(), } } + self.tail_pos = tail_pos; } When(clauses) => { let tail_pos = self.tail_pos; @@ -1497,10 +1500,14 @@ impl<'a> Compiler<'a> { self.emit_byte(1); self.stack_depth -= 1; } - self.tail_pos = tail_pos; self.visit(last); - self.emit_op(Op::Call); + if tail_pos { + self.emit_op(Op::TailCall) + } else { + self.emit_op(Op::Call); + } self.emit_byte(1); + self.tail_pos = tail_pos; self.stack_depth -= 1; } Placeholder => { diff --git a/src/main.rs b/src/main.rs index 800b338..1261fda 100644 --- a/src/main.rs +++ b/src/main.rs @@ -75,12 +75,12 @@ pub fn run(src: &'static str) { pub fn main() { env::set_var("RUST_BACKTRACE", "1"); let src = " -fn recursive { - (0) -> :done - (n) -> recursive (0) -} +fn add_one (x) -> add (x, 1) +fn double (x) -> add (x, x) +fn square (x) -> mult (x, x) + +double (2) -recursive (1) "; run(src); }