Merge pull request #1616 from Kodiologist/autopromotion-update

Streamline auto-promotion and position spoofing
This commit is contained in:
Kodi Arfer 2018-06-05 10:29:57 -07:00 committed by GitHub
commit 3e943209fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 67 additions and 92 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -63,7 +63,7 @@ def test_ast_bad_type():
try:
hy_compile(C(), "__main__")
assert True is False
except HyCompileError:
except TypeError:
pass

View File

@ -3,6 +3,7 @@
;; license. See the LICENSE.
(require [hy.contrib.multi [defmulti defmethod default-method defn]])
(import pytest)
(defn test-different-signatures []
"NATIVE: Test multimethods with different signatures"
@ -28,20 +29,16 @@
(defn test-different-signatures-defn []
"NATIVE: Test defn with different signatures"
(defn fun
(defn f1
([] "")
([a] "a")
([a b] "a b"))
(assert (= (fun) ""))
(assert (= (fun "a") "a"))
(assert (= (fun "a" "b") "a b"))
(try
(do
(fun "a" "b" "c")
(assert False))
(except [e Exception]
(assert (isinstance e TypeError)))))
(assert (= (f1) ""))
(assert (= (f1 "a") "a"))
(assert (= (f1 "a" "b") "a b"))
(with [(pytest.raises TypeError)]
(f1 "a" "b" "c")))
(defn test-basic-dispatch []
"NATIVE: Test basic dispatch"
@ -92,35 +89,35 @@
(defn test-basic-multi []
"NATIVE: Test a basic arity overloaded defn"
(defn fun
(defn f2
([] "Hello!")
([a] a)
([a b] "a b")
([a b c] "a b c"))
(assert (= (fun) "Hello!"))
(assert (= (fun "a") "a"))
(assert (= (fun "a" "b") "a b"))
(assert (= (fun "a" "b" "c") "a b c")))
(assert (= (f2) "Hello!"))
(assert (= (f2 "a") "a"))
(assert (= (f2 "a" "b") "a b"))
(assert (= (f2 "a" "b" "c") "a b c")))
(defn test-kw-args []
"NATIVE: Test if kwargs are handled correctly for arity overloading"
(defn fun
(defn f3
([a] a)
([&optional [a "nop"] [b "p"]] (+ a b)))
(assert (= (fun 1) 1))
(assert (= (fun :a "t") "t"))
(assert (= (fun "hello " :b "world") "hello world"))
(assert (= (fun :a "hello " :b "world") "hello world")))
(assert (= (f3 1) 1))
(assert (= (f3 :a "t") "t"))
(assert (= (f3 "hello " :b "world") "hello world"))
(assert (= (f3 :a "hello " :b "world") "hello world")))
(defn test-docs []
"NATIVE: Test if docs are properly handled for arity overloading"
(defn fun
(defn f4
"docs"
([a] (print a))
([a b] (print b)))
(assert (= fun.--doc-- "docs")))
(assert (= f4.--doc-- "docs")))

View File

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

View File

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