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:
parent
032247e380
commit
87aced2370
4
NEWS.rst
4
NEWS.rst
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
24
hy/models.py
24
hy/models.py
@ -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)
|
||||
"""
|
||||
|
@ -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))")
|
||||
|
@ -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]
|
||||
|
@ -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 []
|
||||
|
Loading…
Reference in New Issue
Block a user