aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArtur Malabarba2014-12-19 18:25:06 -0200
committerArtur Malabarba2014-12-19 18:30:26 -0200
commitf447d33fdb082ce8e5d336be6034df24339b4c45 (patch)
tree7ad100c335eb0e84024c0aab266f674e2078ff47
parent948fa912de164a1374c87e9206cddca741b7fa33 (diff)
downloademacs-f447d33fdb082ce8e5d336be6034df24339b4c45.tar.gz
emacs-f447d33fdb082ce8e5d336be6034df24339b4c45.zip
* let-alist.el (let-alist): Enable access to deeper alists
Acces them by using extra dots inside the dotted symbols.
-rw-r--r--lisp/ChangeLog5
-rw-r--r--lisp/let-alist.el71
-rw-r--r--test/ChangeLog1
-rw-r--r--test/automated/let-alist.el26
4 files changed, 85 insertions, 18 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 12530a997ba..b658cc1d0fa 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,8 @@
12014-12-19 Artur Malabarba <bruce.connor.am@gmail.com>
2
3 * let-alist.el (let-alist): Enable access to deeper alists by
4 using dots inside the dotted symbols.
5
12014-12-19 Alan Mackenzie <acm@muc.de> 62014-12-19 Alan Mackenzie <acm@muc.de>
2 7
3 Make C++11 uniform init syntax work. New keywords "final" and "override" 8 Make C++11 uniform init syntax work. New keywords "final" and "override"
diff --git a/lisp/let-alist.el b/lisp/let-alist.el
index 813b8417aaa..692beba16dd 100644
--- a/lisp/let-alist.el
+++ b/lisp/let-alist.el
@@ -4,7 +4,7 @@
4 4
5;; Author: Artur Malabarba <bruce.connor.am@gmail.com> 5;; Author: Artur Malabarba <bruce.connor.am@gmail.com>
6;; Maintainer: Artur Malabarba <bruce.connor.am@gmail.com> 6;; Maintainer: Artur Malabarba <bruce.connor.am@gmail.com>
7;; Version: 1.0.1 7;; Version: 1.0.2
8;; Keywords: extensions lisp 8;; Keywords: extensions lisp
9;; Prefix: let-alist 9;; Prefix: let-alist
10;; Separator: - 10;; Separator: -
@@ -39,21 +39,25 @@
39;; (let-alist alist 39;; (let-alist alist
40;; (if (and .title .body) 40;; (if (and .title .body)
41;; .body 41;; .body
42;; .site)) 42;; .site
43;; .site.contents))
43;; 44;;
44;; expands to 45;; essentially expands to
45;; 46;;
46;; (let ((.title (cdr (assq 'title alist))) 47;; (let ((.title (cdr (assq 'title alist)))
47;; (.body (cdr (assq 'body alist))) 48;; (.body (cdr (assq 'body alist)))
48;; (.site (cdr (assq 'site alist)))) 49;; (.site (cdr (assq 'site alist)))
50;; (.site.contents (cdr (assq 'contents (cdr (assq 'site alist))))))
49;; (if (and .title .body) 51;; (if (and .title .body)
50;; .body 52;; .body
51;; .site)) 53;; .site
54;; .site.contents))
55;;
56;; If you nest `let-alist' invocations, the inner one can't access
57;; the variables of the outer one. You can, however, access alists
58;; inside the original alist by using dots inside the symbol, as
59;; displayed in the example above by the `.site.contents'.
52;; 60;;
53;; Note that only one level is supported. If you nest `let-alist'
54;; invocations, the inner one can't access the variables of the outer
55;; one.
56
57;;; Code: 61;;; Code:
58 62
59 63
@@ -72,6 +76,31 @@ symbol, and each cdr is the same symbol without the `.'."
72 (t (apply #'append 76 (t (apply #'append
73 (mapcar #'let-alist--deep-dot-search data))))) 77 (mapcar #'let-alist--deep-dot-search data)))))
74 78
79(defun let-alist--access-sexp (symbol variable)
80 "Return a sexp used to acess SYMBOL inside VARIABLE."
81 (let* ((clean (let-alist--remove-dot symbol))
82 (name (symbol-name clean)))
83 (if (string-match "\\`\\." name)
84 clean
85 (let-alist--list-to-sexp
86 (mapcar #'intern (nreverse (split-string name "\\.")))
87 variable))))
88
89(defun let-alist--list-to-sexp (list var)
90 "Turn symbols LIST into recursive calls to `cdr' `assq' on VAR."
91 `(cdr (assq ',(car list)
92 ,(if (cdr list) (let-alist--list-to-sexp (cdr list) var)
93 var))))
94
95(defun let-alist--remove-dot (symbol)
96 "Return SYMBOL, sans an initial dot."
97 (let ((name (symbol-name symbol)))
98 (if (string-match "\\`\\." name)
99 (intern (replace-match "" nil nil name))
100 symbol)))
101
102
103;;; The actual macro.
75;;;###autoload 104;;;###autoload
76(defmacro let-alist (alist &rest body) 105(defmacro let-alist (alist &rest body)
77 "Let-bind dotted symbols to their cdrs in ALIST and execute BODY. 106 "Let-bind dotted symbols to their cdrs in ALIST and execute BODY.
@@ -83,20 +112,28 @@ For instance, the following code
83 (let-alist alist 112 (let-alist alist
84 (if (and .title .body) 113 (if (and .title .body)
85 .body 114 .body
86 .site)) 115 .site
116 .site.contents))
87 117
88expands to 118essentially expands to
89 119
90 (let ((.title (cdr (assq 'title alist))) 120 (let ((.title (cdr (assq 'title alist)))
91 (.body (cdr (assq 'body alist))) 121 (.body (cdr (assq 'body alist)))
92 (.site (cdr (assq 'site alist)))) 122 (.site (cdr (assq 'site alist)))
123 (.site.contents (cdr (assq 'contents (cdr (assq 'site alist))))))
93 (if (and .title .body) 124 (if (and .title .body)
94 .body 125 .body
95 .site))" 126 .site
127 .site.contents))
128
129If you nest `let-alist' invocations, the inner one can't access
130the variables of the outer one. You can, however, access alists
131inside the original alist by using dots inside the symbol, as
132displayed in the example above."
96 (declare (indent 1) (debug t)) 133 (declare (indent 1) (debug t))
97 (let ((var (gensym "let-alist"))) 134 (let ((var (gensym "alist")))
98 `(let ((,var ,alist)) 135 `(let ((,var ,alist))
99 (let ,(mapcar (lambda (x) `(,(car x) (cdr (assq ',(cdr x) ,var)))) 136 (let ,(mapcar (lambda (x) `(,(car x) ,(let-alist--access-sexp (car x) var)))
100 (delete-dups (let-alist--deep-dot-search body))) 137 (delete-dups (let-alist--deep-dot-search body)))
101 ,@body)))) 138 ,@body))))
102 139
diff --git a/test/ChangeLog b/test/ChangeLog
index 80d2a40bc4c..7d23b3efe1c 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,6 +1,7 @@
12014-12-19 Artur Malabarba <bruce.connor.am@gmail.com> 12014-12-19 Artur Malabarba <bruce.connor.am@gmail.com>
2 2
3 * automated/let-alist.el: require `cl-lib' 3 * automated/let-alist.el: require `cl-lib'
4 New tests for accessing alists inside alists.
4 5
52014-12-18 Artur Malabarba <bruce.connor.am@gmail.com> 62014-12-18 Artur Malabarba <bruce.connor.am@gmail.com>
6 7
diff --git a/test/automated/let-alist.el b/test/automated/let-alist.el
index a700a4773ff..391ccb44a8d 100644
--- a/test/automated/let-alist.el
+++ b/test/automated/let-alist.el
@@ -33,7 +33,19 @@
33 (cl-letf (((symbol-function #'gensym) (lambda (x) 'symbol))) 33 (cl-letf (((symbol-function #'gensym) (lambda (x) 'symbol)))
34 (macroexpand 34 (macroexpand
35 '(let-alist data (list .test-one .test-two 35 '(let-alist data (list .test-one .test-two
36 .test-two .test-two))))))) 36 .test-two .test-two))))))
37 (should
38 (equal
39 (let ((.external "ext")
40 (.external.too "et"))
41 (let-alist '((test-two . 0)
42 (test-three . 1)
43 (sublist . ((foo . 2)
44 (bar . 3))))
45 (list .test-one .test-two .test-three
46 .sublist.foo .sublist.bar
47 ..external ..external.too)))
48 (list nil 0 1 2 3 "ext" "et"))))
37 49
38(defvar let-alist--test-counter 0 50(defvar let-alist--test-counter 0
39 "Used to count number of times a function is called.") 51 "Used to count number of times a function is called.")
@@ -49,5 +61,17 @@
49 (list .test-one .test-two .test-two .test-three .cl-incf)) 61 (list .test-one .test-two .test-two .test-three .cl-incf))
50 '(nil 1 1 2 nil))))) 62 '(nil 1 1 2 nil)))))
51 63
64(ert-deftest let-alist-remove-dot ()
65 "Remove firt dot from symbol."
66 (should (equal (let-alist--remove-dot 'hi) 'hi))
67 (should (equal (let-alist--remove-dot '.hi) 'hi))
68 (should (equal (let-alist--remove-dot '..hi) '.hi)))
69
70(ert-deftest let-alist-list-to-sexp ()
71 "Check that multiple dots are handled correctly."
72 (should (= 1 (eval (let-alist--list-to-sexp '(a b c d) ''((d (c (b (a . 1)))))))))
73 (should (equal (let-alist--access-sexp '.foo.bar.baz 'var)
74 '(cdr (assq 'baz (cdr (assq 'bar (cdr (assq 'foo var))))))))
75 (should (equal (let-alist--access-sexp '..foo.bar.baz 'var) '.foo.bar.baz)))
52 76
53;;; let-alist.el ends here 77;;; let-alist.el ends here