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
}
#[derive(PartialEq, Clone, Debug)]
pub struct Call {
pub target: BoxExpr,
pub args: Vec<Expr>
}
/// One of the five arithmetical operators
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum Operator {
@@ -166,7 +172,7 @@ pub enum Expr {
Neg(BoxExpr),
Deref(BoxExpr),
Address(Lvalue),
Call(BoxExpr, Vec<Expr>),
Call(Call),
Subscript(BoxExpr, BoxExpr),
Infix(BoxExpr, Operator, BoxExpr),
String(String),
+75 -6
View File
@@ -99,6 +99,12 @@ impl CompiledFn {
fn emit_arg<T: Display>(&mut self, opcode: &str, arg: T) {
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
@@ -296,9 +302,6 @@ impl Compilable for Function {
// and setting signature immutably right now, passing context down ("block?") and setting it
// 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
self.body.process(state, Some(&mut sig), loc)?;
@@ -380,7 +383,7 @@ impl Compilable for Lvalue {
Expr::Neg(_) |
Expr::Not(_) |
Expr::Address(_) |
Expr::Call(_, _) |
Expr::Call(_) |
Expr::Infix(_, _, _) |
Expr::String(_) => {
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.
/// This recursively evaluates a Node and leaves its value on the stack.
impl Compilable for Expr {
@@ -506,7 +553,7 @@ impl Compilable for Expr {
sig.emit_arg("push", label);
Ok(())
}
Expr::Call(_, _) => todo!(),
Expr::Call(call) => call.process(state, Some(sig), loc),
Expr::Subscript(_, _) => todo!("Structs and arrays are not yen supported"),
Expr::Infix(lhs, op, rhs) => {
// Recurse on expressions, handling operators
@@ -628,7 +675,7 @@ pub fn eval_const(expr: Expr, scope: &Scope) -> Result<i32, CompileError> {
0,
String::from("Addresses are not known at compile time"),
)),
Expr::Call(_, _) | Expr::Subscript(_, _) => Err(CompileError(
Expr::Call(_) | Expr::Subscript(_, _) => Err(CompileError(
0,
0,
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.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() {
Rule::arglist => Expr::Call(
expr.into(),
suffix.into_inner().map(Expr::from_pair).collect(),
Call {
target: expr.into(),
args: suffix.into_inner().map(Expr::from_pair).collect(),
}
),
Rule::subscript => {
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.
assert_eq!(
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
@@ -712,7 +714,7 @@ mod test {
#[test]
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?
assert_eq!(Expr::from_str("blah()"), Ok(blah.clone()));
@@ -723,13 +725,13 @@ mod test {
// Calls with args
assert_eq!(
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
assert_eq!(
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!(
statements,
vec![
Statement::Expr(Expr::Call("foo".into(), vec![])),
Statement::Expr(Expr::Call("bar".into(), vec![])),
Statement::Expr(Expr::Call(Call { target: "foo".into(), args: vec![] })),
Statement::Expr(Expr::Call(Call { target: "bar".into(), args: vec![] })),
]
);
}