From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Jeremy Bryant Newsgroups: gmane.emacs.devel Subject: Re: Quality of life improvements to macroexp.el Date: Wed, 17 Jul 2024 21:38:47 +0100 Message-ID: <87frs7da94.fsf@jeremybryant.net> References: <87ikx62j5e.fsf@gmail.com> Mime-Version: 1.0 Content-Type: text/plain Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="8772"; mail-complaints-to="usenet@ciao.gmane.io" Cc: emacs-devel@gnu.org To: Thuna Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Wed Jul 17 22:39:56 2024 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1sUBRK-00021Q-Nh for ged-emacs-devel@m.gmane-mx.org; Wed, 17 Jul 2024 22:39:55 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1sUBQT-0006pG-R6; Wed, 17 Jul 2024 16:39:01 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sUBQR-0006p8-N3 for emacs-devel@gnu.org; Wed, 17 Jul 2024 16:38:59 -0400 Original-Received: from out-180.mta0.migadu.com ([91.218.175.180]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1sUBQN-0002jf-VI for emacs-devel@gnu.org; Wed, 17 Jul 2024 16:38:59 -0400 X-Envelope-To: emacs-devel@gnu.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jeremybryant.net; s=key1; t=1721248732; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: in-reply-to:in-reply-to:references:references; bh=yJPEWPzJeYswibzxQcjWwEGX1bDmuoiNSAit8hvtvyY=; b=jZvakWfIsLtHXcNVtIbiSDcnfW0hyn7lUGBqmXkrN2jzoDgb1zu7wJd9oh42+R5iWDjTyS +tmV/V5SfM/cKFzoaev6BjrCjn5McV7SQalb82lRMyTpy5zNVtrOdvsCymtXpZ8WLzosUO nV+vutVb3ciDEO7KGRiANroZYSfJN2ywnBoHidOzoqJfmaWwY5maRAPtenPwx3zhAFpBUk 8i0GJ55FVF3DyO6pjvF+fKwtU+OIRcq9qvas4HC+wGHpSFNX7w7xrJn9O9H5FUaDNw+OeI 30BMmpZHAw811Gv5Dtxc6him7Ns26Oq6++9fF/3PmfrUQm9z1TJPwh37TFCXTQ== X-Envelope-To: thuna.cing@gmail.com X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. In-Reply-To: <87ikx62j5e.fsf@gmail.com> (Thuna's message of "Tue, 16 Jul 2024 03:57:33 +0200") X-Migadu-Flow: FLOW_OUT Received-SPF: pass client-ip=91.218.175.180; envelope-from=jb@jeremybryant.net; helo=out-180.mta0.migadu.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.devel:321764 Archived-At: Thuna writes: > I was just looking into using macroexp.el and found some features that I > felt were lacking. These were: 1. accepting multiple forms in > `macroexp-if' and `macroexp-let*', 2. flattening of `progn's in > `macroexp-progn' and `macroexp-unprogn', 3. getting rid of branches in > `macroexp-if' in case the TEST is constant (and consequently a way to > tell whether a constant form is nil or non-nil). I've went through the > rest of macroexp.el and haven't found anything else that stood out, > though I might change my mind as I keep using it. > > I've attached a patch for possible implementations of these, though this Thanks. For the fastest response and ease of tracking in the bug tracker: Every patch must have several pieces of information before we can properly evaluate it. They are described below. When you have all these pieces, use the M-x submit-emacs-patch command to send the patch. > is mostly so that I put this out there as a suggestion; considering how > many things depend on macroexp.el there might be good reasons why these > features are not worth the effort and possible bugs that come about due > to these changes. > > From 46f8f769aac7b59d25ecd413d9a7d6f75a78b5d2 Mon Sep 17 00:00:00 2001 > From: Thuna > Date: Tue, 16 Jul 2024 03:44:28 +0200 > Subject: [PATCH] Quality of life improvements in macroexp.el > > * macroexp.el (macroexp-progn macroexp-unprogn): Flatten any nested > `progn's. > (macroexp-if): Accept multiple ELSE arguments. If TEST is always nil or > non-nil, return only TEST or ELSE (as a progn) respectively. > (macroexp-null): Define form to check whether a form will always return > nil. With this and `macroexp-const-p' we also have a way to check if it > will always return non-nil(?). > --- > lisp/emacs-lisp/macroexp.el | 94 ++++++++++++++++++++++--------------- > 1 file changed, 57 insertions(+), 37 deletions(-) > > diff --git a/lisp/emacs-lisp/macroexp.el b/lisp/emacs-lisp/macroexp.el > index f4df40249de..c8e0850226e 100644 > --- a/lisp/emacs-lisp/macroexp.el > +++ b/lisp/emacs-lisp/macroexp.el > @@ -536,50 +536,61 @@ macroexp-parse-body > > (defun macroexp-progn (exps) > "Return EXPS (a list of expressions) with `progn' prepended. > -If EXPS is a list with a single expression, `progn' is not > -prepended, but that expression is returned instead." > - (if (cdr exps) `(progn ,@exps) (car exps))) > +If EXPS is a list with a single expression, `progn' is not prepended, > +but that expression is returned instead. If EXPS is the empty list, > +return nil." > + (let ((exps (remq nil (mapcan #'macroexp-unprogn exps)))) > + (cond ((null exps) nil) > + ((null (cdr exps)) (car exps)) > + (t `(progn ,@exps))))) > > (defun macroexp-unprogn (exp) > "Turn EXP into a list of expressions to execute in sequence. > Never returns an empty list." > - (if (eq (car-safe exp) 'progn) (or (cdr exp) '(nil)) (list exp))) > - > -(defun macroexp-let* (bindings exp) > - "Return an expression equivalent to \\=`(let* ,BINDINGS ,EXP)." > - (cond > - ((null bindings) exp) > - ((eq 'let* (car-safe exp)) `(let* (,@bindings ,@(cadr exp)) ,@(cddr exp))) > - (t `(let* ,bindings ,exp)))) > + (if (eq (car-safe exp) 'progn) > + (or (remq nil (mapcan #'macroexp-unprogn (cdr exp))) > + (list nil)) > + (list exp))) > + > +(defun macroexp-let* (bindings &rest exps) > + "Return an expression equivalent to \\=`(let* ,BINDINGS ,@EXPS)." > + (let ((exp (macroexp-progn exps))) > + (cond > + ((null bindings) exp) > + ((eq 'let* (car-safe exp)) `(let* (,@bindings ,@(cadr exp)) ,@(cddr exp))) > + (t `(let* ,bindings ,@exps))))) > > -(defun macroexp-if (test then else) > +(defun macroexp-if (test then &rest else) > "Return an expression equivalent to \\=`(if ,TEST ,THEN ,ELSE)." > - (cond > - ((eq (car-safe else) 'if) > + (let ((else (macroexp-progn else))) > (cond > - ;; Drop this optimization: It's unsafe (it assumes that `test' is > - ;; pure, or at least idempotent), and it's not used even a single > - ;; time while compiling Emacs's sources. > - ;;((equal test (nth 1 else)) > - ;; ;; Doing a test a second time: get rid of the redundancy. > - ;; (message "macroexp-if: sharing 'test' %S" test) > - ;; `(if ,test ,then ,@(nthcdr 3 else))) > - ((equal then (nth 2 else)) > - ;; (message "macroexp-if: sharing 'then' %S" then) > - `(if (or ,test ,(nth 1 else)) ,then ,@(nthcdr 3 else))) > - ((equal (macroexp-unprogn then) (nthcdr 3 else)) > - ;; (message "macroexp-if: sharing 'then' with not %S" then) > - `(if (or ,test (not ,(nth 1 else))) > - ,then ,@(macroexp-unprogn (nth 2 else)))) > - (t > - `(cond (,test ,@(macroexp-unprogn then)) > - (,(nth 1 else) ,@(macroexp-unprogn (nth 2 else))) > - ,@(let ((def (nthcdr 3 else))) (if def `((t ,@def)))))))) > - ((eq (car-safe else) 'cond) > - `(cond (,test ,@(macroexp-unprogn then)) ,@(cdr else))) > - ;; Invert the test if that lets us reduce the depth of the tree. > - ((memq (car-safe then) '(if cond)) (macroexp-if `(not ,test) else then)) > - (t `(if ,test ,then ,@(if else (macroexp-unprogn else)))))) > + ((macroexp-null test) else) > + ((macroexp-const-p test) then) > + ((eq (car-safe else) 'if) > + (cond > + ;; Drop this optimization: It's unsafe (it assumes that `test' is > + ;; pure, or at least idempotent), and it's not used even a single > + ;; time while compiling Emacs's sources. > + ;;((equal test (nth 1 else)) > + ;; ;; Doing a test a second time: get rid of the redundancy. > + ;; (message "macroexp-if: sharing 'test' %S" test) > + ;; `(if ,test ,then ,@(nthcdr 3 else))) > + ((equal then (nth 2 else)) > + ;; (message "macroexp-if: sharing 'then' %S" then) > + `(if (or ,test ,(nth 1 else)) ,then ,@(nthcdr 3 else))) > + ((equal (macroexp-unprogn then) (nthcdr 3 else)) > + ;; (message "macroexp-if: sharing 'then' with not %S" then) > + `(if (or ,test (not ,(nth 1 else))) > + ,then ,@(macroexp-unprogn (nth 2 else)))) > + (t > + `(cond (,test ,@(macroexp-unprogn then)) > + (,(nth 1 else) ,@(macroexp-unprogn (nth 2 else))) > + ,@(let ((def (nthcdr 3 else))) (if def `((t ,@def)))))))) > + ((eq (car-safe else) 'cond) > + `(cond (,test ,@(macroexp-unprogn then)) ,@(cdr else))) > + ;; Invert the test if that lets us reduce the depth of the tree. > + ((memq (car-safe then) '(if cond)) (macroexp-if `(not ,test) else then)) > + (t `(if ,test ,then ,@(if else (macroexp-unprogn else))))))) > > (defmacro macroexp-let2 (test sym exp &rest body) > "Evaluate BODY with SYM bound to an expression for EXP's value. > @@ -677,6 +688,15 @@ macroexp--const-symbol-p > (progn (set symbol (symbol-value symbol)) nil) > (setting-constant t))))))) > > +(defun macroexp-null (exp) > + "Return non-nil if EXP will always evaluate to nil." > + (or (eq exp nil) > + (and (consp exp) > + (memq (car exp) '(function quote \`)) > + (cdr exp) > + (eq (cadr exp) nil) > + (null (cadr exp))))) > + > (defun macroexp-const-p (exp) > "Return non-nil if EXP will always evaluate to the same value." > (cond ((consp exp) (or (eq (car exp) 'quote)