diff options
| author | Stefan Monnier | 2014-12-01 09:45:15 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2014-12-01 09:45:15 -0500 |
| commit | f0e8c1eac226641ea8acab9e0f47ce3541803f0d (patch) | |
| tree | 88af1f8b3ecf2c3e576cb5db3ff8957fb234d77c | |
| parent | 578d91ac509a9856cf854bea75b6328cf40d1d03 (diff) | |
| download | emacs-f0e8c1eac226641ea8acab9e0f47ce3541803f0d.tar.gz emacs-f0e8c1eac226641ea8acab9e0f47ce3541803f0d.zip | |
New macro `define-inline'.
* lisp/emacs-lisp/inline.el: New file.
| -rw-r--r-- | etc/NEWS | 2 | ||||
| -rw-r--r-- | lisp/ChangeLog | 16 | ||||
| -rw-r--r-- | lisp/emacs-lisp/autoload.el | 11 | ||||
| -rw-r--r-- | lisp/emacs-lisp/inline.el | 251 |
4 files changed, 268 insertions, 12 deletions
| @@ -373,6 +373,8 @@ Emacs-21. | |||
| 373 | 373 | ||
| 374 | * Lisp Changes in Emacs 25.1 | 374 | * Lisp Changes in Emacs 25.1 |
| 375 | 375 | ||
| 376 | ** define-inline provides a new way to define inlinable functions. | ||
| 377 | |||
| 376 | ** New function macroexpand-1 to perform a single step of macroexpansion. | 378 | ** New function macroexpand-1 to perform a single step of macroexpansion. |
| 377 | 379 | ||
| 378 | ** Some "x-*" were obsoleted: | 380 | ** Some "x-*" were obsoleted: |
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index d6691f51d17..41b3ddbc3aa 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,9 +1,13 @@ | |||
| 1 | 2014-12-01 Stefan Monnier <monnier@iro.umontreal.ca> | ||
| 2 | |||
| 3 | * emacs-lisp/inline.el: New file. | ||
| 4 | |||
| 1 | 2014-12-01 Eric S. Raymond <esr@snark.thyrsus.com> | 5 | 2014-12-01 Eric S. Raymond <esr@snark.thyrsus.com> |
| 2 | 6 | ||
| 3 | * vc/vc.el, vc-hooks.el, and all backends: API simplification; | 7 | * vc/vc.el, vc-hooks.el, and all backends: API simplification; |
| 4 | vc-state-heuristic is no longer a public method, having been | 8 | vc-state-heuristic is no longer a public method, having been |
| 5 | removed where it is redundant, unnecessary, or known buggy. This | 9 | removed where it is redundant, unnecessary, or known buggy. |
| 6 | eliminated all backends except CVS. Eliminates bug#7850. | 10 | This eliminated all backends except CVS. Eliminates bug#7850. |
| 7 | 11 | ||
| 8 | * vc/vc-cvs.el, vc/vc-hooks.el, vc/vc-rcs.el, vc/vc-sccs.el: | 12 | * vc/vc-cvs.el, vc/vc-hooks.el, vc/vc-rcs.el, vc/vc-sccs.el: |
| 9 | Eliminate vc-mistrust-permissions. It was only relevant to the | 13 | Eliminate vc-mistrust-permissions. It was only relevant to the |
| @@ -41,8 +45,8 @@ | |||
| 41 | 45 | ||
| 42 | 2014-11-29 Fabián Ezequiel Gallina <fgallina@gnu.org> | 46 | 2014-11-29 Fabián Ezequiel Gallina <fgallina@gnu.org> |
| 43 | 47 | ||
| 44 | * progmodes/python.el (python-shell-completion-setup-code): Use | 48 | * progmodes/python.el (python-shell-completion-setup-code): |
| 45 | __builtin__ module (or builtins in Python 3) and catch all errors | 49 | Use __builtin__ module (or builtins in Python 3) and catch all errors |
| 46 | when importing readline and rlcompleter. | 50 | when importing readline and rlcompleter. |
| 47 | 51 | ||
| 48 | 2014-11-29 Stephen Berman <stephen.berman@gmx.net> | 52 | 2014-11-29 Stephen Berman <stephen.berman@gmx.net> |
| @@ -94,8 +98,8 @@ | |||
| 94 | 98 | ||
| 95 | 2014-11-29 Eli Zaretskii <eliz@gnu.org> | 99 | 2014-11-29 Eli Zaretskii <eliz@gnu.org> |
| 96 | 100 | ||
| 97 | * vc/vc-git.el (vc-git-command, vc-git--call): Bind | 101 | * vc/vc-git.el (vc-git-command, vc-git--call): |
| 98 | coding-system-for-read and coding-system-for-write to | 102 | Bind coding-system-for-read and coding-system-for-write to |
| 99 | vc-git-commits-coding-system. | 103 | vc-git-commits-coding-system. |
| 100 | (vc-git-previous-revision): Use "~1" instead of "^", since the | 104 | (vc-git-previous-revision): Use "~1" instead of "^", since the |
| 101 | latter is a special character for MS-Windows system shells. | 105 | latter is a special character for MS-Windows system shells. |
diff --git a/lisp/emacs-lisp/autoload.el b/lisp/emacs-lisp/autoload.el index 38956df66de..01f59704a39 100644 --- a/lisp/emacs-lisp/autoload.el +++ b/lisp/emacs-lisp/autoload.el | |||
| @@ -120,7 +120,8 @@ expression, in which case we want to handle forms differently." | |||
| 120 | ;; Look for an interactive spec. | 120 | ;; Look for an interactive spec. |
| 121 | (interactive (pcase body | 121 | (interactive (pcase body |
| 122 | ((or `((interactive . ,_) . ,_) | 122 | ((or `((interactive . ,_) . ,_) |
| 123 | `(,_ (interactive . ,_) . ,_)) t)))) | 123 | `(,_ (interactive . ,_) . ,_)) |
| 124 | t)))) | ||
| 124 | ;; Add the usage form at the end where describe-function-1 | 125 | ;; Add the usage form at the end where describe-function-1 |
| 125 | ;; can recover it. | 126 | ;; can recover it. |
| 126 | (when (listp args) (setq doc (help-add-fundoc-usage doc args))) | 127 | (when (listp args) (setq doc (help-add-fundoc-usage doc args))) |
| @@ -140,11 +141,9 @@ expression, in which case we want to handle forms differently." | |||
| 140 | ;; For complex cases, try again on the macro-expansion. | 141 | ;; For complex cases, try again on the macro-expansion. |
| 141 | ((and (memq car '(easy-mmode-define-global-mode define-global-minor-mode | 142 | ((and (memq car '(easy-mmode-define-global-mode define-global-minor-mode |
| 142 | define-globalized-minor-mode defun defmacro | 143 | define-globalized-minor-mode defun defmacro |
| 143 | ;; FIXME: we'd want `defmacro*' here as well, so as | 144 | easy-mmode-define-minor-mode define-minor-mode |
| 144 | ;; to handle its `declare', but when autoload is run | 145 | define-inline cl-defun cl-defmacro)) |
| 145 | ;; CL is not loaded so macroexpand doesn't know how | 146 | (macrop car) |
| 146 | ;; to expand it! | ||
| 147 | easy-mmode-define-minor-mode define-minor-mode)) | ||
| 148 | (setq expand (let ((load-file-name file)) (macroexpand form))) | 147 | (setq expand (let ((load-file-name file)) (macroexpand form))) |
| 149 | (memq (car expand) '(progn prog1 defalias))) | 148 | (memq (car expand) '(progn prog1 defalias))) |
| 150 | (make-autoload expand file 'expansion)) ;Recurse on the expansion. | 149 | (make-autoload expand file 'expansion)) ;Recurse on the expansion. |
diff --git a/lisp/emacs-lisp/inline.el b/lisp/emacs-lisp/inline.el new file mode 100644 index 00000000000..3f11781aec0 --- /dev/null +++ b/lisp/emacs-lisp/inline.el | |||
| @@ -0,0 +1,251 @@ | |||
| 1 | ;;; inline.el --- Define functions by their inliner -*- lexical-binding:t; -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2014 Stefan Monnier | ||
| 4 | |||
| 5 | ;; Author: Stefan Monnier <monnier@iro.umontreal.ca> | ||
| 6 | |||
| 7 | ;; This program is free software; you can redistribute it and/or modify | ||
| 8 | ;; it under the terms of the GNU General Public License as published by | ||
| 9 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 10 | ;; (at your option) any later version. | ||
| 11 | |||
| 12 | ;; This program is distributed in the hope that it will be useful, | ||
| 13 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | ;; GNU General Public License for more details. | ||
| 16 | |||
| 17 | ;; You should have received a copy of the GNU General Public License | ||
| 18 | ;; along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 19 | |||
| 20 | ;;; Commentary: | ||
| 21 | |||
| 22 | ;; This package provides the macro `define-inline' which lets you define | ||
| 23 | ;; functions by defining their (exhaustive) compiler macro. | ||
| 24 | ;; | ||
| 25 | ;; The idea is that instead of doing like defsubst and cl-defsubst (i.e. from | ||
| 26 | ;; the function's definition, guess the best way to inline the function), | ||
| 27 | ;; we go the other way around: the programmer provides the code that does the | ||
| 28 | ;; inlining (as a compiler-macro) and from that we derive the definition of the | ||
| 29 | ;; function itself. The idea originated in an attempt to clean up `cl-typep', | ||
| 30 | ;; whose function definition amounted to (eval (cl--make-type-test EXP TYPE)). | ||
| 31 | ;; | ||
| 32 | ;; The simplest use is for plain and simple inlinable functions. Rather than: | ||
| 33 | ;; | ||
| 34 | ;; (defmacro myaccessor (obj) | ||
| 35 | ;; (macroexp-let2 macroexp-copyable-p obj obj | ||
| 36 | ;; `(if (foop ,obj) (aref (cdr ,obj) 3) (aref ,obj 2)))) | ||
| 37 | ;; Or | ||
| 38 | ;; (defsubst myaccessor (obj) | ||
| 39 | ;; (if (foop obj) (aref (cdr obj) 3) (aref obj 2))) | ||
| 40 | ;; Or | ||
| 41 | ;; (cl-defsubst myaccessor (obj) | ||
| 42 | ;; (if (foop obj) (aref (cdr obj) 3) (aref obj 2))) | ||
| 43 | ;; | ||
| 44 | ;; You'd do | ||
| 45 | ;; | ||
| 46 | ;; (define-inline myaccessor (obj) | ||
| 47 | ;; (inline-letevals (obj) | ||
| 48 | ;; (inline-quote (if (foop ,obj) (aref (cdr ,obj) 3) (aref ,obj 2))))) | ||
| 49 | ;; | ||
| 50 | ;; Other than verbosity, you get the best of all 3 above without their | ||
| 51 | ;; respective downsides: | ||
| 52 | ;; - defmacro: can't be passed to `mapcar' since it's not a function. | ||
| 53 | ;; - defsubst: not as efficient, and doesn't work as a `gv' place. | ||
| 54 | ;; - cl-defsubst: only works by accident, since it has latent bugs in its | ||
| 55 | ;; handling of variables and scopes which could bite you at any time. | ||
| 56 | ;; (e.g. try (cl-defsubst my-test1 (x) (let ((y 5)) (+ x y))) | ||
| 57 | ;; and then M-: (macroexpand-all '(my-test1 y)) RET) | ||
| 58 | ;; There is still one downside shared with the defmacro and cl-defsubst | ||
| 59 | ;; approach: when the function is inlined, the scoping rules (dynamic or | ||
| 60 | ;; lexical) will be inherited from the the call site. | ||
| 61 | |||
| 62 | ;; Of course, since define-inline defines a compiler macro, you can also do | ||
| 63 | ;; call-site optimizations, just like you can with `defmacro', but not with | ||
| 64 | ;; defsubst nor cl-defsubst. | ||
| 65 | |||
| 66 | ;;; Code: | ||
| 67 | |||
| 68 | (require 'macroexp) | ||
| 69 | |||
| 70 | (defmacro inline-quote (exp) | ||
| 71 | "Similar to backquote, but quotes code and only accepts , and not ,@." | ||
| 72 | (declare (debug t)) | ||
| 73 | (error "inline-quote can only be used within define-inline")) | ||
| 74 | |||
| 75 | (defmacro inline-const-p (exp) | ||
| 76 | "Return non-nil if the value of EXP is already known." | ||
| 77 | (declare (debug t)) | ||
| 78 | (error "inline-const-p can only be used within define-inline")) | ||
| 79 | |||
| 80 | (defmacro inline-const-val (exp) | ||
| 81 | "Return the value of EXP." | ||
| 82 | (declare (debug t)) | ||
| 83 | (error "inline-const-val can only be used within define-inline")) | ||
| 84 | |||
| 85 | (defmacro inline-error (format &rest args) | ||
| 86 | "Signal an error." | ||
| 87 | (declare (debug t)) | ||
| 88 | (error "inline-error can only be used within define-inline")) | ||
| 89 | |||
| 90 | (defmacro inline--leteval (_var-exp &rest _body) | ||
| 91 | (declare (indent 1) (debug (sexp &rest body))) | ||
| 92 | (error "inline-letevals can only be used within define-inline")) | ||
| 93 | (defmacro inline--letlisteval (_list &rest _body) | ||
| 94 | (declare (indent 1) (debug (sexp &rest body))) | ||
| 95 | (error "inline-letevals can only be used within define-inline")) | ||
| 96 | |||
| 97 | (defmacro inline-letevals (vars &rest body) | ||
| 98 | "Make sure the expressions in VARS are evaluated. | ||
| 99 | VARS should be a list of elements of the form (VAR EXP) or just VAR, in case | ||
| 100 | EXP is equal to VAR. The result is to evaluate EXP and bind the result to VAR. | ||
| 101 | |||
| 102 | The tail of VARS can be either nil or a symbol VAR which should hold a list | ||
| 103 | of arguments,in which case each argument is evaluated and the resulting | ||
| 104 | new list is re-bound to VAR. | ||
| 105 | |||
| 106 | After VARS is handled, BODY is evaluated in the new environment." | ||
| 107 | (declare (indent 1) (debug (sexp &rest body))) | ||
| 108 | (cond | ||
| 109 | ((consp vars) | ||
| 110 | `(inline--leteval ,(pop vars) (inline-letevals ,vars ,@body))) | ||
| 111 | (vars | ||
| 112 | `(inline--letlisteval ,vars ,@body)) | ||
| 113 | (t (macroexp-progn body)))) | ||
| 114 | |||
| 115 | |||
| 116 | ;;;###autoload | ||
| 117 | (defmacro define-inline (name args &rest body) | ||
| 118 | ;; FIXME: How can this work with CL arglists? | ||
| 119 | (declare (indent defun) (debug defun) (doc-string 3)) | ||
| 120 | (let ((doc (if (stringp (car-safe body)) (list (pop body)))) | ||
| 121 | (declares (if (eq (car-safe (car-safe body)) 'declare) (pop body))) | ||
| 122 | (cm-name (intern (format "%s--inliner" name))) | ||
| 123 | (bodyexp (macroexp-progn body))) | ||
| 124 | ;; If the function is autoloaded then when we load the .el file, the | ||
| 125 | ;; `compiler-macro' property is already set (from loaddefs.el) and might | ||
| 126 | ;; hence be called during the macroexpand-all calls below (if the function | ||
| 127 | ;; is recursive). | ||
| 128 | ;; So we disable any pre-loaded compiler-macro setting to avoid this. | ||
| 129 | (function-put name 'compiler-macro nil) | ||
| 130 | `(progn | ||
| 131 | (defun ,name ,args | ||
| 132 | ,@doc | ||
| 133 | (declare (compiler-macro ,cm-name) ,@(cdr declares)) | ||
| 134 | ,(macroexpand-all bodyexp | ||
| 135 | `((inline-quote . inline--dont-quote) | ||
| 136 | ;; (inline-\` . inline--dont-quote) | ||
| 137 | (inline--leteval . inline--dont-leteval) | ||
| 138 | (inline--letlisteval . inline--dont-letlisteval) | ||
| 139 | (inline-const-p . inline--alwaysconst-p) | ||
| 140 | (inline-const-val . inline--alwaysconst-val) | ||
| 141 | (inline-error . inline--error) | ||
| 142 | ,@macroexpand-all-environment))) | ||
| 143 | :autoload-end | ||
| 144 | (eval-and-compile | ||
| 145 | (defun ,cm-name ,(cons 'inline--form args) | ||
| 146 | (ignore inline--form) ;In case it's not used! | ||
| 147 | (catch 'inline--just-use | ||
| 148 | ,(macroexpand-all | ||
| 149 | bodyexp | ||
| 150 | `((inline-quote . inline--do-quote) | ||
| 151 | ;; (inline-\` . inline--do-quote) | ||
| 152 | (inline--leteval . inline--do-leteval) | ||
| 153 | (inline--letlisteval | ||
| 154 | . inline--do-letlisteval) | ||
| 155 | (inline-const-p . inline--testconst-p) | ||
| 156 | (inline-const-val . inline--getconst-val) | ||
| 157 | (inline-error . inline--warning) | ||
| 158 | ,@macroexpand-all-environment)))))))) | ||
| 159 | |||
| 160 | (defun inline--do-quote (exp) | ||
| 161 | (pcase exp | ||
| 162 | (`(,'\, ,e) e) ;Eval `e' now *and* later. | ||
| 163 | (`'(,'\, ,e) `(list 'quote ,e)) ;Only eval `e' now, not later. | ||
| 164 | (`#'(,'\, ,e) `(list 'function ,e)) ;Only eval `e' now, not later. | ||
| 165 | ((pred consp) | ||
| 166 | (let ((args ())) | ||
| 167 | (while (and (consp exp) (not (eq '\, (car exp)))) | ||
| 168 | (push (inline--do-quote (pop exp)) args)) | ||
| 169 | (setq args (nreverse args)) | ||
| 170 | (if exp | ||
| 171 | `(backquote-list* ,@args ,(inline--do-quote exp)) | ||
| 172 | `(list ,@args)))) | ||
| 173 | (_ (macroexp-quote exp)))) | ||
| 174 | |||
| 175 | (defun inline--dont-quote (exp) | ||
| 176 | (pcase exp | ||
| 177 | (`(,'\, ,e) e) | ||
| 178 | (`'(,'\, ,e) e) | ||
| 179 | (`#'(,'\, ,e) e) | ||
| 180 | ((pred consp) | ||
| 181 | (let ((args ())) | ||
| 182 | (while (and (consp exp) (not (eq '\, (car exp)))) | ||
| 183 | (push (inline--dont-quote (pop exp)) args)) | ||
| 184 | (setq args (nreverse args)) | ||
| 185 | (if exp | ||
| 186 | `(apply ,@args ,(inline--dont-quote exp)) | ||
| 187 | args))) | ||
| 188 | (_ exp))) | ||
| 189 | |||
| 190 | (defun inline--do-leteval (var-exp &rest body) | ||
| 191 | `(macroexp-let2 ,(if (symbolp var-exp) #'macroexp-copyable-p #'ignore) | ||
| 192 | ,(or (car-safe var-exp) var-exp) | ||
| 193 | ,(or (car (cdr-safe var-exp)) var-exp) | ||
| 194 | ,@body)) | ||
| 195 | |||
| 196 | (defun inline--dont-leteval (var-exp &rest body) | ||
| 197 | (if (symbolp var-exp) | ||
| 198 | (macroexp-progn body) | ||
| 199 | `(let (,var-exp) ,@body))) | ||
| 200 | |||
| 201 | (defun inline--do-letlisteval (listvar &rest body) | ||
| 202 | ;; Here's a sample situation: | ||
| 203 | ;; (define-inline foo (arg &rest keys) | ||
| 204 | ;; (inline-letevals (arg . keys) | ||
| 205 | ;; <check-keys>)) | ||
| 206 | ;; I.e. in <check-keys> we need `keys' to contain a list of | ||
| 207 | ;; macroexp-copyable-p expressions. | ||
| 208 | (let ((bsym (make-symbol "bindings"))) | ||
| 209 | `(let* ((,bsym ()) | ||
| 210 | (,listvar (mapcar (lambda (e) | ||
| 211 | (if (macroexp-copyable-p e) e | ||
| 212 | (let ((v (make-symbol "v"))) | ||
| 213 | (push (list v e) ,bsym) | ||
| 214 | v))) | ||
| 215 | ,listvar))) | ||
| 216 | (macroexp-let* (nreverse ,bsym) | ||
| 217 | ,(macroexp-progn body))))) | ||
| 218 | |||
| 219 | (defun inline--dont-letlisteval (_listvar &rest body) | ||
| 220 | (macroexp-progn body)) | ||
| 221 | |||
| 222 | (defun inline--testconst-p (exp) | ||
| 223 | `(macroexp-const-p ,exp)) | ||
| 224 | |||
| 225 | (defun inline--alwaysconst-p (_exp) | ||
| 226 | t) | ||
| 227 | |||
| 228 | (defun inline--getconst-val (exp) | ||
| 229 | (macroexp-let2 macroexp-copyable-p exp exp | ||
| 230 | `(cond | ||
| 231 | ((not (macroexp-const-p ,exp)) | ||
| 232 | (throw 'inline--just-use inline--form)) | ||
| 233 | ((consp ,exp) (cadr ,exp)) | ||
| 234 | (t ,exp)))) | ||
| 235 | |||
| 236 | (defun inline--alwaysconst-val (exp) | ||
| 237 | exp) | ||
| 238 | |||
| 239 | (defun inline--error (&rest args) | ||
| 240 | `(error ,@args)) | ||
| 241 | |||
| 242 | (defun inline--warning (&rest _args) | ||
| 243 | `(throw 'inline--just-use | ||
| 244 | ;; FIXME: This would inf-loop by calling us right back when | ||
| 245 | ;; macroexpand-all recurses to expand inline--form. | ||
| 246 | ;; (macroexp--warn-and-return (format ,@args) | ||
| 247 | ;; inline--form) | ||
| 248 | inline--form)) | ||
| 249 | |||
| 250 | (provide 'inline) | ||
| 251 | ;;; inline.el ends here | ||