From e8af4374454965d541ad3b8062e6793217d4e86f Mon Sep 17 00:00:00 2001 From: Thuna Date: Sun, 28 Jul 2024 22:26:41 +0200 Subject: [PATCH] Add a version of cl-once-only which handles lists of forms * lisp/emacs-lisp/cl-macs.el (cl-once-only*): Define macro. --- lisp/emacs-lisp/cl-macs.el | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index 2e501005bf7..70d66b3a250 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el @@ -2544,6 +2544,43 @@ cl-once-only collect `(,(car name) ,gensym)) ,@body))))) +(defmacro cl-once-only* (listvar &rest body) + "Generate code to evaluate the list of forms LISTVAR just once in BODY. + +This is a macro to be used while defining other macros. LISTVAR should +be a variable which holds a list of forms, such as ARGS in a function +call of the form \\=`(,head ,@args). Within BODY, the variable LISTVAR +will be bound to a list of uninterned symbols of the same length, all of +which are variables in the final macroexpansion which are bound to the +result of evaluating the corresponding form. + +For example, the following macro + + (defmacro my-list (head &rest args) + (cl-once-only* args + \\=`(list (,head ,@args) ,@args))) + +when called like + + (let ((x \\='(1 5 4))) + (my-list + (pop x) (1+ (pop x)) (1- (pop x)))) + +will expand into + + (let ((x \\='(1 5 4))) + (let* ((arg1 (pop x)) (arg2 (1+ (pop x))) (arg3 (1- (pop x)))) + (list (+ arg1 arg2 arg3) arg1 arg2 arg3))) + +and the arguments will be evaluated only once and in order." + (declare (debug (arg body)) (indent 1)) + (let ((args (gensym (symbol-name listvar)))) + `(let ((,args + (cl-loop for i from 1 to (length ,listvar) collect + (make-symbol (format "%s$%d" ',(symbol-name args) i))))) + `(let* ,(cl-mapcar #'list ,args ,listvar) + ,(let ((,listvar ,args)) + ,@body))))) + ;;; Multiple values. ;;;###autoload -- 2.44.2