diff --git a/hy/compiler.py b/hy/compiler.py index f864fcf..3325b35 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -12,7 +12,8 @@ from hy.lex.parser import hy_symbol_mangle import hy.macros from hy._compat import ( - str_type, bytes_type, long_type, PY3, PY34, PY35, raise_empty) + str_type, string_types, bytes_type, long_type, PY3, PY34, PY35, + raise_empty) from hy.macros import require, macroexpand, tag_macroexpand import hy.importer @@ -110,6 +111,17 @@ def builds_if(_type, condition): return lambda fn: fn +def spoof_positions(obj): + if not hasattr(obj, "start_column"): + obj.start_column = 0 + if not hasattr(obj, "start_line"): + obj.start_line = 0 + if (hasattr(obj, "__iter__") and + not isinstance(obj, (string_types, bytes_type))): + for x in obj: + spoof_positions(x) + + class Result(object): """ Smart representation of the result of a hy->AST compilation @@ -378,23 +390,23 @@ class HyASTCompiler(object): ret = Result() for module, names in self.imports.items(): if None in names: - ret += self.compile([ - HyExpression([ + e = HyExpression([ HySymbol("import"), HySymbol(module), ]).replace(expr) - ]) + spoof_positions(e) + ret += self.compile(e) names = sorted(name for name in names if name) if names: - ret += self.compile([ - HyExpression([ + e = HyExpression([ HySymbol("import"), HyList([ HySymbol(module), HyList([HySymbol(name) for name in names]) ]) ]).replace(expr) - ]) + spoof_positions(e) + ret += self.compile(e) self.imports = defaultdict(set) return ret.stmts @@ -602,12 +614,6 @@ class HyASTCompiler(object): ast.copy_location(new_name, name) return new_name - @builds(list) - def compile_raw_list(self, entries): - ret = self._compile_branch(entries) - ret += ret.expr_as_stmt() - return ret - def _render_quoted_form(self, form, level): """ Render a quoted form as a new HyExpression. @@ -741,6 +747,7 @@ class HyASTCompiler(object): return ret @builds("try") + @checkargs(min=2) def compile_try_expression(self, expr): expr.pop(0) # try @@ -2594,21 +2601,17 @@ def hy_compile(tree, module_name, root=ast.Module, get_expr=False): body = [] expr = None - if not (isinstance(tree, HyObject) or type(tree) is list): - raise HyCompileError("tree must be a HyObject or a list") + if not isinstance(tree, HyObject): + raise HyCompileError("tree must be a HyObject") - if isinstance(tree, HyObject) or tree: - compiler = HyASTCompiler(module_name) - result = compiler.compile(tree) - expr = result.force_expr + compiler = HyASTCompiler(module_name) + result = compiler.compile(tree) + expr = result.force_expr - if not get_expr: - result += result.expr_as_stmt() + if not get_expr: + result += result.expr_as_stmt() - # We need to test that the type is *exactly* `list` because we don't - # want to do `tree[0]` on HyList or such. - spoof_tree = tree[0] if type(tree) is list else tree - body = compiler.imports_as_stmts(spoof_tree) + result.stmts + body = compiler.imports_as_stmts(tree) + result.stmts ret = root(body=body) diff --git a/hy/importer.py b/hy/importer.py index ce58190..63b87d4 100644 --- a/hy/importer.py +++ b/hy/importer.py @@ -3,7 +3,7 @@ # license. See the LICENSE. from hy.compiler import hy_compile, HyTypeError -from hy.models import HyObject, replace_hy_obj +from hy.models import HyObject, HyExpression, HySymbol, replace_hy_obj from hy.lex import tokenize, LexException from hy.errors import HyIOError @@ -32,7 +32,7 @@ def ast_compile(ast, filename, mode): def import_buffer_to_hst(buf): """Import content from buf and return a Hy AST.""" - return tokenize(buf + "\n") + return HyExpression([HySymbol("do")] + tokenize(buf + "\n")) def import_file_to_hst(fpath): diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index 2e88046..0fe7485 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -7,9 +7,9 @@ from __future__ import unicode_literals from hy import HyString from hy.models import HyObject from hy.compiler import hy_compile +from hy.importer import import_buffer_to_hst from hy.errors import HyCompileError, HyTypeError from hy.lex.exceptions import LexException -from hy.lex import tokenize from hy._compat import PY3 import ast @@ -25,12 +25,12 @@ def _ast_spotcheck(arg, root, secondary): def can_compile(expr): - return hy_compile(tokenize(expr), "__main__") + return hy_compile(import_buffer_to_hst(expr), "__main__") def cant_compile(expr): try: - hy_compile(tokenize(expr), "__main__") + hy_compile(import_buffer_to_hst(expr), "__main__") assert False except HyTypeError as e: # Anything that can't be compiled should raise a user friendly @@ -252,7 +252,7 @@ def test_ast_require(): def test_ast_no_pointless_imports(): def contains_import_from(code): return any([isinstance(node, ast.ImportFrom) - for node in hy_compile(tokenize(code), "__main__").body]) + for node in can_compile(code).body]) # `reduce` is a builtin in Python 2, but not Python 3. # The version of `map` that returns an iterator is a builtin in # Python 3, but not Python 2. @@ -460,7 +460,7 @@ def test_ast_unicode_strings(): hy_s.start_line = hy_s.end_line = 0 hy_s.start_column = hy_s.end_column = 0 - code = hy_compile([hy_s], "__main__") + code = hy_compile(hy_s, "__main__") # code == ast.Module(body=[ast.Expr(value=ast.Str(s=xxx))]) return code.body[0].value.s @@ -471,7 +471,7 @@ def test_ast_unicode_strings(): def test_ast_unicode_vs_bytes(): - def f(x): return hy_compile(tokenize(x), "__main__").body[0].value.s + def f(x): return can_compile(x).body[0].value.s assert f('"hello"') == u"hello" assert type(f('"hello"')) is (str if PY3 else unicode) # noqa assert f('b"hello"') == (eval('b"hello"') if PY3 else "hello")