diff options
| author | Sean Whitton | 2022-04-11 09:20:35 -0700 |
|---|---|---|
| committer | Sean Whitton | 2022-04-11 23:01:55 -0700 |
| commit | 2e9111813b1dfdda1bf56c2b70a4220dbd8abce1 (patch) | |
| tree | e55c50ac4a5d63538cc693e5e5dfa3b5f8f04d2a | |
| parent | e2b64f8999f79a5820ba00d2987885d7dda492d5 (diff) | |
| download | emacs-2e9111813b1dfdda1bf56c2b70a4220dbd8abce1.tar.gz emacs-2e9111813b1dfdda1bf56c2b70a4220dbd8abce1.zip | |
Add two classic Common Lisp macro-writing macros
* lisp/emacs-lisp/cl-macs.el (cl-with-gensyms, cl-once-only): New macros.
| -rw-r--r-- | lisp/emacs-lisp/cl-macs.el | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index da7157f4341..af8855516ca 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el | |||
| @@ -2430,6 +2430,57 @@ by EXPANSION, and (setq NAME ...) will act like (setf EXPANSION ...). | |||
| 2430 | (unless advised | 2430 | (unless advised |
| 2431 | (advice-remove 'macroexpand #'cl--sm-macroexpand))))) | 2431 | (advice-remove 'macroexpand #'cl--sm-macroexpand))))) |
| 2432 | 2432 | ||
| 2433 | ;;;###autoload | ||
| 2434 | (defmacro cl-with-gensyms (names &rest body) | ||
| 2435 | "Bind each of NAMES to an uninterned symbol and evaluate BODY." | ||
| 2436 | (declare (debug (sexp body)) (indent 1)) | ||
| 2437 | `(let ,(cl-loop for name in names collect | ||
| 2438 | `(,name (gensym (symbol-name ',name)))) | ||
| 2439 | ,@body)) | ||
| 2440 | |||
| 2441 | ;;;###autoload | ||
| 2442 | (defmacro cl-once-only (names &rest body) | ||
| 2443 | "Generate code to evaluate each of NAMES just once in BODY. | ||
| 2444 | |||
| 2445 | This macro helps with writing other macros. Each of names is | ||
| 2446 | either (NAME FORM) or NAME, which latter means (NAME NAME). | ||
| 2447 | During macroexpansion, each NAME is bound to an uninterned | ||
| 2448 | symbol. The expansion evaluates each FORM and binds it to the | ||
| 2449 | corresponding uninterned symbol. | ||
| 2450 | |||
| 2451 | For example, consider this macro: | ||
| 2452 | |||
| 2453 | (defmacro my-cons (x) | ||
| 2454 | (cl-once-only (x) | ||
| 2455 | \\=`(cons ,x ,x))) | ||
| 2456 | |||
| 2457 | The call (my-cons (pop y)) will expand to something like this: | ||
| 2458 | |||
| 2459 | (let ((g1 (pop y))) | ||
| 2460 | (cons g1 g1)) | ||
| 2461 | |||
| 2462 | The use of `cl-once-only' ensures that the pop is performed only | ||
| 2463 | once, as intended. | ||
| 2464 | |||
| 2465 | See also `macroexp-let2'." | ||
| 2466 | (declare (debug (sexp body)) (indent 1)) | ||
| 2467 | (setq names (mapcar #'ensure-list names)) | ||
| 2468 | (let ((our-gensyms (cl-loop for _ in names collect (gensym)))) | ||
| 2469 | ;; During macroexpansion, obtain a gensym for each NAME. | ||
| 2470 | `(let ,(cl-loop for sym in our-gensyms collect `(,sym (gensym))) | ||
| 2471 | ;; Evaluate each FORM and bind to the corresponding gensym. | ||
| 2472 | ;; | ||
| 2473 | ;; We require this explicit call to `list' rather than using | ||
| 2474 | ;; (,,@(cl-loop ...)) due to a limitation of Elisp's backquote. | ||
| 2475 | `(let ,(list | ||
| 2476 | ,@(cl-loop for name in names and gensym in our-gensyms | ||
| 2477 | for to-eval = (or (cadr name) (car name)) | ||
| 2478 | collect ``(,,gensym ,,to-eval))) | ||
| 2479 | ;; During macroexpansion, bind each NAME to its gensym. | ||
| 2480 | ,(let ,(cl-loop for name in names and gensym in our-gensyms | ||
| 2481 | collect `(,(car name) ,gensym)) | ||
| 2482 | ,@body))))) | ||
| 2483 | |||
| 2433 | ;;; Multiple values. | 2484 | ;;; Multiple values. |
| 2434 | 2485 | ||
| 2435 | ;;;###autoload | 2486 | ;;;###autoload |