Compiling calls

This commit is contained in:
2023-09-01 22:24:36 -05:00
parent a7c7610105
commit 82a7ac0152
3 changed files with 92 additions and 15 deletions
+7 -1
View File
@@ -135,6 +135,12 @@ pub struct Asm {
pub body: String pub body: String
} }
#[derive(PartialEq, Clone, Debug)]
pub struct Call {
pub target: BoxExpr,
pub args: Vec<Expr>
}
/// One of the five arithmetical operators /// One of the five arithmetical operators
#[derive(Debug, Eq, PartialEq, Copy, Clone)] #[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum Operator { pub enum Operator {
@@ -166,7 +172,7 @@ pub enum Expr {
Neg(BoxExpr), Neg(BoxExpr),
Deref(BoxExpr), Deref(BoxExpr),
Address(Lvalue), Address(Lvalue),
Call(BoxExpr, Vec<Expr>), Call(Call),
Subscript(BoxExpr, BoxExpr), Subscript(BoxExpr, BoxExpr),
Infix(BoxExpr, Operator, BoxExpr), Infix(BoxExpr, Operator, BoxExpr),
String(String), String(String),
+75 -6
View File
@@ -99,6 +99,12 @@ impl CompiledFn {
fn emit_arg<T: Display>(&mut self, opcode: &str, arg: T) { fn emit_arg<T: Display>(&mut self, opcode: &str, arg: T) {
self.body.push(format!("{} {}", opcode, arg)) self.body.push(format!("{} {}", opcode, arg))
} }
/// The size of the local scope in bytes. This increases as variables are declared
/// todo: this needs to change for arrays; it won't like having vars that aren't 3 bytes long
fn frame_size(&self) -> usize {
self.local_scope.len() * 3
}
} }
/// Maps from names to the variables they represent /// Maps from names to the variables they represent
@@ -296,9 +302,6 @@ impl Compilable for Function {
// and setting signature immutably right now, passing context down ("block?") and setting it // and setting signature immutably right now, passing context down ("block?") and setting it
// at the end // at the end
// Add it to the global namespace so we can make recursive calls
// state.add_global(&self.name, |_| Variable::DirectLabel(sig.label.clone()))?;
// Compile the body, storing all of it in the CompiledFn we just created / added // Compile the body, storing all of it in the CompiledFn we just created / added
self.body.process(state, Some(&mut sig), loc)?; self.body.process(state, Some(&mut sig), loc)?;
@@ -380,7 +383,7 @@ impl Compilable for Lvalue {
Expr::Neg(_) | Expr::Neg(_) |
Expr::Not(_) | Expr::Not(_) |
Expr::Address(_) | Expr::Address(_) |
Expr::Call(_, _) | Expr::Call(_) |
Expr::Infix(_, _, _) | Expr::Infix(_, _, _) |
Expr::String(_) => { Expr::String(_) => {
Err(CompileError(0, 0, String::from("Not a valid lvalue"))) Err(CompileError(0, 0, String::from("Not a valid lvalue")))
@@ -428,6 +431,50 @@ impl Compilable for Lvalue {
/////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////
impl Compilable for Call {
fn process(self, state: &mut State, sig: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> {
// Require a function
let sig = sig.expect("lvalue outside a function");
// We need a stack consisting of the arguments (last on top), followed by the address to call
// So, first eval the args:
for arg in self.args {
arg.process(state, Some(sig), loc)?;
}
// Eval the target
self.target.0.process(state, Some(sig), loc)?;
// Before we actually do the call though, we need to deal with some paperwork around the
// frame pointer. We will store the current frame pointer in the rstack:
sig.emit("loadw frame");
sig.emit("pushr");
// Now we increment the frame ptr to right after the current frame:
let frame_size = sig.frame_size();
if frame_size > 0 {
sig.emit("loadw frame");
sig.emit_arg("add", frame_size);
sig.emit("storew frame");
}
// Frame is now pointing at a safe place, the top of stack is the target, do the call:
sig.emit("call");
// And this is where we'll return to. The function has popped its args and left a word on
// the stack as a return value, which someone else will deal with (this is the word that
// this expr::call will end up evaluating to). But before we're done, we need to restore
// our frame pointerS
sig.emit("popr");
sig.emit("storew frame");
// And we're finished!
Ok(())
}
}
///////////////////////////////////////////////////////////
/// Evaluate an expression in the context of a local scope. The runtime brother to eval_const. /// Evaluate an expression in the context of a local scope. The runtime brother to eval_const.
/// This recursively evaluates a Node and leaves its value on the stack. /// This recursively evaluates a Node and leaves its value on the stack.
impl Compilable for Expr { impl Compilable for Expr {
@@ -506,7 +553,7 @@ impl Compilable for Expr {
sig.emit_arg("push", label); sig.emit_arg("push", label);
Ok(()) Ok(())
} }
Expr::Call(_, _) => todo!(), Expr::Call(call) => call.process(state, Some(sig), loc),
Expr::Subscript(_, _) => todo!("Structs and arrays are not yen supported"), Expr::Subscript(_, _) => todo!("Structs and arrays are not yen supported"),
Expr::Infix(lhs, op, rhs) => { Expr::Infix(lhs, op, rhs) => {
// Recurse on expressions, handling operators // Recurse on expressions, handling operators
@@ -628,7 +675,7 @@ pub fn eval_const(expr: Expr, scope: &Scope) -> Result<i32, CompileError> {
0, 0,
String::from("Addresses are not known at compile time"), String::from("Addresses are not known at compile time"),
)), )),
Expr::Call(_, _) | Expr::Subscript(_, _) => Err(CompileError( Expr::Call(_) | Expr::Subscript(_, _) => Err(CompileError(
0, 0,
0, 0,
String::from("Constants must be statically defined"), String::from("Constants must be statically defined"),
@@ -976,4 +1023,26 @@ mod test {
assert_eq!(state.functions.remove("blah").unwrap().label, String::from("_forge_gensym_1")); assert_eq!(state.functions.remove("blah").unwrap().label, String::from("_forge_gensym_1"));
assert_eq!(state.prototypes.remove("blah"), None); assert_eq!(state.prototypes.remove("blah"), None);
} }
#[test]
fn test_calls() {
assert_eq!(
test_body(state_for("fn test(a, b) { test(2, 3); }")),
vec![
"push 2", // evaluating args, in order
"push 3",
"push _forge_gensym_1", // evaluating target (this fn)
"loadw frame", // Store the frame ptr
"pushr",
"loadw frame", // Increment the frame ptr
"add 6",
"storew frame",
"call", // Actually make the call
"popr", // Restore the frame ptr
"storew frame",
"pop" // expr-as-statement drops the evaluated value
]
.join("\n")
);
}
} }
+10 -8
View File
@@ -416,8 +416,10 @@ impl AstNode for Expr {
}) })
.map_postfix(|expr, suffix| match suffix.as_rule() { .map_postfix(|expr, suffix| match suffix.as_rule() {
Rule::arglist => Expr::Call( Rule::arglist => Expr::Call(
expr.into(), Call {
suffix.into_inner().map(Expr::from_pair).collect(), target: expr.into(),
args: suffix.into_inner().map(Expr::from_pair).collect(),
}
), ),
Rule::subscript => { Rule::subscript => {
Expr::Subscript(expr.into(), Expr::from_pair(suffix.first()).into()) Expr::Subscript(expr.into(), Expr::from_pair(suffix.first()).into())
@@ -695,7 +697,7 @@ mod test {
// compiler can detect this and error at that stage. // compiler can detect this and error at that stage.
assert_eq!( assert_eq!(
Expr::from_str("&foo()"), Expr::from_str("&foo()"),
Ok(Expr::Address(Expr::Call("foo".into(), vec![]).into())) Ok(Expr::Address(Expr::Call(Call { target: "foo".into(), args: vec![] }).into()))
); );
// Dereferencing // Dereferencing
@@ -712,7 +714,7 @@ mod test {
#[test] #[test]
fn parse_calls() { fn parse_calls() {
let blah = Expr::Call("blah".into(), vec![]); let blah = Expr::Call(Call { target: "blah".into(), args: vec![] });
// Can Node parse a call? // Can Node parse a call?
assert_eq!(Expr::from_str("blah()"), Ok(blah.clone())); assert_eq!(Expr::from_str("blah()"), Ok(blah.clone()));
@@ -723,13 +725,13 @@ mod test {
// Calls with args // Calls with args
assert_eq!( assert_eq!(
Expr::from_str("blah(1, 2)"), Expr::from_str("blah(1, 2)"),
Ok(Expr::Call("blah".into(), vec![1.into(), 2.into()])) Ok(Expr::Call(Call { target: "blah".into(), args: vec![1.into(), 2.into()] }))
); );
//Calls with strings //Calls with strings
assert_eq!( assert_eq!(
Expr::from_str("blah(\"foo\", 2)"), Expr::from_str("blah(\"foo\", 2)"),
Ok(Expr::Call("blah".into(), vec![Expr::String("foo".into()), 2.into()])) Ok(Expr::Call(Call { target: "blah".into(), args: vec![Expr::String("foo".into()), 2.into()] }))
); );
} }
@@ -801,8 +803,8 @@ mod test {
assert_eq!( assert_eq!(
statements, statements,
vec![ vec![
Statement::Expr(Expr::Call("foo".into(), vec![])), Statement::Expr(Expr::Call(Call { target: "foo".into(), args: vec![] })),
Statement::Expr(Expr::Call("bar".into(), vec![])), Statement::Expr(Expr::Call(Call { target: "bar".into(), args: vec![] })),
] ]
); );
} }