From fdb6bf944cca67c87db8582dd5795f89766668f8 Mon Sep 17 00:00:00 2001 From: Julien Danjou Date: Tue, 9 Apr 2013 21:19:24 +0200 Subject: [PATCH] try: add support for `finally' This fixes #75 Signed-off-by: Julien Danjou --- hy/compiler.py | 96 +++++++++++++++++++++++----------- tests/compilers/test_ast.py | 5 ++ tests/native_tests/language.hy | 28 ++++++++++ 3 files changed, 98 insertions(+), 31 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 318174b..575ff35 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -235,12 +235,6 @@ class HyASTCompiler(object): def compile_try_expression(self, expr): expr.pop(0) # try - if sys.version_info[0] >= 3 and sys.version_info[1] >= 3: - # Python 3.3 features a rename of TryExcept to Try. - Try = ast.Try - else: - Try = ast.TryExcept - try: body = expr.pop(0) except IndexError: @@ -252,8 +246,42 @@ class HyASTCompiler(object): expr.start_column) orelse = [] - if len(expr) == 0: - # (try) or (try body) + finalbody = [] + handlers = [] + + for e in expr: + if not len(e): + raise TypeError("Empty list not allowed in `try'") + + if e[0] in (HySymbol("except"), HySymbol("catch")): + handlers.append(self.compile(e)) + elif e[0] == HySymbol("else"): + if orelse: + raise TypeError( + "`try' cannot have more than one `else'") + else: + orelse = self._code_branch(self.compile(e[1:]), + e.start_line, + e.start_column) + elif e[0] == HySymbol("finally"): + if finalbody: + raise TypeError( + "`try' cannot have more than one `finally'") + else: + finalbody = self._code_branch(self.compile(e[1:]), + e.start_line, + e.start_column) + else: + raise TypeError("Unknown expression in `try'") + + # Using (else) without (except) is verboten! + if orelse and not handlers: + raise TypeError( + "`try' cannot have `else' without `except'") + + # (try) or (try BODY) + # Generate a default handler for Python >= 3.3 and pypy + if not handlers and not finalbody and not orelse: handlers = [ast.ExceptHandler( lineno=expr.start_line, col_offset=expr.start_column, @@ -261,35 +289,41 @@ class HyASTCompiler(object): name=None, body=[ast.Pass(lineno=expr.start_line, col_offset=expr.start_column)])] - else: - handlers = [] - for e in expr: - if not len(e): - raise TypeError("Empty list not allowed in `try'") - if e[0] in (HySymbol("except"), HySymbol("catch")): - handlers.append(self.compile(e)) - elif e[0] == HySymbol("else"): - if orelse: - raise TypeError( - "`try' cannot have more than one `else'") - else: - orelse = self._code_branch(self.compile(e[1:]), - e.start_line, - e.start_column) - else: - raise TypeError("Unknown expression in `try'") + if sys.version_info[0] >= 3 and sys.version_info[1] >= 3: + # Python 3.3 features a merge of TryExcept+TryFinally into Try. + return ast.Try( + lineno=expr.start_line, + col_offset=expr.start_column, + body=body, + handlers=handlers, + orelse=orelse, + finalbody=finalbody) - if handlers == []: - raise TypeError( - "`try' must have at least `except' or `finally'") + if finalbody: + if handlers: + return ast.TryFinally( + lineno=expr.start_line, + col_offset=expr.start_column, + body=[ast.TryExcept( + lineno=expr.start_line, + col_offset=expr.start_column, + handlers=handlers, + body=body, + orelse=orelse)], + finalbody=finalbody) - return Try( + return ast.TryFinally( + lineno=expr.start_line, + col_offset=expr.start_column, + body=body, + finalbody=finalbody) + + return ast.TryExcept( lineno=expr.start_line, col_offset=expr.start_column, - body=body, handlers=handlers, - finalbody=[], + body=body, orelse=orelse) @builds("catch") diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 05cf72b..3082049 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -123,6 +123,11 @@ def test_ast_good_try(): hy_compile(tokenize("(try 1)")) hy_compile(tokenize("(try 1 (except) (else 1))")) hy_compile(tokenize("(try 1 (else 1) (except))")) + hy_compile(tokenize("(try 1 (finally 1) (except))")) + hy_compile(tokenize("(try 1 (finally 1))")) + hy_compile(tokenize("(try 1 (except) (finally 1))")) + hy_compile(tokenize("(try 1 (except) (finally 1) (else 1))")) + hy_compile(tokenize("(try 1 (except) (else 1) (finally 1))")) def test_ast_bad_try(): diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 45045c6..692bb2d 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -198,6 +198,34 @@ (setv passed true))) (assert passed)) + + ;; Test (finally) + (let [[passed false]] + (try + (pass) + (finally (setv passed true))) + (assert passed)) + + ;; Test (finally) + (raise) + (let [[passed false]] + (try + (raise Exception) + (except) + (finally (setv passed true))) + (assert passed)) + + + ;; Test (finally) + (raise) + (else) + (let [[passed false] + [not-elsed true]] + (try + (raise Exception) + (except) + (else (setv not-elsed false)) + (finally (setv passed true))) + (assert passed) + (assert not-elsed)) + (try (raise (KeyError)) (catch [[IOError]] (assert false))