Capturing args in function bodies

This commit is contained in:
2023-11-08 22:06:35 -06:00
parent 62d340e507
commit 4a92723f97
2 changed files with 82 additions and 24 deletions
+55 -5
View File
@@ -77,6 +77,7 @@ pub struct CompiledFn {
pub label: Label, pub label: Label,
pub frame_size: usize, pub frame_size: usize,
pub local_scope: Scope, pub local_scope: Scope,
pub arity: usize,
pub body: Vec<String>, pub body: Vec<String>,
} }
@@ -112,6 +113,36 @@ impl CompiledFn {
fn frame_size(&self) -> usize { fn frame_size(&self) -> usize {
self.local_scope.len() * 3 self.local_scope.len() * 3
} }
/// Emit the code to add the arguments' names to the local scopr, and move the arguments off
/// the stack into the frame. Should be run before the body is compiled, because this code
/// has to be the first thing in the body
fn handle_args(&mut self, function: &Function) -> Result<(), CompileError> {
let mut arg_names: Vec<&str> = Vec::new();
// Add each argument as a local
for name in function.args.iter() {
self.add_local(name.as_str())?;
arg_names.push(name.as_str());
self.arity += 1;
}
// Arguments in calls are pushed with the last on top, so, reverse the vec just to make
// the following loop easier:
arg_names.reverse();
for name in arg_names {
if let Variable::Local(offset) = self.local_scope[name] {
self.emit("loadw frame");
if offset != 0 {
self.emit_arg("add", offset);
}
self.emit("storew");
}
}
Ok(())
}
} }
/// Maps from names to the variables they represent /// Maps from names to the variables they represent
@@ -367,10 +398,9 @@ impl Compilable for Function {
..Default::default() ..Default::default()
}; };
// Add each argument as a local // This generates the code to copy the args into the frame as well as adds all the arguments
for arg in self.args { // to the local scope and calculates the arity.
sig.add_local(&arg)? sig.handle_args(&self)?;
}
// todo we need to store arity somehow in the state, so we can check arglists even // todo we need to store arity somehow in the state, so we can check arglists even
// with recursive calls. This probably becomes splitting "signature" from "function context" // with recursive calls. This probably becomes splitting "signature" from "function context"
@@ -383,7 +413,9 @@ impl Compilable for Function {
// This can't fail because if it were a dupe name, adding the global would have failed // This can't fail because if it were a dupe name, adding the global would have failed
state.functions.insert(self.name.clone(), sig); state.functions.insert(self.name.clone(), sig);
// todo actually emit the function header / body // And we're done: the function body now stores everything we need to emit that function...
// but we can't actually emit it yet because we don't know how we're building things. This
// is the equivalent of the object-file step in a more real compiler.
Ok(()) Ok(())
} }
} }
@@ -923,6 +955,11 @@ mod test {
assert_eq!( assert_eq!(
test_body(state_for("fn test(a, b) { b = 17 + a; }")), test_body(state_for("fn test(a, b) { b = 17 + a; }")),
vec![ vec![
"loadw frame", // Capture var b
"add 3",
"storew",
"loadw frame", // Capture var a
"storew",
"push 17", // Start calculating the rvalue, push the literal "push 17", // Start calculating the rvalue, push the literal
"loadw frame", // This is looking up the "a" arg, at frame + 0 "loadw frame", // This is looking up the "a" arg, at frame + 0
"loadw", "loadw",
@@ -1148,6 +1185,11 @@ mod test {
assert_eq!( assert_eq!(
test_body(state_for("fn test(a, b) { test(2, 3); }")), test_body(state_for("fn test(a, b) { test(2, 3); }")),
vec![ vec![
"loadw frame", // capture arg b
"add 3",
"storew",
"loadw frame", // capture arg a
"storew",
"push 2", // evaluating args, in order "push 2", // evaluating args, in order
"push 3", "push 3",
"push _forge_gensym_1", // evaluating target (this fn) "push _forge_gensym_1", // evaluating target (this fn)
@@ -1170,6 +1212,8 @@ mod test {
assert_eq!( assert_eq!(
test_body(state_for("fn test(a) { return a + 3; }")), test_body(state_for("fn test(a) { return a + 3; }")),
vec![ vec![
"loadw frame", // capture arg a
"storew",
"loadw frame", // Load a "loadw frame", // Load a
"loadw", "loadw",
"push 3", // Add 3 "push 3", // Add 3
@@ -1182,6 +1226,8 @@ mod test {
assert_eq!( assert_eq!(
test_body(state_for("fn test(a) { if (a > 0) { return; } }")), test_body(state_for("fn test(a) { if (a > 0) { return; } }")),
vec![ vec![
"loadw frame", // capture arg a
"storew",
"loadw frame", // Load a "loadw frame", // Load a
"loadw", "loadw",
"push 0", // Compare to 0 "push 0", // Compare to 0
@@ -1200,6 +1246,8 @@ mod test {
assert_eq!( assert_eq!(
test_body(state_for("fn test(a) { var x = 0; repeat(a) c { x = x + c; } return x; }")), test_body(state_for("fn test(a) { var x = 0; repeat(a) c { x = x + c; } return x; }")),
vec![ vec![
"loadw frame", // capture arg a
"storew",
"push 0", // Create the 'x' var and store 0 in it "push 0", // Create the 'x' var and store 0 in it
"loadw frame", "loadw frame",
"add 3", "add 3",
@@ -1249,6 +1297,8 @@ mod test {
assert_eq!( assert_eq!(
test_body(state_for("fn test(a) { var x = 1; repeat(a) { x = x * 2; } return x; }")), test_body(state_for("fn test(a) { var x = 1; repeat(a) { x = x * 2; } return x; }")),
vec![ vec![
"loadw frame", // capture arg a
"storew",
"push 1", // Create the 'x' var and store 1 in it "push 1", // Create the 'x' var and store 1 in it
"loadw frame", "loadw frame",
"add 3", "add 3",
+27 -19
View File
@@ -24,22 +24,30 @@ fn basic_forge_test() {
) )
} }
// TODO The reason this fails is that functions don't copy their args to the frame when they enter. #[test]
// The fix is for fns to know their arity and to do that copy before the body, probably as some fn loop_test() {
// subroutine call itself (push frame, push arity, call blah, which is written in asm) assert_eq!(
// #[test] main_return(
// fn loop_test() { "fn triangle(rows) {
// assert_eq!( var total = 0;
// main_return( repeat (rows) n {
// "fn triangle(rows) { total = total + (n + 1);
// var total = 0; }
// repeat (rows) n { return total;
// total = total + (n + 1); }
// }
// return total; fn main() { return triangle(5); }"),
// } 15
// )
// fn main() { return triangle(5); }"), }
// 15
// ) #[test]
// } fn arg_test() {
// Send in two args where the order matters, to ensure that args are being captured correctly
assert_eq!(
main_return(
"fn sub(a, b) { return a - b; }
fn main() { return sub(5, 3); }"),
2
)
}