Use model patterns for try

This commit is contained in:
Kodi Arfer 2018-04-16 11:59:02 -07:00
parent 79c02514b9
commit 11f1c149ef

View File

@ -6,7 +6,7 @@
from hy.models import (HyObject, HyExpression, HyKeyword, HyInteger, HyComplex, from hy.models import (HyObject, HyExpression, HyKeyword, HyInteger, HyComplex,
HyString, HyBytes, HySymbol, HyFloat, HyList, HySet, HyString, HyBytes, HySymbol, HyFloat, HyList, HySet,
HyDict, HySequence, wrap_value) HyDict, HySequence, wrap_value)
from hy.model_patterns import FORM, SYM, sym, brackets, whole, notpexpr, dolike from hy.model_patterns import FORM, SYM, sym, brackets, whole, notpexpr, dolike, pexpr
from funcparserlib.parser import many, oneplus, maybe, NoParseError from funcparserlib.parser import many, oneplus, maybe, NoParseError
from hy.errors import HyCompileError, HyTypeError from hy.errors import HyCompileError, HyTypeError
@ -802,52 +802,41 @@ class HyASTCompiler(object):
return ret return ret
@builds("try") @special("try",
@checkargs(min=2) [many(notpexpr("except", "else", "finally")),
def compile_try_expression(self, expr): many(pexpr(sym("except"),
expr = copy.deepcopy(expr) brackets() | brackets(FORM) | brackets(SYM, FORM),
expr.pop(0) # try many(FORM))),
maybe(dolike("else")),
# (try something somethingelse…) maybe(dolike("finally"))])
body = [] def compile_try_expression(self, expr, root, body, catchers, orelse, finalbody):
# Check against HyExpression and HySymbol to avoid incorrectly
# matching [except ...] or ("except" ...)
while expr and not (isinstance(expr[0], HyExpression)
and isinstance(expr[0][0], HySymbol)
and expr[0][0] in ("except", "else", "finally")):
body.append(expr.pop(0))
body = self._compile_branch(body) body = self._compile_branch(body)
var = self.get_anon_var() return_var = asty.Name(
name = asty.Name(expr, id=ast_str(var), ctx=ast.Store()) expr, id=ast_str(self.get_anon_var()), ctx=ast.Store())
expr_name = asty.Name(expr, id=ast_str(var), ctx=ast.Load())
returnable = Result(expr=expr_name, temp_variables=[expr_name, name],
contains_yield=body.contains_yield)
handler_results = Result() handler_results = Result()
handlers = [] handlers = []
while expr and expr[0][0] == HySymbol("except"): for catcher in catchers:
handler_results += self._compile_catch_expression(expr.pop(0), handler_results += self._compile_catch_expression(
name) catcher, return_var, *catcher)
handlers.append(handler_results.stmts.pop()) handlers.append(handler_results.stmts.pop())
if orelse is None:
orelse = [] orelse = []
if expr and expr[0][0] == HySymbol("else"): else:
orelse = self._compile_branch(expr.pop(0)[1:]) orelse = self._compile_branch(orelse)
orelse += asty.Assign(expr, targets=[name], orelse += asty.Assign(expr, targets=[return_var],
value=orelse.force_expr) value=orelse.force_expr)
orelse += orelse.expr_as_stmt() orelse += orelse.expr_as_stmt()
orelse = orelse.stmts orelse = orelse.stmts
if finalbody is None:
finalbody = [] finalbody = []
if expr and expr[0][0] == HySymbol("finally"): else:
finalbody = self._compile_branch(expr.pop(0)[1:]) finalbody = self._compile_branch(finalbody)
finalbody += finalbody.expr_as_stmt() finalbody += finalbody.expr_as_stmt()
finalbody = finalbody.stmts finalbody = finalbody.stmts
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! # Using (else) without (except) is verboten!
if orelse and not handlers: if orelse and not handlers:
@ -860,50 +849,45 @@ class HyASTCompiler(object):
expr, expr,
"`try' must have an `except' or `finally' clause") "`try' must have an `except' or `finally' clause")
ret = handler_results returnable = Result(
expr=asty.Name(expr, id=return_var.id, ctx=ast.Load()),
temp_variables=[return_var],
contains_yield=body.contains_yield)
body += body.expr_as_stmt() if orelse else asty.Assign( body += body.expr_as_stmt() if orelse else asty.Assign(
expr, targets=[name], value=body.force_expr) expr, targets=[return_var], value=body.force_expr)
body = body.stmts or [asty.Pass(expr)] body = body.stmts or [asty.Pass(expr)]
if PY3: if PY3:
# Python 3.3 features a merge of TryExcept+TryFinally into Try. # Python 3.3 features a merge of TryExcept+TryFinally into Try.
return ret + asty.Try( x = asty.Try(
expr, expr,
body=body, body=body,
handlers=handlers, handlers=handlers,
orelse=orelse, orelse=orelse,
finalbody=finalbody) + returnable finalbody=finalbody)
elif finalbody and handlers:
if finalbody: x = asty.TryFinally(
if handlers:
return ret + asty.TryFinally(
expr, expr,
body=[asty.TryExcept( body=[asty.TryExcept(
expr, expr,
handlers=handlers,
body=body, body=body,
handlers=handlers,
orelse=orelse)], orelse=orelse)],
finalbody=finalbody) + returnable finalbody=finalbody)
elif finalbody:
return ret + asty.TryFinally( x = asty.TryFinally(
expr, body=body, finalbody=finalbody) + returnable expr, body=body, finalbody=finalbody)
else:
return ret + asty.TryExcept( x = asty.TryExcept(
expr, handlers=handlers, body=body, orelse=orelse) + returnable expr, body=body, handlers=handlers, orelse=orelse)
return handler_results + x + returnable
@builds("except") @builds("except")
def magic_internal_form(self, expr): def magic_internal_form(self, expr):
raise HyTypeError(expr, raise HyTypeError(expr,
"Error: `%s' can't be used like that." % (expr[0])) "Error: `%s' can't be used like that." % (expr[0]))
def _compile_catch_expression(self, expr, var): def _compile_catch_expression(self, expr, var, exceptions, body):
catch = expr.pop(0) # catch
if not expr:
raise HyTypeError(expr, "`%s' missing exceptions list" % catch)
exceptions = expr.pop(0)
# exceptions catch should be either: # exceptions catch should be either:
# [[list of exceptions]] # [[list of exceptions]]
# or # or
@ -915,61 +899,34 @@ class HyASTCompiler(object):
# or # or
# [] # []
if not isinstance(exceptions, HyList):
raise HyTypeError(exceptions,
"`%s' exceptions list is not a list" % catch)
if len(exceptions) > 2:
raise HyTypeError(exceptions,
"`%s' exceptions list is too long" % catch)
# [variable [list of exceptions]] # [variable [list of exceptions]]
# let's pop variable and use it as name # let's pop variable and use it as name
name = None name = None
if len(exceptions) == 2: if len(exceptions) == 2:
name = exceptions.pop(0) name = exceptions[0]
if not isinstance(name, HySymbol): name = (ast_str(name) if PY3
raise HyTypeError( else self._storeize(name, self.compile(name)))
exceptions,
"Exception storage target name must be a symbol.")
if PY3: exceptions_list = exceptions[-1] if exceptions else HyList()
# Python3 features a change where the Exception handler if isinstance(exceptions_list, HyList):
# moved the name from a Name() to a pure Python String type.
#
# We'll just make sure it's a pure "string", and let it work
# it's magic.
name = ast_str(name)
else:
# Python2 requires an ast.Name, set to ctx Store.
name = self._storeize(name, self.compile(name))
exceptions_list = exceptions.pop(0) if exceptions else []
if isinstance(exceptions_list, list):
if len(exceptions_list): if len(exceptions_list):
# [FooBar BarFoo] → catch Foobar and BarFoo exceptions # [FooBar BarFoo] → catch Foobar and BarFoo exceptions
elts, _type, _ = self._compile_collect(exceptions_list) elts, _type, _ = self._compile_collect(exceptions_list)
_type += asty.Tuple(expr, elts=elts, ctx=ast.Load()) _type += asty.Tuple(exceptions_list, elts=elts, ctx=ast.Load())
else: else:
# [] → all exceptions caught # [] → all exceptions caught
_type = Result() _type = Result()
elif isinstance(exceptions_list, HySymbol):
_type = self.compile(exceptions_list)
else: else:
raise HyTypeError(exceptions, _type = self.compile(exceptions_list)
"`%s' needs a valid exception list" % catch)
body = self._compile_branch(expr) body = self._compile_branch(body)
body += asty.Assign(expr, targets=[var], value=body.force_expr) body += asty.Assign(expr, targets=[var], value=body.force_expr)
body += body.expr_as_stmt() body += body.expr_as_stmt()
body = body.stmts
if not body:
body = [asty.Pass(expr)]
# use _type.expr to get a literal `None` # use _type.expr to get a literal `None`
return _type + asty.ExceptHandler( return _type + asty.ExceptHandler(
expr, type=_type.expr, name=name, body=body) expr, type=_type.expr, name=name,
body=body.stmts or [asty.Pass(expr)])
@special("if*", [FORM, FORM, maybe(FORM)]) @special("if*", [FORM, FORM, maybe(FORM)])
def compile_if(self, expr, _, cond, body, orel_expr): def compile_if(self, expr, _, cond, body, orel_expr):