Streamline auto-promotion and position spoofing
Auto-promotion now occurs in only two cases: when we start the compiler and when we expand a macro. It's fully recursive so even a non-model nested in a model will be promoted. This change fixes some regressions induced by the stricter type checks of the pattern-matching compiler.
This commit is contained in:
parent
aae1f8718c
commit
3204a9e8a3
@ -106,19 +106,6 @@ def builds_model(*model_types):
|
||||
return _dec
|
||||
|
||||
|
||||
def spoof_positions(obj):
|
||||
if not isinstance(obj, HyObject):
|
||||
return
|
||||
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)
|
||||
|
||||
|
||||
# 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)
|
||||
@ -382,7 +369,6 @@ class HyASTCompiler(object):
|
||||
HySymbol("import"),
|
||||
HySymbol(module),
|
||||
]).replace(expr)
|
||||
spoof_positions(e)
|
||||
ret += self.compile(e)
|
||||
names = sorted(name for name in names if name)
|
||||
if names:
|
||||
@ -393,24 +379,18 @@ class HyASTCompiler(object):
|
||||
HyList([HySymbol(name) for name in names])
|
||||
])
|
||||
]).replace(expr)
|
||||
spoof_positions(e)
|
||||
ret += self.compile(e)
|
||||
self.imports = defaultdict(set)
|
||||
return ret.stmts
|
||||
|
||||
def compile_atom(self, atom):
|
||||
if not isinstance(atom, HyObject):
|
||||
atom = wrap_value(atom)
|
||||
if not isinstance(atom, HyObject):
|
||||
return
|
||||
spoof_positions(atom)
|
||||
if type(atom) not in _model_compilers:
|
||||
return
|
||||
# Compilation methods may mutate the atom, so copy it first.
|
||||
atom = copy.copy(atom)
|
||||
return Result() + _model_compilers[type(atom)](self, atom)
|
||||
|
||||
def compile(self, tree):
|
||||
if tree is None:
|
||||
return Result()
|
||||
try:
|
||||
ret = self.compile_atom(tree)
|
||||
if ret:
|
||||
@ -1744,15 +1724,10 @@ def hy_compile(tree, module_name, root=ast.Module, get_expr=False):
|
||||
`last_expression` is the.
|
||||
"""
|
||||
|
||||
body = []
|
||||
expr = None
|
||||
|
||||
tree = wrap_value(tree)
|
||||
if not isinstance(tree, HyObject):
|
||||
tree = wrap_value(tree)
|
||||
if not isinstance(tree, HyObject):
|
||||
raise HyCompileError("`tree` must be a HyObject or capable of "
|
||||
"being promoted to one")
|
||||
spoof_positions(tree)
|
||||
raise HyCompileError("`tree` must be a HyObject or capable of "
|
||||
"being promoted to one")
|
||||
|
||||
compiler = HyASTCompiler(module_name)
|
||||
result = compiler.compile(tree)
|
||||
|
@ -21,7 +21,7 @@
|
||||
(import [hy.models [HySymbol HyKeyword]])
|
||||
(import [hy.lex [LexException PrematureEndOfInput tokenize]])
|
||||
(import [hy.lex.parser [mangle unmangle]])
|
||||
(import [hy.compiler [HyASTCompiler spoof-positions]])
|
||||
(import [hy.compiler [HyASTCompiler]])
|
||||
(import [hy.importer [hy-eval :as eval]])
|
||||
|
||||
(defn butlast [coll]
|
||||
@ -70,7 +70,6 @@ If the second argument `codegen` is true, generate python code instead."
|
||||
(import astor)
|
||||
(import hy.compiler)
|
||||
|
||||
(spoof-positions tree)
|
||||
(setv compiled (hy.compiler.hy-compile tree (calling-module-name)))
|
||||
((if codegen
|
||||
astor.code-gen.to-source
|
||||
|
@ -5,7 +5,7 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
from hy.compiler import hy_compile, HyTypeError
|
||||
from hy.models import HyObject, HyExpression, HySymbol, replace_hy_obj
|
||||
from hy.models import HyObject, HyExpression, HySymbol
|
||||
from hy.lex import tokenize, LexException
|
||||
from hy.errors import HyIOError
|
||||
|
||||
@ -172,15 +172,8 @@ def hy_eval(hytree, namespace=None, module_name=None, ast_callback=None):
|
||||
m = inspect.getmodule(inspect.stack()[1][0])
|
||||
module_name = '__eval__' if m is None else m.__name__
|
||||
|
||||
foo = HyObject()
|
||||
foo.start_line = 0
|
||||
foo.end_line = 0
|
||||
foo.start_column = 0
|
||||
foo.end_column = 0
|
||||
replace_hy_obj(hytree, foo)
|
||||
|
||||
if not isinstance(module_name, string_types):
|
||||
raise HyTypeError(foo, "Module name must be a string")
|
||||
raise TypeError("Module name must be a string")
|
||||
|
||||
_ast, expr = hy_compile(hytree, module_name, get_expr=True)
|
||||
|
||||
@ -197,7 +190,7 @@ def hy_eval(hytree, namespace=None, module_name=None, ast_callback=None):
|
||||
ast_callback(_ast, expr)
|
||||
|
||||
if not isinstance(namespace, dict):
|
||||
raise HyTypeError(foo, "Globals must be a dictionary")
|
||||
raise TypeError("Globals must be a dictionary")
|
||||
|
||||
# Two-step eval: eval() the body of the exec call
|
||||
eval(ast_compile(_ast, "<eval_body>", "exec"), namespace)
|
||||
|
12
hy/macros.py
12
hy/macros.py
@ -4,7 +4,7 @@
|
||||
|
||||
from hy._compat import PY3
|
||||
import hy.inspect
|
||||
from hy.models import replace_hy_obj, HyExpression, HySymbol
|
||||
from hy.models import replace_hy_obj, HyExpression, HySymbol, wrap_value
|
||||
from hy.lex.parser import mangle
|
||||
from hy._compat import str_type
|
||||
|
||||
@ -167,16 +167,16 @@ def macroexpand(tree, compiler, once=False):
|
||||
while True:
|
||||
|
||||
if not isinstance(tree, HyExpression) or tree == []:
|
||||
return tree
|
||||
break
|
||||
|
||||
fn = tree[0]
|
||||
if fn in ("quote", "quasiquote") or not isinstance(fn, HySymbol):
|
||||
return tree
|
||||
break
|
||||
|
||||
fn = mangle(fn)
|
||||
m = _hy_macros[compiler.module_name].get(fn) or _hy_macros[None].get(fn)
|
||||
if not m:
|
||||
return tree
|
||||
break
|
||||
|
||||
opts = {}
|
||||
if m._hy_macro_pass_compiler:
|
||||
@ -202,8 +202,10 @@ def macroexpand(tree, compiler, once=False):
|
||||
tree = replace_hy_obj(obj, tree)
|
||||
|
||||
if once:
|
||||
return tree
|
||||
break
|
||||
|
||||
tree = wrap_value(tree)
|
||||
return tree
|
||||
|
||||
def macroexpand_1(tree, compiler):
|
||||
"""Expand the toplevel macro from `tree` once, in the context of
|
||||
|
43
hy/models.py
43
hy/models.py
@ -33,7 +33,7 @@ class HyObject(object):
|
||||
Hy lexing Objects at once.
|
||||
"""
|
||||
|
||||
def replace(self, other):
|
||||
def replace(self, other, recursive=False):
|
||||
if isinstance(other, HyObject):
|
||||
for attr in ["start_line", "end_line",
|
||||
"start_column", "end_column"]:
|
||||
@ -60,25 +60,20 @@ def wrap_value(x):
|
||||
|
||||
"""
|
||||
|
||||
wrapper = _wrappers.get(type(x))
|
||||
if wrapper is None:
|
||||
return x
|
||||
else:
|
||||
return wrapper(x)
|
||||
new = _wrappers.get(type(x), lambda y: y)(x)
|
||||
if not isinstance(new, HyObject):
|
||||
raise TypeError("Don't know how to wrap {!r}: {!r}".format(type(x), x))
|
||||
if isinstance(x, HyObject):
|
||||
new = new.replace(x, recursive=False)
|
||||
if not hasattr(new, "start_column"):
|
||||
new.start_column = 0
|
||||
if not hasattr(new, "start_line"):
|
||||
new.start_line = 0
|
||||
return new
|
||||
|
||||
|
||||
def replace_hy_obj(obj, other):
|
||||
|
||||
if isinstance(obj, HyObject):
|
||||
return obj.replace(other)
|
||||
|
||||
wrapped_obj = wrap_value(obj)
|
||||
|
||||
if isinstance(wrapped_obj, HyObject):
|
||||
return wrapped_obj.replace(other)
|
||||
else:
|
||||
raise TypeError("Don't know how to wrap a %s object to a HyObject"
|
||||
% type(obj))
|
||||
return wrap_value(obj).replace(other)
|
||||
|
||||
|
||||
def repr_indent(obj):
|
||||
@ -280,8 +275,12 @@ class HySequence(HyObject, list):
|
||||
class HyList(HySequence):
|
||||
color = staticmethod(colored.cyan)
|
||||
|
||||
_wrappers[list] = lambda l: HyList(wrap_value(x) for x in l)
|
||||
_wrappers[tuple] = lambda t: HyList(wrap_value(x) for x in t)
|
||||
def recwrap(f):
|
||||
return lambda l: f(wrap_value(x) for x in l)
|
||||
|
||||
_wrappers[HyList] = recwrap(HyList)
|
||||
_wrappers[list] = recwrap(HyList)
|
||||
_wrappers[tuple] = recwrap(HyList)
|
||||
|
||||
|
||||
class HyDict(HySequence):
|
||||
@ -317,6 +316,7 @@ class HyDict(HySequence):
|
||||
def items(self):
|
||||
return list(zip(self.keys(), self.values()))
|
||||
|
||||
_wrappers[HyDict] = recwrap(HyDict)
|
||||
_wrappers[dict] = lambda d: HyDict(wrap_value(x) for x in sum(d.items(), ()))
|
||||
|
||||
|
||||
@ -326,7 +326,7 @@ class HyExpression(HySequence):
|
||||
"""
|
||||
color = staticmethod(colored.yellow)
|
||||
|
||||
_wrappers[HyExpression] = lambda e: HyExpression(wrap_value(x) for x in e)
|
||||
_wrappers[HyExpression] = recwrap(HyExpression)
|
||||
_wrappers[Fraction] = lambda e: HyExpression(
|
||||
[HySymbol("fraction"), wrap_value(e.numerator), wrap_value(e.denominator)])
|
||||
|
||||
@ -337,4 +337,5 @@ class HySet(HySequence):
|
||||
"""
|
||||
color = staticmethod(colored.red)
|
||||
|
||||
_wrappers[set] = lambda s: HySet(wrap_value(x) for x in s)
|
||||
_wrappers[HySet] = recwrap(HySet)
|
||||
_wrappers[set] = recwrap(HySet)
|
||||
|
@ -63,7 +63,7 @@ def test_ast_bad_type():
|
||||
try:
|
||||
hy_compile(C(), "__main__")
|
||||
assert True is False
|
||||
except HyCompileError:
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
|
||||
|
@ -1327,7 +1327,7 @@
|
||||
(try (eval '(eval)) (except [e TypeError]) (else (assert False)))
|
||||
(defclass C)
|
||||
(try (eval (C)) (except [e TypeError]) (else (assert False)))
|
||||
(try (eval 'False []) (except [e HyTypeError]) (else (assert False)))
|
||||
(try (eval 'False []) (except [e TypeError]) (else (assert False)))
|
||||
(try (eval 'False {} 1) (except [e TypeError]) (else (assert False))))
|
||||
|
||||
|
||||
|
@ -86,6 +86,14 @@
|
||||
`(f #* [~x]))
|
||||
(assert (= (mac) "f:(None,)")))
|
||||
|
||||
(defn test-macro-autoboxing-docstring []
|
||||
(defmacro m []
|
||||
(setv mystring "hello world")
|
||||
`(fn [] ~mystring (+ 1 2)))
|
||||
(setv f (m))
|
||||
(assert (= (f) 3))
|
||||
(assert (= f.__doc__ "hello world")))
|
||||
|
||||
(defn test-midtree-yield []
|
||||
"NATIVE: test yielding with a returnable"
|
||||
(defn kruft [] (yield) (+ 1 1)))
|
||||
|
Loading…
Reference in New Issue
Block a user