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 longer names
* Periods are no longer allowed in keywords * Periods are no longer allowed in keywords
* `eval` is now a function instead of a special form * `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 ] [ Bug Fixes ]
* Numeric literals are no longer parsed as symbols when followed by a dot * 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, from hy.models import (HyObject, HyExpression, HyKeyword, HyInteger, HyComplex,
HyString, HyBytes, HySymbol, HyFloat, HyList, HySet, HyString, HyBytes, HySymbol, HyFloat, HyList, HySet,
HyDict, HyCons) HyDict, HyCons, wrap_value)
from hy.errors import HyCompileError, HyTypeError from hy.errors import HyCompileError, HyTypeError
from hy.lex.parser import hy_symbol_mangle from hy.lex.parser import hy_symbol_mangle
@ -112,6 +112,8 @@ def builds_if(_type, condition):
def spoof_positions(obj): def spoof_positions(obj):
if not isinstance(obj, HyObject) or isinstance(obj, HyCons):
return
if not hasattr(obj, "start_column"): if not hasattr(obj, "start_column"):
obj.start_column = 0 obj.start_column = 0
if not hasattr(obj, "start_line"): if not hasattr(obj, "start_line"):
@ -416,6 +418,11 @@ class HyASTCompiler(object):
if not isinstance(ret, Result): if not isinstance(ret, Result):
ret = Result() + ret ret = Result() + ret
return 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): def compile(self, tree):
try: try:
@ -1840,6 +1847,7 @@ class HyASTCompiler(object):
op = ops[expression.pop(0)] op = ops[expression.pop(0)]
right_associative = op == ast.Pow right_associative = op == ast.Pow
lineno, col_offset = expression.start_line, expression.start_column
if right_associative: if right_associative:
expression = expression[::-1] expression = expression[::-1]
ret = self.compile(expression.pop(0)) ret = self.compile(expression.pop(0))
@ -1852,8 +1860,8 @@ class HyASTCompiler(object):
ret += ast.BinOp(left=left_expr, ret += ast.BinOp(left=left_expr,
op=op(), op=op(),
right=right_expr, right=right_expr,
lineno=child.start_line, lineno=lineno,
col_offset=child.start_column) col_offset=col_offset)
return ret return ret
@builds("**") @builds("**")
@ -2602,7 +2610,11 @@ def hy_compile(tree, module_name, root=ast.Module, get_expr=False):
expr = None expr = None
if not isinstance(tree, HyObject): 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) compiler = HyASTCompiler(module_name)
result = compiler.compile(tree) result = compiler.compile(tree)

View File

@ -18,7 +18,7 @@
(import [hy._compat [long-type]]) ; long for python2, int for python3 (import [hy._compat [long-type]]) ; long for python2, int for python3
(import [hy.models [HyCons HySymbol HyKeyword]]) (import [hy.models [HyCons HySymbol HyKeyword]])
(import [hy.lex [LexException PrematureEndOfInput tokenize]]) (import [hy.lex [LexException PrematureEndOfInput tokenize]])
(import [hy.compiler [HyASTCompiler]]) (import [hy.compiler [HyASTCompiler spoof-positions]])
(import [hy.importer [hy-eval :as eval]]) (import [hy.importer [hy-eval :as eval]])
(defn butlast [coll] (defn butlast [coll]
@ -75,8 +75,8 @@
(import astor) (import astor)
(import hy.compiler) (import hy.compiler)
(fake-source-positions tree) (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.codegen.to_source astor.codegen.to_source
astor.dump) astor.dump)
@ -175,15 +175,6 @@
"Return true if (pred x) is logical true for every x in coll, else false" "Return true if (pred x) is logical true for every x in coll, else false"
(all (map pred coll))) (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] (defn flatten [coll]
"Return a single flat list expanding all members of coll" "Return a single flat list expanding all members of coll"
(if (coll? coll) (if (coll? coll)

View File

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

View File

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

View File

@ -5,6 +5,7 @@
(import [tests.resources [kwtest function-with-a-dash]] (import [tests.resources [kwtest function-with-a-dash]]
[os.path [exists isdir isfile]] [os.path [exists isdir isfile]]
[sys :as systest] [sys :as systest]
re
[operator [or_]] [operator [or_]]
[hy.errors [HyTypeError]] [hy.errors [HyTypeError]]
pytest) pytest)
@ -1087,13 +1088,44 @@
(defn test-eval-failure [] (defn test-eval-failure []
"NATIVE: test eval failure modes" "NATIVE: test eval failure modes"
; yo dawg ; yo dawg
(import [hy.compiler [HyCompileError]])
(try (eval '(eval)) (except [e TypeError]) (else (assert False))) (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 []) (except [e HyTypeError]) (else (assert False)))
(try (eval 'False {} 1) (except [e TypeError]) (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 [] (defn test-import-syntax []
"NATIVE: test the import syntax." "NATIVE: test the import syntax."
@ -1367,7 +1399,9 @@
(assert (= (disassemble '(do (leaky) (leaky) (macros))) (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))])"))) "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) (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 [] (defn test-attribute-access []

View File

@ -84,6 +84,14 @@
"NATIVE: test macro calling a plain function" "NATIVE: test macro calling a plain function"
(assert (= 3 (bar 1 2)))) (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 [] (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)))