Don't let HyExpression etc. inherit from HyList

This means the compiler no longer allows e.g. `(fn (x) ...)` in place of `(fn [x] ...)`.
This commit is contained in:
Kodi Arfer 2018-04-14 20:58:52 -07:00
parent 032247e380
commit 87aced2370
7 changed files with 76 additions and 56 deletions

View File

@ -12,6 +12,10 @@ Removals
Other Breaking Changes
------------------------------
* `HyExpression`, `HyDict`, and `HySet` no longer inherit from `HyList`.
This means you can no longer use alternative punctuation in place of
square brackets in special forms (e.g. `(fn (x) ...)` instead of
the standard `(fn [x] ...)`).
* Mangling rules have been overhauled, such that mangled names
are always legal Python identifiers
* `_` and `-` are now equivalent even as single-character names

View File

@ -48,26 +48,35 @@ Hy also attempts to color pretty reprs using ``clint.textui.colored``.
This module has a flag to disable coloring,
and a method ``clean`` to strip colored strings of their color tags.
.. _hysequence:
HySequence
~~~~~~~~~~
``hy.models.HySequence`` is the abstract base class of "iterable" Hy
models, such as HyExpression and HyList.
Adding a HySequence to another iterable object reuses the class of the
left-hand-side object, a useful behavior when you want to concatenate Hy
objects in a macro, for instance.
.. _hylist:
HyList
~~~~~~
~~~~~~~~~~~~
``hy.models.HyList`` is the base class of "iterable" Hy models. Its
basic use is to represent bracketed ``[]`` lists, which, when used as a
top-level expression, translate to Python list literals in the
compilation phase.
``hy.models.HyExpression`` is a :ref:`HySequence` for bracketed ``[]``
lists, which, when used as a top-level expression, translate to Python
list literals in the compilation phase.
Adding a HyList to another iterable object reuses the class of the
left-hand-side object, a useful behavior when you want to concatenate Hy
objects in a macro, for instance.
.. _hyexpression:
HyExpression
~~~~~~~~~~~~
``hy.models.HyExpression`` inherits :ref:`HyList` for
``hy.models.HyExpression`` inherits :ref:`HySequence` for
parenthesized ``()`` expressions. The compilation result of those
expressions depends on the first element of the list: the compiler
dispatches expressions between compiler special-forms, user-defined
@ -78,8 +87,8 @@ macros, and regular Python function calls.
HyDict
~~~~~~
``hy.models.HyDict`` inherits :ref:`HyList` for curly-bracketed ``{}``
expressions, which compile down to a Python dictionary literal.
``hy.models.HyDict`` inherits :ref:`HySequence` for curly-bracketed
``{}`` expressions, which compile down to a Python dictionary literal.
The decision of using a list instead of a dict as the base class for
``HyDict`` allows easier manipulation of dicts in macros, with the added

View File

@ -5,7 +5,7 @@
from hy.models import (HyObject, HyExpression, HyKeyword, HyInteger, HyComplex,
HyString, HyBytes, HySymbol, HyFloat, HyList, HySet,
HyDict, wrap_value)
HyDict, HySequence, wrap_value)
from hy.errors import HyCompileError, HyTypeError
from hy.lex.parser import mangle
@ -670,7 +670,7 @@ class HyASTCompiler(object):
name = form.__class__.__name__
imports = set([name])
if isinstance(form, (HyList, HyDict, HySet)):
if isinstance(form, HySequence):
if not form:
contents = HyList()
else:

View File

@ -233,9 +233,9 @@ class HyComplex(HyObject, complex):
_wrappers[complex] = HyComplex
class HyList(HyObject, list):
class HySequence(HyObject, list):
"""
Hy List. Basically just a list.
An abstract type for sequence-like models to inherit from.
"""
def replace(self, other):
@ -246,23 +246,23 @@ class HyList(HyObject, list):
return self
def __add__(self, other):
return self.__class__(super(HyList, self).__add__(other))
return self.__class__(super(HySequence, self).__add__(other))
def __getslice__(self, start, end):
return self.__class__(super(HyList, self).__getslice__(start, end))
return self.__class__(super(HySequence, self).__getslice__(start, end))
def __getitem__(self, item):
ret = super(HyList, self).__getitem__(item)
ret = super(HySequence, self).__getitem__(item)
if isinstance(item, slice):
return self.__class__(ret)
return ret
color = staticmethod(colored.cyan)
color = None
def __repr__(self):
return str(self) if PRETTY else super(HyList, self).__repr__()
return str(self) if PRETTY else super(HySequence, self).__repr__()
def __str__(self):
with pretty():
@ -276,11 +276,15 @@ class HyList(HyObject, list):
else:
return '' + c(self.__class__.__name__ + "()")
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)
class HyDict(HyList):
class HyDict(HySequence):
"""
HyDict (just a representation of a dict)
"""
@ -316,7 +320,7 @@ class HyDict(HyList):
_wrappers[dict] = lambda d: HyDict(wrap_value(x) for x in sum(d.items(), ()))
class HyExpression(HyList):
class HyExpression(HySequence):
"""
Hy S-Expression. Basically just a list.
"""
@ -327,7 +331,7 @@ _wrappers[Fraction] = lambda e: HyExpression(
[HySymbol("fraction"), wrap_value(e.numerator), wrap_value(e.denominator)])
class HySet(HyList):
class HySet(HySequence):
"""
Hy set (just a representation of a set)
"""

