diff --git a/forge_core/src/compiler/ast_nodes/block.rs b/forge_core/src/compiler/ast_nodes/block.rs index 3b3c0d8..edc8f40 100644 --- a/forge_core/src/compiler/ast_nodes/block.rs +++ b/forge_core/src/compiler/ast_nodes/block.rs @@ -1,4 +1,4 @@ -use crate::ast::{Asm, Block, Conditional, Location, Statement, WhileLoop}; +use crate::ast::{Asm, Block, Location, Statement}; use crate::compiler::compilable::Compilable; use crate::compiler::compiled_fn::CompiledFn; use crate::compiler::CompileError; @@ -34,24 +34,11 @@ impl Compilable for Block { // Emit the body sig.emit(body.as_str()) } - Statement::Conditional(Conditional { condition, body, alternative }) => { - // todo split into new file - condition.process(state, Some(sig), loc)?; - sig.emit("#if"); // We went to a lot of trouble making macros, shame not to use them - body.process(state, Some(sig), loc)?; - if let Some(alternative) = alternative { - sig.emit("#else"); - alternative.process(state, Some(sig), loc)?; - } - sig.emit("#end") + Statement::Conditional(cond) => { + cond.process(state, Some(sig), loc)?; } - Statement::WhileLoop(WhileLoop { condition, body }) => { - // todo split into new file - sig.emit("#while"); - condition.process(state, Some(sig), loc)?; - sig.emit("#do"); - body.process(state, Some(sig), loc)?; - sig.emit("#end") + Statement::WhileLoop(while_loop) => { + while_loop.process(state, Some(sig), loc)?; } Statement::RepeatLoop(repeat_loop) => { repeat_loop.process(state, Some(sig), loc)?; @@ -86,90 +73,6 @@ mod test { ) } - #[test] - fn test_conditionals() { - assert_eq!( - test_body(state_for("fn test() { var x = 3; if (x > 2) { x = 1; } }")), - vec![ - "push 3", - "loadw frame", - "storew", // x = 3 - "loadw frame", - "loadw", - "push 2", - "agt", // The condition, x > 2 - "#if", // The if itself - "push 1", - "loadw frame", - "storew", // The branch, x = 1 - "#end"] - .join("\n") - ); - - assert_eq!( - test_body(state_for("fn test() { var x = 3; if (x > 2) { x = 1; } else { x = 7; } }")), - vec![ - "push 3", - "loadw frame", - "storew", // x = 3 - "loadw frame", - "loadw", - "push 2", - "agt", // The condition, x > 2 - "#if", // The if itself - "push 1", - "loadw frame", - "storew", // The affirmative branch, x = 1 - "#else", // The alternative - "push 7", - "loadw frame", - "storew", // x = 7 - "#end"] - .join("\n") - ) - } - - #[test] - fn test_while_loops() { - assert_eq!( - test_body(state_for("fn test() { var x = 0; var c = 0; while (c < 10) { x = x + c; c = c + 1; } }")), - vec![ - "push 0", - "loadw frame", - "storew", // var x = 0 - "push 0", - "loadw frame", - "add 3", - "storew", // var c = 0 - "#while", // Start the loop - "loadw frame", - "add 3", - "loadw", - "push 10", - "alt", // c > 10 - "#do", // Start loop body - "loadw frame", - "loadw", - "loadw frame", - "add 3", - "loadw", - "add", - "loadw frame", - "storew", // x = x + c - "loadw frame", - "add 3", - "loadw", - "push 1", - "add", - "loadw frame", - "add 3", - "storew", // c = c + 1 - "#end", // End the loop body - ] - .join("\n") - ); - } - #[test] fn test_block_scoping() { assert_eq!( diff --git a/forge_core/src/compiler/ast_nodes/conditional.rs b/forge_core/src/compiler/ast_nodes/conditional.rs new file mode 100644 index 0000000..5d79e31 --- /dev/null +++ b/forge_core/src/compiler/ast_nodes/conditional.rs @@ -0,0 +1,74 @@ +use crate::ast::{Conditional, Location}; +use crate::compiler::compilable::Compilable; +use crate::compiler::compiled_fn::CompiledFn; +use crate::compiler::CompileError; +use crate::compiler::state::State; + +impl Compilable for Conditional { + fn process(self, state: &mut State, sig: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> { + let sig = sig.expect("Conditional outside function"); + + let Conditional { condition, body, alternative } = self; + + condition.process(state, Some(sig), loc)?; + sig.emit("#if"); // We went to a lot of trouble making macros, shame not to use them + body.process(state, Some(sig), loc)?; + if let Some(alternative) = alternative { + sig.emit("#else"); + alternative.process(state, Some(sig), loc)?; + } + sig.emit("#end"); + Ok(()) + } +} + +#[cfg(test)] +mod test { + use crate::compiler::test_utils::*; + + #[test] + fn test_conditionals() { + assert_eq!( + test_body(state_for("fn test() { var x = 3; if (x > 2) { x = 1; } }")), + vec![ + "push 3", + "loadw frame", + "storew", // x = 3 + "loadw frame", + "loadw", + "push 2", + "agt", // The condition, x > 2 + "#if", // The if itself + "push 1", + "loadw frame", + "storew", // The branch, x = 1 + "#end"] + .join("\n") + ); + } + + #[test] + fn test_if_else() { + assert_eq!( + test_body(state_for("fn test() { var x = 3; if (x > 2) { x = 1; } else { x = 7; } }")), + vec![ + "push 3", + "loadw frame", + "storew", // x = 3 + "loadw frame", + "loadw", + "push 2", + "agt", // The condition, x > 2 + "#if", // The if itself + "push 1", + "loadw frame", + "storew", // The affirmative branch, x = 1 + "#else", // The alternative + "push 7", + "loadw frame", + "storew", // x = 7 + "#end"] + .join("\n") + ) + } +} \ No newline at end of file diff --git a/forge_core/src/compiler/ast_nodes/mod.rs b/forge_core/src/compiler/ast_nodes/mod.rs index 05b2ae8..e4b892b 100644 --- a/forge_core/src/compiler/ast_nodes/mod.rs +++ b/forge_core/src/compiler/ast_nodes/mod.rs @@ -12,3 +12,5 @@ mod expr; mod global; mod r#const; mod r#return; +mod conditional; +mod while_loop; diff --git a/forge_core/src/compiler/ast_nodes/while_loop.rs b/forge_core/src/compiler/ast_nodes/while_loop.rs new file mode 100644 index 0000000..bb37c73 --- /dev/null +++ b/forge_core/src/compiler/ast_nodes/while_loop.rs @@ -0,0 +1,66 @@ +use crate::ast::{Location, WhileLoop}; +use crate::compiler::compilable::Compilable; +use crate::compiler::compiled_fn::CompiledFn; +use crate::compiler::CompileError; +use crate::compiler::state::State; + +impl Compilable for WhileLoop { + fn process(self, state: &mut State, sig: Option<&mut CompiledFn>, loc: Location) -> Result<(), CompileError> { + let sig = sig.expect("Loop outside function"); + let WhileLoop { condition, body } = self; + sig.emit("#while"); + condition.process(state, Some(sig), loc)?; + sig.emit("#do"); + body.process(state, Some(sig), loc)?; + sig.emit("#end"); + + Ok(()) + } +} + +#[cfg(test)] +mod test { + use crate::compiler::test_utils::*; + + #[test] + fn test_while_loops() { + assert_eq!( + test_body(state_for("fn test() { var x = 0; var c = 0; while (c < 10) { x = x + c; c = c + 1; } }")), + vec![ + "push 0", + "loadw frame", + "storew", // var x = 0 + "push 0", + "loadw frame", + "add 3", + "storew", // var c = 0 + "#while", // Start the loop + "loadw frame", + "add 3", + "loadw", + "push 10", + "alt", // c > 10 + "#do", // Start loop body + "loadw frame", + "loadw", + "loadw frame", + "add 3", + "loadw", + "add", + "loadw frame", + "storew", // x = x + c + "loadw frame", + "add 3", + "loadw", + "push 1", + "add", + "loadw frame", + "add 3", + "storew", // c = c + 1 + "#end", // End the loop body + ] + .join("\n") + ); + } + +} \ No newline at end of file diff --git a/forge_core/src/parser/mod.rs b/forge_core/src/parser/mod.rs index b81f978..dec9310 100644 --- a/forge_core/src/parser/mod.rs +++ b/forge_core/src/parser/mod.rs @@ -83,7 +83,6 @@ pub fn parse(src: &str) -> Result { /////////////////////////////////////////////////////////////////////////////////////////// -// todo: can this die? it's only used in compiler tests but needs to be here for visibility reasons #[cfg(test)] impl Expr { pub(crate) fn parse(src: &str) -> Result {