From 058197a24f46bba677883fd56c4a7c0b793a4a49 Mon Sep 17 00:00:00 2001 From: Paul Tagliamonte Date: Wed, 8 May 2013 20:00:09 -0400 Subject: [PATCH] Allow returning inside a try / except. Closes #163 --- hy/compiler.py | 49 +++++++++++++++++++++++++++------- tests/compilers/test_ast.py | 2 ++ tests/native_tests/language.hy | 4 +++ 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index dd6af76..1b5f810 100644 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -557,8 +557,24 @@ class HyASTCompiler(object): # (try something…) body = self.compile(body) - # XXX we will likely want to make this a tempvar - body += body.expr_as_stmt() + var = self.get_anon_var() + name = ast.Name(id=ast_str(var), arg=ast_str(var), + ctx=ast.Store(), + lineno=expr.start_line, + col_offset=expr.start_column) + + expr_name = ast.Name(id=ast_str(var), arg=ast_str(var), + ctx=ast.Load(), + lineno=expr.start_line, + col_offset=expr.start_column) + + returnable = Result(expr=expr_name, temp_variables=[expr_name, name]) + + body += ast.Assign(targets=[name], + value=body.force_expr, + lineno=expr.start_line, + col_offset=expr.start_column) + body = body.stmts if not body: body = [ast.Pass(lineno=expr.start_line, @@ -574,7 +590,7 @@ class HyASTCompiler(object): raise HyTypeError(e, "Empty list not allowed in `try'") if e[0] in (HySymbol("except"), HySymbol("catch")): - handler_results += self.compile(e) + handler_results += self._compile_catch_expression(e, var) handlers.append(handler_results.stmts.pop()) elif e[0] == HySymbol("else"): if orelse: @@ -626,7 +642,7 @@ class HyASTCompiler(object): body=body, handlers=handlers, orelse=orelse, - finalbody=finalbody) + finalbody=finalbody) + returnable if finalbody: if handlers: @@ -639,30 +655,39 @@ class HyASTCompiler(object): handlers=handlers, body=body, orelse=orelse)], - finalbody=finalbody) + finalbody=finalbody) + returnable return ret + ast.TryFinally( lineno=expr.start_line, col_offset=expr.start_column, body=body, - finalbody=finalbody) + finalbody=finalbody) + returnable return ret + ast.TryExcept( lineno=expr.start_line, col_offset=expr.start_column, handlers=handlers, body=body, - orelse=orelse) + orelse=orelse) + returnable - @builds("catch") @builds("except") - def compile_catch_expression(self, expr): + @builds("catch") + def magic_internal_form(self, expr): + raise TypeError("Error: `%s' can't be used like that." % (expr[0])) + + def _compile_catch_expression(self, expr, var): catch = expr.pop(0) # catch + ret_name = ast.Name(id=ast_str(var), arg=ast_str(var), + ctx=ast.Store(), + lineno=expr.start_line, + col_offset=expr.start_column) + try: exceptions = expr.pop(0) except IndexError: exceptions = HyList() + # exceptions catch should be either: # [[list of exceptions]] # or @@ -673,6 +698,7 @@ class HyASTCompiler(object): # [exception] # or # [] + if not isinstance(exceptions, HyList): raise HyTypeError(exceptions, "`%s' exceptions list is not a list" % catch) @@ -720,7 +746,10 @@ class HyASTCompiler(object): "`%s' needs a valid exception list" % catch) body = self._compile_branch(expr) - # XXX tempvar handling magic + body += ast.Assign(targets=[ret_name], + value=body.force_expr, + lineno=expr.start_line, + col_offset=expr.start_column) body += body.expr_as_stmt() body = body.stmts diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index cd85c59..8fd7cb6 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -151,6 +151,7 @@ def test_ast_good_catch(): def test_ast_bad_catch(): "Make sure AST can't compile invalid catch" + cant_compile("(catch 22)") # heh cant_compile("(try (catch 1))") cant_compile("(try (catch \"A\"))") cant_compile("(try (catch [1 3]))") @@ -170,6 +171,7 @@ def test_ast_good_except(): def test_ast_bad_except(): "Make sure AST can't compile invalid except" + cant_compile("(except 1)") cant_compile("(try 1 (except 1))") cant_compile("(try 1 (except [1 3]))") cant_compile("(try 1 (except [x [FooBar] BarBar]))") diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index d4ec580..0a30d57 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -665,3 +665,7 @@ (if (if 0 True False) 42 43)))) + +(defn test-try-except-return [] + "NATIVE: test we can return from in a try except" + (assert ((fn [] (try xxx (except [NameError] (+ 1 1)))))) 2)