]> git.eshelyaron.com Git - emacs.git/commitdiff
* let-alist.el (let-alist): Enable access to deeper alists
authorArtur Malabarba <bruce.connor.am@gmail.com>
Fri, 19 Dec 2014 20:25:06 +0000 (18:25 -0200)
committerArtur Malabarba <bruce.connor.am@gmail.com>
Fri, 19 Dec 2014 20:30:26 +0000 (18:30 -0200)
Acces them by using extra dots inside the dotted symbols.

lisp/ChangeLog
lisp/let-alist.el
test/ChangeLog
test/automated/let-alist.el

index 12530a997ba817133428c655128c92f14dd0d2e8..b658cc1d0fac31c2cf5bbf2cd0250caadf18b6b5 100644 (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"
index 813b8417aaac4c339e95cd856da62151314baabe..692beba16dde097440e3a0f981c93f03cdc1791d 100644 (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: -
 ;;   (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:
 \f
 
@@ -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)))
+
+\f
+;;; 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))))
 
index 80d2a40bc4c9e829b4224d8a375917f5acfc5c37..7d23b3efe1c23dad02f2588a0ecfbf32189db6be 100644 (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>
 
index a700a4773ff7cd0c259e9d39a9e91025acc5a06d..391ccb44a8d5adc23ca85c76cabd7b1f22dd82ee 100644 (file)
           (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.")
         (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