merge trunk
[bpt/emacs.git] / lisp / emacs-lisp / gv.el
1 ;;; gv.el --- Generalized variables -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 2012 Free Software Foundation, Inc.
4
5 ;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
6 ;; Keywords: extensions
7
8 ;; This program is free software; you can redistribute it and/or modify
9 ;; it under the terms of the GNU General Public License as published by
10 ;; the Free Software Foundation, either version 3 of the License, or
11 ;; (at your option) any later version.
12
13 ;; This program is distributed in the hope that it will be useful,
14 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;; GNU General Public License for more details.
17
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
20
21 ;;; Commentary:
22
23 ;; This is a re-implementation of the setf machinery using a different
24 ;; underlying approach than the one used earlier in CL, which was based on
25 ;; define-setf-expander.
26 ;; `define-setf-expander' makes every "place-expander" return a 5-tuple
27 ;; (VARS VALUES STORES GETTER SETTER)
28 ;; where STORES is a list with a single variable (Common-Lisp allows multiple
29 ;; variables for use with multiple-return-values, but this is rarely used and
30 ;; not applicable to Elisp).
31 ;; It basically says that GETTER is an expression that returns the place's
32 ;; value, and (lambda STORES SETTER) is an expression that assigns the value(s)
33 ;; passed to that function to the place, and that you need to wrap the whole
34 ;; thing within a `(let* ,(zip VARS VALUES) ...).
35 ;;
36 ;; Instead, we use here a higher-order approach: instead
37 ;; of a 5-tuple, a place-expander returns a function.
38 ;; If you think about types, the old approach return things of type
39 ;; {vars: List Var, values: List Exp,
40 ;; stores: List Var, getter: Exp, setter: Exp}
41 ;; whereas the new approach returns a function of type
42 ;; (do: ((getter: Exp, setter: ((store: Exp) -> Exp)) -> Exp)) -> Exp.
43 ;; You can get the new function from the old 5-tuple with something like:
44 ;; (lambda (do)
45 ;; `(let* ,(zip VARS VALUES)
46 ;; (funcall do GETTER (lambda ,STORES ,SETTER))))
47 ;; You can't easily do the reverse, because this new approach is more
48 ;; expressive than the old one, so we can't provide a backward-compatible
49 ;; get-setf-method.
50 ;;
51 ;; While it may seem intimidating for people not used to higher-order
52 ;; functions, you will quickly see that its use (especially with the
53 ;; `gv-letplace' macro) is actually much easier and more elegant than the old
54 ;; approach which is clunky and often leads to unreadable code.
55
56 ;; Food for thought: the syntax of places does not actually conflict with the
57 ;; pcase patterns. The `cons' gv works just like a `(,a . ,b) pcase
58 ;; pattern, and actually the `logand' gv is even closer since it should
59 ;; arguably fail when trying to set a value outside of the mask.
60 ;; Generally, places are used for destructors (gethash, aref, car, ...)
61 ;; whereas pcase patterns are used for constructors (backquote, constants,
62 ;; vectors, ...).
63
64 ;;; Code:
65
66 (require 'macroexp)
67
68 ;; What we call a "gvar" is basically a function of type "(getter * setter ->
69 ;; code) -> code", where "getter" is code and setter is "code -> code".
70
71 ;; (defvar gv--macro-environment nil
72 ;; "Macro expanders for generalized variables.")
73
74 ;;;###autoload
75 (defun gv-get (place do)
76 "Build the code that applies DO to PLACE.
77 PLACE must be a valid generalized variable.
78 DO must be a function; it will be called with 2 arguments: GETTER and SETTER,
79 where GETTER is a (copyable) Elisp expression that returns the value of PLACE,
80 and SETTER is a function which returns the code to set PLACE when called
81 with a (not necessarily copyable) Elisp expression that returns the value to
82 set it to.
83 DO must return an Elisp expression."
84 (if (symbolp place)
85 (funcall do place (lambda (v) `(setq ,place ,v)))
86 (let* ((head (car place))
87 (gf (get head 'gv-expander)))
88 ;; Autoload the head, if applicable, since that might define
89 ;; `gv-expander'.
90 (when (and (null gf) (fboundp head)
91 (eq 'autoload (car-safe (symbol-function head))))
92 (with-demoted-errors
93 (load (nth 1 (symbol-function head)) 'noerror 'nomsg)
94 (setq gf (get head 'gv-expander))))
95 (if gf (apply gf do (cdr place))
96 (let ((me (macroexpand place ;FIXME: expand one step at a time!
97 ;; (append macroexpand-all-environment
98 ;; gv--macro-environment)
99 macroexpand-all-environment)))
100 (if (and (eq me place) (get head 'compiler-macro))
101 ;; Expand compiler macros: this takes care of all the accessors
102 ;; defined via cl-defsubst, such as cXXXr and defstruct slots.
103 (setq me (apply (get head 'compiler-macro) place (cdr place))))
104 (if (and (eq me place) (fboundp head)
105 (symbolp (symbol-function head)))
106 ;; Follow aliases.
107 (setq me (cons (symbol-function head) (cdr place))))
108 (if (eq me place)
109 (error "%S is not a valid place expression" place)
110 (gv-get me do)))))))
111
112 ;;;###autoload
113 (defmacro gv-letplace (vars place &rest body)
114 "Build the code manipulating the generalized variable PLACE.
115 GETTER will be bound to a copyable expression that returns the value
116 of PLACE.
117 SETTER will be bound to a function that takes an expression V and returns
118 and new expression that sets PLACE to V.
119 BODY should return some Elisp expression E manipulating PLACE via GETTER
120 and SETTER.
121 The returned value will then be an Elisp expression that first evaluates
122 all the parts of PLACE that can be evaluated and then runs E.
123
124 \(fn (GETTER SETTER) PLACE &rest BODY)"
125 (declare (indent 2) (debug (sexp form body)))
126 `(gv-get ,place (lambda ,vars ,@body)))
127
128 ;; Different ways to declare a generalized variable.
129 ;;;###autoload
130 (defmacro gv-define-expander (name handler)
131 "Use HANDLER to handle NAME as a generalized var.
132 NAME is a symbol: the name of a function, macro, or special form.
133 HANDLER is a function which takes an argument DO followed by the same
134 arguments as NAME. DO is a function as defined in `gv-get'."
135 (declare (indent 1) (debug (sexp form)))
136 ;; Use eval-and-compile so the method can be used in the same file as it
137 ;; is defined.
138 ;; FIXME: Just like byte-compile-macro-environment, we should have something
139 ;; like byte-compile-symbolprop-environment so as to handle these things
140 ;; cleanly without affecting the running Emacs.
141 `(eval-and-compile (put ',name 'gv-expander ,handler)))
142
143 ;;;###autoload
144 (defun gv--defun-declaration (symbol name args handler &optional fix)
145 `(progn
146 ;; No need to autoload this part, since gv-get will auto-load the
147 ;; function's definition before checking the `gv-expander' property.
148 :autoload-end
149 ,(pcase (cons symbol handler)
150 (`(gv-expander . (lambda (,do) . ,body))
151 `(gv-define-expander ,name (lambda (,do ,@args) ,@body)))
152 (`(gv-expander . ,(pred symbolp))
153 `(gv-define-expander ,name #',handler))
154 (`(gv-setter . (lambda (,store) . ,body))
155 `(gv-define-setter ,name (,store ,@args) ,@body))
156 (`(gv-setter . ,(pred symbolp))
157 `(gv-define-simple-setter ,name ,handler ,fix))
158 ;; (`(expand ,expander) `(gv-define-expand ,name ,expander))
159 (_ (message "Unknown %s declaration %S" symbol handler) nil))))
160
161 ;;;###autoload
162 (push `(gv-expander ,(apply-partially #'gv--defun-declaration 'gv-expander))
163 defun-declarations-alist)
164 ;;;###autoload
165 (push `(gv-setter ,(apply-partially #'gv--defun-declaration 'gv-setter))
166 defun-declarations-alist)
167
168 ;; (defmacro gv-define-expand (name expander)
169 ;; "Use EXPANDER to handle NAME as a generalized var.
170 ;; NAME is a symbol: the name of a function, macro, or special form.
171 ;; EXPANDER is a function that will be called as a macro-expander to reduce
172 ;; uses of NAME to some other generalized variable."
173 ;; (declare (debug (sexp form)))
174 ;; `(eval-and-compile
175 ;; (if (not (boundp 'gv--macro-environment))
176 ;; (setq gv--macro-environment nil))
177 ;; (push (cons ',name ,expander) gv--macro-environment)))
178
179 (defun gv--defsetter (name setter do args &optional vars)
180 "Helper function used by code generated by `gv-define-setter'.
181 NAME is the name of the getter function.
182 SETTER is a function that generates the code for the setter.
183 NAME accept ARGS as arguments and SETTER accepts (NEWVAL . ARGS).
184 VARS is used internally for recursive calls."
185 (if (null args)
186 (let ((vars (nreverse vars)))
187 (funcall do `(,name ,@vars) (lambda (v) (apply setter v vars))))
188 ;; FIXME: Often it would be OK to skip this `let', but in general,
189 ;; `do' may have all kinds of side-effects.
190 (macroexp-let2 nil v (car args)
191 (gv--defsetter name setter do (cdr args) (cons v vars)))))
192
193 ;;;###autoload
194 (defmacro gv-define-setter (name arglist &rest body)
195 "Define a setter method for generalized variable NAME.
196 This macro is an easy-to-use substitute for `gv-define-expander' that works
197 well for simple place forms.
198 Assignments of VAL to (NAME ARGS...) are expanded by binding the argument
199 forms (VAL ARGS...) according to ARGLIST, then executing BODY, which must
200 return a Lisp form that does the assignment.
201 Actually, ARGLIST may be bound to temporary variables which are introduced
202 automatically to preserve proper execution order of the arguments. Example:
203 (gv-define-setter aref (v a i) `(aset ,a ,i ,v))"
204 (declare (indent 2) (debug (&define name sexp body)))
205 `(gv-define-expander ,name
206 (lambda (do &rest args)
207 (gv--defsetter ',name (lambda ,arglist ,@body) do args))))
208
209 ;;;###autoload
210 (defmacro gv-define-simple-setter (name setter &optional fix-return)
211 "Define a simple setter method for generalized variable NAME.
212 This macro is an easy-to-use substitute for `gv-define-expander' that works
213 well for simple place forms. Assignments of VAL to (NAME ARGS...) are
214 turned into calls of the form (SETTER ARGS... VAL).
215 If FIX-RETURN is non-nil, then SETTER is not assumed to return VAL and
216 instead the assignment is turned into (prog1 VAL (SETTER ARGS... VAL))
217 so as to preserve the semantics of `setf'."
218 (declare (debug (sexp (&or symbolp lambda-expr) &optional sexp)))
219 (let ((set-call `(cons ',setter (append args (list val)))))
220 `(gv-define-setter ,name (val &rest args)
221 ,(if fix-return `(list 'prog1 val ,set-call) set-call))))
222
223 ;;; Typical operations on generalized variables.
224
225 ;;;###autoload
226 (defmacro setf (&rest args)
227 "Set each PLACE to the value of its VAL.
228 This is a generalized version of `setq'; the PLACEs may be symbolic
229 references such as (car x) or (aref x i), as well as plain symbols.
230 For example, (setf (cadr x) y) is equivalent to (setcar (cdr x) y).
231 The return value is the last VAL in the list.
232
233 \(fn PLACE VAL PLACE VAL ...)"
234 (declare (debug (gv-place form)))
235 (if (and args (null (cddr args)))
236 (let ((place (pop args))
237 (val (car args)))
238 (gv-letplace (_getter setter) place
239 (funcall setter val)))
240 (let ((sets nil))
241 (while args (push `(setf ,(pop args) ,(pop args)) sets))
242 (cons 'progn (nreverse sets)))))
243
244 ;; (defmacro gv-pushnew! (val place)
245 ;; "Like `gv-push!' but only adds VAL if it's not yet in PLACE.
246 ;; Presence is checked with `member'.
247 ;; The return value is unspecified."
248 ;; (declare (debug (form gv-place)))
249 ;; (macroexp-let2 macroexp-copyable-p v val
250 ;; (gv-letplace (getter setter) place
251 ;; `(if (member ,v ,getter) nil
252 ;; ,(funcall setter `(cons ,v ,getter))))))
253
254 ;; (defmacro gv-inc! (place &optional val)
255 ;; "Increment PLACE by VAL (default to 1)."
256 ;; (declare (debug (gv-place &optional form)))
257 ;; (gv-letplace (getter setter) place
258 ;; (funcall setter `(+ ,getter ,(or val 1)))))
259
260 ;; (defmacro gv-dec! (place &optional val)
261 ;; "Decrement PLACE by VAL (default to 1)."
262 ;; (declare (debug (gv-place &optional form)))
263 ;; (gv-letplace (getter setter) place
264 ;; (funcall setter `(- ,getter ,(or val 1)))))
265
266 ;; For Edebug, the idea is to let Edebug instrument gv-places just like it does
267 ;; for normal expressions, and then give it a gv-expander to DTRT.
268 ;; Maybe this should really be in edebug.el rather than here.
269
270 ;; Autoload this `put' since a user might use C-u C-M-x on an expression
271 ;; containing a non-trivial `push' even before gv.el was loaded.
272 ;;;###autoload
273 (put 'gv-place 'edebug-form-spec 'edebug-match-form)
274 ;; CL did the equivalent of:
275 ;;(gv-define-expand edebug-after (lambda (before index place) place))
276
277 (put 'edebug-after 'gv-expander
278 (lambda (do before index place)
279 (gv-letplace (getter setter) place
280 (funcall do `(edebug-after ,before ,index ,getter)
281 setter))))
282
283 ;;; The common generalized variables.
284
285 (gv-define-simple-setter aref aset)
286 (gv-define-simple-setter car setcar)
287 (gv-define-simple-setter cdr setcdr)
288 ;; FIXME: add compiler-macros for `cXXr' instead!
289 (gv-define-setter caar (val x) `(setcar (car ,x) ,val))
290 (gv-define-setter cadr (val x) `(setcar (cdr ,x) ,val))
291 (gv-define-setter cdar (val x) `(setcdr (car ,x) ,val))
292 (gv-define-setter cddr (val x) `(setcdr (cdr ,x) ,val))
293 (gv-define-setter elt (store seq n)
294 `(if (listp ,seq) (setcar (nthcdr ,n ,seq) ,store)
295 (aset ,seq ,n ,store)))
296 (gv-define-simple-setter get put)
297 (gv-define-setter gethash (val k h &optional _d) `(puthash ,k ,val ,h))
298
299 ;; (gv-define-expand nth (lambda (idx list) `(car (nthcdr ,idx ,list))))
300 (put 'nth 'gv-expander
301 (lambda (do idx list)
302 (macroexp-let2 nil c `(nthcdr ,idx ,list)
303 (funcall do `(car ,c) (lambda (v) `(setcar ,c ,v))))))
304 (gv-define-simple-setter symbol-function fset)
305 (gv-define-simple-setter symbol-plist setplist)
306 (gv-define-simple-setter symbol-value set)
307
308 (put 'nthcdr 'gv-expander
309 (lambda (do n place)
310 (macroexp-let2 nil idx n
311 (gv-letplace (getter setter) place
312 (funcall do `(nthcdr ,idx ,getter)
313 (lambda (v) `(if (<= ,idx 0) ,(funcall setter v)
314 (setcdr (nthcdr (1- ,idx) ,getter) ,v))))))))
315
316 ;;; Elisp-specific generalized variables.
317
318 (gv-define-simple-setter default-value set-default)
319 (gv-define-simple-setter frame-parameter set-frame-parameter 'fix)
320 (gv-define-simple-setter terminal-parameter set-terminal-parameter)
321 (gv-define-simple-setter keymap-parent set-keymap-parent)
322 (gv-define-simple-setter match-data set-match-data 'fix)
323 (gv-define-simple-setter overlay-get overlay-put)
324 (gv-define-setter overlay-start (store ov)
325 `(progn (move-overlay ,ov ,store (overlay-end ,ov)) ,store))
326 (gv-define-setter overlay-end (store ov)
327 `(progn (move-overlay ,ov (overlay-start ,ov) ,store) ,store))
328 (gv-define-simple-setter process-buffer set-process-buffer)
329 (gv-define-simple-setter process-filter set-process-filter)
330 (gv-define-simple-setter process-sentinel set-process-sentinel)
331 (gv-define-simple-setter process-get process-put)
332 (gv-define-simple-setter window-buffer set-window-buffer)
333 (gv-define-simple-setter window-display-table set-window-display-table 'fix)
334 (gv-define-simple-setter window-dedicated-p set-window-dedicated-p)
335 (gv-define-simple-setter window-hscroll set-window-hscroll)
336 (gv-define-simple-setter window-parameter set-window-parameter)
337 (gv-define-simple-setter window-point set-window-point)
338 (gv-define-simple-setter window-start set-window-start)
339
340 ;;; Some occasionally handy extensions.
341
342 ;; While several of the "places" below are not terribly useful for direct use,
343 ;; they can show up as the output of the macro expansion of reasonable places,
344 ;; such as struct-accessors.
345
346 (put 'progn 'gv-expander
347 (lambda (do &rest exps)
348 (let ((start (butlast exps))
349 (end (car (last exps))))
350 (if (null start) (gv-get end do)
351 `(progn ,@start ,(gv-get end do))))))
352
353 (let ((let-expander
354 (lambda (letsym)
355 (lambda (do bindings &rest body)
356 `(,letsym ,bindings
357 ,@(macroexp-unprogn
358 (gv-get (macroexp-progn body) do)))))))
359 (put 'let 'gv-expander (funcall let-expander 'let))
360 (put 'let* 'gv-expander (funcall let-expander 'let*)))
361
362 (put 'if 'gv-expander
363 (lambda (do test then &rest else)
364 (if (macroexp-small-p (funcall do 'dummy (lambda (_) 'dummy)))
365 ;; This duplicates the `do' code, which is a problem if that
366 ;; code is large, but otherwise results in more efficient code.
367 `(if ,test ,(gv-get then do)
368 ,@(macroexp-unprogn (gv-get (macroexp-progn else) do)))
369 (let ((v (make-symbol "v")))
370 (macroexp-let2 nil
371 gv `(if ,test ,(gv-letplace (getter setter) then
372 `(cons (lambda () ,getter)
373 (lambda (,v) ,(funcall setter v))))
374 ,(gv-letplace (getter setter) (macroexp-progn else)
375 `(cons (lambda () ,getter)
376 (lambda (,v) ,(funcall setter v)))))
377 (funcall do `(funcall (car ,gv))
378 (lambda (v) `(funcall (cdr ,gv) ,v))))))))
379
380 (put 'cond 'gv-expander
381 (lambda (do &rest branches)
382 (if (macroexp-small-p (funcall do 'dummy (lambda (_) 'dummy)))
383 ;; This duplicates the `do' code, which is a problem if that
384 ;; code is large, but otherwise results in more efficient code.
385 `(cond
386 ,@(mapcar (lambda (branch)
387 (if (cdr branch)
388 (cons (car branch)
389 (macroexp-unprogn
390 (gv-get (macroexp-progn (cdr branch)) do)))
391 (gv-get (car branch) do)))
392 branches))
393 (let ((v (make-symbol "v")))
394 (macroexp-let2 nil
395 gv `(cond
396 ,@(mapcar
397 (lambda (branch)
398 (if (cdr branch)
399 `(,(car branch)
400 ,@(macroexp-unprogn
401 (gv-letplace (getter setter)
402 (macroexp-progn (cdr branch))
403 `(cons (lambda () ,getter)
404 (lambda (,v) ,(funcall setter v))))))
405 (gv-letplace (getter setter)
406 (car branch)
407 `(cons (lambda () ,getter)
408 (lambda (,v) ,(funcall setter v))))))
409 branches))
410 (funcall do `(funcall (car ,gv))
411 (lambda (v) `(funcall (cdr ,gv) ,v))))))))
412
413 ;;; Even more debatable extensions.
414
415 (put 'cons 'gv-expander
416 (lambda (do a d)
417 (gv-letplace (agetter asetter) a
418 (gv-letplace (dgetter dsetter) d
419 (funcall do
420 `(cons ,agetter ,dgetter)
421 (lambda (v) `(progn
422 ,(funcall asetter `(car ,v))
423 ,(funcall dsetter `(cdr ,v)))))))))
424
425 (put 'logand 'gv-expander
426 (lambda (do place &rest masks)
427 (gv-letplace (getter setter) place
428 (macroexp-let2 macroexp-copyable-p
429 mask (if (cdr masks) `(logand ,@masks) (car masks))
430 (funcall
431 do `(logand ,getter ,mask)
432 (lambda (v)
433 (funcall setter
434 `(logior (logand ,v ,mask)
435 (logand ,getter (lognot ,mask))))))))))
436
437 ;;; Vaguely related definitions that should be moved elsewhere.
438
439 ;; (defun alist-get (key alist)
440 ;; "Get the value associated to KEY in ALIST."
441 ;; (declare
442 ;; (gv-expander
443 ;; (lambda (do)
444 ;; (macroexp-let2 macroexp-copyable-p k key
445 ;; (gv-letplace (getter setter) alist
446 ;; (macroexp-let2 nil p `(assoc ,k ,getter)
447 ;; (funcall do `(cdr ,p)
448 ;; (lambda (v)
449 ;; `(if ,p (setcdr ,p ,v)
450 ;; ,(funcall setter
451 ;; `(cons (cons ,k ,v) ,getter)))))))))))
452 ;; (cdr (assoc key alist)))
453
454 (provide 'gv)
455 ;;; gv.el ends here