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