From 567fa14f1d3aa8bba776f414b09aa3a24e3d8bee Mon Sep 17 00:00:00 2001 From: Rob Day Date: Wed, 13 Sep 2017 22:31:28 +0100 Subject: [PATCH] Allow else after a while loop --- NEWS | 1 + hy/compiler.py | 10 +++++++++- tests/compilers/test_ast.py | 1 + tests/native_tests/language.hy | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 3e23f66..66bb80d 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,7 @@ Changes from 0.13.0 * new `comment` macro * support EDN `#_` syntax to discard the next term * `return` has been implemented as a special form + * `while` loops may now contain an `else` clause, like `for` loops [ Bug Fixes ] * Numeric literals are no longer parsed as symbols when followed by a dot diff --git a/hy/compiler.py b/hy/compiler.py index ca478ed..34dc285 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -2146,12 +2146,20 @@ class HyASTCompiler(object): expr.pop(0) # "while" ret = self.compile(expr.pop(0)) + orel = Result() + # (while cond body (else …)) + if expr and expr[-1][0] == HySymbol("else"): + else_expr = expr.pop() + for else_body in else_expr[1:]: + orel += self.compile(else_body) + orel += orel.expr_as_stmt() + body = self._compile_branch(expr) body += body.expr_as_stmt() ret += ast.While(test=ret.force_expr, body=body.stmts, - orelse=[], + orelse=orel.stmts, lineno=expr.start_line, col_offset=expr.start_column) diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 4e0e00b..9dccfd9 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -324,6 +324,7 @@ def test_ast_bad_with(): def test_ast_valid_while(): "Make sure AST can't compile invalid while" can_compile("(while foo bar)") + can_compile("(while foo bar (else baz))") def test_ast_valid_for(): diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 1f069af..c221d03 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -266,6 +266,39 @@ (assert (= count 0)) (assert (= fact 120))) +(defn test-while-loop-else [] + (setv count 5) + (setv fact 1) + (setv myvariable 18) + (while (> count 0) + (setv fact (* fact count)) + (setv count (- count 1)) + (else (setv myvariable 26))) + (assert (= count 0)) + (assert (= fact 120)) + (assert (= myvariable 26)) + + ; multiple statements in a while loop should work + (setv count 5) + (setv fact 1) + (setv myvariable 18) + (setv myothervariable 15) + (while (> count 0) + (setv fact (* fact count)) + (setv count (- count 1)) + (else (setv myvariable 26) + (setv myothervariable 24))) + (assert (= count 0)) + (assert (= fact 120)) + (assert (= myvariable 26)) + (assert (= myothervariable 24)) + + ; else clause shouldn't get run after a break + (while True + (break) + (else (setv myvariable 53))) + (assert (= myvariable 26))) + (defn test-branching [] "NATIVE: test if branching"