View File

@ -241,6 +241,9 @@ def test_ast_good_lambda():
def test_ast_bad_lambda():
"Make sure AST can't compile invalid lambda"
cant_compile("(fn)")
cant_compile("(fn ())")
cant_compile("(fn () 1)")
cant_compile("(fn (x) 1)")
def test_ast_good_yield():
@ -381,11 +384,11 @@ def test_ast_expression_basics():
def test_ast_anon_fns_basics():
""" Ensure anon fns work. """
code = can_compile("(fn (x) (* x x))").body[0].value
code = can_compile("(fn [x] (* x x))").body[0].value
assert type(code) == ast.Lambda
code = can_compile("(fn (x) (print \"multiform\") (* x x))").body[0]
code = can_compile("(fn [x] (print \"multiform\") (* x x))").body[0]
assert type(code) == ast.FunctionDef
can_compile("(fn (x))")
can_compile("(fn [x])")
cant_compile("(fn)")
@ -420,16 +423,16 @@ def test_argument_destructuring():
def test_lambda_list_keywords_rest():
""" Ensure we can compile functions with lambda list keywords."""
can_compile("(fn (x &rest xs) (print xs))")
cant_compile("(fn (x &rest xs &rest ys) (print xs))")
can_compile("(fn (&optional a &rest xs) (print xs))")
can_compile("(fn [x &rest xs] (print xs))")
cant_compile("(fn [x &rest xs &rest ys] (print xs))")
can_compile("(fn [&optional a &rest xs] (print xs))")
def test_lambda_list_keywords_kwargs():
""" Ensure we can compile functions with &kwargs."""
can_compile("(fn (x &kwargs kw) (list x kw))")
cant_compile("(fn (x &kwargs xs &kwargs ys) (list x xs ys))")
can_compile("(fn (&optional x &kwargs kw) (list x kw))")
can_compile("(fn [x &kwargs kw] (list x kw))")
cant_compile("(fn [x &kwargs xs &kwargs ys] (list x xs ys))")
can_compile("(fn [&optional x &kwargs kw] (list x kw))")
def test_lambda_list_keywords_kwonly():
@ -452,8 +455,8 @@ def test_lambda_list_keywords_kwonly():
def test_lambda_list_keywords_mixed():
""" Ensure we can mix them up."""
can_compile("(fn (x &rest xs &kwargs kw) (list x xs kw))")
cant_compile("(fn (x &rest xs &fasfkey {bar \"baz\"}))")
can_compile("(fn [x &rest xs &kwargs kw] (list x xs kw))")
cant_compile("(fn [x &rest xs &fasfkey {bar \"baz\"}])")
if PY3:
can_compile("(fn [x &rest xs &kwargs kwxs &kwonly kwoxs]"
" (list x xs kwxs kwoxs))")

View File

