branch: elpa/loopy commit 3c6beb610fded397088d616c1cc3cf6c90c8e568 Author: okamsn <28612288+oka...@users.noreply.github.com> Commit: GitHub <nore...@github.com>
Combine `loopy-iter-bare-commands` and `loopy-iter-bare-special-macro-arguments`. (#242) Closes #238. - Create `loopy-iter-bare-names`, a list which contains both. Use this variable in `loopy-iter`. - Filter out supposed commands whose parsing function is a symbol matching one of the known pseudo-functions for special macro arguments when creating the bare-name sub-macros. - Add a NOTE saying to update the list of known pseudo-functions when changing the default value of `loopy-parsers`. - Make the `loopy-iter-bare-commands` and `loopy-iter-bare-special-macro-arguments` deprecated aliases of the new `loopy-iter-bare-names`. - Rename `loopy-iter-overwritten-command-parsers` to `loopy-iter-overwritten-parsers` to be consistent with the new `loopy-parsers` variable. Make the old name an alias, but not deprecated. - Update the Org doc. - Update the README. - Update the CHANGELOG. - Add tests: - Add test `bare-names` to `tests/iter-tests.el` to test use of `loopy-iter-bare-names`. - Change occurences of `loopy-iter-bare-commands` to `loopy-iter-bare-names` in these tests in `tests/tests.el`: - accumulation-conflicting-final-updates - accumulation-conflicting-final-updates-ht - custom-command-sum - custom-command-sum-ht - custom-command-always-pass - Change occurences of `loopy-iter-bare-special-macro-arguments` to `loopy-iter-bare-names` in these tests in `tests/tests.el`: - custom-alias-flag - custom-alias-with - custom-alias-without - custom-alias-before-do - custom-alias-after-do --- CHANGELOG.md | 22 +++++++--- README.org | 2 + doc/loopy-doc.org | 29 ++++++------ lisp/loopy-iter.el | 124 +++++++++++++++++++++++++++++++++------------------- lisp/loopy-vars.el | 4 ++ tests/iter-tests.el | 5 +++ tests/tests.el | 40 ++++++++--------- 7 files changed, 139 insertions(+), 87 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 927a1671e8c..5c1aaade4e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,14 +17,20 @@ For Loopy Dash, see <https://github.com/okamsn/loopy-dash>. binds `VAR` to `nil`, but since this form is indistinguishable from a mistake, and since `nil` is a short word to write, this behavior is deprecated. -- `loopy-command-parsers` and `loopy-aliases` are both deprecated in favor of - the newly added `loopy-parsers` ([#237]). The new user option simplifies the - code internally, making it easier to add local overrides in the future, which - will make code which custom commands more portable. +- Some variables were combined to simplify the code internally and make it + easier to add local overrides in the future, which will make code which custom + commands more portable. - The new user option is a hash table which maps symbols to parsing functions. - There is no longer a separate mapping of aliases to original names. However, - `loopy-defalias` will continue to work. + - `loopy-command-parsers` and `loopy-aliases` are both deprecated in favor of + the newly added `loopy-parsers` ([#237]). The new user option is a hash + table which maps symbols to parsing functions. There is no longer a + separate mapping of aliases to original names. However, `loopy-defalias` + will continue to work. + + - `loopy-iter-bare-special-marco-arguments` and `loopy-iter-bare-commands` are + both deprecated in favor of the newly added `loopy-iter-bare-names` ([#242], + [#238]). The new user option is a list which by default contains all symbols + previously listed in the old variables. - Separate `when` and `unless` commands to have different parsing functions ([#234], [#240]). The old implementation used the name of the command in the @@ -44,8 +50,10 @@ For Loopy Dash, see <https://github.com/okamsn/loopy-dash>. [#229]: https://github.com/okamsn/loopy/PR/229 [#234]: https://github.com/okamsn/loopy/issues/234 [#237]: https://github.com/okamsn/loopy/PR/237 +[#238]: https://github.com/okamsn/loopy/issues/238 [#240]: https://github.com/okamsn/loopy/PR/240 [#241]: https://github.com/okamsn/loopy/PR/241 +[#242]: https://github.com/okamsn/loopy/PR/242 ## 0.14.0 diff --git a/README.org b/README.org index fc095ca0a19..827186c511b 100644 --- a/README.org +++ b/README.org @@ -41,6 +41,8 @@ please let me know. - ~loopy-command-parsers~ and ~loopy-aliases~ are deprecated in favor of a single hash table in the new user option ~loopy-parsers~. This simplified the code and will make adding local overrides easier. + - ~loopy-iter-bare-special-macro-arguments~ and ~loopy-iter-bare-commands~ + are deprecated in favor of the single variable ~loopy-iter-bare-names~. - =when= and =unless= are now implemented separately, fixing when the commands are aliased. - Version 0.14.0: diff --git a/doc/loopy-doc.org b/doc/loopy-doc.org index 75066ddaf9c..65dedf5d7bf 100644 --- a/doc/loopy-doc.org +++ b/doc/loopy-doc.org @@ -4511,8 +4511,8 @@ which loop commands are treated as macros to be expanded by ~macroexpand-all~. Hence, a loop command could overshadow the function value of a symbol. There are two ways to avoid such conflicts. -#+cindex: loopy-iter bare commands -#+vindex: loopy-iter-bare-commands +#+cindex: loopy-iter bare names +#+vindex: loopy-iter-bare-names The first way is to use non-conflicting aliases. Like in Iterate (and ~cl-loop~, to an extent), almost all commands in ~loopy~ have aliases in the present-participle form (the "-ing" form). For example, Loopy provides the @@ -4540,17 +4540,17 @@ arguments that are recognized by default are given in [[#iter-default-names]]. (at outer (collecting j)))) #+end_src -The non-conflicting command aliases recognized by ~loopy-iter~ can be customized -with the user option ~loopy-iter-bare-commands~, which is a list of symbols -naming commands and their aliases. Again, these commands are found in the loop -body by using Emacs Lisp's macro-expansion features, so adding an alias that -overrides a symbol's function definition can cause errors. ~loopy~, whose -environment is more limited, does not have this restriction. - -#+vindex: loopy-iter-bare-special-macro-arguments -The special macro arguments (and their aliases) recognized by ~loopy-iter~ can -be set in the user option ~loopy-iter-bare-special-macro-arguments~. Some of -their built-in aliases, such as =let*= for =with=, are excluded by default. +The non-conflicting names of loop commands and special macro arguments +recognized by ~loopy-iter~ can be customized with the user option +~loopy-iter-bare-names~, which is a list of symbols naming commands, special +macro arguments, and their aliases. Again, these symbols are found in the loop +body by using Emacs Lisp's macro-expansion features, so adding a name that +overrides a symbol's function definition can cause errors. Names that are +obvious conflicts, such as the non-suffixed versions of most loop commands (such +as =list=), loop commands that are unneeded in ~loopy-iter~ (such as =when=), +and the names of Loopy features that are the same as the names of Emacs Lisp +features (such as =let*= for =with=) are by default excluded from +~loopy-iter-bare-names~. #+cindex: loopy-iter keywords #+vindex: loopy-iter-keywords @@ -4673,8 +4673,7 @@ Finally, there are a few things to keep in mind when using ~loopy-iter~: This section lists the default aliases supported as bare names in the macro ~loopy-iter~. The list of supported bare names can be customized in the user -options ~loopy-iter-bare-commands~ and -~loopy-iter-bare-special-macro-arguments~. +options ~loopy-iter-bare-names~. By default, the following commands are not recognized: - =do= and =command-do=, which are not needed. diff --git a/lisp/loopy-iter.el b/lisp/loopy-iter.el index da806bcbe2f..86a63912e03 100644 --- a/lisp/loopy-iter.el +++ b/lisp/loopy-iter.el @@ -99,8 +99,40 @@ Without these keywords, one must use one of the names given in ;;;; For parsing commands -(defcustom loopy-iter-bare-commands - '(accumulating +(define-obsolete-variable-alias 'loopy-iter-bare-commands + 'loopy-iter-bare-names + "2025-07") +(define-obsolete-variable-alias 'loopy-iter-bare-special-macro-arguments + 'loopy-iter-bare-names + "2025-07") +(defcustom loopy-iter-bare-names + '(;; Bare special macro arguments + after-do + after + else-do + else + before-do + before + initially-do + initially + finally-do + finally + finally-return + finally-protect + finally-protected + flag + flags + named + accum-opt + opt-accum + with + init + without + no-with + no-init + wrap + ;; Bare commands + accumulating adjoining always appending @@ -164,9 +196,11 @@ Without these keywords, one must use one of the names given in thereis unioning vconcating) - "Commands recognized in `loopy-iter' without a preceding keyword. + "`loopy' features recognized in `loopy-iter' without a preceeding keyword. -For special marco arguments, see `loopy-iter-bare-special-macro-arguments'." +This list includes special macro arguments as well as loop commands. +See the Info node `(loopy)The loopy-iter Macro' and the Info node +`(loopy)Default Bare Names in loopy-iter'." :type '(repeat symbol) :group 'loopy-iter) @@ -215,7 +249,8 @@ during a second pass on the expanded code." ;;;;; Overwritten parser definitions -(defcustom loopy-iter-overwritten-command-parsers +(defvaralias 'loopy-iter-overwritten-command-parsers 'loopy-iter-overwritten-parsers) +(defcustom loopy-iter-overwritten-parsers '((at . loopy-iter--parse-at-command)) "Overwritten command parsers. @@ -258,24 +293,6 @@ These commands affect other loops higher up in the call list." ;;;; For parsing special macro arguments -(defcustom loopy-iter-bare-special-macro-arguments - '( after-do after else-do else - before-do before initially-do initially - finally-do finally - finally-return - finally-protect finally-protected - flag flags - named - accum-opt opt-accum - with init - without no-with no-init - wrap) - "Symbols naming recognized special macro arguments and their aliases. - -These should not overwrite any other macros or functions in Emacs Lisp." - :type '(repeat symbol) - :group 'loopy-iter) - ;; TODO: Combine this with `loopy--def-special-processor'. (defmacro loopy-iter--def-special-processor (name &rest body) "Create a processor for the special macro argument NAME and its aliases. @@ -306,7 +323,7 @@ Returns BODY without the `%s' argument." (let ((first (cl-first expr))) (or (and (memq first loopy-iter-keywords) (eq ,fn-sym (loopy--get-command-parser (cl-second expr)))) - (and (memq first loopy-iter-bare-special-macro-arguments) + (and (memq first loopy-iter-bare-names) (eq ,fn-sym (loopy--get-command-parser first)))))) (collecting matching-args expr) (collecting new-body expr)) @@ -338,7 +355,7 @@ Returns BODY without the `%s' argument." ,(pred (lambda (x) (eq 'loopy--parse-named-special-macro-argument (loopy--get-command-parser x)))) ,name . ,rest) - `(,(and (pred (lambda (x) (memq x loopy-iter-bare-special-macro-arguments))) + `(,(and (pred (lambda (x) (memq x loopy-iter-bare-names))) (pred (lambda (x) (eq 'loopy--parse-named-special-macro-argument (loopy--get-command-parser x))))) ,name . ,rest)) @@ -468,7 +485,7 @@ to use `loopy' in general. loopy-command-parsers)) ;; NOTE: This one isn't obsolete but needs to happen before aliases. - (when loopy-iter-overwritten-command-parsers + (when loopy-iter-overwritten-parsers (map-do (lambda (k v) (puthash k v loopy--parsers-internal)) loopy-iter-overwritten-command-parsers)) @@ -513,25 +530,42 @@ to use `loopy' in general. (loopy--parse-loop-command args)) (push other loopy-iter--non-main-body-instructions) (macroexp-progn main)))))) - (loopy (list command loopy-iter-bare-commands) - (collect - (cons command - ;; Expanding functions do not receive the head - ;; of the expression, only the arguments, so - ;; we use a lexical lambda to include that - ;; information. - (let ((cmd command)) - (lambda (&rest args) - (loopy--bind-main-body (main other) - ;; Bind here in case a command required to - ;; be in the top level is found in an - ;; expression while parsing an actual - ;; top-level command. - (let* ((loopy-iter--level (1+ loopy-iter--level)) - (loopy--in-sub-level (> loopy-iter--level 1))) - (loopy--parse-loop-command (cons cmd args))) - (push other loopy-iter--non-main-body-instructions) - (macroexp-progn main))))))))) + ;; NOTE: Processing the special macro arguments removes + ;; their occurences from the body, so no remaining symbol + ;; should lead to one of their pseudo-functions, but we + ;; check here anyway, just in case the symbol might show up + ;; at a lower level in a wrapped Lisp feature. + (loopy (list symbol loopy-iter-bare-names) + (unless (memq (loopy--get-command-parser symbol) + '( loopy--parse-accum-opt-special-macro-argument + loopy--parse-after-do-special-macro-argument + loopy--parse-before-do-special-macro-argument + loopy--parse-finally-do-special-macro-argument + loopy--parse-finally-protect-special-macro-argument + loopy--parse-finally-return-special-macro-argument + loopy--parse-flag-special-macro-argument + loopy--parse-with-special-macro-argument + loopy--parse-without-special-macro-argument + loopy--parse-named-special-macro-argument + loopy--parse-wrap-special-macro-argument)) + (collect + (cons symbol + ;; Expanding functions do not receive the head + ;; of the expression, only the arguments, so + ;; we use a lexical lambda to include that + ;; information. + (let ((cmd symbol)) + (lambda (&rest args) + (loopy--bind-main-body (main other) + ;; Bind here in case a command required to + ;; be in the top level is found in an + ;; expression while parsing an actual + ;; top-level command. + (let* ((loopy-iter--level (1+ loopy-iter--level)) + (loopy--in-sub-level (> loopy-iter--level 1))) + (loopy--parse-loop-command (cons cmd args))) + (push other loopy-iter--non-main-body-instructions) + (macroexp-progn main)))))))))) (common-env `(,@suppressed-expanders ,@command-env ,@macroexpand-all-environment)) diff --git a/lisp/loopy-vars.el b/lisp/loopy-vars.el index 891a192e56b..849eece7390 100644 --- a/lisp/loopy-vars.el +++ b/lisp/loopy-vars.el @@ -157,6 +157,10 @@ Definition must exist. Neither argument need be quoted." #s(hash-table test eq data (;; Special macro arguments + ;; + ;; NOTE: When editing the hash table, also edit the list of + ;; pseudo-functions for special macro arguments used in the + ;; definition of `loopy-iter'. accum-opt loopy--parse-accum-opt-special-macro-argument opt-accum loopy--parse-accum-opt-special-macro-argument after loopy--parse-after-do-special-macro-argument diff --git a/tests/iter-tests.el b/tests/iter-tests.el index 8694e6db289..a5b5cf516ab 100644 --- a/tests/iter-tests.el +++ b/tests/iter-tests.el @@ -31,6 +31,11 @@ (repeating 3) (collecting (list a b c))))) +(ert-deftest bare-names () + (let ((loopy-iter-bare-names (cons 'array loopy-iter-bare-names))) + (should (equal '(0 1 2 3) + (liq (array i [0 1 2 3]) + (collecting i)))))) ;; A list of special-form code walkers in Iterate. In Emacs Lisp, many of these ;; are macros, and so we should not need to test them, as they expand to simpler diff --git a/tests/tests.el b/tests/tests.el index 1c0336a9e5d..69cfd277db9 100644 --- a/tests/tests.el +++ b/tests/tests.el @@ -3860,8 +3860,8 @@ expansion time." #'my-loopy-sum-command1) (map-insert 'sum2 #'my-loopy-sum-command2))) - (loopy-iter-bare-commands (append '(sum1 sum2) - loopy-iter-bare-commands))) + (loopy-iter-bare-names (append '(sum1 sum2) + loopy-iter-bare-names))) (eval (quote ,x) t))))) :multi-body t :body [((list i '(1 2 3 4 5)) @@ -3929,8 +3929,8 @@ expansion time." (let ((loopy-parsers (thread-first loopy-parsers (my-ht-map-insert 'sum1 #'my-loopy-sum-command1) (my-ht-map-insert 'sum2 #'my-loopy-sum-command2))) - (loopy-iter-bare-commands (append '(sum1 sum2) - loopy-iter-bare-commands))) + (loopy-iter-bare-names (append '(sum1 sum2) + loopy-iter-bare-names))) (eval (quote ,x) t))))) :multi-body t :body [((list i '(1 2 3 4 5)) @@ -6652,8 +6652,8 @@ Wrapping with another eval to make sure variables are set by expansion time." (let ((loopy-command-parsers (map-insert loopy-command-parsers 'target-sum #'my-loopy-sum-command)) - (loopy-iter-bare-commands (cons 'target-sum - loopy-iter-bare-commands))) + (loopy-iter-bare-names (cons 'target-sum + loopy-iter-bare-names))) (eval (quote ,x) t))))) :result 6 :body ((target-sum my-target 1 2 3) @@ -6672,8 +6672,8 @@ Wrapping with another eval to make sure variables are set by expansion time." (let ((loopy-parsers (my-ht-map-insert loopy-parsers 'target-sum #'my-loopy-sum-command)) - (loopy-iter-bare-commands (cons 'target-sum - loopy-iter-bare-commands))) + (loopy-iter-bare-names (cons 'target-sum + loopy-iter-bare-names))) (eval (quote ,x) t))))) :result 6 :body ((target-sum my-target 1 2 3) @@ -6709,8 +6709,8 @@ Otherwise, `loopy' should return t." (let ((loopy-command-parsers (map-insert loopy-command-parsers 'my-always #'my--loopy-always-command-parser)) - (loopy-iter-bare-commands (cons 'my-always - loopy-iter-bare-commands))) + (loopy-iter-bare-names (cons 'my-always + loopy-iter-bare-names))) (eval (quote ,x) t))))) :result t :body ((list i (number-sequence 1 9)) @@ -6896,8 +6896,8 @@ NOTE: This should eventually be removed." :result '(1) :wrap ((x . `(let ((loopy-parsers (my-ht-map-insert loopy-parsers 'f (map-elt loopy-parsers 'flag))) - (loopy-iter-bare-special-macro-arguments - (cons 'f loopy-iter-bare-special-macro-arguments))) + (loopy-iter-bare-names + (cons 'f loopy-iter-bare-names))) (eval (quote ,x) t)))) :body ((f default) (list i '(1)) @@ -6911,8 +6911,8 @@ NOTE: This should eventually be removed." (loopy-deftest custom-alias-with :result 1 :wrap ((x . `(let ((loopy-parsers (my-ht-map-insert loopy-parsers 'as (map-elt loopy-parsers 'with))) - (loopy-iter-bare-special-macro-arguments - (cons 'as loopy-iter-bare-special-macro-arguments))) + (loopy-iter-bare-names + (cons 'as loopy-iter-bare-names))) (eval (quote ,x) t)))) :body ((as (a 1)) (return a)) @@ -6925,8 +6925,8 @@ NOTE: This should eventually be removed." :result 5 :wrap ((x . `(let ((loopy-parsers (my-ht-map-insert loopy-parsers 'ignore (map-elt loopy-parsers 'without))) - (loopy-iter-bare-special-macro-arguments - (cons 'ignore loopy-iter-bare-special-macro-arguments))) + (loopy-iter-bare-names + (cons 'ignore loopy-iter-bare-names))) (eval (quote (let ((a 1) (b 2)) ,x @@ -6945,8 +6945,8 @@ NOTE: This should eventually be removed." (loopy-deftest custom-alias-before-do :result 7 :wrap ((x . `(let ((loopy-aliases (map-copy loopy-aliases)) - (loopy-iter-bare-special-macro-arguments - (cons 'precode loopy-iter-bare-special-macro-arguments))) + (loopy-iter-bare-names + (cons 'precode loopy-iter-bare-names))) (loopy-defalias precode before-do) (eval (quote ,x) t)))) :body ((with (i 2)) @@ -6960,8 +6960,8 @@ NOTE: This should eventually be removed." (loopy-deftest custom-alias-after-do :result t :wrap ((x . `(let ((loopy-aliases (map-copy loopy-aliases)) - (loopy-iter-bare-special-macro-arguments - (cons 'postcode loopy-iter-bare-special-macro-arguments))) + (loopy-iter-bare-names + (cons 'postcode loopy-iter-bare-names))) (loopy-defalias postcode after-do) (eval (quote ,x) t)))) :body ((with (my-ret nil))