try: add support for `finally'

This fixes #75

Signed-off-by: Julien Danjou <julien@danjou.info>
This commit is contained in:
Julien Danjou 2013-04-09 21:19:24 +02:00
parent 403be35aa3
commit fdb6bf944c
3 changed files with 98 additions and 31 deletions

View File

@ -235,12 +235,6 @@ class HyASTCompiler(object):
def compile_try_expression(self, expr): def compile_try_expression(self, expr):
expr.pop(0) # try 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: try:
body = expr.pop(0) body = expr.pop(0)
except IndexError: except IndexError:
@ -252,8 +246,42 @@ class HyASTCompiler(object):
expr.start_column) expr.start_column)
orelse = [] orelse = []
if len(expr) == 0: finalbody = []
# (try) or (try body) 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( handlers = [ast.ExceptHandler(
lineno=expr.start_line, lineno=expr.start_line,
col_offset=expr.start_column, col_offset=expr.start_column,
@ -261,35 +289,41 @@ class HyASTCompiler(object):
name=None, name=None,
body=[ast.Pass(lineno=expr.start_line, body=[ast.Pass(lineno=expr.start_line,
col_offset=expr.start_column)])] 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")): if sys.version_info[0] >= 3 and sys.version_info[1] >= 3:
handlers.append(self.compile(e)) # Python 3.3 features a merge of TryExcept+TryFinally into Try.
elif e[0] == HySymbol("else"): return ast.Try(
if orelse: lineno=expr.start_line,
raise TypeError( col_offset=expr.start_column,
"`try' cannot have more than one `else'") body=body,
else: handlers=handlers,
orelse = self._code_branch(self.compile(e[1:]), orelse=orelse,
e.start_line, finalbody=finalbody)
e.start_column)
else:
raise TypeError("Unknown expression in `try'")
if handlers == []: if finalbody:
raise TypeError( if handlers:
"`try' must have at least `except' or `finally'") 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, lineno=expr.start_line,
col_offset=expr.start_column, col_offset=expr.start_column,
body=body,
handlers=handlers, handlers=handlers,
finalbody=[], body=body,
orelse=orelse) orelse=orelse)
@builds("catch") @builds("catch")

View File

@ -123,6 +123,11 @@ def test_ast_good_try():
hy_compile(tokenize("(try 1)")) hy_compile(tokenize("(try 1)"))
hy_compile(tokenize("(try 1 (except) (else 1))")) hy_compile(tokenize("(try 1 (except) (else 1))"))
hy_compile(tokenize("(try 1 (else 1) (except))")) 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(): def test_ast_bad_try():

View File

@ -198,6 +198,34 @@
(setv passed true))) (setv passed true)))
(assert passed)) (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 (try
(raise (KeyError)) (raise (KeyError))
(catch [[IOError]] (assert false)) (catch [[IOError]] (assert false))