From 81d89c9d1204157220fe8b57ab73f8c29bf3fe0e Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Sat, 13 May 2017 18:52:59 -0400 Subject: [PATCH] Enforce the standard order of `try` elements --- NEWS | 4 +++ hy/compiler.py | 51 ++++++++++++++++--------------------- tests/compilers/test_ast.py | 8 +++--- 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/NEWS b/NEWS index ed448ec..490e080 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,10 @@ Changes from 0.12.1 * `defreader` has been renamed to `defsharp`; what were previously called "reader macros", which were never true reader macros, are now called "sharp macros" + * `try` now enforces the usual Python order for its elements (`else` must + follow all `except`s, and `finally` must come last). This is only a + syntactic change; the elements were already run in Python order even when + defined out of order. * Importing or executing a Hy file automatically byte-compiles it, or loads a byte-compiled version if it exists and is up to date. This brings big speed boosts, even for one-liners, because Hy no longer needs to recompile diff --git a/hy/compiler.py b/hy/compiler.py index 70eb5bb..e3a4224 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -793,30 +793,29 @@ class HyASTCompiler(object): body = body.stmts - orelse = [] - finalbody = [] - handlers = [] + if not all(expr): + raise HyTypeError(expr, "Empty list not allowed in `try'") handler_results = Result() - - for e in expr: - if not len(e): - raise HyTypeError(e, "Empty list not allowed in `try'") - - if e[0] == HySymbol("except"): - handler_results += self._compile_catch_expression(e, name) - handlers.append(handler_results.stmts.pop()) - elif e[0] == HySymbol("else"): - orelse = self.try_except_helper(e, HySymbol("else"), orelse) - elif e[0] == HySymbol("finally"): - finalbody = self.try_except_helper(e, HySymbol("finally"), - finalbody) - else: - raise HyTypeError(e, "Unknown expression in `try'") + handlers = [] + while expr and expr[0][0] == HySymbol("except"): + handler_results += self._compile_catch_expression(expr.pop(0), + name) + handlers.append(handler_results.stmts.pop()) + orelse = [] + if expr and expr[0][0] == HySymbol("else"): + orelse = self.try_except_helper(expr.pop(0), HySymbol("else")) + finalbody = [] + if expr and expr[0][0] == HySymbol("finally"): + finalbody = self.try_except_helper(expr.pop(0), HySymbol("finally")) + if expr: + if expr[0][0] in ("except", "else", "finally"): + raise HyTypeError(expr, "Incorrect order of `except'/`else'/`finally' in `try'") + raise HyTypeError(expr, "Unknown expression in `try'") # Using (else) without (except) is verboten! if orelse and not handlers: raise HyTypeError( - e, + expr, "`try' cannot have `else' without `except'") # (try) or (try BODY) @@ -868,16 +867,10 @@ class HyASTCompiler(object): body=body, orelse=orelse) + returnable - def try_except_helper(self, hy_obj, symbol, accumulated): - if accumulated: - raise HyTypeError( - hy_obj, - "`try' cannot have more than one `%s'" % symbol) - else: - accumulated = self._compile_branch(hy_obj[1:]) - accumulated += accumulated.expr_as_stmt() - accumulated = accumulated.stmts - return accumulated + def try_except_helper(self, hy_obj, symbol): + x = self._compile_branch(hy_obj[1:]) + x += x.expr_as_stmt() + return x.stmts @builds("except") def magic_internal_form(self, expr): diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index f273874..bc22967 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -116,12 +116,11 @@ def test_ast_good_try(): can_compile("(try)") can_compile("(try 1)") can_compile("(try 1 (except) (else 1))") - can_compile("(try 1 (else 1) (except))") - can_compile("(try 1 (finally 1) (except))") can_compile("(try 1 (finally 1))") can_compile("(try 1 (except) (finally 1))") - can_compile("(try 1 (except) (finally 1) (else 1))") + can_compile("(try 1 (except [x]) (except [y]) (finally 1))") can_compile("(try 1 (except) (else 1) (finally 1))") + can_compile("(try 1 (except [x]) (except [y]) (else 1) (finally 1))") def test_ast_bad_try(): @@ -130,6 +129,9 @@ def test_ast_bad_try(): cant_compile("(try 1 bla bla)") cant_compile("(try (do) (else 1) (else 2))") cant_compile("(try 1 (else 1))") + cant_compile("(try 1 (else 1) (except))") + cant_compile("(try 1 (finally 1) (except))") + cant_compile("(try 1 (except) (finally 1) (else 1))") def test_ast_good_except():