diff --git a/forge_core/src/ast.rs b/forge_core/src/ast.rs index 92f0a9e..9bc9393 100644 --- a/forge_core/src/ast.rs +++ b/forge_core/src/ast.rs @@ -135,6 +135,12 @@ pub struct Asm { pub body: String } +#[derive(PartialEq, Clone, Debug)] +pub struct Call { + pub target: BoxExpr, + pub args: Vec +} + /// 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), + Call(Call), Subscript(BoxExpr, BoxExpr), Infix(BoxExpr, Operator, BoxExpr), String(String), diff --git a/forge_core/src/compiler.rs b/forge_core/src/compiler.rs index 4f4ad74..9e94ffc 100644 --- a/forge_core/src/compiler.rs +++ b/forge_core/src/compiler.rs @@ -99,6 +99,12 @@ impl CompiledFn { fn emit_arg(&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 { 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") + ); + } } diff --git a/forge_core/src/forge_parser.rs b/forge_core/src/forge_parser.rs index bf97984..9cdb161 100644 --- a/forge_core/src/forge_parser.rs +++ b/forge_core/src/forge_parser.rs @@ -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![] })), ] ); }