diff --git a/hy/compiler.py b/hy/compiler.py index 8ddd993..52806a8 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -24,6 +24,7 @@ import ast import sys import keyword import copy +import inspect from collections import defaultdict @@ -90,28 +91,20 @@ def ast_str(foobar): return "hy_%s" % (str(foobar).replace("-", "_")) -def builds(_type): - - unpythonic_chars = ["-"] - really_ok = ["-"] - if any(x in unpythonic_chars for x in str_type(_type)): - if _type not in really_ok: - raise TypeError("Dear Hypster: `build' needs to be *post* " - "translated strings... `%s' sucks." % (_type)) +def builds(*types, **kwargs): + # A decorator that adds the decorated method to _compile_table for + # compiling `types`, but only if kwargs['iff'] (if provided) is + # true. + if not kwargs.get('iff', True): + return lambda fn: fn def _dec(fn): - _compile_table[_type] = fn + for t in types: + _compile_table[t] = fn return fn return _dec -def builds_if(_type, condition): - if condition: - return builds(_type) - else: - return lambda fn: fn - - def spoof_positions(obj): if not isinstance(obj, HyObject) or isinstance(obj, HyCons): return @@ -125,6 +118,21 @@ def spoof_positions(obj): spoof_positions(x) +# Provide asty.Foo(x, ...) as shorthand for +# ast.Foo(..., lineno=x.start_line, col_offset=x.start_column) or +# ast.Foo(..., lineno=x.lineno, col_offset=x.col_offset) +class Asty(object): + def __getattr__(self, name): + setattr(Asty, name, lambda self, x, **kwargs: getattr(ast, name)( + lineno=getattr( + x, 'start_line', getattr(x, 'lineno', None)), + col_offset=getattr( + x, 'start_column', getattr(x, 'col_offset', None)), + **kwargs)) + return getattr(self, name) +asty = Asty() + + class Result(object): """ Smart representation of the result of a hy->AST compilation @@ -209,7 +217,6 @@ class Result(object): col_offset = self.stmts[-1].col_offset return ast.Name(id=ast_str("None"), - arg=ast_str("None"), ctx=ast.Load(), lineno=lineno, col_offset=col_offset) @@ -227,9 +234,7 @@ class Result(object): If there is no expression context, return an empty result. """ if self.expr and not (isinstance(self.expr, ast.Name) and self.stmts): - return Result() + ast.Expr(lineno=self.expr.lineno, - col_offset=self.expr.col_offset, - value=self.expr) + return Result() + asty.Expr(self.expr, value=self.expr) return Result() def rename(self, new_name): @@ -375,7 +380,6 @@ class HyASTCompiler(object): def __init__(self, module_name): self.allow_builtins = module_name.startswith("hy.core") - self.anon_fn_count = 0 self.anon_var_count = 0 self.imports = defaultdict(set) self.module_name = module_name @@ -388,10 +392,6 @@ class HyASTCompiler(object): self.anon_var_count += 1 return "_hy_anon_var_%s" % self.anon_var_count - def get_anon_fn(self): - self.anon_fn_count += 1 - return "_hy_anon_fn_%d" % self.anon_fn_count - def update_imports(self, result): """Retrieve the imports from the result object""" for mod in result.imports: @@ -424,7 +424,13 @@ class HyASTCompiler(object): def compile_atom(self, atom_type, atom): if atom_type in _compile_table: - ret = _compile_table[atom_type](self, atom) + # _compile_table[atom_type] is a method for compiling this + # type of atom, so call it. If it has an extra parameter, + # pass in `atom_type`. + arity = len(inspect.getargspec(_compile_table[atom_type])[0]) + ret = (_compile_table[atom_type](self, atom, atom_type) + if arity == 3 + else _compile_table[atom_type](self, atom)) if not isinstance(ret, Result): ret = Result() + ret return ret @@ -485,11 +491,8 @@ class HyASTCompiler(object): compiled_exprs.append(None) compiled_exprs.append(ret.force_expr) elif with_kwargs: - keywords.append(ast.keyword( - arg=None, - value=ret.force_expr, - lineno=expr.start_line, - col_offset=expr.start_column)) + keywords.append(asty.keyword( + expr, arg=None, value=ret.force_expr)) elif oldpy_unpack: if oldpy_kwargs: raise HyTypeError(expr, "Pythons < 3.5 allow only one " @@ -512,10 +515,8 @@ class HyASTCompiler(object): if "-" in keyword and keyword != "-": keyword = keyword.replace("-", "_") - keywords.append(ast.keyword(arg=keyword, - value=compiled_value.force_expr, - lineno=expr.start_line, - col_offset=expr.start_column)) + keywords.append(asty.keyword( + expr, arg=keyword, value=compiled_value.force_expr)) else: ret += self.compile(expr) @@ -651,7 +652,7 @@ class HyASTCompiler(object): new_elts.append(self._storeize(expr, x, func)) new_name = typ(elts=new_elts) elif isinstance(name, ast.Name): - new_name = ast.Name(id=name.id, arg=name.arg) + new_name = ast.Name(id=name.id) elif isinstance(name, ast.Subscript): new_name = ast.Subscript(value=name.value, slice=name.slice) elif isinstance(name, ast.Attribute): @@ -754,8 +755,7 @@ class HyASTCompiler(object): return imports, HyExpression([HySymbol(name), form]).replace(form), False - @builds("quote") - @builds("quasiquote") + @builds("quote", "quasiquote") @checkargs(exact=1) def compile_quote(self, entries): if entries[0] == "quote": @@ -768,8 +768,7 @@ class HyASTCompiler(object): ret.add_imports("hy", imports) return ret - @builds("unquote") - @builds("unquote_splicing") + @builds("unquote", "unquote_splicing") def compile_unquote(self, expr): raise HyTypeError(expr, "`%s' can't be used at the top-level" % expr[0]) @@ -780,10 +779,7 @@ class HyASTCompiler(object): if not PY3: raise HyTypeError(expr, "`unpack-iterable` isn't allowed here") ret = self.compile(expr[1]) - ret += ast.Starred(value=ret.force_expr, - lineno=expr.start_line, - col_offset=expr.start_column, - ctx=ast.Load()) + ret += asty.Starred(expr, value=ret.force_expr, ctx=ast.Load()) return ret @builds("unpack_mapping") @@ -791,15 +787,14 @@ class HyASTCompiler(object): def compile_unpack_mapping(self, expr): raise HyTypeError(expr, "`unpack-mapping` isn't allowed here") - @builds_if("exec*", not PY3) + @builds("exec*", iff=(not PY3)) # Under Python 3, `exec` is a function rather than a statement type, so Hy # doesn't need a special form for it. @checkargs(min=1, max=3) def compile_exec(self, expr): expr.pop(0) - return ast.Exec( - lineno=expr.start_line, - col_offset=expr.start_column, + return asty.Exec( + expr, body=self.compile(expr.pop(0)).force_expr, globals=self.compile(expr.pop(0)).force_expr if expr else None, locals=self.compile(expr.pop(0)).force_expr if expr else None) @@ -827,14 +822,9 @@ class HyASTCompiler(object): cause = cause.expr # Use ret.expr to get a literal `None` - ret += ast.Raise( - lineno=expr.start_line, - col_offset=expr.start_column, - type=ret.expr, - exc=ret.expr, - inst=None, - tback=None, - cause=cause) + ret += asty.Raise( + expr, type=ret.expr, exc=ret.expr, + inst=None, tback=None, cause=cause) return ret @@ -847,15 +837,8 @@ class HyASTCompiler(object): body = self.compile(expr.pop(0) if expr else []) 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) + name = asty.Name(expr, id=ast_str(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) @@ -871,10 +854,8 @@ class HyASTCompiler(object): orelse = [] if expr and expr[0][0] == HySymbol("else"): orelse = self._compile_branch(expr.pop(0)[1:]) - orelse += ast.Assign(targets=[name], - value=orelse.force_expr, - lineno=expr.start_line, - col_offset=expr.start_column) + orelse += asty.Assign(expr, targets=[name], + value=orelse.force_expr) orelse += orelse.expr_as_stmt() orelse = orelse.stmts finalbody = [] @@ -901,20 +882,15 @@ class HyASTCompiler(object): ret = handler_results - body += body.expr_as_stmt() if orelse else ast.Assign( - targets=[name], - value=body.force_expr, - lineno=expr.start_line, - col_offset=expr.start_column) + body += body.expr_as_stmt() if orelse else asty.Assign( + expr, targets=[name], value=body.force_expr) - body = body.stmts or [ast.Pass(lineno=expr.start_line, - col_offset=expr.start_column)] + body = body.stmts or [asty.Pass(expr)] if PY3: # Python 3.3 features a merge of TryExcept+TryFinally into Try. - return ret + ast.Try( - lineno=expr.start_line, - col_offset=expr.start_column, + return ret + asty.Try( + expr, body=body, handlers=handlers, orelse=orelse, @@ -922,29 +898,20 @@ class HyASTCompiler(object): if finalbody: if handlers: - return ret + ast.TryFinally( - lineno=expr.start_line, - col_offset=expr.start_column, - body=[ast.TryExcept( - lineno=expr.start_line, - col_offset=expr.start_column, + return ret + asty.TryFinally( + expr, + body=[asty.TryExcept( + expr, handlers=handlers, body=body, orelse=orelse)], finalbody=finalbody) + returnable - return ret + ast.TryFinally( - lineno=expr.start_line, - col_offset=expr.start_column, - body=body, - finalbody=finalbody) + returnable + return ret + asty.TryFinally( + expr, body=body, finalbody=finalbody) + returnable - return ret + ast.TryExcept( - lineno=expr.start_line, - col_offset=expr.start_column, - handlers=handlers, - body=body, - orelse=orelse) + returnable + return ret + asty.TryExcept( + expr, handlers=handlers, body=body, orelse=orelse) + returnable @builds("except") def magic_internal_form(self, expr): @@ -1001,10 +968,7 @@ class HyASTCompiler(object): if len(exceptions_list): # [FooBar BarFoo] → catch Foobar and BarFoo exceptions elts, _type, _ = self._compile_collect(exceptions_list) - _type += ast.Tuple(elts=elts, - lineno=expr.start_line, - col_offset=expr.start_column, - ctx=ast.Load()) + _type += asty.Tuple(expr, elts=elts, ctx=ast.Load()) else: # [] → all exceptions caught _type = Result() @@ -1015,24 +979,16 @@ class HyASTCompiler(object): "`%s' needs a valid exception list" % catch) body = self._compile_branch(expr) - body += ast.Assign(targets=[var], - value=body.force_expr, - lineno=expr.start_line, - col_offset=expr.start_column) + body += asty.Assign(expr, targets=[var], value=body.force_expr) body += body.expr_as_stmt() body = body.stmts if not body: - body = [ast.Pass(lineno=expr.start_line, - col_offset=expr.start_column)] + body = [asty.Pass(expr)] # use _type.expr to get a literal `None` - return _type + ast.ExceptHandler( - lineno=expr.start_line, - col_offset=expr.start_column, - type=_type.expr, - name=name, - body=body) + return _type + asty.ExceptHandler( + expr, type=_type.expr, name=name, body=body) @builds("if*") @checkargs(min=2, max=3) @@ -1062,16 +1018,13 @@ class HyASTCompiler(object): branch = orel if branch is not None: if self.temp_if and branch.stmts: - name = ast.Name(id=ast_str(self.temp_if), - arg=ast_str(self.temp_if), - ctx=ast.Store(), - lineno=expression.start_line, - col_offset=expression.start_column) + name = asty.Name(expression, + id=ast_str(self.temp_if), + ctx=ast.Store()) - branch += ast.Assign(targets=[name], - value=body.force_expr, - lineno=expression.start_line, - col_offset=expression.start_column) + branch += asty.Assign(expression, + targets=[name], + value=body.force_expr) return branch @@ -1082,24 +1035,21 @@ class HyASTCompiler(object): # We have statements in our bodies # Get a temporary variable for the result storage var = self.temp_if or self.get_anon_var() - name = ast.Name(id=ast_str(var), arg=ast_str(var), - ctx=ast.Store(), - lineno=expression.start_line, - col_offset=expression.start_column) + name = asty.Name(expression, + id=ast_str(var), + ctx=ast.Store()) # Store the result of the body - body += ast.Assign(targets=[name], - value=body.force_expr, - lineno=expression.start_line, - col_offset=expression.start_column) + body += asty.Assign(expression, + targets=[name], + value=body.force_expr) # and of the else clause if not nested or not orel.stmts or (not root and var != self.temp_if): - orel += ast.Assign(targets=[name], - value=orel.force_expr, - lineno=expression.start_line, - col_offset=expression.start_column) + orel += asty.Assign(expression, + targets=[name], + value=orel.force_expr) # Then build the if ret += ast.If(test=ret.force_expr, @@ -1109,10 +1059,7 @@ class HyASTCompiler(object): col_offset=expression.start_column) # And make our expression context our temp variable - expr_name = ast.Name(id=ast_str(var), arg=ast_str(var), - ctx=ast.Load(), - lineno=expression.start_line, - col_offset=expression.start_column) + expr_name = asty.Name(expression, id=ast_str(var), ctx=ast.Load()) ret += Result(expr=expr_name, temp_variables=[expr_name, name]) else: @@ -1131,117 +1078,57 @@ class HyASTCompiler(object): @builds("break") @checkargs(0) def compile_break_expression(self, expr): - ret = ast.Break(lineno=expr.start_line, - col_offset=expr.start_column) - - return ret + return asty.Break(expr) @builds("continue") @checkargs(0) def compile_continue_expression(self, expr): - ret = ast.Continue(lineno=expr.start_line, - col_offset=expr.start_column) - - return ret + return asty.Continue(expr) @builds("assert") @checkargs(min=1, max=2) def compile_assert_expression(self, expr): expr.pop(0) # assert - e = expr.pop(0) - if len(expr) == 1: + ret = self.compile(expr.pop(0)) + e = ret.force_expr + msg = None + if expr: msg = self.compile(expr.pop(0)).force_expr - else: - msg = None - ret = self.compile(e) - ret += ast.Assert(test=ret.force_expr, - msg=msg, - lineno=e.start_line, - col_offset=e.start_column) - - return ret + return ret + asty.Assert(expr, test=e, msg=msg) @builds("global") + @builds("nonlocal", iff=PY3) @checkargs(min=1) - def compile_global_expression(self, expr): - expr.pop(0) # global + def compile_global_or_nonlocal(self, expr): + form = expr.pop(0) names = [] while len(expr) > 0: identifier = expr.pop(0) name = ast_str(identifier) names.append(name) if not isinstance(identifier, HySymbol): - raise HyTypeError(identifier, "(global) arguments must " - " be Symbols") - - return ast.Global(names=names, - lineno=expr.start_line, - col_offset=expr.start_column) - - @builds("nonlocal") - @checkargs(min=1) - def compile_nonlocal_expression(self, expr): - if not PY3: - raise HyCompileError( - "nonlocal only supported in python 3!") - - expr.pop(0) # nonlocal - names = [] - while len(expr) > 0: - identifier = expr.pop(0) - name = ast_str(identifier) - names.append(name) - if not isinstance(identifier, HySymbol): - raise HyTypeError(identifier, "(nonlocal) arguments must " - "be Symbols.") - - return ast.Nonlocal(names=names, - lineno=expr.start_line, - col_offset=expr.start_column) + raise HyTypeError( + identifier, + "({}) arguments must be Symbols".format(form)) + node = asty.Global if form == "global" else asty.Nonlocal + return node(expr, names=names) @builds("yield") + @builds("yield_from", iff=PY3) @checkargs(max=1) def compile_yield_expression(self, expr): - expr.pop(0) ret = Result(contains_yield=(not PY3)) - - value = None - if expr != []: - ret += self.compile(expr.pop(0)) - value = ret.force_expr - - ret += ast.Yield( - value=value, - lineno=expr.start_line, - col_offset=expr.start_column) - - return ret - - @builds_if("yield_from", PY3) - @checkargs(max=1) - def compile_yield_from_expression(self, expr): - expr.pop(0) - ret = Result(contains_yield=True) - - value = None - if expr != []: - ret += self.compile(expr.pop(0)) - value = ret.force_expr - - ret += ast.YieldFrom( - value=value, - lineno=expr.start_line, - col_offset=expr.start_column) - - return ret + if len(expr) > 1: + ret += self.compile(expr[1]) + node = asty.Yield if expr[0] == "yield" else asty.YieldFrom + return ret + node(expr, value=ret.force_expr) @builds("import") def compile_import_expression(self, expr): - def _compile_import(expr, module, names=None, importer=ast.Import): + def _compile_import(expr, module, names=None, importer=asty.Import): if not names: names = [ast.alias(name=ast_str(module), asname=None)] - ret = importer(lineno=expr.start_line, - col_offset=expr.start_column, + ret = importer(expr, module=ast_str(module), names=names, level=0) @@ -1291,7 +1178,7 @@ class HyASTCompiler(object): asname=alias)) rimports += _compile_import(expr, module, - names, ast.ImportFrom) + names, asty.ImportFrom) continue raise HyTypeError( @@ -1306,21 +1193,17 @@ class HyASTCompiler(object): def compile_index_expression(self, expr): expr.pop(0) # index - val = self.compile(expr.pop(0)) - slices, ret, _ = self._compile_collect(expr) + indices, ret, _ = self._compile_collect(expr[1:]) + ret += self.compile(expr[0]) - if val.stmts: - ret += val - - for sli in slices: - val = Result() + ast.Subscript( - lineno=expr.start_line, - col_offset=expr.start_column, - value=val.force_expr, - slice=ast.Index(value=sli), + for ix in indices: + ret += asty.Subscript( + expr, + value=ret.force_expr, + slice=ast.Index(value=ix), ctx=ast.Load()) - return ret + val + return ret @builds(".") @checkargs(min=1) @@ -1331,24 +1214,20 @@ class HyASTCompiler(object): for attr in expr: if isinstance(attr, HySymbol): - ret += ast.Attribute(lineno=attr.start_line, - col_offset=attr.start_column, - value=ret.force_expr, - attr=ast_str(attr), - ctx=ast.Load()) + ret += asty.Attribute(attr, + value=ret.force_expr, + attr=ast_str(attr), + ctx=ast.Load()) elif type(attr) == HyList: if len(attr) != 1: raise HyTypeError( attr, "The attribute access DSL only accepts HySymbols " "and one-item lists, got {0}-item list instead".format( - len(attr), - ), - ) - compiled_attr = self.compile(attr.pop(0)) - ret = compiled_attr + ret + ast.Subscript( - lineno=attr.start_line, - col_offset=attr.start_column, + len(attr))) + compiled_attr = self.compile(attr[0]) + ret = compiled_attr + ret + asty.Subscript( + attr, value=ret.force_expr, slice=ast.Index(value=compiled_attr.force_expr), ctx=ast.Load()) @@ -1357,9 +1236,7 @@ class HyASTCompiler(object): attr, "The attribute access DSL only accepts HySymbols " "and one-item lists, got {0} instead".format( - type(attr).__name__, - ), - ) + type(attr).__name__)) return ret @@ -1367,11 +1244,7 @@ class HyASTCompiler(object): def compile_del_expression(self, expr): root = expr.pop(0) if not expr: - result = Result() - result += ast.Name(id='None', ctx=ast.Load(), - lineno=root.start_line, - col_offset=root.start_column) - return result + return asty.Pass(root) del_targets = [] ret = Result() @@ -1381,44 +1254,28 @@ class HyASTCompiler(object): del_targets.append(self._storeize(target, compiled_target, ast.Del)) - return ret + ast.Delete( - lineno=expr.start_line, - col_offset=expr.start_column, - targets=del_targets) + return ret + asty.Delete(expr, targets=del_targets) @builds("cut") @checkargs(min=1, max=4) def compile_cut_expression(self, expr): - expr.pop(0) # index - val = self.compile(expr.pop(0)) # target + ret = Result() + nodes = [None] * 4 + for i, e in enumerate(expr[1:]): + ret += self.compile(e) + nodes[i] = ret.force_expr - low = Result() - if expr != []: - low = self.compile(expr.pop(0)) - - high = Result() - if expr != []: - high = self.compile(expr.pop(0)) - - step = Result() - if expr != []: - step = self.compile(expr.pop(0)) - - # use low.expr, high.expr and step.expr to use a literal `None`. - return val + low + high + step + ast.Subscript( - lineno=expr.start_line, - col_offset=expr.start_column, - value=val.force_expr, - slice=ast.Slice(lower=low.expr, - upper=high.expr, - step=step.expr), + return ret + asty.Subscript( + expr, + value=nodes[0], + slice=ast.Slice(lower=nodes[1], upper=nodes[2], step=nodes[3]), ctx=ast.Load()) @builds("with_decorator") @checkargs(min=1) def compile_decorate_expression(self, expr): expr.pop(0) # with-decorator - fn = self.compile(expr.pop(-1)) + fn = self.compile(expr.pop()) if not fn.stmts or not isinstance(fn.stmts[-1], (ast.FunctionDef, ast.ClassDef)): raise HyTypeError(expr, "Decorated a non-function") @@ -1448,42 +1305,27 @@ class HyASTCompiler(object): # Store the result of the body in a tempvar 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) - body += ast.Assign(targets=[name], - value=body.force_expr, - lineno=expr.start_line, - col_offset=expr.start_column) + name = asty.Name(expr, id=ast_str(var), ctx=ast.Store()) + body += asty.Assign(expr, targets=[name], value=body.force_expr) # Initialize the tempvar to None in case the `with` exits # early with an exception. - initial_assign = ast.Assign(targets=[name], - value=ast.Name( - id=ast_str("None"), - ctx=ast.Load(), - lineno=expr.start_line, - col_offset=expr.start_column), - lineno=expr.start_line, - col_offset=expr.start_column) + initial_assign = asty.Assign( + expr, targets=[name], value=asty.Name( + expr, id=ast_str("None"), ctx=ast.Load())) - the_with = ast.With(context_expr=ctx.force_expr, - lineno=expr.start_line, - col_offset=expr.start_column, - optional_vars=thing, - body=body.stmts) + the_with = asty.With(expr, + context_expr=ctx.force_expr, + optional_vars=thing, + body=body.stmts) if PY3: the_with.items = [ast.withitem(context_expr=ctx.force_expr, optional_vars=thing)] - ret = Result(stmts = [initial_assign]) + ctx + the_with + ret = Result(stmts=[initial_assign]) + ctx + the_with ret.contains_yield = ret.contains_yield or body.contains_yield # And make our expression context our temp variable - expr_name = ast.Name(id=ast_str(var), arg=ast_str(var), - ctx=ast.Load(), - lineno=expr.start_line, - col_offset=expr.start_column) + expr_name = asty.Name(expr, id=ast_str(var), ctx=ast.Load()) ret += Result(expr=expr_name) # We don't give the Result any temp_vars because we don't want @@ -1494,13 +1336,8 @@ class HyASTCompiler(object): @builds(",") def compile_tuple(self, expr): - expr.pop(0) - elts, ret, _ = self._compile_collect(expr) - ret += ast.Tuple(elts=elts, - lineno=expr.start_line, - col_offset=expr.start_column, - ctx=ast.Load()) - return ret + elts, ret, _ = self._compile_collect(expr[1:]) + return ret + asty.Tuple(expr, elts=elts, ctx=ast.Load()) def _compile_generator_iterables(self, trailers): """Helper to compile the "trailing" parts of comprehensions: @@ -1508,7 +1345,7 @@ class HyASTCompiler(object): generators = trailers.pop(0) - cond = self.compile(trailers.pop(0)) if trailers != [] else Result() + cond = self.compile(trailers.pop(0)) if trailers else Result() gen_it = iter(generators) paired_gens = zip(gen_it, gen_it) @@ -1516,11 +1353,9 @@ class HyASTCompiler(object): gen_res = Result() gen = [] for target, iterable in paired_gens: - comp_target = self.compile(target) - target = self._storeize(target, comp_target) gen_res += self.compile(iterable) gen.append(ast.comprehension( - target=target, + target=self._storeize(target, self.compile(target)), iter=gen_res.force_expr, ifs=[], is_async=False)) @@ -1530,11 +1365,11 @@ class HyASTCompiler(object): return gen_res + cond, gen - @builds("list_comp") + @builds("list_comp", "set_comp", "genexpr") @checkargs(min=2, max=3) - def compile_list_comprehension(self, expr): + def compile_comprehension(self, expr): # (list-comp expr (target iter) cond?) - expr.pop(0) + form = expr.pop(0) expression = expr.pop(0) gen_gen = expr[0] @@ -1546,63 +1381,30 @@ class HyASTCompiler(object): if len(gen) == 0: raise HyTypeError(gen_gen, "Generator expression cannot be empty.") - compiled_expression = self.compile(expression) - ret = compiled_expression + gen_res - ret += ast.ListComp( - lineno=expr.start_line, - col_offset=expr.start_column, - elt=compiled_expression.force_expr, - generators=gen) - - return ret - - @builds("set_comp") - @checkargs(min=2, max=3) - def compile_set_comprehension(self, expr): - ret = self.compile_list_comprehension(expr) - expr = ret.expr - ret.expr = ast.SetComp( - lineno=expr.lineno, - col_offset=expr.col_offset, - elt=expr.elt, - generators=expr.generators) - - return ret + ret = self.compile(expression) + node_class = ( + asty.ListComp if form == "list_comp" else + asty.SetComp if form == "set_comp" else + asty.GeneratorExp) + return ret + gen_res + node_class( + expr, elt=ret.force_expr, generators=gen) @builds("dict_comp") @checkargs(min=3, max=4) def compile_dict_comprehension(self, expr): expr.pop(0) # dict-comp - key = expr.pop(0) - value = expr.pop(0) + key = self.compile(expr.pop(0)) + value = self.compile(expr.pop(0)) gen_res, gen = self._compile_generator_iterables(expr) - compiled_key = self.compile(key) - compiled_value = self.compile(value) - ret = compiled_key + compiled_value + gen_res - ret += ast.DictComp( - lineno=expr.start_line, - col_offset=expr.start_column, - key=compiled_key.force_expr, - value=compiled_value.force_expr, + return key + value + gen_res + asty.DictComp( + expr, + key=key.force_expr, + value=value.force_expr, generators=gen) - return ret - - @builds("genexpr") - def compile_genexpr(self, expr): - ret = self.compile_list_comprehension(expr) - expr = ret.expr - ret.expr = ast.GeneratorExp( - lineno=expr.lineno, - col_offset=expr.col_offset, - elt=expr.elt, - generators=expr.generators) - return ret - - @builds("not") - @builds("~") + @builds("not", "~") @checkargs(1) def compile_unary_operator(self, expression): ops = {"not": ast.Not, @@ -1610,10 +1412,9 @@ class HyASTCompiler(object): operator = expression.pop(0) operand = self.compile(expression.pop(0)) - operand += ast.UnaryOp(op=ops[operator](), - operand=operand.expr, - lineno=operator.start_line, - col_offset=operator.start_column) + operand += asty.UnaryOp( + expression, op=ops[operator](), operand=operand.force_expr) + return operand @builds("require") @@ -1664,51 +1465,33 @@ class HyASTCompiler(object): raise HyTypeError(entry, "unrecognized (require) syntax") return Result() - @builds("and") - @builds("or") + @builds("and", "or") def compile_logical_or_and_and_operator(self, expression): ops = {"and": (ast.And, "True"), "or": (ast.Or, "None")} operator = expression.pop(0) opnode, default = ops[operator] - root_line, root_column = operator.start_line, operator.start_column if len(expression) == 0: - return ast.Name(id=default, - ctx=ast.Load(), - lineno=root_line, - col_offset=root_column) + return asty.Name(operator, id=default, ctx=ast.Load()) elif len(expression) == 1: return self.compile(expression[0]) ret = Result() values = list(map(self.compile, expression)) - has_stmt = any(value.stmts for value in values) - if has_stmt: + if any(value.stmts for value in values): # Compile it to an if...else sequence var = self.get_anon_var() - name = ast.Name(id=var, - ctx=ast.Store(), - lineno=root_line, - col_offset=root_column) - expr_name = ast.Name(id=var, - ctx=ast.Load(), - lineno=root_line, - col_offset=root_column) + name = asty.Name(operator, id=var, ctx=ast.Store()) + expr_name = asty.Name(operator, id=var, ctx=ast.Load()) temp_variables = [name, expr_name] def make_assign(value, node=None): - if node is None: - line, column = root_line, root_column - else: - line, column = node.lineno, node.col_offset - positioned_name = ast.Name(id=var, ctx=ast.Store(), - lineno=line, col_offset=column) + positioned_name = asty.Name( + node or operator, id=var, ctx=ast.Store()) temp_variables.append(positioned_name) - return ast.Assign(targets=[positioned_name], - value=value, - lineno=line, - col_offset=column) - root = [] - current = root + return asty.Assign( + node or operator, targets=[positioned_name], value=value) + + current = root = [] for i, value in enumerate(values): if value.stmts: node = value.stmts[0] @@ -1722,23 +1505,15 @@ class HyASTCompiler(object): if operator == "and": cond = expr_name elif operator == "or": - cond = ast.UnaryOp(op=ast.Not(), - operand=expr_name, - lineno=node.lineno, - col_offset=node.col_offset) - current.append(ast.If(test=cond, - body=[], - lineno=node.lineno, - col_offset=node.col_offset, - orelse=[])) + cond = asty.UnaryOp(node, op=ast.Not(), operand=expr_name) + current.append(asty.If(node, test=cond, body=[], orelse=[])) current = current[-1].body ret = sum(root, ret) ret += Result(expr=expr_name, temp_variables=temp_variables) else: - ret += ast.BoolOp(op=opnode(), - lineno=root_line, - col_offset=root_column, - values=[value.force_expr for value in values]) + ret += asty.BoolOp(operator, + op=opnode(), + values=[value.force_expr for value in values]) return ret def _compile_compare_op_expression(self, expression): @@ -1749,46 +1524,32 @@ class HyASTCompiler(object): "in": ast.In, "not_in": ast.NotIn} inv = expression.pop(0) - op = ops[inv] - ops = [op() for x in range(1, len(expression))] + ops = [ops[inv]() for _ in range(len(expression) - 1)] e = expression[0] exprs, ret, _ = self._compile_collect(expression) - return ret + ast.Compare(left=exprs[0], - ops=ops, - comparators=exprs[1:], - lineno=e.start_line, - col_offset=e.start_column) + return ret + asty.Compare( + e, left=exprs[0], ops=ops, comparators=exprs[1:]) - @builds("=") - @builds("is") - @builds("<") - @builds("<=") - @builds(">") - @builds(">=") + @builds("=", "is", "<", "<=", ">", ">=") @checkargs(min=1) def compile_compare_op_expression(self, expression): if len(expression) == 2: - return ast.Name(id="True", - ctx=ast.Load(), - lineno=expression.start_line, - col_offset=expression.start_column) + return asty.Name(expression, id="True", ctx=ast.Load()) return self._compile_compare_op_expression(expression) - @builds("!=") - @builds("is_not") + @builds("!=", "is_not") @checkargs(min=2) def compile_compare_op_expression_coll(self, expression): return self._compile_compare_op_expression(expression) - @builds("in") - @builds("not_in") + @builds("in", "not_in") @checkargs(2) def compile_compare_op_expression_binary(self, expression): return self._compile_compare_op_expression(expression) - def _compile_maths_expression(self, expression): + def _compile_maths_expression(self, expr): ops = {"+": ast.Add, "/": ast.Div, "//": ast.FloorDiv, @@ -1804,49 +1565,35 @@ class HyASTCompiler(object): if PY35: ops.update({"@": ast.MatMult}) - op = ops[expression.pop(0)] - right_associative = op == ast.Pow + op = ops[expr.pop(0)] + right_associative = op is ast.Pow - lineno, col_offset = expression.start_line, expression.start_column - if right_associative: - expression = expression[::-1] - ret = self.compile(expression.pop(0)) - for child in expression: + ret = self.compile(expr.pop(-1 if right_associative else 0)) + for child in expr[:: -1 if right_associative else 1]: left_expr = ret.force_expr ret += self.compile(child) right_expr = ret.force_expr if right_associative: left_expr, right_expr = right_expr, left_expr - ret += ast.BinOp(left=left_expr, - op=op(), - right=right_expr, - lineno=lineno, - col_offset=col_offset) + ret += asty.BinOp(expr, left=left_expr, op=op(), right=right_expr) + return ret - @builds("**") - @builds("//") - @builds("<<") - @builds(">>") - @builds("&") + @builds("**", "//", "<<", ">>", "&") @checkargs(min=2) def compile_maths_expression_2_or_more(self, expression): return self._compile_maths_expression(expression) - @builds("%") - @builds("^") + @builds("%", "^") @checkargs(2) def compile_maths_expression_exactly_2(self, expression): return self._compile_maths_expression(expression) - @builds("*") - @builds("|") + @builds("*", "|") def compile_maths_expression_mul(self, expression): id_elem = {"*": 1, "|": 0}[expression[0]] if len(expression) == 1: - return ast.Num(n=long_type(id_elem), - lineno=expression.start_line, - col_offset=expression.start_column) + return asty.Num(expression, n=long_type(id_elem)) elif len(expression) == 2: return self.compile(expression[1]) else: @@ -1866,16 +1613,12 @@ class HyASTCompiler(object): return self._compile_maths_expression(expression) else: op = {"+": ast.UAdd, "-": ast.USub}[expression.pop(0)]() - arg = expression.pop(0) - ret = self.compile(arg) - ret += ast.UnaryOp(op=op, - operand=ret.force_expr, - lineno=arg.start_line, - col_offset=arg.start_column) - return ret + ret = self.compile(expression.pop(0)) + return ret + asty.UnaryOp( + expression, op=op, operand=ret.force_expr) @builds("&") - @builds_if("@", PY35) + @builds("@", iff=PY35) @checkargs(min=1) def compile_maths_expression_unary_idempotent(self, expression): if len(expression) == 2: @@ -1889,9 +1632,7 @@ class HyASTCompiler(object): def compile_maths_expression_add(self, expression): if len(expression) == 1: # Nullary + - return ast.Num(n=long_type(0), - lineno=expression.start_line, - col_offset=expression.start_column) + return asty.Num(expression, n=long_type(0)) else: return self._compile_maths_expression_additive(expression) @@ -1900,19 +1641,9 @@ class HyASTCompiler(object): def compile_maths_expression_sub(self, expression): return self._compile_maths_expression_additive(expression) - @builds("+=") - @builds("/=") - @builds("//=") - @builds("*=") - @builds("_=") - @builds("%=") - @builds("**=") - @builds("<<=") - @builds(">>=") - @builds("|=") - @builds("^=") - @builds("&=") - @builds_if("@=", PY35) + @builds("+=", "/=", "//=", "*=", "_=", "%=", "**=", "<<=", ">>=", "|=", + "^=", "&=") + @builds("@=", iff=PY35) @checkargs(2) def compile_augassign_expression(self, expression): ops = {"+=": ast.Add, @@ -1935,14 +1666,8 @@ class HyASTCompiler(object): target = self._storeize(expression[1], self.compile(expression[1])) ret = self.compile(expression[2]) - ret += ast.AugAssign( - target=target, - value=ret.force_expr, - op=op(), - lineno=expression.start_line, - col_offset=expression.start_column) - - return ret + return ret + asty.AugAssign( + expression, target=target, value=ret.force_expr, op=op()) @checkargs(1) def _compile_keyword_call(self, expression): @@ -1959,7 +1684,7 @@ class HyASTCompiler(object): return self.compile(expression) if expression == []: - return self.compile_list(expression) + return self.compile_list(expression, HyList) fn = expression[0] func = None @@ -2010,49 +1735,32 @@ class HyASTCompiler(object): attrs)) # And get the method - func += ast.Attribute(lineno=fn.start_line, - col_offset=fn.start_column, - value=func.force_expr, - attr=ast_str(fn), - ctx=ast.Load()) + func += asty.Attribute(fn, + value=func.force_expr, + attr=ast_str(fn), + ctx=ast.Load()) if not func: func = self.compile(fn) # An exception for pulling together keyword args is if we're doing # a typecheck, eg (type :foo) - if fn in ("type", "HyKeyword", "keyword", "name", "is_keyword"): - with_kwargs = False - else: - with_kwargs = True - - args, ret, keywords, oldpy_starargs, oldpy_kwargs = self._compile_collect( + with_kwargs = fn not in ( + "type", "HyKeyword", "keyword", "name", "is_keyword") + args, ret, keywords, oldpy_star, oldpy_kw = self._compile_collect( expression[1:], with_kwargs, oldpy_unpack=True) - ret += ast.Call(func=func.expr, - args=args, - keywords=keywords, - starargs=oldpy_starargs, - kwargs=oldpy_kwargs, - lineno=expression.start_line, - col_offset=expression.start_column) + return func + ret + asty.Call( + expression, func=func.expr, args=args, keywords=keywords, + starargs=oldpy_star, kwargs=oldpy_kw) - return func + ret - - @builds("def") - @builds("setv") + @builds("def", "setv") def compile_def_expression(self, expression): root = expression.pop(0) if not expression: - result = Result() - result += ast.Name(id='None', ctx=ast.Load(), - lineno=root.start_line, - col_offset=root.start_column) - return result + return asty.Name(root, id='None', ctx=ast.Load()) elif len(expression) == 2: - return self._compile_assign(expression[0], expression[1], - expression.start_line, - expression.start_column) + return self._compile_assign(expression[0], expression[1]) elif len(expression) % 2 != 0: raise HyTypeError(expression, "`{}' needs an even number of arguments".format( @@ -2060,16 +1768,14 @@ class HyASTCompiler(object): else: result = Result() for tgt, target in zip(expression[::2], expression[1::2]): - result += self._compile_assign(tgt, target, tgt.start_line, - tgt.start_column) + result += self._compile_assign(tgt, target) return result - def _compile_assign(self, name, result, - start_line, start_column): + def _compile_assign(self, name, result): str_name = "%s" % name - if _is_hy_builtin(str_name, self.module_name) and \ - not self.allow_builtins: + if (_is_hy_builtin(str_name, self.module_name) and + not self.allow_builtins): raise HyTypeError(name, "Can't assign to a builtin: `%s'" % str_name) @@ -2080,17 +1786,16 @@ class HyASTCompiler(object): raise HyTypeError(name, "Can't assign to a callable: `%s'" % str_name) - if result.temp_variables \ - and isinstance(name, HyString) \ - and '.' not in name: + if (result.temp_variables + and isinstance(name, HyString) + and '.' not in name): result.rename(name) # Throw away .expr to ensure that (setv ...) returns None. result.expr = None else: st_name = self._storeize(name, ld_name) - result += ast.Assign( - lineno=start_line, - col_offset=start_column, + result += asty.Assign( + name if hasattr(name, "start_line") else result, targets=[st_name], value=result.force_expr) @@ -2105,14 +1810,14 @@ class HyASTCompiler(object): if not isinstance(args, HyList): raise HyTypeError(expression, - "for expects a list, received `{0}'".format( + "`for` expects a list, received `{0}`".format( type(args).__name__)) try: target_name, iterable = args except ValueError: raise HyTypeError(expression, - "for requires two forms in the list") + "`for` requires two forms in the list") target = self._storeize(target_name, self.compile(target_name)) @@ -2135,12 +1840,11 @@ class HyASTCompiler(object): body = self._compile_branch(expression) body += body.expr_as_stmt() - ret += ast.For(lineno=expression.start_line, - col_offset=expression.start_column, - target=target, - iter=ret.force_expr, - body=body.stmts, - orelse=orel.stmts) + ret += asty.For(expression, + target=target, + iter=ret.force_expr, + body=body.stmts, + orelse=orel.stmts) ret.contains_yield = body.contains_yield @@ -2163,36 +1867,16 @@ class HyASTCompiler(object): body = self._compile_branch(expr) body += body.expr_as_stmt() - ret += ast.While(test=ret.force_expr, - body=body.stmts, - orelse=orel.stmts, - lineno=expr.start_line, - col_offset=expr.start_column) + ret += asty.While(expr, + test=ret.force_expr, + body=body.stmts, + orelse=orel.stmts) ret.contains_yield = body.contains_yield return ret - @builds(HyList) - def compile_list(self, expression): - elts, ret, _ = self._compile_collect(expression) - ret += ast.List(elts=elts, - ctx=ast.Load(), - lineno=expression.start_line, - col_offset=expression.start_column) - return ret - - @builds(HySet) - def compile_set(self, expression): - elts, ret, _ = self._compile_collect(expression) - ret += ast.Set(elts=elts, - ctx=ast.Load(), - lineno=expression.start_line, - col_offset=expression.start_column) - return ret - - @builds("fn") - @builds("fn*") + @builds("fn", "fn*") # The starred version is for internal use (particularly, in the # definition of `defn`). It ensures that a FunctionDef is # produced rather than a Lambda. @@ -2224,40 +1908,21 @@ class HyASTCompiler(object): if PY3: # Python 3.4+ requires that args are an ast.arg object, rather # than an ast.Name or bare string. - args = [ast.arg(arg=ast_str(x), - annotation=None, # Fix me! - lineno=x.start_line, - col_offset=x.start_column) for x in args] + # FIXME: Set annotations properly. + # XXX: Beware. Beware. `starargs` and `kwargs` weren't put + # into the parse lambda list because they're really just an + # internal parsing thing. Let's find a better home for these guys. + args, kwonlyargs, [stararg], [kwargs] = ( + [[x and asty.arg(x, arg=ast_str(x), annotation=None) + for x in o] + for o in (args, kwonlyargs, [stararg], [kwargs])]) - kwonlyargs = [ast.arg(arg=ast_str(x), annotation=None, - lineno=x.start_line, - col_offset=x.start_column) - for x in kwonlyargs] - - # XXX: Beware. Beware. This wasn't put into the parse lambda - # list because it's really just an internal parsing thing. - - if kwargs: - kwargs = ast.arg(arg=ast_str(kwargs), annotation=None, - lineno=kwargs.start_line, - col_offset=kwargs.start_column) - - if stararg: - stararg = ast.arg(arg=ast_str(stararg), annotation=None, - lineno=stararg.start_line, - col_offset=stararg.start_column) - - # Let's find a better home for these guys. else: - args = [ast.Name(arg=ast_str(x), id=ast_str(x), - ctx=ast.Param(), - lineno=x.start_line, - col_offset=x.start_column) for x in args] + args = [asty.Name(x, id=ast_str(x), ctx=ast.Param()) + for x in args] if PY3: - kwonlyargs = [ast.Name(arg=ast_str(x), id=ast_str(x), - ctx=ast.Param(), lineno=x.start_line, - col_offset=x.start_column) + kwonlyargs = [asty.Name(x, arg=ast_str(x), ctx=ast.Param()) for x in kwonlyargs] if kwargs: @@ -2276,12 +1941,7 @@ class HyASTCompiler(object): body = self._compile_branch(expression) if not force_functiondef and not body.stmts: - ret += ast.Lambda( - lineno=expression.start_line, - col_offset=expression.start_column, - args=args, - body=body.force_expr) - + ret += asty.Lambda(expression, args=args, body=body.force_expr) return ret if body.expr: @@ -2291,28 +1951,20 @@ class HyASTCompiler(object): # statement. body += body.expr_as_stmt() else: - body += ast.Return(value=body.expr, - lineno=body.expr.lineno, - col_offset=body.expr.col_offset) + body += asty.Return(body.expr, value=body.expr) if not body.stmts: - body += ast.Pass(lineno=expression.start_line, - col_offset=expression.start_column) + body += asty.Pass(expression) - name = self.get_anon_fn() + name = self.get_anon_var() - ret += ast.FunctionDef(name=name, - lineno=expression.start_line, - col_offset=expression.start_column, - args=args, - body=body.stmts, - decorator_list=[]) + ret += asty.FunctionDef(expression, + name=name, + args=args, + body=body.stmts, + decorator_list=[]) - ast_name = ast.Name(id=name, - arg=name, - ctx=ast.Load(), - lineno=expression.start_line, - col_offset=expression.start_column) + ast_name = asty.Name(expression, id=name, ctx=ast.Load()) ret += Result(expr=ast_name, temp_variables=[ast_name, ret.stmts[-1]]) @@ -2324,9 +1976,7 @@ class HyASTCompiler(object): ret = Result() if len(expr) > 1: ret += self.compile(expr[1]) - return ret + ast.Return(value=ret.force_expr, - lineno=expr.start_line, - col_offset=expr.start_column) + return ret + asty.Return(expr, value=ret.force_expr) @builds("defclass") @checkargs(min=1) @@ -2351,15 +2001,14 @@ class HyASTCompiler(object): class_name = expressions.pop(0) + bases_expr = [] + bases = Result() if expressions: base_list = expressions.pop(0) if not isinstance(base_list, HyList): raise HyTypeError(expressions, "Bases class must be a list") bases_expr, bases, _ = self._compile_collect(base_list) - else: - bases_expr = [] - bases = Result() body = Result() @@ -2369,9 +2018,7 @@ class HyASTCompiler(object): symb = HySymbol("__doc__") symb.start_line = docstring.start_line symb.start_column = docstring.start_column - body += self._compile_assign(symb, docstring, - docstring.start_line, - docstring.start_column) + body += self._compile_assign(symb, docstring) body += body.expr_as_stmt() allow_builtins = self.allow_builtins @@ -2385,18 +2032,15 @@ class HyASTCompiler(object): body += self.compile(rewire_init(expr)) for expression in expressions: - expr = rewire_init(macroexpand(expression, self)) - body += self.compile(expr) + body += self.compile(rewire_init(macroexpand(expression, self))) self.allow_builtins = allow_builtins if not body.stmts: - body += ast.Pass(lineno=expressions.start_line, - col_offset=expressions.start_column) + body += asty.Pass(expressions) - return bases + ast.ClassDef( - lineno=expressions.start_line, - col_offset=expressions.start_column, + return bases + asty.ClassDef( + expressions, decorator_list=[], name=ast_str(class_name), keywords=[], @@ -2497,23 +2141,12 @@ class HyASTCompiler(object): def compile_cons(self, cons): raise HyTypeError(cons, "Can't compile a top-level cons cell") - @builds(HyInteger) - def compile_integer(self, number): - return ast.Num(n=long_type(number), - lineno=number.start_line, - col_offset=number.start_column) - - @builds(HyFloat) - def compile_float(self, number): - return ast.Num(n=float(number), - lineno=number.start_line, - col_offset=number.start_column) - - @builds(HyComplex) - def compile_complex(self, number): - return ast.Num(n=complex(number), - lineno=number.start_line, - col_offset=number.start_column) + @builds(HyInteger, HyFloat, HyComplex) + def compile_numeric_literal(self, number, building): + f = {HyInteger: long_type, + HyFloat: float, + HyComplex: complex}[building] + return asty.Num(number, n=f(number)) @builds(HySymbol) def compile_symbol(self, symbol): @@ -2535,52 +2168,33 @@ class HyASTCompiler(object): glob = HySymbol(glob).replace(symbol) ret = self.compile_symbol(glob) - ret = ast.Attribute( - lineno=symbol.start_line, - col_offset=symbol.start_column, + return asty.Attribute( + symbol, value=ret, attr=ast_str(local), - ctx=ast.Load() - ) - return ret + ctx=ast.Load()) if symbol in _stdlib: self.imports[_stdlib[symbol]].add(symbol) - return ast.Name(id=ast_str(symbol), - arg=ast_str(symbol), - ctx=ast.Load(), - lineno=symbol.start_line, - col_offset=symbol.start_column) + return asty.Name(symbol, id=ast_str(symbol), ctx=ast.Load()) - @builds(HyString) - def compile_string(self, string): - return ast.Str(s=str_type(string), - lineno=string.start_line, - col_offset=string.start_column) + @builds(HyString, HyKeyword, HyBytes) + def compile_string(self, string, building): + node = asty.Bytes if PY3 and building is HyBytes else asty.Str + f = bytes_type if building is HyBytes else str_type + return node(string, s=f(string)) - @builds(HyBytes) - def compile_bytes(self, bytestring): - f = ast.Bytes if PY3 else ast.Str - return f(s=bytes_type(bytestring), - lineno=bytestring.start_line, - col_offset=bytestring.start_column) - - @builds(HyKeyword) - def compile_keyword(self, keyword): - return ast.Str(s=str_type(keyword), - lineno=keyword.start_line, - col_offset=keyword.start_column) + @builds(HyList, HySet) + def compile_list(self, expression, building): + elts, ret, _ = self._compile_collect(expression) + node = {HyList: asty.List, HySet: asty.Set}[building] + return ret + node(expression, elts=elts, ctx=ast.Load()) @builds(HyDict) def compile_dict(self, m): keyvalues, ret, _ = self._compile_collect(m, dict_display=True) - - ret += ast.Dict(lineno=m.start_line, - col_offset=m.start_column, - keys=keyvalues[::2], - values=keyvalues[1::2]) - return ret + return ret + asty.Dict(m, keys=keyvalues[::2], values=keyvalues[1::2]) def hy_compile(tree, module_name, root=ast.Module, get_expr=False): diff --git a/tests/compilers/test_compiler.py b/tests/compilers/test_compiler.py index 90a1d2a..b27d596 100644 --- a/tests/compilers/test_compiler.py +++ b/tests/compilers/test_compiler.py @@ -9,18 +9,6 @@ from hy.models import HyExpression, HyList, HySymbol, HyInteger from hy._compat import PY3 -def test_builds_with_dash(): - assert callable(compiler.builds("foobar")) - assert callable(compiler.builds("foo_bar")) - assert callable(compiler.builds("-")) - try: - compiler.builds("foobar-with-dash-") - except TypeError as e: - assert "*post* translated strings" in str(e) - else: - assert False - - def make_expression(*args): h = HyExpression(args) h.start_line = 1 diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index 1a2ab0f..6b62556 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -1631,3 +1631,14 @@ "Make sure argument destructuring works" (defn f [[a b] [c]] (, a b c)) (assert (= (f [1 2] [3]) (, 1 2 3)))) + +#@(pytest.mark.xfail +(defn test-assert-multistatements [] + ; https://github.com/hylang/hy/issues/1390 + (setv s (set)) + (defn f [x] + (.add s x) + False) + (with [(pytest.raises AssertionError)] + (assert (do (f 1) (f 2)) (do (f 3) (f 4)))) + (assert (= s #{1 2 3 4}))))