]> code.delx.au - gnu-emacs/blobdiff - lisp/emacs-lisp/gv.el
Merge from emacs-24; up to 2012-12-15T12:19:04Z!juri@jurta.org
[gnu-emacs] / lisp / emacs-lisp / gv.el
index 4caa0a7386625514749f678badf6721e0219cf14..cf090e5e758c8270daa88f7c870abf13c77c5cd7 100644 (file)
@@ -1,22 +1,25 @@
-;;; gv.el --- Generalized variables  -*- lexical-binding: t -*-
+;;; gv.el --- generalized variables  -*- lexical-binding: t -*-
 
-;; Copyright (C) 2012  Free Software Foundation, Inc.
+;; Copyright (C) 2012-2013 Free Software Foundation, Inc.
 
 ;; Author: Stefan Monnier <monnier@iro.umontreal.ca>
 ;; Keywords: extensions
+;; Package: emacs
 
-;; This program is free software; you can redistribute it and/or modify
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
 ;; the Free Software Foundation, either version 3 of the License, or
 ;; (at your option) any later version.
 
-;; This program is distributed in the hope that it will be useful,
+;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
@@ -108,7 +111,7 @@ DO must return an Elisp expression."
 GETTER will be bound to a copyable expression that returns the value
 of PLACE.
 SETTER will be bound to a function that takes an expression V and returns
-and new expression that sets PLACE to V.
+a new expression that sets PLACE to V.
 BODY should return some Elisp expression E manipulating PLACE via GETTER
 and SETTER.
 The returned value will then be an Elisp expression that first evaluates
@@ -191,7 +194,7 @@ well for simple place forms.
 Assignments of VAL to (NAME ARGS...) are expanded by binding the argument
 forms (VAL ARGS...) according to ARGLIST, then executing BODY, which must
 return a Lisp form that does the assignment.
-The first arg in ARLIST (the one that receives VAL) receives an expression
+The first arg in ARGLIST (the one that receives VAL) receives an expression
 which can do arbitrary things, whereas the other arguments are all guaranteed
 to be pure and copyable.  Example use:
   (gv-define-setter aref (v a i) `(aset ,a ,i ,v))"
@@ -206,13 +209,21 @@ to be pure and copyable.  Example use:
 This macro is an easy-to-use substitute for `gv-define-expander' that works
 well for simple place forms.  Assignments of VAL to (NAME ARGS...) are
 turned into calls of the form (SETTER ARGS... VAL).
+
 If FIX-RETURN is non-nil, then SETTER is not assumed to return VAL and
-instead the assignment is turned into (prog1 VAL (SETTER ARGS... VAL))
+instead the assignment is turned into something equivalent to
+  \(let ((temp VAL))
+    (SETTER ARGS... temp)
+    temp)
 so as to preserve the semantics of `setf'."
   (declare (debug (sexp (&or symbolp lambda-expr) &optional sexp)))
-  (let ((set-call `(cons ',setter (append args (list val)))))
   `(gv-define-setter ,name (val &rest args)
-     ,(if fix-return `(list 'prog1 val ,set-call) set-call))))
+     ,(if fix-return
+          `(macroexp-let2 nil v val
+             `(progn
+                (,',setter ,@(append args (list v)))
+                ,v))
+        `(cons ',setter (append args (list val))))))
 
 ;;; Typical operations on generalized variables.
 
@@ -225,7 +236,7 @@ For example, (setf (cadr x) y) is equivalent to (setcar (cdr x) y).
 The return value is the last VAL in the list.
 
 \(fn PLACE VAL PLACE VAL ...)"
-  (declare (debug (gv-place form)))
+  (declare (debug (&rest [gv-place form])))
   (if (and args (null (cddr args)))
       (let ((place (pop args))
             (val (car args)))
@@ -266,7 +277,7 @@ The return value is the last VAL in the list.
 ;;;###autoload
 (put 'gv-place 'edebug-form-spec 'edebug-match-form)
 ;; CL did the equivalent of:
-;;(gv-define-expand edebug-after (lambda (before index place) place))
+;;(gv-define-macroexpand edebug-after (lambda (before index place) place))
 
 (put 'edebug-after 'gv-expander
      (lambda (do before index place)
@@ -355,7 +366,8 @@ The return value is the last VAL in the list.
 
 (put 'if 'gv-expander
      (lambda (do test then &rest else)
-       (if (macroexp-small-p (funcall do 'dummy (lambda (_) 'dummy)))
+       (if (or (not lexical-binding)  ;The other code requires lexical-binding.
+               (macroexp-small-p (funcall do 'dummy (lambda (_) 'dummy))))
            ;; This duplicates the `do' code, which is a problem if that
            ;; code is large, but otherwise results in more efficient code.
            `(if ,test ,(gv-get then do)
@@ -373,7 +385,8 @@ The return value is the last VAL in the list.
 
 (put 'cond 'gv-expander
      (lambda (do &rest branches)
-       (if (macroexp-small-p (funcall do 'dummy (lambda (_) 'dummy)))
+       (if (or (not lexical-binding)  ;The other code requires lexical-binding.
+               (macroexp-small-p (funcall do 'dummy (lambda (_) 'dummy))))
            ;; This duplicates the `do' code, which is a problem if that
            ;; code is large, but otherwise results in more efficient code.
            `(cond
@@ -428,6 +441,26 @@ The return value is the last VAL in the list.
                        `(logior (logand ,v ,mask)
                                 (logand ,getter (lognot ,mask))))))))))
 
+;;; References
+
+;;;###autoload
+(defmacro gv-ref (place)
+  "Return a reference to PLACE.
+This is like the `&' operator of the C language."
+  (gv-letplace (getter setter) place
+    `(cons (lambda () ,getter)
+           (lambda (gv--val) ,(funcall setter 'gv--val)))))
+
+(defsubst gv-deref (ref)
+  "Dereference REF, returning the referenced value.
+This is like the `*' operator of the C language.
+REF must have been previously obtained with `gv-ref'."
+  (funcall (car ref)))
+;; Don't use `declare' because it seems to introduce circularity problems:
+;; Warning: Eager macro-expansion skipped due to cycle:
+;;  … => (load "gv.el") => (macroexpand-all (defsubst gv-deref …)) => (macroexpand (defun …)) => (load "gv.el")
+(gv-define-setter gv-deref (v ref) `(funcall (cdr ,ref) ,v))
+
 ;;; Vaguely related definitions that should be moved elsewhere.
 
 ;; (defun alist-get (key alist)