diff --git a/NEWS b/NEWS index cc19a6b..9554081 100644 --- a/NEWS +++ b/NEWS @@ -44,6 +44,7 @@ Changes from 0.13.0 instead of silently ignoring them * Multiple expressions are now allowed in the else clause of a for loop + * `else` clauses in `for` and `while` are recognized more reliably * Argument destructuring no longer interferes with function docstrings. [ Misc. Improvements ] diff --git a/hy/compiler.py b/hy/compiler.py index 953d5d8..9f8f8f8 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -376,6 +376,14 @@ def is_unpack(kind, x): and x[0] == "unpack_" + kind) +def ends_with_else(expr): + return (expr and + isinstance(expr[-1], HyExpression) and + expr[-1] and + isinstance(expr[-1][0], HySymbol) and + expr[-1][0] == HySymbol("else")) + + class HyASTCompiler(object): def __init__(self, module_name): @@ -1825,7 +1833,7 @@ class HyASTCompiler(object): orel = Result() # (for* [] body (else …)) - if expression and expression[-1][0] == HySymbol("else"): + if ends_with_else(expression): else_expr = expression.pop() for else_body in else_expr[1:]: orel += self.compile(else_body) @@ -1854,7 +1862,7 @@ class HyASTCompiler(object): orel = Result() # (while cond body (else …)) - if expr and expr[-1][0] == HySymbol("else"): + if ends_with_else(expr): else_expr = expr.pop() for else_body in else_expr[1:]: orel += self.compile(else_body) diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 1970a12..8c9520e 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -221,6 +221,22 @@ (+= count 10))) (assert (= count 161)) + ; don't be fooled by constructs that look like else + (setv s "") + (setv (get (globals) "else") True) + (for [x "abcde"] + (+= s x) + [else (+= s "_")]) + (assert (= s "a_b_c_d_e_")) + + (setv s "") + (setv (get (globals) "else") True) + (with [(pytest.raises TypeError)] + (for [x "abcde"] + (+= s x) + ("else" (+= s "z")))) + (assert (= s "az")) + (assert (= (list ((fn [] (for [x [[1] [2 3]] y x] (yield y))))) (list-comp y [x [[1] [2 3]] y x]))) (assert (= (list ((fn [] (for [x [[1] [2 3]] y x z (range 5)] (yield z))))) @@ -308,7 +324,26 @@ (while True (break) (else (setv myvariable 53))) - (assert (= myvariable 26))) + (assert (= myvariable 26)) + + ; don't be fooled by constructs that look like else clauses + (setv x 2) + (setv a []) + (setv (get (globals) "else") True) + (while x + (.append a x) + (-= x 1) + [else (.append a "e")]) + (assert (= a [2 "e" 1 "e"])) + + (setv x 2) + (setv a []) + (with [(pytest.raises TypeError)] + (while x + (.append a x) + (-= x 1) + ("else" (.append a "e")))) + (assert (= a [2 "e"]))) (defn test-branching []