1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-11-25 07:28:20 +00:00

* let-alist.el (let-alist): Enable access to deeper alists

Acces them by using extra dots inside the dotted symbols.
This commit is contained in:
Artur Malabarba 2014-12-19 18:25:06 -02:00
parent 948fa912de
commit f447d33fdb
4 changed files with 85 additions and 18 deletions

View File

@ -1,3 +1,8 @@
2014-12-19 Artur Malabarba <bruce.connor.am@gmail.com>
* let-alist.el (let-alist): Enable access to deeper alists by
using dots inside the dotted symbols.
2014-12-19 Alan Mackenzie <acm@muc.de>
Make C++11 uniform init syntax work. New keywords "final" and "override"

View File

@ -4,7 +4,7 @@
;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
;; Maintainer: Artur Malabarba <bruce.connor.am@gmail.com>
;; Version: 1.0.1
;; Version: 1.0.2
;; Keywords: extensions lisp
;; Prefix: let-alist
;; Separator: -
@ -39,21 +39,25 @@
;; (let-alist alist
;; (if (and .title .body)
;; .body
;; .site))
;; .site
;; .site.contents))
;;
;; expands to
;; essentially expands to
;;
;; (let ((.title (cdr (assq 'title alist)))
;; (.body (cdr (assq 'body alist)))
;; (.site (cdr (assq 'site alist))))
;; (.body (cdr (assq 'body alist)))
;; (.site (cdr (assq 'site alist)))
;; (.site.contents (cdr (assq 'contents (cdr (assq 'site alist))))))
;; (if (and .title .body)
;; .body
;; .site))
;; .site
;; .site.contents))
;;
;; If you nest `let-alist' invocations, the inner one can't access
;; the variables of the outer one. You can, however, access alists
;; inside the original alist by using dots inside the symbol, as
;; displayed in the example above by the `.site.contents'.
;;
;; Note that only one level is supported. If you nest `let-alist'
;; invocations, the inner one can't access the variables of the outer
;; one.
;;; Code:
@ -72,6 +76,31 @@ symbol, and each cdr is the same symbol without the `.'."
(t (apply #'append
(mapcar #'let-alist--deep-dot-search data)))))
(defun let-alist--access-sexp (symbol variable)
"Return a sexp used to acess SYMBOL inside VARIABLE."
(let* ((clean (let-alist--remove-dot symbol))
(name (symbol-name clean)))
(if (string-match "\\`\\." name)
clean
(let-alist--list-to-sexp
(mapcar #'intern (nreverse (split-string name "\\.")))
variable))))
(defun let-alist--list-to-sexp (list var)
"Turn symbols LIST into recursive calls to `cdr' `assq' on VAR."
`(cdr (assq ',(car list)
,(if (cdr list) (let-alist--list-to-sexp (cdr list) var)
var))))
(defun let-alist--remove-dot (symbol)
"Return SYMBOL, sans an initial dot."
(let ((name (symbol-name symbol)))
(if (string-match "\\`\\." name)
(intern (replace-match "" nil nil name))
symbol)))
;;; The actual macro.
;;;###autoload
(defmacro let-alist (alist &rest body)
"Let-bind dotted symbols to their cdrs in ALIST and execute BODY.
@ -83,20 +112,28 @@ For instance, the following code
(let-alist alist
(if (and .title .body)
.body
.site))
.site
.site.contents))
expands to
essentially expands to
(let ((.title (cdr (assq 'title alist)))
(.body (cdr (assq 'body alist)))
(.site (cdr (assq 'site alist))))
(.body (cdr (assq 'body alist)))
(.site (cdr (assq 'site alist)))
(.site.contents (cdr (assq 'contents (cdr (assq 'site alist))))))
(if (and .title .body)
.body
.site))"
.site
.site.contents))
If you nest `let-alist' invocations, the inner one can't access
the variables of the outer one. You can, however, access alists
inside the original alist by using dots inside the symbol, as
displayed in the example above."
(declare (indent 1) (debug t))
(let ((var (gensym "let-alist")))
(let ((var (gensym "alist")))
`(let ((,var ,alist))
(let ,(mapcar (lambda (x) `(,(car x) (cdr (assq ',(cdr x) ,var))))
(let ,(mapcar (lambda (x) `(,(car x) ,(let-alist--access-sexp (car x) var)))
(delete-dups (let-alist--deep-dot-search body)))
,@body))))

View File

@ -1,6 +1,7 @@
2014-12-19 Artur Malabarba <bruce.connor.am@gmail.com>
* automated/let-alist.el: require `cl-lib'
New tests for accessing alists inside alists.
2014-12-18 Artur Malabarba <bruce.connor.am@gmail.com>

View File

@ -33,7 +33,19 @@
(cl-letf (((symbol-function #'gensym) (lambda (x) 'symbol)))
(macroexpand
'(let-alist data (list .test-one .test-two
.test-two .test-two)))))))
.test-two .test-two))))))
(should
(equal
(let ((.external "ext")
(.external.too "et"))
(let-alist '((test-two . 0)
(test-three . 1)
(sublist . ((foo . 2)
(bar . 3))))
(list .test-one .test-two .test-three
.sublist.foo .sublist.bar
..external ..external.too)))
(list nil 0 1 2 3 "ext" "et"))))
(defvar let-alist--test-counter 0
"Used to count number of times a function is called.")
@ -49,5 +61,17 @@
(list .test-one .test-two .test-two .test-three .cl-incf))
'(nil 1 1 2 nil)))))
(ert-deftest let-alist-remove-dot ()
"Remove firt dot from symbol."
(should (equal (let-alist--remove-dot 'hi) 'hi))
(should (equal (let-alist--remove-dot '.hi) 'hi))
(should (equal (let-alist--remove-dot '..hi) '.hi)))
(ert-deftest let-alist-list-to-sexp ()
"Check that multiple dots are handled correctly."
(should (= 1 (eval (let-alist--list-to-sexp '(a b c d) ''((d (c (b (a . 1)))))))))
(should (equal (let-alist--access-sexp '.foo.bar.baz 'var)
'(cdr (assq 'baz (cdr (assq 'bar (cdr (assq 'foo var))))))))
(should (equal (let-alist--access-sexp '..foo.bar.baz 'var) '.foo.bar.baz)))
;;; let-alist.el ends here