diff --git a/NEWS.rst b/NEWS.rst index acd3d09..57d69a7 100644 --- a/NEWS.rst +++ b/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 diff --git a/docs/language/internals.rst b/docs/language/internals.rst index 3c5e0b1..31adf0d 100644 --- a/docs/language/internals.rst +++ b/docs/language/internals.rst @@ -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 diff --git a/hy/compiler.py b/hy/compiler.py index 83c4e25..284c7fc 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -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: diff --git a/hy/models.py b/hy/models.py index 097c7b9..a5e3b6e 100644 --- a/hy/models.py +++ b/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) """ diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index fb23169..e5a0ddd 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -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))") diff --git a/tests/importer/test_importer.py b/tests/importer/test_importer.py index 31b4705..44669eb 100644 --- a/tests/importer/test_importer.py +++ b/tests/importer/test_importer.py @@ -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] diff --git a/tests/native_tests/language.hy b/tests/native_tests/language.hy index f8cea69..6840f29 100644 --- a/tests/native_tests/language.hy +++ b/tests/native_tests/language.hy @@ -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 []