From 097647bf6fb9026a5d84f1bcd05101189392440a Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Sun, 8 Apr 2018 15:56:33 -0700 Subject: [PATCH 1/3] Remove tests of cons cells --- tests/compilers/test_ast.py | 5 -- tests/native_tests/cons.hy | 71 ---------------------------- tests/native_tests/contrib/walk.hy | 1 - tests/test_lex.py | 30 +----------- tests/test_models.py | 74 ++---------------------------- 5 files changed, 6 insertions(+), 175 deletions(-) delete mode 100644 tests/native_tests/cons.hy diff --git a/tests/compilers/test_ast.py b/tests/compilers/test_ast.py index c779fbd..dbf5858 100644 --- a/tests/compilers/test_ast.py +++ b/tests/compilers/test_ast.py @@ -569,11 +569,6 @@ def test_attribute_empty(): cant_compile('[2].foo') -def test_cons_correct(): - """Ensure cons gets compiled correctly""" - can_compile("(cons a b)") - - def test_invalid_list_comprehension(): """Ensure that invalid list comprehensions do not break the compiler""" cant_compile("(genexpr x [])") diff --git a/tests/native_tests/cons.hy b/tests/native_tests/cons.hy deleted file mode 100644 index 5afde7d..0000000 --- a/tests/native_tests/cons.hy +++ /dev/null @@ -1,71 +0,0 @@ -;; Copyright 2018 the authors. -;; This file is part of Hy, which is free software licensed under the Expat -;; license. See the LICENSE. - -(defmacro car [x] `(get ~x 0)) -(defmacro cdr [x] `(cut ~x 1)) - - -(defn test-cons-mutability [] - "Test the mutability of conses" - (setv tree (cons (cons 1 2) (cons 2 3))) - (setv (car tree) "foo") - (assert (= tree (cons "foo" (cons 2 3)))) - (setv (cdr tree) "bar") - (assert (= tree (cons "foo" "bar")))) - - -(defn test-cons-quoting [] - "Test quoting of conses" - (assert (= (cons 1 2) (quote (1 . 2)))) - (assert (= (quote foo) (car (quote (foo . bar))))) - (assert (= (quote bar) (cdr (quote (foo . bar)))))) - - -(defn test-cons-behavior [] - "NATIVE: test the behavior of cons is consistent" - (defn t= [a b] - (and (= a b) (= (type a) (type b)))) - (assert (t= (cons 1 2) '(1 . 2))) - (assert (t= (cons 1 None) '(1))) - (assert (t= (cons None 2) '(None . 2))) - (assert (t= (cons 1 []) [1])) - (setv tree (cons (cons 1 2) (cons 2 3))) - (assert (t= (car tree) (cons 1 2))) - (assert (t= (cdr tree) (cons 2 3)))) - - -(defn test-cons-iteration [] - "NATIVE: test the iteration behavior of cons" - (setv x '(0 1 2 3 4 . 5)) - (setv it (iter x)) - (for* [i (range 6)] - (assert (= i (next it)))) - (assert - (= 'success - (try - (do - (next it) - 'failurenext) - (except [e TypeError] (if (= e.args (, "Iteration on malformed cons")) - 'success - 'failureexc)) - (except [e Exception] 'failureexc2))))) - - -(defn test-cons? [] - "NATIVE: test behavior of cons?" - (assert (cons? (cons 1 2))) - (assert (cons? '(1 . 2))) - (assert (cons? '(1 2 3 . 4))) - (assert (cons? (list* 1 2 3))) - (assert (not (cons? (cons 1 [2])))) - (assert (not (cons? (list* 1 None))))) - - -(defn test-list* [] - "NATIVE: test behavior of list*" - (assert (= 1 (list* 1))) - (assert (= (cons 1 2) (list* 1 2))) - (assert (= (cons 1 (cons 2 3)) (list* 1 2 3))) - (assert (= '(1 2 3 4 . 5) (list* 1 2 3 4 5)))) diff --git a/tests/native_tests/contrib/walk.hy b/tests/native_tests/contrib/walk.hy index 6882490..f6463e1 100644 --- a/tests/native_tests/contrib/walk.hy +++ b/tests/native_tests/contrib/walk.hy @@ -10,7 +10,6 @@ (setv walk-form '(print {"foo" "bar" "array" [1 2 3 [4]] "something" (+ 1 2 3 4) - "cons!" (cons 1 2) "quoted?" '(foo)})) (defn collector [acc x] diff --git a/tests/test_lex.py b/tests/test_lex.py index ca06560..411c246 100644 --- a/tests/test_lex.py +++ b/tests/test_lex.py @@ -4,7 +4,7 @@ from math import isnan from hy.models import (HyExpression, HyInteger, HyFloat, HyComplex, HySymbol, - HyString, HyDict, HyList, HySet, HyCons, HyKeyword) + HyString, HyDict, HyList, HySet, HyKeyword) from hy.lex import LexException, PrematureEndOfInput, tokenize import pytest @@ -350,34 +350,6 @@ def test_lex_comment_382(): assert entry == [HySymbol("foo")] -def test_simple_cons(): - """Check that cons gets tokenized correctly""" - entry = tokenize("(a . b)")[0] - assert entry == HyCons(HySymbol("a"), HySymbol("b")) - - -def test_dotted_list(): - """Check that dotted lists get tokenized correctly""" - entry = tokenize("(a b c . (d . e))")[0] - assert entry == HyCons(HySymbol("a"), - HyCons(HySymbol("b"), - HyCons(HySymbol("c"), - HyCons(HySymbol("d"), - HySymbol("e"))))) - - -def test_cons_list(): - """Check that cons of something and a list gets tokenized as a list""" - entry = tokenize("(a . [])")[0] - assert entry == HyList([HySymbol("a")]) - assert type(entry) == HyList - entry = tokenize("(a . ())")[0] - assert entry == HyExpression([HySymbol("a")]) - assert type(entry) == HyExpression - entry = tokenize("(a b . {})")[0] - assert entry == HyDict([HySymbol("a"), HySymbol("b")]) - assert type(entry) == HyDict - def test_discard(): """Check that discarded terms are removed properly.""" # empty diff --git a/tests/test_models.py b/tests/test_models.py index 9d924e9..ddd00de 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -7,8 +7,7 @@ import hy from clint.textui.colored import clean from hy._compat import long_type, str_type from hy.models import (wrap_value, replace_hy_obj, HyString, HyInteger, HyList, - HyDict, HySet, HyExpression, HyCons, HyComplex, HyFloat, - pretty) + HyDict, HySet, HyExpression, HyComplex, HyFloat, pretty) def test_wrap_long_type(): @@ -96,41 +95,6 @@ def test_set(): assert hyset == [3, 1, 2, 2] -def test_cons_slicing(): - """Check that cons slicing works as expected""" - cons = HyCons("car", "cdr") - assert cons[0] == "car" - assert cons[1:] == "cdr" - try: - cons[:] - assert True is False - except IndexError: - pass - - try: - cons[1] - assert True is False - except IndexError: - pass - - -def test_cons_replacing(): - """Check that assigning to a cons works as expected""" - cons = HyCons("foo", "bar") - cons[0] = "car" - - assert cons == HyCons("car", "bar") - - cons[1:] = "cdr" - assert cons == HyCons("car", "cdr") - - try: - cons[:] = "foo" - assert True is False - except IndexError: - pass - - def test_number_model_copy(): i = HyInteger(42) assert (i == copy.copy(i)) @@ -146,7 +110,7 @@ def test_number_model_copy(): PRETTY_STRINGS = { - k % ('[1.0] {1.0} (1.0) #{1.0} (0.0 1.0 . 2.0)',): + k % ('[1.0] {1.0} (1.0) #{1.0}',): v.format(""" HyList([ HyFloat(1.0)]), @@ -156,16 +120,12 @@ PRETTY_STRINGS = { HyExpression([ HyFloat(1.0)]), HySet([ - HyFloat(1.0)]), - """) + HyFloat(1.0)])""") for k, v in {'[%s]': 'HyList([{}])', '#{%s}': 'HySet([{}])'}.items()} PRETTY_STRINGS.update({ - '{[1.0] {1.0} (1.0) #{1.0} (0.0 1.0 . 2.0)}': + '{[1.0] {1.0} (1.0) #{1.0}}': """HyDict([ HyList([ HyFloat(1.0)]), @@ -177,29 +137,7 @@ PRETTY_STRINGS.update({ HyFloat(1.0)]), HySet([ HyFloat(1.0)]) - , - # odd -])""" - , - '([1.0] {1.0} (1.0) #{1.0} (0.0 1.0 . 2.0) . 3.0)': - """ -. HyFloat(3.0))>""" + ])""" , '[1.0 1j [] {} () #{}]': """HyList([ @@ -239,8 +177,6 @@ PRETTY_STRINGS.update({ def test_compound_model_repr(): HY_LIST_MODELS = (HyExpression, HyDict, HySet, HyList) with pretty(False): - assert eval(repr(HyCons(1, 2))).__class__ is HyCons - assert eval(repr(HyCons(1, 2))) == HyCons(1, 2) for model in HY_LIST_MODELS: assert eval(repr(model())).__class__ is model assert eval(repr(model([1, 2]))) == model([1, 2]) From c93a60ede001246a5695133916d3917475058d49 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Sun, 8 Apr 2018 16:05:23 -0700 Subject: [PATCH 2/3] Remove support for cons cells --- hy/__init__.py | 2 +- hy/compiler.py | 26 +----------- hy/contrib/walk.hy | 3 -- hy/core/language.hy | 22 ++-------- hy/lex/parser.py | 27 +------------ hy/models.py | 98 --------------------------------------------- 6 files changed, 9 insertions(+), 169 deletions(-) diff --git a/hy/__init__.py b/hy/__init__.py index 42d3133..d13c35a 100644 --- a/hy/__init__.py +++ b/hy/__init__.py @@ -5,7 +5,7 @@ except ImportError: __version__ = 'unknown' -from hy.models import HyExpression, HyInteger, HyKeyword, HyComplex, HyString, HyBytes, HySymbol, HyFloat, HyDict, HyList, HySet, HyCons # NOQA +from hy.models import HyExpression, HyInteger, HyKeyword, HyComplex, HyString, HyBytes, HySymbol, HyFloat, HyDict, HyList, HySet # NOQA import hy.importer # NOQA diff --git a/hy/compiler.py b/hy/compiler.py index b1ee4f2..d94e94b 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, HyCons, wrap_value) + HyDict, wrap_value) from hy.errors import HyCompileError, HyTypeError from hy.lex.parser import mangle @@ -89,7 +89,7 @@ def builds(*types, **kwargs): def spoof_positions(obj): - if not isinstance(obj, HyObject) or isinstance(obj, HyCons): + if not isinstance(obj, HyObject): return if not hasattr(obj, "start_column"): obj.start_column = 0 @@ -719,24 +719,6 @@ class HyASTCompiler(object): return imports, HyExpression([HySymbol(name), contents]).replace(form), False - elif isinstance(form, HyCons): - ret = HyExpression([HySymbol(name)]) - nimport, contents, splice = self._render_quoted_form(form.car, - level) - if splice: - raise HyTypeError(form, "Can't splice dotted lists yet") - imports.update(nimport) - ret.append(contents) - - nimport, contents, splice = self._render_quoted_form(form.cdr, - level) - if splice: - raise HyTypeError(form, "Can't splice the cdr of a cons") - imports.update(nimport) - ret.append(contents) - - return imports, ret.replace(form), False - elif isinstance(form, HySymbol): return imports, HyExpression([HySymbol(name), HyString(form)]).replace(form), False @@ -2131,10 +2113,6 @@ class HyASTCompiler(object): if building == "eval_and_compile" else Result()) - @builds(HyCons) - def compile_cons(self, cons): - raise HyTypeError(cons, "Can't compile a top-level cons cell") - @builds(HyInteger, HyFloat, HyComplex) def compile_numeric_literal(self, x, building): f = {HyInteger: long_type, diff --git a/hy/contrib/walk.hy b/hy/contrib/walk.hy index 7ce171d..6bc93d1 100644 --- a/hy/contrib/walk.hy +++ b/hy/contrib/walk.hy @@ -18,9 +18,6 @@ (outer (HyExpression (map inner form)))] [(instance? HyDict form) (HyDict (outer (HyExpression (map inner form))))] - [(cons? form) - (outer (cons (inner (first form)) - (inner (rest form))))] [(instance? list form) ((type form) (outer (HyExpression (map inner form))))] [(coll? form) diff --git a/hy/core/language.hy b/hy/core/language.hy index 0904b22..4cba9a3 100644 --- a/hy/core/language.hy +++ b/hy/core/language.hy @@ -18,7 +18,7 @@ (if-python2 (import [collections :as cabc]) (import [collections.abc :as cabc])) -(import [hy.models [HyCons HySymbol HyKeyword]]) +(import [hy.models [HySymbol HyKeyword]]) (import [hy.lex [LexException PrematureEndOfInput tokenize]]) (import [hy.lex.parser [mangle unmangle]]) (import [hy.compiler [HyASTCompiler spoof-positions]]) @@ -50,14 +50,6 @@ (fn [&rest args &kwargs kwargs] (not (f #* args #** kwargs)))) -(defn cons [a b] - "Return a fresh cons cell with car = `a` and cdr = `b`." - (HyCons a b)) - -(defn cons? [c] - "Check whether `c` is a cons cell." - (instance? HyCons c)) - (defn constantly [value] "Create a new function that always returns `value` regardless of its input." (fn [&rest args &kwargs kwargs] @@ -294,7 +286,7 @@ Return series of accumulated sums (or other binary function results)." (defn juxt [f &rest fs] "Return a function applying each `fs` to args, collecting results in a list." - (setv fs (cons f fs)) + (setv fs (+ (, f) fs)) (fn [&rest args &kwargs kwargs] (list-comp (f #* args #** kwargs) [f fs]))) @@ -302,12 +294,6 @@ Return series of accumulated sums (or other binary function results)." "Return last item from `coll`." (get (tuple coll) -1)) -(defn list* [hd &rest tl] - "Return a chain of nested cons cells (dotted list) containing `hd` and `tl`." - (if (not tl) - hd - (cons hd (list* #* tl)))) - (defn macroexpand [form] "Return the full macro expansion of `form`." (import hy.macros) @@ -488,11 +474,11 @@ Even objects with the __name__ magic will work." (setv EXPORTS '[*map accumulate butlast calling-module-name chain coll? combinations - comp complement compress cons cons? constantly count cycle dec distinct + comp complement compress constantly count cycle dec distinct disassemble drop drop-last drop-while empty? eval even? every? exec first filter flatten float? fraction gensym group-by identity inc input instance? integer integer? integer-char? interleave interpose islice iterable? - iterate iterator? juxt keyword keyword? last list* macroexpand + iterate iterator? juxt keyword keyword? last macroexpand macroexpand-1 mangle map merge-with multicombinations name neg? none? nth numeric? odd? partition permutations pos? product range read read-str remove repeat repeatedly rest reduce second some string string? symbol? diff --git a/hy/lex/parser.py b/hy/lex/parser.py index e94e5a4..236b825 100755 --- a/hy/lex/parser.py +++ b/hy/lex/parser.py @@ -11,9 +11,8 @@ import string, re, unicodedata from rply import ParserGenerator from hy._compat import PY3, str_type, isidentifier, UCS4 -from hy.models import (HyBytes, HyComplex, HyCons, HyDict, HyExpression, - HyFloat, HyInteger, HyKeyword, HyList, HySet, HyString, - HySymbol) +from hy.models import (HyBytes, HyComplex, HyDict, HyExpression, HyFloat, + HyInteger, HyKeyword, HyList, HySet, HyString, HySymbol) from .lexer import lexer from .exceptions import LexException, PrematureEndOfInput @@ -151,28 +150,6 @@ def reject_spurious_dots(*items): @pg.production("paren : LPAREN list_contents RPAREN") @set_boundaries def paren(p): - cont = p[1] - - # Dotted lists are expressions of the form - # (a b c . d) - # that evaluate to nested cons cells of the form - # (a . (b . (c . d))) - if len(cont) >= 3 and isinstance(cont[-2], HySymbol) and cont[-2] == ".": - - reject_spurious_dots(cont[:-2], cont[-1:]) - - if len(cont) == 3: - # Two-item dotted list: return the cons cell directly - return HyCons(cont[0], cont[2]) - else: - # Return a nested cons cell - return HyCons(cont[0], paren([p[0], cont[1:], p[2]])) - - # Warn preemptively on a malformed dotted list. - # Only check for dots after the first item to allow for a potential - # attribute accessor shorthand - reject_spurious_dots(cont[1:]) - return HyExpression(p[1]) diff --git a/hy/models.py b/hy/models.py index 5a16c02..097c7b9 100644 --- a/hy/models.py +++ b/hy/models.py @@ -334,101 +334,3 @@ class HySet(HyList): color = staticmethod(colored.red) _wrappers[set] = lambda s: HySet(wrap_value(x) for x in s) - - -class HyCons(HyObject): - """ - HyCons: a cons object. - - Building a HyCons of something and a HyList really builds a HyList - """ - - __slots__ = ["car", "cdr"] - - def __new__(cls, car, cdr): - if isinstance(cdr, list): - - # Keep unquotes in the cdr of conses - if type(cdr) == HyExpression: - if len(cdr) > 0 and type(cdr[0]) == HySymbol: - if cdr[0] in ("unquote", "unquote-splice"): - return super(HyCons, cls).__new__(cls) - - return cdr.__class__([wrap_value(car)] + cdr) - - elif cdr is None: - return HyExpression([wrap_value(car)]) - - else: - return super(HyCons, cls).__new__(cls) - - def __init__(self, car, cdr): - self.car = wrap_value(car) - self.cdr = wrap_value(cdr) - - def __getitem__(self, n): - if n == 0: - return self.car - if n == slice(1, None): - return self.cdr - - raise IndexError( - "Can only get the car ([0]) or the cdr ([1:]) of a HyCons") - - def __setitem__(self, n, new): - if n == 0: - self.car = new - return - if n == slice(1, None): - self.cdr = new - return - - raise IndexError( - "Can only set the car ([0]) or the cdr ([1:]) of a HyCons") - - def __iter__(self): - yield self.car - try: - iterator = (i for i in self.cdr) - except TypeError: - if self.cdr is not None: - yield self.cdr - raise TypeError("Iteration on malformed cons") - else: - for i in iterator: - yield i - - def replace(self, other): - if self.car is not None: - replace_hy_obj(self.car, other) - if self.cdr is not None: - replace_hy_obj(self.cdr, other) - - HyObject.replace(self, other) - - def __repr__(self): - if PRETTY: - return str(self) - else: - return "HyCons({}, {})".format( - repr(self.car), repr(self.cdr)) - - def __str__(self): - with pretty(): - c = colored.yellow - lines = ['' + c(""))) - return '\n'.join(lines) - - def __eq__(self, other): - return ( - isinstance(other, self.__class__) and - self.car == other.car and - self.cdr == other.cdr - ) From bbf669d4072355f431c5ab2813dc5f32fa5eafc2 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Sun, 8 Apr 2018 16:16:40 -0700 Subject: [PATCH 3/3] Update docs and NEWS for HyCons removal --- NEWS.rst | 6 +++ docs/language/core.rst | 78 ------------------------------------- docs/language/internals.rst | 33 ---------------- 3 files changed, 6 insertions(+), 111 deletions(-) diff --git a/NEWS.rst b/NEWS.rst index a2c047b..a52a2ae 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -3,6 +3,12 @@ Unreleased ============================== +Removals +------------------------------ +* Dotted lists, `HyCons`, `cons`, `cons?`, and `list*` have been removed. + These were redundant with Python's built-in data structures and Hy's most + common model types (`HyExpression`, `HyList`, etc.). + Other Breaking Changes ------------------------------ * Mangling rules have been overhauled, such that mangled names diff --git a/docs/language/core.rst b/docs/language/core.rst index 4df50c8..7c8963c 100644 --- a/docs/language/core.rst +++ b/docs/language/core.rst @@ -98,49 +98,6 @@ inverted. So, ``((complement f) x)`` is equivalent to ``(not (f x))``. True -cons ----- - -.. versionadded:: 0.10.0 - -Usage: ``(cons a b)`` - -Returns a fresh :ref:`cons cell ` with car *a* and cdr *b*. - -.. code-block:: hy - - => (setv a (cons 'hd 'tl)) - - => (= 'hd (get a 0)) - True - - => (= 'tl (cut a 1)) - True - - -cons? ------ - -.. versionadded:: 0.10.0 - -Usage: ``(cons? foo)`` - -Checks whether *foo* is a :ref:`cons cell `. - -.. code-block:: hy - - => (setv a (cons 'hd 'tl)) - - => (cons? a) - True - - => (cons? None) - False - - => (cons? [1 2 3]) - False - - .. _constantly: constantly @@ -606,41 +563,6 @@ Check whether *foo* is a :ref:`keyword`. => (keyword? foo) False -.. _list*-fn: - -list* ------ - -Usage: ``(list* head &rest tail)`` - -Generates a chain of nested cons cells (a dotted list) containing the -arguments. If the argument list only has one element, return it. - -.. code-block:: hy - - => (list* 1 2 3 4) - - => (list* 1 2 3 [4]) - [HyInteger(1), HyInteger(2), HyInteger(3), 4] - => (list* 1) - 1 - => (cons? (list* 1 2 3 4)) - True - => (list* 1 10 2 20 '{}) - HyDict([ - HyInteger(1), HyInteger(10), - HyInteger(2), HyInteger(20)]) - => (list* 1 10 2 20 {}) - .. _macroexpand-fn: diff --git a/docs/language/internals.rst b/docs/language/internals.rst index 155ab0a..3c5e0b1 100644 --- a/docs/language/internals.rst +++ b/docs/language/internals.rst @@ -168,39 +168,6 @@ HyKeyword ``hy.models.HyKeyword`` represents keywords in Hy. Keywords are symbols starting with a ``:``. See :ref:`syntax-keywords`. -.. _hycons: - -Cons Cells -========== - -``hy.models.HyCons`` is a representation of Python-friendly `cons -cells`_. Cons cells are especially useful to mimic features of "usual" -LISP variants such as Scheme or Common Lisp. - -.. _cons cells: https://en.wikipedia.org/wiki/Cons - -A cons cell is a 2-item object, containing a ``car`` (head) and a -``cdr`` (tail). In some Lisp variants, the cons cell is the fundamental -building block, and S-expressions are actually represented as linked -lists of cons cells. This is not the case in Hy, as the usual -expressions are made of Python lists wrapped in a -``HyExpression``. However, the ``HyCons`` mimics the behavior of -"usual" Lisp variants thusly: - - - ``(cons something None)`` is ``(HyExpression [something])`` - - ``(cons something some-list)`` is ``((type some-list) (+ [something] - some-list))`` (if ``some-list`` inherits from ``list``). - - ``(get (cons a b) 0)`` is ``a`` - - ``(cut (cons a b) 1)`` is ``b`` - -Hy supports a dotted-list syntax, where ``'(a . b)`` means ``(cons 'a -'b)`` and ``'(a b . c)`` means ``(cons 'a (cons 'b 'c))``. If the -compiler encounters a cons cell at the top level, it raises a -compilation error. - -``HyCons`` wraps the passed arguments (car and cdr) in Hy types, to ease -the manipulation of cons cells in a macro context. - Hy Internal Theory ==================