Add support for loop
/exit
statements. This kind of statement should start with the loop
keyword, followed by a list of zero or more statements enclosed inside curly braces (the body). Inside the body zero or more exit
statements can be placed. This statement starts with the exit
and when
keywords, followed by an expression (the condition) and ends with a semicolon. The body of the loop is executed and repeated until the exit
statement is executed with a condition that evaluates to true (any non-zero value).
It’s a semantic error to have an exit
statement outside a loop
statement. Also, make sure to consider loop
, exit
, and when
as a Delta reserved keywords from now on.
The Delta program:
var n, r, i; n = 5; r = 1; i = 0; loop { i = i + 1; exit when !((n + 1) - i); r = r * i; } r
should produce the following WAT code:
(module (func (export "_start") (result i32) (local $n i32) (local $r i32) (local $i i32) i32.const 5 local.set $n i32.const 1 local.set $r i32.const 0 local.set $i block $00000 loop local.get $i i32.const 1 i32.add local.set $i local.get $n i32.const 1 i32.add local.get $i i32.sub i32.eqz br_if $00000 local.get $r local.get $i i32.mul local.set $r br 0 end end local.get $r ) )
The above WAT function’s return value should be:
120
# File: tests/test_19_loop_exit.py from unittest import TestCase from delta import Compiler, SyntaxMistake from delta.semantics import SemanticMistake class TestLoopExit(TestCase): def setUp(self): self.c = Compiler('program_start') def test_syntax_mistake(self): with self.assertRaises(SyntaxMistake): self.c.realize('loop {') def test_semantic_mistake1(self): with self.assertRaises(SemanticMistake): self.c.realize('var loop; 0') def test_semantic_mistake2(self): with self.assertRaises(SemanticMistake): self.c.realize('var exit; 0') def test_semantic_mistake3(self): with self.assertRaises(SemanticMistake): self.c.realize('var when; 0') def test_loop_exit_zero(self): self.assertEqual(0, self.c.realize( ''' var x, y; x = 1; y = 0; loop { x = x - 1; exit when !x; y = 1; } x + y ''')) def test_loop_exit_fact(self): self.assertEqual(120, self.c.realize( ''' var n, r, i; n = 5; r = 1; i = 0; loop { i = i + 1; exit when !((n + 1) - i); r = r * i; } r ''')) def test_loop_exit_count_down(self): self.assertEqual(0, self.c.realize( ''' var i; i = 10; loop { exit when !i; i = i - 1; } i ''')) def test_loop_exit_skip_body(self): self.assertEqual(9, self.c.realize( ''' var n; n = 10; loop { n = n - 1; if false { } else { exit when n; } } n ''')) def test_loop_exit_fibo(self): self.assertEqual(55, self.c.realize( ''' var n, a, b; n = 10; a = 0; b = 1; loop { exit when !n; var t; t = b; b = a + b; a = t; n = n - 1; } a ''')) def test_loop_exit_nested(self): self.assertEqual(1500, self.c.realize( ''' var r, i; r = 0; i = 10; loop { var j; j = 50; loop { var k; k = 3; loop { r = r + 1; k = k - 1; exit when !k; } j = j - 1; exit when !j; } i = i - 1; exit when !i; } r ''')) def test_loop_exit_multiple_exits(self): self.assertEqual(9, self.c.realize( ''' var r, i; r = 0; i = 10; loop { i = i - 1; if i { exit when !(i % 2); } else { exit when true; } r = r + i; exit when !i; } r '''))