Auto-promote values to HyObjects in the compiler

This commit is contained in:
Kodi Arfer 2017-06-27 15:09:31 -06:00
parent 4be37b358b
commit 55986b2033
7 changed files with 73 additions and 20 deletions

3
NEWS
View File

@ -5,6 +5,9 @@ Changes from 0.13.0
longer names
* Periods are no longer allowed in keywords
* `eval` is now a function instead of a special form
* The compiler now automatically promotes values to Hy model objects
as necessary, so you can write ``(eval `(+ 1 ~n))`` instead of
``(eval `(+ 1 ~(HyInteger n)))``
[ Bug Fixes ]
* Numeric literals are no longer parsed as symbols when followed by a dot

View File

@ -5,7 +5,7 @@
from hy.models import (HyObject, HyExpression, HyKeyword, HyInteger, HyComplex,
HyString, HyBytes, HySymbol, HyFloat, HyList, HySet,
HyDict, HyCons)
HyDict, HyCons, wrap_value)
from hy.errors import HyCompileError, HyTypeError
from hy.lex.parser import hy_symbol_mangle
@ -112,6 +112,8 @@ def builds_if(_type, condition):
def spoof_positions(obj):
if not isinstance(obj, HyObject) or isinstance(obj, HyCons):
return
if not hasattr(obj, "start_column"):
obj.start_column = 0
if not hasattr(obj, "start_line"):
@ -416,6 +418,11 @@ class HyASTCompiler(object):
if not isinstance(ret, Result):
ret = Result() + ret
return ret
if not isinstance(atom, HyObject):
atom = wrap_value(atom)
if isinstance(atom, HyObject):
spoof_positions(atom)
return self.compile_atom(type(atom), atom)
def compile(self, tree):
try:
@ -1840,6 +1847,7 @@ class HyASTCompiler(object):
op = ops[expression.pop(0)]
right_associative = op == ast.Pow
lineno, col_offset = expression.start_line, expression.start_column
if right_associative:
expression = expression[::-1]
ret = self.compile(expression.pop(0))
@ -1852,8 +1860,8 @@ class HyASTCompiler(object):
ret += ast.BinOp(left=left_expr,
op=op(),
right=right_expr,
lineno=child.start_line,
col_offset=child.start_column)
lineno=lineno,
col_offset=col_offset)
return ret
@builds("**")
@ -2602,7 +2610,11 @@ def hy_compile(tree, module_name, root=ast.Module, get_expr=False):
expr = None
if not isinstance(tree, HyObject):
raise HyCompileError("tree must be a 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)
compiler = HyASTCompiler(module_name)
result = compiler.compile(tree)

View File

@ -18,7 +18,7 @@
(import [hy._compat [long-type]]) ; long for python2, int for python3
(import [hy.models [HyCons HySymbol HyKeyword]])
(import [hy.lex [LexException PrematureEndOfInput tokenize]])
(import [hy.compiler [HyASTCompiler]])
(import [hy.compiler [HyASTCompiler spoof-positions]])
(import [hy.importer [hy-eval :as eval]])
(defn butlast [coll]
@ -75,8 +75,8 @@
(import astor)
(import hy.compiler)
(fake-source-positions tree)
(setv compiled (hy.compiler.hy_compile tree (calling-module-name)))
(spoof-positions tree)
(setv compiled (hy.compiler.hy-compile tree (calling-module-name)))
((if codegen
astor.codegen.to_source
astor.dump)
@ -175,15 +175,6 @@
"Return true if (pred x) is logical true for every x in coll, else false"
(all (map pred coll)))
(defn fake-source-positions [tree]
"Fake the source positions for a given tree"
(if (coll? tree)
(for* [subtree tree]
(fake-source-positions subtree)))
(for* [attr '[start-line end-line start-column end-column]]
(if (not (hasattr tree attr))
(setattr tree attr 1))))
(defn flatten [coll]
"Return a single flat list expanding all members of coll"
(if (coll? coll)

View File

@ -4,6 +4,7 @@
from __future__ import unicode_literals
from hy._compat import PY3, str_type, bytes_type, long_type, string_types
from fractions import Fraction
class HyObject(object):
@ -229,6 +230,8 @@ class HyExpression(HyList):
return "(%s)" % (" ".join([repr(x) for x in self]))
_wrappers[HyExpression] = lambda e: HyExpression(wrap_value(x) for x in e)
_wrappers[Fraction] = lambda e: HyExpression(
[HySymbol("fraction"), wrap_value(e.numerator), wrap_value(e.denominator)])
class HySet(HyList):

View File

@ -48,8 +48,10 @@ def cant_compile(expr):
def test_ast_bad_type():
"Make sure AST breakage can happen"
class C:
pass
try:
hy_compile("foo", "__main__")
hy_compile(C(), "__main__")
assert True is False
except HyCompileError:
pass

View File

@ -5,6 +5,7 @@
(import [tests.resources [kwtest function-with-a-dash]]
[os.path [exists isdir isfile]]
[sys :as systest]
re
[operator [or_]]
[hy.errors [HyTypeError]]
pytest)
@ -1087,13 +1088,44 @@
(defn test-eval-failure []
"NATIVE: test eval failure modes"
; yo dawg
(import [hy.compiler [HyCompileError]])
(try (eval '(eval)) (except [e TypeError]) (else (assert False)))
(try (eval '(eval "snafu")) (except [e HyCompileError]) (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 {} 1) (except [e TypeError]) (else (assert False))))
(defn test-eval-quasiquote []
; https://github.com/hylang/hy/issues/1174
(for [x [
None False True
5 5.1
1/2
5j 5.1j 2+1j 1.2+3.4j
"" b""
"apple bloom" b"apple bloom" "⚘" b"\x00"
:mykeyword
[] #{} {}
[1 2 3] #{1 2 3} {"a" 1 "b" 2}]]
(assert (= (eval `(identity ~x)) x))
(assert (= (eval x) x)))
; Tuples wrap to HyLists, not HyExpressions.
(assert (= (eval (,)) []))
(assert (= (eval (, 1 2 3)) [1 2 3]))
(assert (= (eval `(+ "a" ~(+ "b" "c"))) "abc"))
(setv l ["a" "b"])
(setv n 1)
(assert (= (eval `(get ~l ~n) "b")))
(setv d {"a" 1 "b" 2})
(setv k "b")
(assert (= (eval `(get ~d ~k)) 2)))
(defn test-import-syntax []
"NATIVE: test the import syntax."
@ -1367,7 +1399,9 @@
(assert (= (disassemble '(do (leaky) (leaky) (macros)))
"Module(\n body=[\n Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[], starargs=None, kwargs=None)),\n Expr(value=Call(func=Name(id='leaky'), args=[], keywords=[], starargs=None, kwargs=None)),\n Expr(value=Call(func=Name(id='macros'), args=[], keywords=[], starargs=None, kwargs=None))])")))
(assert (= (disassemble '(do (leaky) (leaky) (macros)) True)
"leaky()\nleaky()\nmacros()")))
"leaky()\nleaky()\nmacros()"))
(assert (= (re.sub r"[L() ]" "" (disassemble `(+ ~(+ 1 1) 40) True))
"2+40")))
(defn test-attribute-access []

View File

@ -84,6 +84,14 @@
"NATIVE: test macro calling a plain function"
(assert (= 3 (bar 1 2))))
(defn test-optional-and-apply-in-macro []
; https://github.com/hylang/hy/issues/1154
(defn f [&rest args]
(+ "f:" (repr args)))
(defmacro mac [&optional x]
`(apply f [~x]))
(assert (= (mac) "f:(None,)")))
(defn test-midtree-yield []
"NATIVE: test yielding with a returnable"
(defn kruft [] (yield) (+ 1 1)))