diff --git a/src/compile.rs b/src/compile.rs index b5a9335..bb0f362 100644 --- a/src/compile.rs +++ b/src/compile.rs @@ -56,6 +56,11 @@ struct Compiler<'bc, 'gc> { /// the current local variables that are in scope, only needed for compiling env: Rc>, rt: &'gc mut RtAlloc, + + /// How nested the current loop is, required for break offsets + loop_nesting: usize, + /// All break instructions currently in need of an offset. K=loop_nesting, V=break_indices + breaks: HashMap>, } pub fn compile<'ast, 'bc, 'gc>( @@ -69,6 +74,8 @@ pub fn compile<'ast, 'bc, 'gc>( bump: bytecode_bump, env: Rc::new(RefCell::new(Env::default())), rt, + loop_nesting: 0, + breaks: HashMap::default(), }; compiler.compile(ast)?; @@ -200,12 +207,17 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> { */ let first_stmt_idx = self.code_len(); + + self.loop_nesting += 1; + self.compile_block(ast_block)?; let jmp_offset = self.back_jmp_offset(first_stmt_idx); self.push_instr(Instr::Jmp(jmp_offset), StackChange::None, span); + self.end_loop(); + Ok(()) } @@ -219,6 +231,9 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> { */ let cond_index = self.code_len(); + + self.loop_nesting += 1; + self.compile_expr(&while_stmt.cond)?; let jmp_false_idx = @@ -232,11 +247,18 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> { let jmp_offset = self.forward_jmp_offset(jmp_false_idx as isize); self.change_instr(jmp_false_idx, Instr::JmpFalse(jmp_offset)); + self.end_loop(); + Ok(()) } - fn compile_break(&mut self, _: Span) -> CResult { - todo!() + fn compile_break(&mut self, span: Span) -> CResult { + let break_idx = self.push_instr(Instr::Jmp(0), StackChange::None, span); + self.breaks + .entry(self.loop_nesting) + .or_default() + .push(break_idx); + Ok(()) } fn compile_return(&mut self, _: &Option, _: Span) -> CResult { @@ -337,6 +359,17 @@ impl<'bc, 'gc> Compiler<'bc, 'gc> { todo!() } + fn end_loop(&mut self) { + let breaks = self.breaks.remove(&self.loop_nesting); + if let Some(breaks) = breaks { + for brk in breaks { + let offset = self.forward_jmp_offset(brk as isize); + self.change_instr(brk, Instr::Jmp(offset)); + } + } + self.loop_nesting -= 1; + } + fn current_stack_top(&self) -> usize { let block = &self.blocks[self.current_block]; // we want the stack position, not the size, so the `- 1` diff --git a/src/vm.rs b/src/vm.rs index 13a16bb..4bbdd68 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -65,7 +65,7 @@ impl<'bc> Vm<'bc, '_> { } self.pc += 1; // debug stack size assertion - todo!() + // todo!() } } diff --git a/test.dil b/test.dil index 536bdb5..2504cee 100644 --- a/test.dil +++ b/test.dil @@ -1,7 +1,5 @@ -let i = 0; - -while i < 100000 { - i = i + 1; +loop { +break; } -print "done"; \ No newline at end of file +print "hi"; \ No newline at end of file diff --git a/tests/control_flow.rs b/tests/control_flow.rs index f4911a7..32dbd15 100644 --- a/tests/control_flow.rs +++ b/tests/control_flow.rs @@ -140,3 +140,74 @@ while i < 100 { } "# ); + +run_test!( + break_out_loop, + r#" +print "Start"; + +loop { + break; + print "WRONG"; +} + +print "Good end"; +"# +); + +run_test!( + break_out_while, + r#" +print "Start"; + +while true { + break; + print "WRONG"; +} + +print "Good end"; +"# +); + +run_test!( + fizzbuzz_with_loop, + r#" +let i = 1; + +loop { + if i % 15 == 0 { + print "FizzBuzz"; + } else if i % 5 == 0 { + print "Buzz"; + } else if i % 3 == 0 { + print "Fizz"; + } else { + print i; + } + i = i + 1; + + if i >= 100 { + break; + } +} +"# +); + +run_test!( + nested_loop_break, + r#" +print "Start"; +loop { + print "Start inner"; + loop { + print "inside inner"; + break; + print "WRONG"; + } + print "Outside inner"; + break; + print "WRONG"; +} +print "End"; +"# +); diff --git a/tests/snapshots/control_flow__break_out.snap b/tests/snapshots/control_flow__break_out.snap new file mode 100644 index 0000000..d18b1c6 --- /dev/null +++ b/tests/snapshots/control_flow__break_out.snap @@ -0,0 +1,7 @@ +--- +source: tests/control_flow.rs +assertion_line: 144 +expression: output + +--- +"Start\nGood end\n" diff --git a/tests/snapshots/control_flow__break_out_loop.snap b/tests/snapshots/control_flow__break_out_loop.snap new file mode 100644 index 0000000..d18b1c6 --- /dev/null +++ b/tests/snapshots/control_flow__break_out_loop.snap @@ -0,0 +1,7 @@ +--- +source: tests/control_flow.rs +assertion_line: 144 +expression: output + +--- +"Start\nGood end\n" diff --git a/tests/snapshots/control_flow__break_out_while.snap b/tests/snapshots/control_flow__break_out_while.snap new file mode 100644 index 0000000..a18c57d --- /dev/null +++ b/tests/snapshots/control_flow__break_out_while.snap @@ -0,0 +1,7 @@ +--- +source: tests/control_flow.rs +assertion_line: 158 +expression: output + +--- +"Start\nGood end\n" diff --git a/tests/snapshots/control_flow__fizzbuzz_with_loop.snap b/tests/snapshots/control_flow__fizzbuzz_with_loop.snap new file mode 100644 index 0000000..c8e2cf1 --- /dev/null +++ b/tests/snapshots/control_flow__fizzbuzz_with_loop.snap @@ -0,0 +1,7 @@ +--- +source: tests/control_flow.rs +assertion_line: 158 +expression: output + +--- +"1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz\n16\n17\nFizz\n19\nBuzz\nFizz\n22\n23\nFizz\nBuzz\n26\nFizz\n28\n29\nFizzBuzz\n31\n32\nFizz\n34\nBuzz\nFizz\n37\n38\nFizz\nBuzz\n41\nFizz\n43\n44\nFizzBuzz\n46\n47\nFizz\n49\nBuzz\nFizz\n52\n53\nFizz\nBuzz\n56\nFizz\n58\n59\nFizzBuzz\n61\n62\nFizz\n64\nBuzz\nFizz\n67\n68\nFizz\nBuzz\n71\nFizz\n73\n74\nFizzBuzz\n76\n77\nFizz\n79\nBuzz\nFizz\n82\n83\nFizz\nBuzz\n86\nFizz\n88\n89\nFizzBuzz\n91\n92\nFizz\n94\nBuzz\nFizz\n97\n98\nFizz\n" diff --git a/tests/snapshots/control_flow__nested_loop_break.snap b/tests/snapshots/control_flow__nested_loop_break.snap new file mode 100644 index 0000000..452f46c --- /dev/null +++ b/tests/snapshots/control_flow__nested_loop_break.snap @@ -0,0 +1,7 @@ +--- +source: tests/control_flow.rs +assertion_line: 196 +expression: output + +--- +"Start\nStart inner\ninside inner\nOutside inner\nEnd\n"