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 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 * Mangling rules have been overhauled, such that mangled names
are always legal Python identifiers are always legal Python identifiers
* `_` and `-` are now equivalent even as single-character names * `_` 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, This module has a flag to disable coloring,
and a method ``clean`` to strip colored strings of their color tags. 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:
HyList HyList
~~~~~~ ~~~~~~~~~~~~
``hy.models.HyList`` is the base class of "iterable" Hy models. Its ``hy.models.HyExpression`` is a :ref:`HySequence` for bracketed ``[]``
basic use is to represent bracketed ``[]`` lists, which, when used as a lists, which, when used as a top-level expression, translate to Python
top-level expression, translate to Python list literals in the list literals in the compilation phase.
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:
HyExpression HyExpression
~~~~~~~~~~~~ ~~~~~~~~~~~~
``hy.models.HyExpression`` inherits :ref:`HyList` for ``hy.models.HyExpression`` inherits :ref:`HySequence` for
parenthesized ``()`` expressions. The compilation result of those parenthesized ``()`` expressions. The compilation result of those
expressions depends on the first element of the list: the compiler expressions depends on the first element of the list: the compiler
dispatches expressions between compiler special-forms, user-defined dispatches expressions between compiler special-forms, user-defined
@ -78,8 +87,8 @@ macros, and regular Python function calls.
HyDict HyDict
~~~~~~ ~~~~~~
``hy.models.HyDict`` inherits :ref:`HyList` for curly-bracketed ``{}`` ``hy.models.HyDict`` inherits :ref:`HySequence` for curly-bracketed
expressions, which compile down to a Python dictionary literal. ``{}`` expressions, which compile down to a Python dictionary literal.
The decision of using a list instead of a dict as the base class for 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 ``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, from hy.models import (HyObject, HyExpression, HyKeyword, HyInteger, HyComplex,
HyString, HyBytes, HySymbol, HyFloat, HyList, HySet, HyString, HyBytes, HySymbol, HyFloat, HyList, HySet,
HyDict, wrap_value) HyDict, HySequence, wrap_value)
from hy.errors import HyCompileError, HyTypeError from hy.errors import HyCompileError, HyTypeError
from hy.lex.parser import mangle from hy.lex.parser import mangle
@ -670,7 +670,7 @@ class HyASTCompiler(object):
name = form.__class__.__name__ name = form.__class__.__name__
imports = set([name]) imports = set([name])
if isinstance(form, (HyList, HyDict, HySet)): if isinstance(form, HySequence):
if not form: if not form:
contents = HyList() contents = HyList()
else: else:

View File

@ -233,9 +233,9 @@ class HyComplex(HyObject, complex):
_wrappers[complex] = HyComplex _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): def replace(self, other):
@ -246,23 +246,23 @@ class HyList(HyObject, list):
return self return self
def __add__(self, other): 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): 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): def __getitem__(self, item):
ret = super(HyList, self).__getitem__(item) ret = super(HySequence, self).__getitem__(item)
if isinstance(item, slice): if isinstance(item, slice):
return self.__class__(ret) return self.__class__(ret)
return ret return ret
color = staticmethod(colored.cyan) color = None
def __repr__(self): 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): def __str__(self):
with pretty(): with pretty():
@ -276,11 +276,15 @@ class HyList(HyObject, list):
else: else:
return '' + c(self.__class__.__name__ + "()") 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[list] = lambda l: HyList(wrap_value(x) for x in l)
_wrappers[tuple] = lambda t: HyList(wrap_value(x) for x in t) _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) 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(), ())) _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. 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)]) [HySymbol("fraction"), wrap_value(e.numerator), wrap_value(e.denominator)])
class HySet(HyList): class HySet(HySequence):
""" """
Hy set (just a representation of a set) Hy set (just a representation of a set)
""" """

View File

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

View File

@ -84,5 +84,5 @@ def test_eval():
assert eval_str('(.strip " fooooo ")') == 'fooooo' assert eval_str('(.strip " fooooo ")') == 'fooooo'
assert eval_str( assert eval_str(
'(if True "this is if true" "this is if false")') == "this is if true" '(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] pow(num, 2) for num in range(100) if num % 2 == 1]

View File

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