Merge pull request #1580 from Kodiologist/nocons

Burninate HyCons
This commit is contained in:
Kodi Arfer 2018-04-12 16:59:15 -07:00 committed by GitHub
commit cef105edcd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 21 additions and 455 deletions

View File

@ -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

View File

@ -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 <hycons>` 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 <hycons>`.
.. 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<HyKeyword>`.
=> (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)
<HyCons (
HyInteger(1)
HyInteger(2)
HyInteger(3)
. HyInteger(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 {})
<HyCons (
HyInteger(1)
HyInteger(10)
HyInteger(2)
HyInteger(20)
. HyDict())>
.. _macroexpand-fn:

View File

@ -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
==================

View File

@ -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

View File

@ -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,

View File

@ -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)

View File

@ -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?

View File

@ -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])

View File

@ -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("<HyCons (")]
while True:
lines.append(" " + repr_indent(self.car))
if not isinstance(self.cdr, HyCons):
break
self = self.cdr
lines.append("{} {}{}".format(
c("."), repr_indent(self.cdr), c(")>")))
return '\n'.join(lines)
def __eq__(self, other):
return (
isinstance(other, self.__class__) and
self.car == other.car and
self.cdr == other.cdr
)

View File

@ -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 [])")

View File

@ -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))))

View File

@ -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]

View File

@ -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

View File

@ -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)]),
<HyCons (
HyFloat(0.0)
HyFloat(1.0)
. HyFloat(2.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)])
,
<HyCons (
HyFloat(0.0)
HyFloat(1.0)
. HyFloat(2.0))> # odd
])"""
,
'([1.0] {1.0} (1.0) #{1.0} (0.0 1.0 . 2.0) . 3.0)':
"""<HyCons (
HyList([
HyFloat(1.0)])
HyDict([
HyFloat(1.0) # odd
])
HyExpression([
HyFloat(1.0)])
HySet([
HyFloat(1.0)])
<HyCons (
HyFloat(0.0)
HyFloat(1.0)
. HyFloat(2.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])