Compiling calls
This commit is contained in:
@@ -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),
|
||||||
|
|||||||
@@ -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")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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![] })),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user