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
|
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
|
# Provide asty.Foo(x, ...) as shorthand for
|
||||||
# ast.Foo(..., lineno=x.start_line, col_offset=x.start_column) or
|
# ast.Foo(..., lineno=x.start_line, col_offset=x.start_column) or
|
||||||
# ast.Foo(..., lineno=x.lineno, col_offset=x.col_offset)
|
# ast.Foo(..., lineno=x.lineno, col_offset=x.col_offset)
|
||||||
@ -382,7 +369,6 @@ class HyASTCompiler(object):
|
|||||||
HySymbol("import"),
|
HySymbol("import"),
|
||||||
HySymbol(module),
|
HySymbol(module),
|
||||||
]).replace(expr)
|
]).replace(expr)
|
||||||
spoof_positions(e)
|
|
||||||
ret += self.compile(e)
|
ret += self.compile(e)
|
||||||
names = sorted(name for name in names if name)
|
names = sorted(name for name in names if name)
|
||||||
if names:
|
if names:
|
||||||
@ -393,24 +379,18 @@ class HyASTCompiler(object):
|
|||||||
HyList([HySymbol(name) for name in names])
|
HyList([HySymbol(name) for name in names])
|
||||||
])
|
])
|
||||||
]).replace(expr)
|
]).replace(expr)
|
||||||
spoof_positions(e)
|
|
||||||
ret += self.compile(e)
|
ret += self.compile(e)
|
||||||
self.imports = defaultdict(set)
|
self.imports = defaultdict(set)
|
||||||
return ret.stmts
|
return ret.stmts
|
||||||
|
|
||||||
def compile_atom(self, atom):
|
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.
|
# Compilation methods may mutate the atom, so copy it first.
|
||||||
atom = copy.copy(atom)
|
atom = copy.copy(atom)
|
||||||
return Result() + _model_compilers[type(atom)](self, atom)
|
return Result() + _model_compilers[type(atom)](self, atom)
|
||||||
|
|
||||||
def compile(self, tree):
|
def compile(self, tree):
|
||||||
|
if tree is None:
|
||||||
|
return Result()
|
||||||
try:
|
try:
|
||||||
ret = self.compile_atom(tree)
|
ret = self.compile_atom(tree)
|
||||||
if ret:
|
if ret:
|
||||||
@ -1744,15 +1724,10 @@ def hy_compile(tree, module_name, root=ast.Module, get_expr=False):
|
|||||||
`last_expression` is the.
|
`last_expression` is the.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
body = []
|
tree = wrap_value(tree)
|
||||||
expr = None
|
|
||||||
|
|
||||||
if not isinstance(tree, HyObject):
|
if not isinstance(tree, HyObject):
|
||||||
tree = wrap_value(tree)
|
raise HyCompileError("`tree` must be a HyObject or capable of "
|
||||||
if not isinstance(tree, HyObject):
|
"being promoted to one")
|
||||||
raise HyCompileError("`tree` must be a HyObject or capable of "
|
|
||||||
"being promoted to one")
|
|
||||||
spoof_positions(tree)
|
|
||||||
|
|
||||||
compiler = HyASTCompiler(module_name)
|
compiler = HyASTCompiler(module_name)
|
||||||
result = compiler.compile(tree)
|
result = compiler.compile(tree)
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
(import [hy.models [HySymbol HyKeyword]])
|
(import [hy.models [HySymbol HyKeyword]])
|
||||||
(import [hy.lex [LexException PrematureEndOfInput tokenize]])
|
(import [hy.lex [LexException PrematureEndOfInput tokenize]])
|
||||||
(import [hy.lex.parser [mangle unmangle]])
|
(import [hy.lex.parser [mangle unmangle]])
|
||||||
(import [hy.compiler [HyASTCompiler spoof-positions]])
|
(import [hy.compiler [HyASTCompiler]])
|
||||||
(import [hy.importer [hy-eval :as eval]])
|
(import [hy.importer [hy-eval :as eval]])
|
||||||
|
|
||||||
(defn butlast [coll]
|
(defn butlast [coll]
|
||||||
@ -70,7 +70,6 @@ If the second argument `codegen` is true, generate python code instead."
|
|||||||
(import astor)
|
(import astor)
|
||||||
(import hy.compiler)
|
(import hy.compiler)
|
||||||
|
|
||||||
(spoof-positions tree)
|
|
||||||
(setv compiled (hy.compiler.hy-compile tree (calling-module-name)))
|
(setv compiled (hy.compiler.hy-compile tree (calling-module-name)))
|
||||||
((if codegen
|
((if codegen
|
||||||
astor.code-gen.to-source
|
astor.code-gen.to-source
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
|
||||||
from hy.compiler import hy_compile, HyTypeError
|
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.lex import tokenize, LexException
|
||||||
from hy.errors import HyIOError
|
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])
|
m = inspect.getmodule(inspect.stack()[1][0])
|
||||||
module_name = '__eval__' if m is None else m.__name__
|
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):
|
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)
|
_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)
|
ast_callback(_ast, expr)
|
||||||
|
|
||||||
if not isinstance(namespace, dict):
|
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
|
# Two-step eval: eval() the body of the exec call
|
||||||
eval(ast_compile(_ast, "<eval_body>", "exec"), namespace)
|
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
|
from hy._compat import PY3
|
||||||
import hy.inspect
|
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.lex.parser import mangle
|
||||||
from hy._compat import str_type
|
from hy._compat import str_type
|
||||||
|
|
||||||
@ -167,16 +167,16 @@ def macroexpand(tree, compiler, once=False):
|
|||||||
while True:
|
while True:
|
||||||
|
|
||||||
if not isinstance(tree, HyExpression) or tree == []:
|
if not isinstance(tree, HyExpression) or tree == []:
|
||||||
return tree
|
break
|
||||||
|
|
||||||
fn = tree[0]
|
fn = tree[0]
|
||||||
if fn in ("quote", "quasiquote") or not isinstance(fn, HySymbol):
|
if fn in ("quote", "quasiquote") or not isinstance(fn, HySymbol):
|
||||||
return tree
|
break
|
||||||
|
|
||||||
fn = mangle(fn)
|
fn = mangle(fn)
|
||||||
m = _hy_macros[compiler.module_name].get(fn) or _hy_macros[None].get(fn)
|
m = _hy_macros[compiler.module_name].get(fn) or _hy_macros[None].get(fn)
|
||||||
if not m:
|
if not m:
|
||||||
return tree
|
break
|
||||||
|
|
||||||
opts = {}
|
opts = {}
|
||||||
if m._hy_macro_pass_compiler:
|
if m._hy_macro_pass_compiler:
|
||||||
@ -202,8 +202,10 @@ def macroexpand(tree, compiler, once=False):
|
|||||||
tree = replace_hy_obj(obj, tree)
|
tree = replace_hy_obj(obj, tree)
|
||||||
|
|
||||||
if once:
|
if once:
|
||||||
return tree
|
break
|
||||||
|
|
||||||
|
tree = wrap_value(tree)
|
||||||
|
return tree
|
||||||
|
|
||||||
def macroexpand_1(tree, compiler):
|
def macroexpand_1(tree, compiler):
|
||||||
"""Expand the toplevel macro from `tree` once, in the context of
|
"""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.
|
Hy lexing Objects at once.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def replace(self, other):
|
def replace(self, other, recursive=False):
|
||||||
if isinstance(other, HyObject):
|
if isinstance(other, HyObject):
|
||||||
for attr in ["start_line", "end_line",
|
for attr in ["start_line", "end_line",
|
||||||
"start_column", "end_column"]:
|
"start_column", "end_column"]:
|
||||||
@ -60,25 +60,20 @@ def wrap_value(x):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
wrapper = _wrappers.get(type(x))
|
new = _wrappers.get(type(x), lambda y: y)(x)
|
||||||
if wrapper is None:
|
if not isinstance(new, HyObject):
|
||||||
return x
|
raise TypeError("Don't know how to wrap {!r}: {!r}".format(type(x), x))
|
||||||
else:
|
if isinstance(x, HyObject):
|
||||||
return wrapper(x)
|
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):
|
def replace_hy_obj(obj, other):
|
||||||
|
return wrap_value(obj).replace(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))
|
|
||||||
|
|
||||||
|
|
||||||
def repr_indent(obj):
|
def repr_indent(obj):
|
||||||
@ -280,8 +275,12 @@ class HySequence(HyObject, list):
|
|||||||
class HyList(HySequence):
|
class HyList(HySequence):
|
||||||
color = staticmethod(colored.cyan)
|
color = staticmethod(colored.cyan)
|
||||||
|
|
||||||
_wrappers[list] = lambda l: HyList(wrap_value(x) for x in l)
|
def recwrap(f):
|
||||||
_wrappers[tuple] = lambda t: HyList(wrap_value(x) for x in t)
|
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):
|
class HyDict(HySequence):
|
||||||
@ -317,6 +316,7 @@ class HyDict(HySequence):
|
|||||||
def items(self):
|
def items(self):
|
||||||
return list(zip(self.keys(), self.values()))
|
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(), ()))
|
_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)
|
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(
|
_wrappers[Fraction] = lambda e: HyExpression(
|
||||||
[HySymbol("fraction"), wrap_value(e.numerator), wrap_value(e.denominator)])
|
[HySymbol("fraction"), wrap_value(e.numerator), wrap_value(e.denominator)])
|
||||||
|
|
||||||
@ -337,4 +337,5 @@ class HySet(HySequence):
|
|||||||
"""
|
"""
|
||||||
color = staticmethod(colored.red)
|
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:
|
try:
|
||||||
hy_compile(C(), "__main__")
|
hy_compile(C(), "__main__")
|
||||||
assert True is False
|
assert True is False
|
||||||
except HyCompileError:
|
except TypeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@ -1327,7 +1327,7 @@
|
|||||||
(try (eval '(eval)) (except [e TypeError]) (else (assert False)))
|
(try (eval '(eval)) (except [e TypeError]) (else (assert False)))
|
||||||
(defclass C)
|
(defclass C)
|
||||||
(try (eval (C)) (except [e TypeError]) (else (assert False)))
|
(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))))
|
(try (eval 'False {} 1) (except [e TypeError]) (else (assert False))))
|
||||||
|
|
||||||
|
|
||||||
|
@ -86,6 +86,14 @@
|
|||||||
`(f #* [~x]))
|
`(f #* [~x]))
|
||||||
(assert (= (mac) "f:(None,)")))
|
(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 []
|
(defn test-midtree-yield []
|
||||||
"NATIVE: test yielding with a returnable"
|
"NATIVE: test yielding with a returnable"
|
||||||
(defn kruft [] (yield) (+ 1 1)))
|
(defn kruft [] (yield) (+ 1 1)))
|
||||||
|
Loading…
Reference in New Issue
Block a user