Support numeric indexing in let-alist

let-alist is very useful.  But sometimes an alist contains a list in
the middle, which contains yet more alists.  Previously, this was
somewhat painful to deal with, and required something like:

(let-alist alist
  (let-alist (nth 0 .a)
    (let-alist (nth 3 .b)
       .c)))

Now, the following works:

(let-alist alist
  .a.0.b.3.c)

* lisp/emacs-lisp/let-alist.el (let-alist--access-sexp): Properly
parse numbers to handle lists.  (Bug#66509)
(let-alist--list-to-sexp): Use nth to handle numbers.
(let-alist): Update docs.
This commit is contained in:
Spencer Baugh 2023-10-12 18:01:46 -04:00 committed by Stefan Kangas
parent 252ed22d62
commit ae4171efdc

View file

@ -36,22 +36,23 @@
;; symbol inside body is let-bound to their cdrs in the alist. Dotted ;; symbol inside body is let-bound to their cdrs in the alist. Dotted
;; symbol is any symbol starting with a `.'. Only those present in ;; symbol is any symbol starting with a `.'. Only those present in
;; the body are let-bound and this search is done at compile time. ;; the body are let-bound and this search is done at compile time.
;; A number will result in a list index.
;; ;;
;; For instance, the following code ;; For instance, the following code
;; ;;
;; (let-alist alist ;; (let-alist alist
;; (if (and .title .body) ;; (if (and .title.0 .body)
;; .body ;; .body
;; .site ;; .site
;; .site.contents)) ;; .site.contents))
;; ;;
;; essentially expands to ;; essentially expands to
;; ;;
;; (let ((.title (cdr (assq 'title alist))) ;; (let ((.title.0 (nth 0 (cdr (assq 'title alist))))
;; (.body (cdr (assq 'body alist))) ;; (.body (cdr (assq 'body alist)))
;; (.site (cdr (assq 'site alist))) ;; (.site (cdr (assq 'site alist)))
;; (.site.contents (cdr (assq 'contents (cdr (assq 'site alist)))))) ;; (.site.contents (cdr (assq 'contents (cdr (assq 'site alist))))))
;; (if (and .title .body) ;; (if (and .title.0 .body)
;; .body ;; .body
;; .site ;; .site
;; .site.contents)) ;; .site.contents))
@ -93,14 +94,17 @@ symbol, and each cdr is the same symbol without the `.'."
(if (string-match "\\`\\." name) (if (string-match "\\`\\." name)
clean clean
(let-alist--list-to-sexp (let-alist--list-to-sexp
(mapcar #'intern (nreverse (split-string name "\\."))) (mapcar #'read (nreverse (split-string name "\\.")))
variable)))) variable))))
(defun let-alist--list-to-sexp (list var) (defun let-alist--list-to-sexp (list var)
"Turn symbols LIST into recursive calls to `cdr' `assq' on VAR." "Turn symbols LIST into recursive calls to `cdr' `assq' on VAR."
`(cdr (assq ',(car list) (let ((sym (car list))
,(if (cdr list) (let-alist--list-to-sexp (cdr list) var) (rest (if (cdr list) (let-alist--list-to-sexp (cdr list) var)
var)))) var)))
(cond
((numberp sym) `(nth ,sym ,rest))
(t `(cdr (assq ',sym ,rest))))))
(defun let-alist--remove-dot (symbol) (defun let-alist--remove-dot (symbol)
"Return SYMBOL, sans an initial dot." "Return SYMBOL, sans an initial dot."
@ -116,22 +120,23 @@ symbol, and each cdr is the same symbol without the `.'."
"Let-bind dotted symbols to their cdrs in ALIST and execute BODY. "Let-bind dotted symbols to their cdrs in ALIST and execute BODY.
Dotted symbol is any symbol starting with a `.'. Only those present Dotted symbol is any symbol starting with a `.'. Only those present
in BODY are let-bound and this search is done at compile time. in BODY are let-bound and this search is done at compile time.
A number will result in a list index.
For instance, the following code For instance, the following code
(let-alist alist (let-alist alist
(if (and .title .body) (if (and .title.0 .body)
.body .body
.site .site
.site.contents)) .site.contents))
essentially expands to essentially expands to
(let ((.title (cdr (assq \\='title alist))) (let ((.title (nth 0 (cdr (assq \\='title alist))))
(.body (cdr (assq \\='body alist))) (.body (cdr (assq \\='body alist)))
(.site (cdr (assq \\='site alist))) (.site (cdr (assq \\='site alist)))
(.site.contents (cdr (assq \\='contents (cdr (assq \\='site alist)))))) (.site.contents (cdr (assq \\='contents (cdr (assq \\='site alist))))))
(if (and .title .body) (if (and .title.0 .body)
.body .body
.site .site
.site.contents)) .site.contents))