From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Jeremy Bryant via "Emacs development discussions." Newsgroups: gmane.emacs.devel Subject: Re: Incorporate package macrostep into Emacs core Date: Tue, 19 Mar 2024 21:57:22 +0000 Message-ID: <87frwlanr1.fsf@jeremybryant.net> References: <87zfvl8r4e.fsf@jeremybryant.net> <874jdspsqb.fsf@bernoul.li> <877cio8fzf.fsf@jeremybryant.net> <87y1b46vhg.fsf@jeremybryant.net> <878r336lvb.fsf@jeremybryant.net> <86y1b1p1ni.fsf@gnu.org> <87y1b0mi4b.fsf@jeremybryant.net> <8734sobkdj.fsf@jeremybryant.net> <87il1jdhzd.fsf@posteo.net> <87le6f9m8b.fsf@jeremybryant.net> Reply-To: Jeremy Bryant Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="10390"; mail-complaints-to="usenet@ciao.gmane.io" Cc: Philip Kaludercic , "Jeremy Bryant via Emacs development discussions." , Eli Zaretskii , Stefan Monnier , Stefan Kangas , Jonas Bernoulli To: Jonathan Oddie Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Tue Mar 19 22:58:47 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 1rmhTq-0002S0-HS for ged-emacs-devel@m.gmane-mx.org; Tue, 19 Mar 2024 22:58:46 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1rmhSe-0007Lf-57; Tue, 19 Mar 2024 17:57:32 -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 1rmhSa-0007LI-Nd for emacs-devel@gnu.org; Tue, 19 Mar 2024 17:57:29 -0400 Original-Received: from out-170.mta0.migadu.com ([2001:41d0:1004:224b::aa]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1rmhSY-0007FU-MX for emacs-devel@gnu.org; Tue, 19 Mar 2024 17:57:28 -0400 X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=jeremybryant.net; s=key1; t=1710885444; 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=aU0Rd3zJRcyIQDEd68MlvOk6hfWAJ778bBmrLPfDYPE=; b=PLSYVjEj+4viEFuv0jkbfpBkPRdI6sLSiGP986ACSU6OBAuSHj9bjsTU4MKUPghviHrL8S 4JMlUjZv64kaYSZswaKASsVnkkfM8HHQ6vKJ3wHAjnMDgJLHW8bRK0LDtN63MJ/1Gmj06d Xb2txBxT12ARJ0jDBfI5P01BtL6K6hgsjVwenLJfHpJMNuutmx6xDKf8rW7mGlY14WUfxT gvOpBQ2x1ho9Ta0wJ0cNxEZGNDBJpLfRQcOkzSS4NAkikJvB7QwieJjJ2SX1NHSN8q0PQS 1UajNXweukUbomxWzJu7DMbvg4WiJY/lo9WMj8Ao3UOC/P0Y5oGtUtORL9eCYA== In-Reply-To: (Jonathan Oddie's message of "Tue, 19 Mar 2024 13:03:33 -0400") X-Migadu-Flow: FLOW_OUT Received-SPF: pass client-ip=2001:41d0:1004:224b::aa; envelope-from=jb@jeremybryant.net; helo=out-170.mta0.migadu.com X-Spam_score_int: -16 X-Spam_score: -1.7 X-Spam_bar: - X-Spam_report: (-1.7 / 5.0 requ) BAYES_00=-1.9, DKIM_INVALID=0.1, DKIM_SIGNED=0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=no 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:317203 Archived-At: --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Jonathan Oddie writes: > Hi all, > > Sorry I=E2=80=99ve been away from this discussion while traveling. > >>> Isn't there also a C-preprecossor implementation for macrostep? Would >>> the plan be to include that as well? >>=20 >> Presumably, also. Any comments on macrostep-c.el (attached)? > > I=E2=80=99m not sure how useful the C preprocessor implementation is, as I > didn=E2=80=99t do too much work on it. It is more of a proof-of-concept f= or > the ability to extend macrostep to different languages. Of course it=E2= =80=99s > your choice whether you find it worthwhile to include. > > There is a Common Lisp implementation maintained as part of SLIME > which is more complete, FYI. Thank you for clarifying. On this basis I will only work on adding the main Lisp related macrostep file for the time being. > I=E2=80=99ve still to sign and return the FSF paperwork but will do so th= is week. > > Jonathan Thanks in advance! What about another file - should the tests file be included in Emacs as par= t of the main Lisp macrostep? Should test files be added in the Emacs tree test/lisp/? --=-=-= Content-Type: application/emacs-lisp Content-Disposition: attachment; filename=macrostep-test.el Content-Transfer-Encoding: quoted-printable ;;; macrostep-test.el --- tests for macrostep.el -*- lexical-binding: t; -= *- ;; Copyright (C) 2014-2015 Jon Oddie ;; Author: Jon Oddie ;; Url: https://github.com/emacsorphanage/macrostep ;; SPDX-License-Identifier: GPL-3.0-or-later ;; This file 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 file 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 file. If not, see . ;;; Code: (require 'ert) (require 'rx) (require 'macrostep) (require 'macrostep-c) ;;;; Conveniences for defining tests (defmacro macrostep-with-text (object &rest forms) (declare (indent 1) (debug (&rest form))) `(with-temp-buffer (emacs-lisp-mode) (let ((print-level nil) (print-length nil) (standard-output (current-buffer))) (save-excursion (print ,object)) ,@forms))) (defun macrostep-goto (text &optional from-point-min) (when from-point-min (goto-char (point-min))) (let ((search-spaces-regexp "[[:space:]\n]+")) (re-search-forward (regexp-quote text)) (goto-char (match-beginning 0)))) (defmacro macrostep-should-expand (form expansion &optional leave-expanded-= p) `(save-excursion (goto-char (point-min)) (let ((print-level nil) (print-length nil)) (search-forward (prin1-to-string ,form)) (goto-char (match-beginning 0)) (unwind-protect (progn (macrostep-expand) (should (equal (read (current-buffer)) ,expansion))) (unless ,leave-expanded-p (macrostep-collapse-all)))))) ;;;; Tests (ert-deftest macrostep-expand-defmacro () (defmacro macrostep-dummy-macro (&rest args) `(expansion of ,@args)) (macrostep-with-text '(progn (first body form) (second body form) (macrostep-dummy-macro (first (argument)) second (third argument)) (remaining body forms)) (macrostep-should-expand '(macrostep-dummy-macro (first (argument)) second (third argument)) '(expansion of (first (argument)) second (third argument))))) (ert-deftest macrostep-expand-and-collapse () (dolist (expander (list (lambda (sexp _env) sexp) (lambda (sexp _env) `(progn ,sexp ,sexp)) (lambda (sexp _env) `(long (complicated (expansion of ,sexp () ()) (with trailing forms)))))) (let ((macrostep-expand-1-function expander) (macrostep-macro-form-p-function (lambda (&rest _) t))) (macrostep-with-text '(progn (first form) (second form) (third (nested form))) (macrostep-goto "(first ") (let ((original-text (buffer-string))) (dotimes (i 10) (dotimes (_ i) (macrostep-expand)) (dotimes (_ i) (macrostep-collapse)) (should (null macrostep-overlays)) (should (string=3D (buffer-string) original-text)))))))) (ert-deftest macrostep-expand-macrolet () (macrostep-with-text '(cl-macrolet ((test (&rest args) `(expansion of ,@args))) (first body form) (second body form) (test (strawberry pie) and (apple pie)) (final body form)) (macrostep-should-expand '(test (strawberry pie) and (apple pie)) '(expansion of (strawberry pie) and (apple pie))))) (ert-deftest macrostep-expand-macrolet-2 () (macrostep-with-text ;; Taken from org-notify.el. '(cl-macrolet ((get (k) `(plist-get list ,k)) (pr (k v) `(setq result (plist-put result ,k ,v)))) (let* ((list (nth 1 heading)) (notify (or (get :notify) "defau= lt")) (deadline (org-notify-convert-deadline (get :deadline))) (heading (get :raw-value)) result) (when (and (eq (get :todo-type) 'todo) heading deadline) (pr :heading heading) (pr :notify (intern notify)) (pr :begin (get :begin)) (pr :file (nth org-notify-parse-file (org-agenda-files 'unrestr= icted))) (pr :timestamp deadline) (pr :uid (md5 (concat heading deadlin= e))) (pr :deadline (- (org-time-string-to-seconds deadline) (org-float-time)))) result)) (macrostep-should-expand '(pr :heading heading) '(setq result (plist-put result :heading heading))) (macrostep-should-expand '(pr :notify (intern notify)) '(setq result (plist-put result :notify (intern notify)))) (macrostep-should-expand '(pr :begin (get :begin)) '(setq result (plist-put result :begin (get :begin)))) (macrostep-should-expand '(get :begin) '(plist-get list :begin)))) (ert-deftest macrostep-expand-cl-macrolet () (macrostep-with-text ;; Taken from slime.el. '(cl-macrolet ((fontify (face string) `(slime-inspector-fontify ,face ,string))) (slime-propertize-region (list 'slime-part-number id 'mouse-face 'highlight 'face 'slime-inspector-value-face) (insert title)) (while (eq (char-before) ?\n) (backward-delete-char 1)) (insert "\n" (fontify label "--------------------") "\n") (save-excursion (slime-inspector-insert-content content)) (when point (cl-check-type point cons) (ignore-errors (goto-char (point-min)) (forward-line (1- (car point))) (move-to-column (cdr point))))) (macrostep-should-expand '(fontify label "--------------------") '(slime-inspector-fontify label "--------------------")))) (ert-deftest macrostep-expand-shadowed-macrolet () (macrostep-with-text '(cl-macrolet ((test-macro (&rest forms) (cons 'shadowed forms)) (test-macro (&rest forms) (cons 'outer-definition forms))) (test-macro first (call)) (cl-macrolet ((test-macro (&rest forms) (cons 'inner-definition forms))) (test-macro (second (call))))) (macrostep-should-expand '(test-macro first (call)) '(outer-definition first (call))) (macrostep-should-expand '(test-macro (second (call))) '(inner-definition (second (call)))))) (ert-deftest macrostep-environment-at-point () (macrostep-with-text ;; Taken from org-notify.el. '(cl-macrolet ((get (k) `(plist-get list ,k)) (pr (k v) `(setq result (plist-put result ,k ,v)))) (body forms)) (search-forward "(body") (let ((env (macrostep-environment-at-point))) (should (assq 'get env)) (should (assq 'pr env)) (should (functionp (cdr (assq 'get env)))) (should (functionp (cdr (assq 'pr env)))) (should (equal (apply (cdr (assq 'pr env)) '(:heading heading)) '(setq result (plist-put result :heading heading)))) (should (equal (apply (cdr (assq 'get env)) '(:begin)) '(plist-get list :begin)))))) (ert-deftest macrostep-environment-at-point-2 () (defmacro macrostep-with-dummy (&rest body) `(cl-macrolet ((dummy (&rest forms) `(expansion of ,@forms))) ,@body)) (macrostep-with-text '(macrostep-with-dummy (body) (forms) (cl-loop for i from 6 to 10 do (something))) (macrostep-goto "(macrostep-with-dummy" t) (should (null (macrostep-environment-at-point))) (dolist (place (list "(body)" "dy)" "(forms)" "rms)" "(something)")) (macrostep-goto place) (let* ((env (macrostep-environment-at-point)) (dummy-defn (cdr (assq 'dummy env)))) (should dummy-defn) (should (functionp dummy-defn)) (should (equal (funcall dummy-defn 'lorem 'ipsum) `(expansion of lorem ipsum))))))) (ert-deftest macrostep-print-sexp () (cl-macrolet ((should-print (form string) `(should (equal (with-temp-buffer (macrostep-print-sexp ,form) (buffer-string)) ,string)))) (should-print nil "nil") (should-print 'symbol "symbol") (should-print '(single-element-list) "(single-element-list)") (should-print '(two-element list) "(two-element list)") (should-print '(three element list) "(three element list)") (should-print '(dotted . list) "(dotted . list)") (should-print '(four element dotted . list) "(four element dotted . lis= t)") (should-print '(nested (list (elements))) "(nested (list (elements)))") (should-print '((deeply (nested)) (list (elements))) "((deeply (nested)) (list (elements)))") (should-print '(quote fishes) "'fishes") (should-print '`(backquoted form) "`(backquoted form)") (should-print '`(backquoted (form) ,with ,@splices) "`(backquoted (form) ,with ,@splices)"))) (ert-deftest macrostep-pp-macrolet-environment () (with-temp-buffer (emacs-lisp-mode) (macrostep-pp '(cl-macrolet ((some-macro (&rest forms) (cons 'progn forms))) (some-macro with (arguments)) (intervening body forms) (some-macro with (more) (arguments))) nil) (cl-labels ((search (text) (macrostep-goto text t) ;; Leave point on the head of the form (forward-char))) ;; The occurrence of "(some-macro" in the binding list should ;; not be fontified as a macro form (search "(some-macro (&rest") (should-not (eq (get-char-property (point) 'font-lock-face) 'macrostep-macro-face)) ;; However, the two occurrences in the body of the macrolet should be. (search "(some-macro with (arguments)") (should (eq (get-char-property (point) 'font-lock-face) 'macrostep-macro-face)) (search "(some-macro with (more)") (should (eq (get-char-property (point) 'font-lock-face) 'macrostep-macro-face))))) (ert-deftest macrostep-expand-macro-defined-macros () (defmacro with-local-dummy-macro (&rest body) `(cl-macrolet ((dummy (&rest args) `(expansion (of) ,@args))) ,@body)) (macrostep-with-text '(with-local-dummy-macro (dummy form (one)) (dummy (form two))) (macrostep-should-expand '(dummy form (one)) '(expansion (of) form (one))) (macrostep-should-expand '(dummy (form two)) '(expansion (of) (form two))))) (ert-deftest macrostep-expand-in-separate-buffer () (defmacro macrostep-dummy-macro (&rest args) `(expansion of ,@args)) (let ((macrostep-expand-in-separate-buffer t)) (macrostep-with-text '(progn (first form) (second form) (macrostep-dummy-macro (some (arguments))) (final form)) (let ((original-buffer (current-buffer))) (search-forward "(macrostep-dummy-macro") (macrostep-expand) (should (not (equal (current-buffer) original-buffer))) (should macrostep-expansion-buffer) (should (equal (read (copy-marker (point))) '(expansion of (some (arguments))))))))) (ert-deftest macrostep-expand-macrolet-in-separate-buffer () (let ((macrostep-expand-in-separate-buffer t)) (macrostep-with-text '(cl-macrolet ((dummy-macro-1 (&rest args) `(dummy-macro-2 ,@args)) (dummy-macro-2 (&rest args) `(expansion of ,@args))) (dummy-macro-1 (some (arguments)))) (let ((original-buffer (current-buffer))) (macrostep-goto "(dummy-macro-1 (some") (macrostep-expand) (should (not (equal (current-buffer) original-buffer))) (should macrostep-expansion-buffer) (should (equal (read (copy-marker (point))) '(dummy-macro-2 (some (arguments))))) (macrostep-expand) (should (equal (read (copy-marker (point))) '(expansion of (some (arguments))))) (macrostep-collapse) (should (equal (read (copy-marker (point))) '(dummy-macro-2 (some (arguments))))))))) (ert-deftest macrostep-expand-compiler-macros () "Test that compiler-macros are expanded" ;; definitions (defun macrostep-dummy-function (&rest args) args) (cl-define-compiler-macro macrostep-dummy-function (&rest args) `(compile-time expansion of ,@args)) (macrostep-with-text '(progn (first body form) (macrostep-dummy-function first second third) (remaining body forms)) (macrostep-should-expand '(macrostep-dummy-function first second third) '(compile-time expansion of first second third)))) (ert-deftest macrostep-fontify-compiler-macros () "Test that compiler-macros are fontified in macro-expansions" ;; definitions (defmacro macrostep-dummy-macro (&rest args) `(expansion including (macrostep-dummy-function ,@args))) (defun macrostep-dummy-function (&rest args) args) (cl-define-compiler-macro macrostep-dummy-4 (&rest args) `(compile-time expansion of ,@args)) (macrostep-with-text '(progn (first body form) (second body form) (macrostep-dummy-macro first second third) (remaining body forms)) (macrostep-should-expand '(macrostep-dummy-macro first second third) '(expansion including (macrostep-dummy-function first second third)) t) (macrostep-goto "(macrostep-dummy-function") (should (eq (get-char-property (1+ (point)) 'font-lock-face) 'macrostep-compiler-macro-face)) =20=20=20=20 (macrostep-should-expand '(macrostep-dummy-function first second third) '(compile-time expansion of first second third)))) ;;;; Tests for C macro expansion (defun macrostep-lax-looking-at (string) (let* ((string-sans-whitespace (replace-regexp-in-string (rx (one-or-more whitespace)) "" string= )) (regexp (cl-loop for char across string-sans-whitespace concat (rx-to-string char t) concat "[[:space:]]*"))) (looking-at regexp))) (defmacro macrostep-lax-should-expand (string) `(progn (macrostep-expand) (should (macrostep-lax-looking-at ,string)) (macrostep-collapse))) (ert-deftest macrostep-expand-c-macros () (with-temp-buffer (insert ;; A random example adapted from Emacs's src/lisp.h. " #define eassert(cond) ((void) (false && (cond))) /* Check COND compiles. */ #define lisp_h_XLI(o) (o) #define lisp_h_XUNTAG(a, type) ((void *) (intptr_t) (XLI (a) - (type))) #define XLI(o) lisp_h_XLI (o) #define XUNTAG(a, type) lisp_h_XUNTAG (a, type) INLINE struct Lisp_String * XSTRING (Lisp_Object a) { eassert (STRINGP (a)); return XUNTAG (a, Lisp_String); }") (c-mode) ;; Test macro-expansion with point at the beginning of the macro (macrostep-goto "eassert (STRINGP (a))" t) (macrostep-lax-should-expand "((void) (false && (STRINGP (a))))") ;; Test with point inside a nested macro call: result should be ;; the same, since point will move up before the outermost macro (macrostep-goto "STRINGP") (macrostep-lax-should-expand "((void) (false && (STRINGP (a))))") ;; Test with point in the middle of a symbol (macrostep-goto "XUNTAG (a, Lisp_String)" t) (forward-char 3) (macrostep-lax-should-expand "((void *) (intptr_t) ((a) - ( Lisp_String)))") ;; Test with point at symbol-end (macrostep-goto "XUNTAG (a, Lisp_String)" t) (forward-sexp) (macrostep-lax-should-expand "((void *) (intptr_t) ((a) - ( Lisp_String)))"))) (when noninteractive (load-file (expand-file-name "macrostep.el" (file-name-directory load-file-name))) (let ((stats (ert-run-tests-batch "^macrostep"))) (if noninteractive (kill-emacs (ert-stats-completed-unexpected stats))))) --=-=-=--