@ -84,5 +84,5 @@ def test_eval():
assert eval_str('(.strip " fooooo ")') == 'fooooo'
assert eval_str(
'(if True "this is if true" "this is if false")') == "this is if true"
assert eval_str('(list-comp (pow num 2) (num (range 100)) (= (% num 2) 1))') == [
assert eval_str('(list-comp (pow num 2) [num (range 100)] (= (% num 2) 1))') == [
pow(num, 2) for num in range(100) if num % 2 == 1]

View File

@ -920,48 +920,48 @@
(defn test-list-comprehensions []
"NATIVE: test list comprehensions"
(assert (= (list-comp (* x 2) (x (range 2))) [0 2]))
(assert (= (list-comp (* x 2) (x (range 4)) (% x 2)) [2 6]))
(assert (= (sorted (list-comp (* y 2) ((, x y) (.items {"1" 1 "2" 2}))))
(assert (= (list-comp (* x 2) [x (range 2)]) [0 2]))
(assert (= (list-comp (* x 2) [x (range 4)] (% x 2)) [2 6]))
(assert (= (sorted (list-comp (* y 2) [(, x y) (.items {"1" 1 "2" 2})]))
[2 4]))
(assert (= (list-comp (, x y) (x (range 2) y (range 2)))
(assert (= (list-comp (, x y) [x (range 2) y (range 2)])
[(, 0 0) (, 0 1) (, 1 0) (, 1 1)]))
(assert (= (list-comp j (j [1 2])) [1 2])))
(assert (= (list-comp j [j [1 2]]) [1 2])))
(defn test-set-comprehensions []
"NATIVE: test set comprehensions"
(assert (instance? set (set-comp x [x (range 2)])))
(assert (= (set-comp (* x 2) (x (range 2))) (set [0 2])))
(assert (= (set-comp (* x 2) (x (range 4)) (% x 2)) (set [2 6])))
(assert (= (set-comp (* y 2) ((, x y) (.items {"1" 1 "2" 2})))
(assert (= (set-comp (* x 2) [x (range 2)]) (set [0 2])))
(assert (= (set-comp (* x 2) [x (range 4)] (% x 2)) (set [2 6])))
(assert (= (set-comp (* y 2) [(, x y) (.items {"1" 1 "2" 2})])
(set [2 4])))
(assert (= (set-comp (, x y) (x (range 2) y (range 2)))
(assert (= (set-comp (, x y) [x (range 2) y (range 2)])
(set [(, 0 0) (, 0 1) (, 1 0) (, 1 1)])))
(assert (= (set-comp j (j [1 2])) (set [1 2]))))
(assert (= (set-comp j [j [1 2]]) (set [1 2]))))
(defn test-dict-comprehensions []
"NATIVE: test dict comprehensions"
(assert (instance? dict (dict-comp x x [x (range 2)])))
(assert (= (dict-comp x (* x 2) (x (range 2))) {1 2 0 0}))
(assert (= (dict-comp x (* x 2) (x (range 4)) (% x 2)) {3 6 1 2}))
(assert (= (dict-comp x (* y 2) ((, x y) (.items {"1" 1 "2" 2})))
(assert (= (dict-comp x (* x 2) [x (range 2)]) {1 2 0 0}))
(assert (= (dict-comp x (* x 2) [x (range 4)] (% x 2)) {3 6 1 2}))
(assert (= (dict-comp x (* y 2) [(, x y) (.items {"1" 1 "2" 2})])
{"2" 4 "1" 2}))
(assert (= (dict-comp (, x y) (+ x y) (x (range 2) y (range 2)))
(assert (= (dict-comp (, x y) (+ x y) [x (range 2) y (range 2)])
{(, 0 0) 0 (, 1 0) 1 (, 0 1) 1 (, 1 1) 2})))
(defn test-generator-expressions []
"NATIVE: test generator expressions"
(assert (not (instance? list (genexpr x [x (range 2)]))))
(assert (= (list (genexpr (* x 2) (x (range 2)))) [0 2]))
(assert (= (list (genexpr (* x 2) (x (range 4)) (% x 2))) [2 6]))
(assert (= (list (sorted (genexpr (* y 2) ((, x y) (.items {"1" 1 "2" 2})))))
(assert (= (list (genexpr (* x 2) [x (range 2)])) [0 2]))
(assert (= (list (genexpr (* x 2) [x (range 4)] (% x 2))) [2 6]))
(assert (= (list (sorted (genexpr (* y 2) [(, x y) (.items {"1" 1 "2" 2})])))
[2 4]))
(assert (= (list (genexpr (, x y) (x (range 2) y (range 2))))
(assert (= (list (genexpr (, x y) [x (range 2) y (range 2)]))
[(, 0 0) (, 0 1) (, 1 0) (, 1 1)]))
(assert (= (list (genexpr j (j [1 2]))) [1 2])))
(assert (= (list (genexpr j [j [1 2]])) [1 2])))
(defn test-defn-order []
@ -1370,7 +1370,7 @@
(defn test-lambda-keyword-lists []
"NATIVE: test lambda keyword lists"
(defn foo (x &rest xs &kwargs kw) [x xs kw])
(defn foo [x &rest xs &kwargs kw] [x xs kw])
(assert (= (foo 10 20 30) [10 (, 20 30) {}])))
@ -1721,7 +1721,7 @@ macros()
(defmacro identify-keywords [&rest elts]
`(list
(map
(fn (x) (if (is-keyword x) "keyword" "other"))
(fn [x] (if (is-keyword x) "keyword" "other"))
~elts)))
(defn test-keywords-and-macros []