Make lists no longer magical for the compiler

This commit is contained in:
Kodi Arfer 2017-06-22 16:32:29 -07:00
parent f55fcf43bd
commit 4be37b358b
3 changed files with 36 additions and 33 deletions

View File

@ -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)

View File

@ -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):

View File

@ -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")