From bc524daee8e270fa1030a2445d7569d773488160 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Fri, 2 Aug 2019 17:16:43 -0400 Subject: [PATCH 01/13] Remove dead code --- tests/native_tests/contrib/walk.hy | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/native_tests/contrib/walk.hy b/tests/native_tests/contrib/walk.hy index 2e59cfe..854b412 100644 --- a/tests/native_tests/contrib/walk.hy +++ b/tests/native_tests/contrib/walk.hy @@ -31,7 +31,6 @@ (assert (= acc [walk-form]))) (defn test-walk-iterators [] - (setv acc []) (assert (= (walk (fn [x] (* 2 x)) (fn [x] x) (drop 1 [1 [2 [3 [4]]]])) [[2 [3 [4]] 2 [3 [4]]]]))) From 88c0f92810d062680ab26e7493f8eb2310a3530b Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Thu, 1 Aug 2019 13:35:24 -0400 Subject: [PATCH 02/13] Clean up string handling in _compile_assign --- hy/compiler.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hy/compiler.py b/hy/compiler.py index 34a9dd5..7b48822 100755 --- a/hy/compiler.py +++ b/hy/compiler.py @@ -1345,17 +1345,16 @@ class HyASTCompiler(object): def _compile_assign(self, root, name, result): - str_name = "%s" % name - if str_name in ("None", "True", "False"): + if name in [HySymbol(x) for x in ("None", "True", "False")]: raise self._syntax_error(name, - "Can't assign to `%s'" % str_name) + "Can't assign to `{}'".format(name)) result = self.compile(result) ld_name = self.compile(name) if isinstance(ld_name.expr, ast.Call): raise self._syntax_error(name, - "Can't assign to a callable: `%s'" % str_name) + "Can't assign to a callable: `{}'".format(name)) if (result.temp_variables and isinstance(name, HySymbol) From 6cced31738b359e5b10a2067e50a4099cbdd4324 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Fri, 2 Aug 2019 14:46:34 -0400 Subject: [PATCH 03/13] Rewrite `cond` --- hy/core/macros.hy | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/hy/core/macros.hy b/hy/core/macros.hy index 1021c76..dc5e035 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -87,29 +87,19 @@ Shorthand for nested with/a* loops: The result in the bracket may be omitted, in which case the condition is also used as the result." - (if (empty? branches) - None - (do - (setv branches (iter branches)) - (setv branch (next branches)) - (defn check-branch [branch] - "check `cond` branch for validity, return the corresponding `if` expr" - (if (not (= (type branch) HyList)) - (macro-error branch "cond branches need to be a list")) - (if (< (len branch) 2) - (do - (setv g (gensym)) - `(if (do (setv ~g ~(first branch)) ~g) ~g)) - `(if ~(first branch) (do ~@(cut branch 1))))) + (or branches + (return)) - (setv root (check-branch branch)) - (setv latest-branch root) - - (for [branch branches] - (setv cur-branch (check-branch branch)) - (.append latest-branch cur-branch) - (setv latest-branch cur-branch)) - root))) + `(if ~@(reduce + (gfor + branch branches + (if + (not (and (is (type branch) hy.HyList) branch)) + (macro-error branch "each cond branch needs to be a nonempty list") + (= (len branch) 1) (do + (setv g (gensym)) + [`(do (setv ~g ~(first branch)) ~g) g]) + True + [(first branch) `(do ~@(cut branch 1))]))))) (defmacro -> [head &rest args] From 9ddb3b1031bd8be37847b3a44cde2e96a2f85c83 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Fri, 2 Aug 2019 14:53:57 -0400 Subject: [PATCH 04/13] Rewrite `_with` --- hy/core/macros.hy | 18 ++++++++---------- tests/native_tests/contrib/walk.hy | 12 ++++++------ 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/hy/core/macros.hy b/hy/core/macros.hy index dc5e035..90ddc22 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -48,16 +48,14 @@ be associated in pairs." (defn _with [node args body] - (if (not (empty? args)) - (do - (if (>= (len args) 2) - (do - (setv p1 (.pop args 0) - p2 (.pop args 0) - primary [p1 p2]) - `(~node [~@primary] ~(_with node args body))) - `(~node [~@args] ~@body))) - `(do ~@body))) + (if + (not args) + `(do ~@body) + (<= (len args) 2) + `(~node [~@args] ~@body) + True (do + (setv [p1 p2 #* args] args) + `(~node [~p1 ~p2] ~(_with node args body))))) (defmacro with [args &rest body] diff --git a/tests/native_tests/contrib/walk.hy b/tests/native_tests/contrib/walk.hy index 854b412..d23e4ae 100644 --- a/tests/native_tests/contrib/walk.hy +++ b/tests/native_tests/contrib/walk.hy @@ -43,19 +43,19 @@ (assert (= (macroexpand-all '(foo-walk)) 42)) (assert (= (macroexpand-all '(with [a 1])) - '(with* [a 1] (do)))) + '(with* [a 1]))) (assert (= (macroexpand-all '(with [a 1 b 2 c 3] (for [d c] foo))) - '(with* [a 1] (with* [b 2] (with* [c 3] (do (for [d c] foo))))))) + '(with* [a 1] (with* [b 2] (with* [c 3] (for [d c] foo)))))) (assert (= (macroexpand-all '(with [a 1] '(with [b 2]) `(with [c 3] ~(with [d 4]) ~@[(with [e 5])]))) '(with* [a 1] - (do '(with [b 2]) - `(with [c 3] - ~(with* [d 4] (do)) - ~@[(with* [e 5] (do))]))))) + '(with [b 2]) + `(with [c 3] + ~(with* [d 4]) + ~@[(with* [e 5])])))) (defmacro require-macro [] `(do From 95ad5a01c829bfa856fef63d78262a77df444f66 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Fri, 2 Aug 2019 17:05:28 -0400 Subject: [PATCH 05/13] Avoid mutating HyLists in hy.contrib --- hy/contrib/multi.hy | 10 +++------- hy/contrib/walk.hy | 4 ++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/hy/contrib/multi.hy b/hy/contrib/multi.hy index 1a19fc9..08016e2 100644 --- a/hy/contrib/multi.hy +++ b/hy/contrib/multi.hy @@ -84,13 +84,9 @@ (setv comment (HyString)) (if (= (type (first bodies)) HyString) (setv [comment bodies] (head-tail bodies))) - (setv ret `(do)) - (.append ret '(import [hy.contrib.multi [MultiDispatch]])) - (for [body bodies] - (setv [let-binds body] (head-tail body)) - (.append ret - `(with-decorator MultiDispatch (defn ~name ~let-binds ~comment ~@body)))) - ret) + (+ '(do (import [hy.contrib.multi [MultiDispatch]])) (lfor + [let-binds #* body] bodies + `(with-decorator MultiDispatch (defn ~name ~let-binds ~comment ~@body))))) (do (setv [lambda-list body] (head-tail bodies)) `(setv ~name (fn* ~lambda-list ~@body))))) diff --git a/hy/contrib/walk.hy b/hy/contrib/walk.hy index d30f29e..40f0c62 100644 --- a/hy/contrib/walk.hy +++ b/hy/contrib/walk.hy @@ -89,7 +89,7 @@ splits a fn argument list into sections based on &-headers. returns an OrderedDict mapping headers to sublists. Arguments without a header are under None. " - (setv headers '[&optional &rest &kwonly &kwargs] + (setv headers ['&optional '&rest '&kwonly '&kwargs] sections (OrderedDict [(, None [])]) header None) (for [arg form] @@ -169,7 +169,7 @@ Arguments without a header are under None. #{}))))) (defn handle-args-list [self] (setv protected #{} - argslist `[]) + argslist []) (for [[header section] (-> self (.tail) first lambda-list .items)] (if header (.append argslist header)) (cond [(in header [None '&rest '&kwargs]) From 4a40ff3d7e9cbfbfa319758d8bf24ab6c19e8cd7 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Fri, 2 Aug 2019 17:06:26 -0400 Subject: [PATCH 06/13] Check for HySequence in hy.contrib.walk --- hy/contrib/walk.hy | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hy/contrib/walk.hy b/hy/contrib/walk.hy index 40f0c62..def9049 100644 --- a/hy/contrib/walk.hy +++ b/hy/contrib/walk.hy @@ -4,6 +4,7 @@ ;; license. See the LICENSE. (import [hy [HyExpression HyDict]] + [hy.models [HySequence]] [functools [partial]] [importlib [import-module]] [collections [OrderedDict]] @@ -17,9 +18,7 @@ (cond [(instance? HyExpression form) (outer (HyExpression (map inner form)))] - [(instance? HyDict form) - (HyDict (outer (HyExpression (map inner form))))] - [(list? form) + [(or (instance? HySequence form) (list? form)) ((type form) (outer (HyExpression (map inner form))))] [(coll? form) (walk inner outer (list form))] From dce0e10f3f073b621d8c93de76306d57b22f7000 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Fri, 2 Aug 2019 17:08:27 -0400 Subject: [PATCH 07/13] Use `nonlocal` instead of a singleton list --- hy/contrib/walk.hy | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hy/contrib/walk.hy b/hy/contrib/walk.hy index def9049..691645e 100644 --- a/hy/contrib/walk.hy +++ b/hy/contrib/walk.hy @@ -45,22 +45,24 @@ (setv module (or (and module-name (import-module module-name)) (calling-module)) - quote-level [0] + quote-level 0 ast-compiler (HyASTCompiler module)) ; TODO: make nonlocal after dropping Python2 (defn traverse [form] (walk expand identity form)) (defn expand [form] + (nonlocal quote-level) ;; manages quote levels (defn +quote [&optional [x 1]] + (nonlocal quote-level) (setv head (first form)) - (+= (get quote-level 0) x) - (when (neg? (get quote-level 0)) + (+= quote-level x) + (when (neg? quote-level) (raise (TypeError "unquote outside of quasiquote"))) (setv res (traverse (cut form 1))) - (-= (get quote-level 0) x) + (-= quote-level x) `(~head ~@res)) (if (call? form) - (cond [(get quote-level 0) + (cond [quote-level (cond [(in (first form) '[unquote unquote-splice]) (+quote -1)] [(= (first form) 'quasiquote) (+quote)] From 52c0e4e221dbbf48c8f68f6630db42f27204af69 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Fri, 2 Aug 2019 17:09:13 -0400 Subject: [PATCH 08/13] Add explicit checks for HyList --- hy/contrib/hy_repr.hy | 2 +- hy/core/macros.hy | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hy/contrib/hy_repr.hy b/hy/contrib/hy_repr.hy index 3baaebb..a3c7631 100644 --- a/hy/contrib/hy_repr.hy +++ b/hy/contrib/hy_repr.hy @@ -144,7 +144,7 @@ (hy-repr (dict x))))) (for [[types fmt] (partition [ - list "[...]" + [list HyList] "[...]" [set HySet] "#{...}" frozenset "(frozenset #{...})" dict-keys "(dict-keys [...])" diff --git a/hy/core/macros.hy b/hy/core/macros.hy index 90ddc22..e1d5d48 100644 --- a/hy/core/macros.hy +++ b/hy/core/macros.hy @@ -201,7 +201,7 @@ Such 'o!' params are available within `body` as the equivalent 'g!' symbol." (defn extract-o!-sym [arg] (cond [(and (symbol? arg) (.startswith arg "o!")) arg] - [(and (list? arg) (.startswith (first arg) "o!")) + [(and (instance? HyList arg) (.startswith (first arg) "o!")) (first arg)])) (setv os (list (filter identity (map extract-o!-sym args))) gs (lfor s os (HySymbol (+ "g!" (cut s 2))))) From 8576d00ce880b2079139e5bfaa760571b200d534 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Fri, 2 Aug 2019 14:48:06 -0400 Subject: [PATCH 09/13] Implement HySequence with tuples instead of lists --- hy/models.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hy/models.py b/hy/models.py index fbced02..56160df 100644 --- a/hy/models.py +++ b/hy/models.py @@ -243,7 +243,7 @@ class HyComplex(HyObject, complex): _wrappers[complex] = HyComplex -class HySequence(HyObject, list): +class HySequence(HyObject, tuple): """ An abstract type for sequence-like models to inherit from. """ @@ -256,7 +256,8 @@ class HySequence(HyObject, list): return self def __add__(self, other): - return self.__class__(super(HySequence, self).__add__(other)) + return self.__class__(super(HySequence, self).__add__( + tuple(other) if isinstance(other, list) else other)) def __getslice__(self, start, end): return self.__class__(super(HySequence, self).__getslice__(start, end)) From cc8948d9b92f3030106241c24a22dd0239b19aea Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Fri, 2 Aug 2019 17:13:40 -0400 Subject: [PATCH 10/13] Return plain lists from HyDict.keys, .values --- hy/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hy/models.py b/hy/models.py index 56160df..55d0b79 100644 --- a/hy/models.py +++ b/hy/models.py @@ -327,10 +327,10 @@ class HyDict(HySequence): return '' + g("HyDict()") def keys(self): - return self[0::2] + return list(self[0::2]) def values(self): - return self[1::2] + return list(self[1::2]) def items(self): return list(zip(self.keys(), self.values())) From e6aac2308addb023e624739d45123d6cbd8ee101 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Fri, 2 Aug 2019 17:17:24 -0400 Subject: [PATCH 11/13] Update tests for tuple HySequences --- tests/native_tests/contrib/walk.hy | 8 ++++---- tests/native_tests/quote.hy | 2 +- tests/test_models.py | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/native_tests/contrib/walk.hy b/tests/native_tests/contrib/walk.hy index d23e4ae..08922bf 100644 --- a/tests/native_tests/contrib/walk.hy +++ b/tests/native_tests/contrib/walk.hy @@ -21,10 +21,10 @@ walk-form))) (defn test-walk [] - (setv acc '()) - (assert (= (walk (partial collector acc) identity walk-form) + (setv acc []) + (assert (= (list (walk (partial collector acc) identity walk-form)) [None None])) - (assert (= acc walk-form)) + (assert (= acc (list walk-form))) (setv acc []) (assert (= (walk identity (partial collector acc) walk-form) None)) @@ -129,7 +129,7 @@ '(foo `(bar a ~a ~"x")))) (assert (= `(foo ~@[a]) '(foo "x"))) - (assert (= `(foo `(bar [a] ~@[a] ~@~[a 'a `a] ~~@[a])) + (assert (= `(foo `(bar [a] ~@[a] ~@~(HyList [a 'a `a]) ~~@[a])) '(foo `(bar [a] ~@[a] ~@["x" a a] ~"x")))))) (defn test-let-except [] diff --git a/tests/native_tests/quote.hy b/tests/native_tests/quote.hy index 84371f0..9560bcc 100644 --- a/tests/native_tests/quote.hy +++ b/tests/native_tests/quote.hy @@ -9,7 +9,7 @@ "NATIVE: test for quoting functionality" (setv q (quote (a b c))) (assert (= (len q) 3)) - (assert (= q [(quote a) (quote b) (quote c)]))) + (assert (= q (HyExpression [(quote a) (quote b) (quote c)])))) (defn test-basic-quoting [] diff --git a/tests/test_models.py b/tests/test_models.py index b9e6a90..52d4518 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -56,8 +56,8 @@ def test_list_add(): a = HyList([1, 2, 3]) b = HyList([3, 4, 5]) c = a + b - assert c == [1, 2, 3, 3, 4, 5] - assert c.__class__ == HyList + assert c == HyList([1, 2, 3, 3, 4, 5]) + assert type(c) is HyList def test_list_slice(): @@ -91,7 +91,7 @@ hyset = HySet([3, 1, 2, 2]) def test_set(): - assert hyset == [3, 1, 2, 2] + assert list(hyset) == [3, 1, 2, 2] def test_number_model_copy(): From 7aaece3725c675ee1d359ce9b44d2e391bb19f93 Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Sat, 3 Aug 2019 09:26:32 -0400 Subject: [PATCH 12/13] Use #* assignments instead of `head-tail` --- hy/contrib/multi.hy | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/hy/contrib/multi.hy b/hy/contrib/multi.hy index 08016e2..c84adf9 100644 --- a/hy/contrib/multi.hy +++ b/hy/contrib/multi.hy @@ -70,9 +70,6 @@ (with-decorator (method-decorator ~name) (defn ~name ~params ~@body)))) -(defn head-tail [l] - (, (get l 0) (cut l 1))) - (defmacro defn [name &rest bodies] (setv arity-overloaded? (fn [bodies] (if (isinstance (first bodies) HyString) @@ -83,10 +80,10 @@ (do (setv comment (HyString)) (if (= (type (first bodies)) HyString) - (setv [comment bodies] (head-tail bodies))) + (setv [comment #* bodies] bodies)) (+ '(do (import [hy.contrib.multi [MultiDispatch]])) (lfor [let-binds #* body] bodies `(with-decorator MultiDispatch (defn ~name ~let-binds ~comment ~@body))))) (do - (setv [lambda-list body] (head-tail bodies)) + (setv [lambda-list #* body] bodies) `(setv ~name (fn* ~lambda-list ~@body))))) From e5461f171c0c4e4f10123d6af75b16c2d15aaf0c Mon Sep 17 00:00:00 2001 From: Kodi Arfer Date: Sun, 18 Aug 2019 09:45:40 -0400 Subject: [PATCH 13/13] Update NEWS and documentation --- NEWS.rst | 6 ++++++ docs/language/internals.rst | 9 ++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/NEWS.rst b/NEWS.rst index 36a681e..6e5c8ff 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -16,6 +16,12 @@ New Features * All augmented assignment operators (except `%=` and `^=`) now allow more than two arguments. +Other Breaking Changes +------------------------------ +* ``HySequence`` is now a subclass of ``tuple`` instead of ``list``. + Thus, a ``HyList`` will never be equal to a ``list``, and you can't + use ``.append``, ``.pop``, etc. on an expression or list. + Bug Fixes ------------------------------ * Statements in the second argument of `assert` are now executed. diff --git a/docs/language/internals.rst b/docs/language/internals.rst index a89b356..2fb29bc 100644 --- a/docs/language/internals.rst +++ b/docs/language/internals.rst @@ -60,6 +60,10 @@ 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. +HySequences are (mostly) immutable: you can't add, modify, or remove +elements. You can still append to a variable containing a HySequence with +``+=`` and otherwise construct new HySequences out of old ones. + .. _hylist: @@ -90,11 +94,6 @@ HyDict ``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 -benefit of allowing compound expressions as dict keys (as, for instance, -the :ref:`HyExpression` Python class isn't hashable). - Atomic Models -------------