aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Petton2015-07-09 19:43:41 +0200
committerNicolas Petton2015-07-09 19:49:47 +0200
commit5509e2f93e790e6bf484160753493e42af04530b (patch)
treefe60e06add283765c174fa9fc7fd3ee2fb0659ac
parent2a1591f4d431777c7956146aff6d9d1602420d9e (diff)
downloademacs-5509e2f93e790e6bf484160753493e42af04530b.tar.gz
emacs-5509e2f93e790e6bf484160753493e42af04530b.zip
Add support for gv.el in map.el
* lisp/emacs-lisp/map.el (map-elt, map-delete): Declare a gv-expander. * lisp/emacs-lisp/map.el (map-put): Refactor using `setf' and `map-elt'. * test/automated/map-tests.el: Update tests to work with the new implementations of map-elt and map-put.
-rw-r--r--lisp/emacs-lisp/map.el126
-rw-r--r--test/automated/map-tests.el103
2 files changed, 108 insertions, 121 deletions
diff --git a/lisp/emacs-lisp/map.el b/lisp/emacs-lisp/map.el
index 1d8a3126bba..5014571a37b 100644
--- a/lisp/emacs-lisp/map.el
+++ b/lisp/emacs-lisp/map.el
@@ -71,36 +71,21 @@ MAP can be a list, hash-table or array."
71 `(pcase-let ((,(map--make-pcase-patterns keys) ,map)) 71 `(pcase-let ((,(map--make-pcase-patterns keys) ,map))
72 ,@body)) 72 ,@body))
73 73
74(defmacro map--dispatch (spec &rest args) 74(eval-when-compile
75 "Evaluate one of the forms specified by ARGS based on the type of MAP. 75 (defmacro map--dispatch (map-var &rest args)
76 76 "Evaluate one of the forms specified by ARGS based on the type of MAP.
77SPEC can be a map or a list of the form (VAR MAP [RESULT]).
78ARGS should have the form [TYPE FORM]...
79 77
80The following keyword types are meaningful: `:list', 78The following keyword types are meaningful: `:list',
81`:hash-table' and `:array'. 79`:hash-table' and `:array'.
82 80
83An error is thrown if MAP is neither a list, hash-table nor array. 81An error is thrown if MAP is neither a list, hash-table nor array.
84 82
85Return RESULT if non-nil or the result of evaluation of the 83Return RESULT if non-nil or the result of evaluation of the form."
86form. 84 (declare (debug t) (indent 1))
87 85 `(cond ((listp ,map-var) ,(plist-get args :list))
88\(fn (VAR MAP [RESULT]) &rest ARGS)" 86 ((hash-table-p ,map-var) ,(plist-get args :hash-table))
89 (declare (debug t) (indent 1)) 87 ((arrayp ,map-var) ,(plist-get args :array))
90 (unless (listp spec) 88 (t (error "Unsupported map: %s" ,map-var)))))
91 (setq spec `(,spec ,spec)))
92 (let ((map-var (car spec))
93 (result-var (make-symbol "result")))
94 `(let ((,map-var ,(cadr spec))
95 ,result-var)
96 (setq ,result-var
97 (cond ((listp ,map-var) ,(plist-get args :list))
98 ((hash-table-p ,map-var) ,(plist-get args :hash-table))
99 ((arrayp ,map-var) ,(plist-get args :array))
100 (t (error "Unsupported map: %s" ,map-var))))
101 ,@(when (cddr spec)
102 `((setq ,result-var ,@(cddr spec))))
103 ,result-var)))
104 89
105(defun map-elt (map key &optional default) 90(defun map-elt (map key &optional default)
106 "Perform a lookup in MAP of KEY and return its associated value. 91 "Perform a lookup in MAP of KEY and return its associated value.
@@ -109,10 +94,28 @@ If KEY is not found, return DEFAULT which defaults to nil.
109If MAP is a list, `eql' is used to lookup KEY. 94If MAP is a list, `eql' is used to lookup KEY.
110 95
111MAP can be a list, hash-table or array." 96MAP can be a list, hash-table or array."
97 (declare
98 (gv-expander
99 (lambda (do)
100 (gv-letplace (mgetter msetter) `(gv-delay-error ,map)
101 (macroexp-let2* nil
102 ;; Eval them once and for all in the right order.
103 ((key key) (default default))
104 `(if (listp ,mgetter)
105 ;; Special case the alist case, since it can't be handled by the
106 ;; map--put function.
107 ,(gv-get `(alist-get ,key (gv-synthetic-place
108 ,mgetter ,msetter)
109 ,default)
110 do)
111 ,(funcall do `(map-elt ,mgetter ,key ,default)
112 (lambda (v) `(map--put ,mgetter ,key ,v)))))))))
112 (map--dispatch map 113 (map--dispatch map
113 :list (alist-get key map default) 114 :list (alist-get key map default)
114 :hash-table (gethash key map default) 115 :hash-table (gethash key map default)
115 :array (map--elt-array map key default))) 116 :array (if (and (>= key 0) (< key (seq-length map)))
117 (seq-elt map key)
118 default)))
116 119
117(defmacro map-put (map key value) 120(defmacro map-put (map key value)
118 "In MAP, associate KEY with VALUE and return MAP. 121 "In MAP, associate KEY with VALUE and return MAP.
@@ -120,15 +123,10 @@ If KEY is already present in MAP, replace the associated value
120with VALUE. 123with VALUE.
121 124
122MAP can be a list, hash-table or array." 125MAP can be a list, hash-table or array."
123 (declare (debug t)) 126 (macroexp-let2 nil map map
124 (let ((symbol (symbolp map)))
125 `(progn 127 `(progn
126 (map--dispatch (m ,map m) 128 (setf (map-elt ,map ,key) ,value)
127 :list (if ,symbol 129 ,map)))
128 (setq ,map (cons (cons ,key ,value) m))
129 (error "Literal lists are not allowed, %s must be a symbol" ',map))
130 :hash-table (puthash ,key ,value m)
131 :array (aset m ,key ,value)))))
132 130
133(defmacro map-delete (map key) 131(defmacro map-delete (map key)
134 "In MAP, delete the key KEY if present and return MAP. 132 "In MAP, delete the key KEY if present and return MAP.
@@ -136,14 +134,16 @@ If MAP is an array, store nil at the index KEY.
136 134
137MAP can be a list, hash-table or array." 135MAP can be a list, hash-table or array."
138 (declare (debug t)) 136 (declare (debug t))
139 (let ((symbol (symbolp map))) 137 (gv-letplace (mgetter msetter) `(gv-delay-error ,map)
140 `(progn 138 (macroexp-let2 nil key key
141 (map--dispatch (m ,map m) 139 `(if (not (listp ,mgetter))
142 :list (if ,symbol 140 (map--delete ,mgetter ,key)
143 (setq ,map (map--delete-alist m ,key)) 141 ;; The alist case is special, since it can't be handled by the
144 (error "Literal lists are not allowed, %s must be a symbol" ',map)) 142 ;; map--delete function.
145 :hash-table (remhash ,key m) 143 (setf (alist-get ,key (gv-synthetic-place ,mgetter ,msetter)
146 :array (map--delete-array m ,key))))) 144 nil t)
145 nil)
146 ,mgetter))))
147 147
148(defun map-nested-elt (map keys &optional default) 148(defun map-nested-elt (map keys &optional default)
149 "Traverse MAP using KEYS and return the looked up value or DEFAULT if nil. 149 "Traverse MAP using KEYS and return the looked up value or DEFAULT if nil.
@@ -285,7 +285,7 @@ MAP can be a list, hash-table or array."
285 (let (result) 285 (let (result)
286 (while maps 286 (while maps
287 (map-apply (lambda (key value) 287 (map-apply (lambda (key value)
288 (map-put result key value)) 288 (setf (map-elt result key) value))
289 (pop maps))) 289 (pop maps)))
290 (map-into result type))) 290 (map-into result type)))
291 291
@@ -299,6 +299,14 @@ MAP can be a list, hash-table or array."
299 (`hash-table (map--into-hash-table map)) 299 (`hash-table (map--into-hash-table map))
300 (_ (error "Not a map type name: %S" type)))) 300 (_ (error "Not a map type name: %S" type))))
301 301
302(defun map--put (map key v)
303 (map--dispatch map
304 :list (let ((p (assoc key map)))
305 (if p (setcdr p v)
306 (error "No place to change the mapping for %S" key)))
307 :hash-table (puthash key v map)
308 :array (aset map key v)))
309
302(defun map--apply-alist (function map) 310(defun map--apply-alist (function map)
303 "Private function used to apply FUNCTION over MAP, MAP being an alist." 311 "Private function used to apply FUNCTION over MAP, MAP being an alist."
304 (seq-map (lambda (pair) 312 (seq-map (lambda (pair)
@@ -307,6 +315,15 @@ MAP can be a list, hash-table or array."
307 (cdr pair))) 315 (cdr pair)))
308 map)) 316 map))
309 317
318(defun map--delete (map key)
319 (map--dispatch map
320 :list (error "No place to remove the mapping for %S" key)
321 :hash-table (remhash key map)
322 :array (and (>= key 0)
323 (<= key (seq-length map))
324 (aset map key nil)))
325 map)
326
310(defun map--apply-hash-table (function map) 327(defun map--apply-hash-table (function map)
311 "Private function used to apply FUNCTION over MAP, MAP being a hash-table." 328 "Private function used to apply FUNCTION over MAP, MAP being a hash-table."
312 (let (result) 329 (let (result)
@@ -324,35 +341,12 @@ MAP can be a list, hash-table or array."
324 (setq index (1+ index)))) 341 (setq index (1+ index))))
325 map))) 342 map)))
326 343
327(defun map--elt-array (map key &optional default)
328 "Return the element of the array MAP at the index KEY.
329If KEY is not found, return DEFAULT which defaults to nil."
330 (let ((len (seq-length map)))
331 (or (and (>= key 0)
332 (<= key len)
333 (seq-elt map key))
334 default)))
335
336(defun map--delete-alist (map key)
337 "Return MAP with KEY removed."
338 (seq-remove (lambda (pair)
339 (equal key (car pair)))
340 map))
341
342(defun map--delete-array (map key)
343 "Set nil in the array MAP at the index KEY if present and return MAP."
344 (let ((len (seq-length map)))
345 (and (>= key 0)
346 (<= key len)
347 (aset map key nil)))
348 map)
349
350(defun map--into-hash-table (map) 344(defun map--into-hash-table (map)
351 "Convert MAP into a hash-table." 345 "Convert MAP into a hash-table."
352 (let ((ht (make-hash-table :size (map-length map) 346 (let ((ht (make-hash-table :size (map-length map)
353 :test 'equal))) 347 :test 'equal)))
354 (map-apply (lambda (key value) 348 (map-apply (lambda (key value)
355 (map-put ht key value)) 349 (setf (map-elt ht key) value))
356 map) 350 map)
357 ht)) 351 ht))
358 352
diff --git a/test/automated/map-tests.el b/test/automated/map-tests.el
index abda03d9d04..2bce643fe3a 100644
--- a/test/automated/map-tests.el
+++ b/test/automated/map-tests.el
@@ -1,4 +1,4 @@
1;;; map-tests.el --- Tests for map.el 1;;; map-tests.el --- Tests for map.el -*- lexical-binding:t -*-
2 2
3;; Copyright (C) 2015 Free Software Foundation, Inc. 3;; Copyright (C) 2015 Free Software Foundation, Inc.
4 4
@@ -40,17 +40,14 @@ Evaluate BODY for each created map.
40 (let ((alist (make-symbol "alist")) 40 (let ((alist (make-symbol "alist"))
41 (vec (make-symbol "vec")) 41 (vec (make-symbol "vec"))
42 (ht (make-symbol "ht"))) 42 (ht (make-symbol "ht")))
43 `(let ((,alist '((0 . 3) 43 `(let ((,alist (list (cons 0 3)
44 (1 . 4) 44 (cons 1 4)
45 (2 . 5))) 45 (cons 2 5)))
46 (,vec (make-vector 3 nil)) 46 (,vec (vector 3 4 5))
47 (,ht (make-hash-table))) 47 (,ht (make-hash-table)))
48 (aset ,vec 0 '3) 48 (puthash 0 3 ,ht)
49 (aset ,vec 1 '4) 49 (puthash 1 4 ,ht)
50 (aset ,vec 2 '5) 50 (puthash 2 5 ,ht)
51 (puthash '0 3 ,ht)
52 (puthash '1 4 ,ht)
53 (puthash '2 5 ,ht)
54 (dolist (,var (list ,alist ,vec ,ht)) 51 (dolist (,var (list ,alist ,vec ,ht))
55 ,@body)))) 52 ,@body))))
56 53
@@ -74,26 +71,21 @@ Evaluate BODY for each created map.
74 71
75(ert-deftest test-map-put () 72(ert-deftest test-map-put ()
76 (with-maps-do map 73 (with-maps-do map
74 (setf (map-elt map 2) 'hello)
75 (should (eq (map-elt map 2) 'hello)))
76 (with-maps-do map
77 (map-put map 2 'hello) 77 (map-put map 2 'hello)
78 (should (eq (map-elt map 2) 'hello))) 78 (should (eq (map-elt map 2) 'hello)))
79 (let ((ht (make-hash-table))) 79 (let ((ht (make-hash-table)))
80 (map-put ht 2 'a) 80 (setf (map-elt ht 2) 'a)
81 (should (eq (map-elt ht 2) 81 (should (eq (map-elt ht 2)
82 'a))) 82 'a)))
83 (let ((alist '((0 . a) (1 . b) (2 . c)))) 83 (let ((alist '((0 . a) (1 . b) (2 . c))))
84 (map-put alist 2 'a) 84 (setf (map-elt alist 2) 'a)
85 (should (eq (map-elt alist 2) 85 (should (eq (map-elt alist 2)
86 'a))) 86 'a)))
87 (let ((vec [3 4 5])) 87 (let ((vec [3 4 5]))
88 (should-error (map-put vec 3 6)))) 88 (should-error (setf (map-elt vec 3) 6))))
89
90(ert-deftest test-map-put-literal ()
91 (should (= (map-elt (map-put [1 2 3] 1 4) 1)
92 4))
93 (should (= (map-elt (map-put (make-hash-table) 'a 2) 'a)
94 2))
95 (should-error (map-put '((a . 1)) 'b 2))
96 (should-error (map-put '() 'a 1)))
97 89
98(ert-deftest test-map-put-return-value () 90(ert-deftest test-map-put-return-value ()
99 (let ((ht (make-hash-table))) 91 (let ((ht (make-hash-table)))
@@ -111,22 +103,22 @@ Evaluate BODY for each created map.
111 (let ((ht (make-hash-table))) 103 (let ((ht (make-hash-table)))
112 (should (eq (map-delete ht 'a) ht)))) 104 (should (eq (map-delete ht 'a) ht))))
113 105
114(ert-deftest test-map-nested-elt () 106;; (ert-deftest test-map-nested-elt ()
115 (let ((vec [a b [c d [e f]]])) 107;; (let ((vec [a b [c d [e f]]]))
116 (should (eq (map-nested-elt vec '(2 2 0)) 'e))) 108;; (should (eq (map-nested-elt vec '(2 2 0)) 'e)))
117 (let ((alist '((a . 1) 109;; (let ((alist '((a . 1)
118 (b . ((c . 2) 110;; (b . ((c . 2)
119 (d . 3) 111;; (d . 3)
120 (e . ((f . 4) 112;; (e . ((f . 4)
121 (g . 5)))))))) 113;; (g . 5))))))))
122 (should (eq (map-nested-elt alist '(b e f)) 114;; (should (eq (map-nested-elt alist '(b e f))
123 4))) 115;; 4)))
124 (let ((ht (make-hash-table))) 116;; (let ((ht (make-hash-table)))
125 (map-put ht 'a 1) 117;; (setf (map-elt ht 'a) 1)
126 (map-put ht 'b (make-hash-table)) 118;; (setf (map-elt ht 'b) (make-hash-table))
127 (map-put (map-elt ht 'b) 'c 2) 119;; (setf (map-elt (map-elt ht 'b) 'c) 2)
128 (should (eq (map-nested-elt ht '(b c)) 120;; (should (eq (map-nested-elt ht '(b c))
129 2)))) 121;; 2))))
130 122
131(ert-deftest test-map-nested-elt-default () 123(ert-deftest test-map-nested-elt-default ()
132 (let ((vec [a b [c d]])) 124 (let ((vec [a b [c d]]))
@@ -215,39 +207,39 @@ Evaluate BODY for each created map.
215 207
216(ert-deftest test-map-filter () 208(ert-deftest test-map-filter ()
217 (with-maps-do map 209 (with-maps-do map
218 (should (equal (map-keys (map-filter (lambda (k v) 210 (should (equal (map-keys (map-filter (lambda (_k v)
219 (<= 4 v)) 211 (<= 4 v))
220 map)) 212 map))
221 '(1 2))) 213 '(1 2)))
222 (should (null (map-filter (lambda (k v) 214 (should (null (map-filter (lambda (k _v)
223 (eq 'd k)) 215 (eq 'd k))
224 map)))) 216 map))))
225 (should (null (map-filter (lambda (k v) 217 (should (null (map-filter (lambda (_k v)
226 (eq 3 v)) 218 (eq 3 v))
227 [1 2 4 5]))) 219 [1 2 4 5])))
228 (should (equal (map-filter (lambda (k v) 220 (should (equal (map-filter (lambda (k _v)
229 (eq 3 k)) 221 (eq 3 k))
230 [1 2 4 5]) 222 [1 2 4 5])
231 '((3 . 5))))) 223 '((3 . 5)))))
232 224
233(ert-deftest test-map-remove () 225(ert-deftest test-map-remove ()
234 (with-maps-do map 226 (with-maps-do map
235 (should (equal (map-keys (map-remove (lambda (k v) 227 (should (equal (map-keys (map-remove (lambda (_k v)
236 (>= v 4)) 228 (>= v 4))
237 map)) 229 map))
238 '(0))) 230 '(0)))
239 (should (equal (map-keys (map-remove (lambda (k v) 231 (should (equal (map-keys (map-remove (lambda (k _v)
240 (eq 'd k)) 232 (eq 'd k))
241 map)) 233 map))
242 (map-keys map)))) 234 (map-keys map))))
243 (should (equal (map-remove (lambda (k v) 235 (should (equal (map-remove (lambda (_k v)
244 (eq 3 v)) 236 (eq 3 v))
245 [1 2 4 5]) 237 [1 2 4 5])
246 '((0 . 1) 238 '((0 . 1)
247 (1 . 2) 239 (1 . 2)
248 (2 . 4) 240 (2 . 4)
249 (3 . 5)))) 241 (3 . 5))))
250 (should (null (map-remove (lambda (k v) 242 (should (null (map-remove (lambda (k _v)
251 (>= k 0)) 243 (>= k 0))
252 [1 2 4 5])))) 244 [1 2 4 5]))))
253 245
@@ -270,35 +262,35 @@ Evaluate BODY for each created map.
270 262
271(ert-deftest test-map-some-p () 263(ert-deftest test-map-some-p ()
272 (with-maps-do map 264 (with-maps-do map
273 (should (equal (map-some-p (lambda (k v) 265 (should (equal (map-some-p (lambda (k _v)
274 (eq 1 k)) 266 (eq 1 k))
275 map) 267 map)
276 (cons 1 4))) 268 (cons 1 4)))
277 (should (not (map-some-p (lambda (k v) 269 (should (not (map-some-p (lambda (k _v)
278 (eq 'd k)) 270 (eq 'd k))
279 map)))) 271 map))))
280 (let ((vec [a b c])) 272 (let ((vec [a b c]))
281 (should (equal (map-some-p (lambda (k v) 273 (should (equal (map-some-p (lambda (k _v)
282 (> k 1)) 274 (> k 1))
283 vec) 275 vec)
284 (cons 2 'c))) 276 (cons 2 'c)))
285 (should (not (map-some-p (lambda (k v) 277 (should (not (map-some-p (lambda (k _v)
286 (> k 3)) 278 (> k 3))
287 vec))))) 279 vec)))))
288 280
289(ert-deftest test-map-every-p () 281(ert-deftest test-map-every-p ()
290 (with-maps-do map 282 (with-maps-do map
291 (should (map-every-p (lambda (k v) 283 (should (map-every-p (lambda (k _v)
292 k) 284 k)
293 map)) 285 map))
294 (should (not (map-every-p (lambda (k v) 286 (should (not (map-every-p (lambda (_k _v)
295 nil) 287 nil)
296 map)))) 288 map))))
297 (let ((vec [a b c])) 289 (let ((vec [a b c]))
298 (should (map-every-p (lambda (k v) 290 (should (map-every-p (lambda (k _v)
299 (>= k 0)) 291 (>= k 0))
300 vec)) 292 vec))
301 (should (not (map-every-p (lambda (k v) 293 (should (not (map-every-p (lambda (k _v)
302 (> k 3)) 294 (> k 3))
303 vec))))) 295 vec)))))
304 296
@@ -324,7 +316,8 @@ Evaluate BODY for each created map.
324 (should (null baz))) 316 (should (null baz)))
325 (map-let (('foo a) 317 (map-let (('foo a)
326 ('bar b) 318 ('bar b)
327 ('baz c)) '((foo . 1) (bar . 2)) 319 ('baz c))
320 '((foo . 1) (bar . 2))
328 (should (= a 1)) 321 (should (= a 1))
329 (should (= b 2)) 322 (should (= b 2))
330 (should (null c)))) 323 (should (null c))))