From 749b11adfa3397c8644e555c27b07684b1e70515 Mon Sep 17 00:00:00 2001 From: Ross Andrews Date: Mon, 13 Nov 2023 00:44:26 -0600 Subject: [PATCH] A bunch of prerequisites before we can do arrays, part 3.1, using the pool pointer in calls (removes the cursed test case) --- forge_core/src/compiler/ast_nodes/call.rs | 14 +++++--------- forge_core/src/compiler/mod.rs | 2 +- vtest/src/forge_tests.rs | 21 ++++++++++++++++++++- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/forge_core/src/compiler/ast_nodes/call.rs b/forge_core/src/compiler/ast_nodes/call.rs index 18ff5ac..9c9bf5d 100644 --- a/forge_core/src/compiler/ast_nodes/call.rs +++ b/forge_core/src/compiler/ast_nodes/call.rs @@ -16,13 +16,10 @@ impl Compilable for Call { arg.process(state, Some(sig), loc)?; } - // Now we increment the frame ptr to right after the current frame: - // see cursed test case in vtest/forge_tests.rs - sig.emit("peekr"); - let frame_size = sig.frame_size(); - if frame_size > 0 { - sig.emit_arg("add", frame_size); - } + // Whatever shall we send as a frame pointer to the new fn? Our pool pointer, because we + // know that's free, even if we've alloced anything, it'll increment it: + sig.emit("popr"); sig.emit("peekr"); // take the frame ptr off and copy the pool off + sig.emit("swap"); sig.emit("pushr"); // swap the pool under the frame and put the frame back on // Eval the target self.target.0.process(state, Some(sig), loc)?; @@ -51,8 +48,7 @@ mod test { vec![ "push 2", // evaluating args, in order "push 3", - "peekr", // The new fn's frame ptr: - "add 6", + "popr", "peekr", "swap", "pushr", // Pool ptr to send to the new call "push _forge_gensym_1", // evaluating target (this fn) "call", // Actually make the call "pop", // expr-as-statement drops the evaluated value diff --git a/forge_core/src/compiler/mod.rs b/forge_core/src/compiler/mod.rs index 67631d9..524bd99 100644 --- a/forge_core/src/compiler/mod.rs +++ b/forge_core/src/compiler/mod.rs @@ -140,7 +140,7 @@ mod test { "_forge_gensym_3:", // fn main() "dup", "pushr", // capture pool ptr "pushr", // capture frame ptr - "peekr", // prep frame ptr to send to foo + "popr", "peekr", "swap", "pushr", // Grab pool ptr to send to foo "push _forge_gensym_1", // load foo "call", // call it "pop", // Throw away its return value diff --git a/vtest/src/forge_tests.rs b/vtest/src/forge_tests.rs index c55deb9..a0dc8b7 100644 --- a/vtest/src/forge_tests.rs +++ b/vtest/src/forge_tests.rs @@ -86,7 +86,26 @@ fn scope_test() { } #[test] -fn cursed_call_test() { +fn recursion_test() { + // Recursive call with some stuff in it + assert_eq!( + main_return( + "fn sum(n) { + if (n > 0) { + return n + sum(n - 1); + } else { + return 0; + } + } + fn main() { return sum(5); }"), + 15 + ) +} + +//#[test] +// This is no longer cursed, or a test. The revised calling convention with the pool pointer makes +// it now perfectly sane. Left here for posterity. +fn _cursed_call_test() { // This is a cursed subtlety of block scoping and the way calls are compiled. It's not actually // a bug. It's explained below: let src = "