Hi!

jcroisant reminded us of a pending patch on the bugtracker (#1770),
which apparently was forgotten. I'm not quite sure how this could
linger along for such a long time, but once-only-expansion was
always a somewhat brittle transformation as the interpreter and
compiler handle special forms a bit differently and there often
appear undocumented intermediate special forms. Nevertheless,
this functionality is useful for debugging and has been requested
often enough in the past.

Find attached a slightly modified, signed off version. The use of
"expand1" in csi.scm needs to have a hard coded module prefix, which
I assume is caused by "expand[1]" being treated specifically by the
bootstrapping compiler, so the new addition to the import lib
appears not to be visible.

There are some unrelated whitespace changes in csi.scm, due to
reasons, please ignore them.


cheers,
felix
From 568b0728aa405177ecea1e0ef293f2e37984c735 Mon Sep 17 00:00:00 2001
From: felix <[email protected]>
Date: Fri, 1 Aug 2025 16:14:04 +0200
Subject: [PATCH] Add `expand1` procedure in module (chicken syntax).

(original patch by John Croisant, slightly modified to avoid
bootstrapping issue)

Like `expand` but expands the form only once, instead of repeating
until a non-macro form is produced. This is helpful when debugging
macros because you can see the intermediate results of the expansion.

Also add `,x`, which is like `,x` but uses `expand1` instead of `expand`. The form is expanded
only once, then pretty-printed.

Signed-off-by: felix <[email protected]>
---
 chicken.syntax.import.scm      |  1 +
 csi.scm                        | 28 ++++++++++++++++++++--------
 expand.scm                     |  4 ++++
 manual/Module (chicken syntax) |  8 ++++++++
 manual/Using the interpreter   |  2 ++
 5 files changed, 35 insertions(+), 8 deletions(-)

diff --git a/chicken.syntax.import.scm b/chicken.syntax.import.scm
index d11a3ffa..81b61eaf 100644
--- a/chicken.syntax.import.scm
+++ b/chicken.syntax.import.scm
@@ -31,6 +31,7 @@
  'chicken.syntax
  'expand
  '((expand . chicken.syntax#expand)
+   (expand1 . chicken.syntax#expand1)
    (get-line-number . chicken.syntax#get-line-number)
    (read-with-source-info . chicken.syntax#read-with-source-info)
    (strip-syntax . chicken.syntax#strip-syntax)
diff --git a/csi.scm b/csi.scm
index 2a4c8477..e3c95d2e 100644
--- a/csi.scm
+++ b/csi.scm
@@ -424,16 +424,28 @@ EOF
    (lambda ()
      (let ((name (read)))
        (cond ((not name)
-	      (##sys#switch-module #f)
-	      (printf "; resetting current module to toplevel~%"))
-	     ((##sys#find-module (##sys#resolve-module-name name #f) #f) =>
-	      (lambda (m)
-		(##sys#switch-module m)
-		(printf "; switching current module to `~a'~%" name)))
-	     (else
-	      (printf "undefined module `~a'~%" name))))))
+              (##sys#switch-module #f)
+              (printf "; resetting current module to toplevel~%"))
+             ((##sys#find-module (##sys#resolve-module-name name #f) #f) =>
+              (lambda (m)
+                (##sys#switch-module m)
+                (printf "; switching current module to `~a'~%" name)))
+             (else
+              (printf "undefined module `~a'~%" name))))))
  ",m MODULE         switch to module with name `MODULE'")
 
+(toplevel-command
+ 'x1
+ (let ((pretty-print pretty-print))
+   (lambda ()
+     (let ([expr (read)])
+       ;; avoid bootstrapping issue, as chicken.syntax is not
+       ;; imported dynamically by bootstrap compiler
+       ;; this can be replaced by "expand1" later
+       (pretty-print (strip-syntax (chicken.syntax#expand1 expr)))
+       (##sys#void))))
+ ",x1 EXP           Pretty print expand1-ed expression EXP")
+
 
 ;;; Parse options from string:
 
diff --git a/expand.scm b/expand.scm
index a329c4ba..858c9f68 100644
--- a/expand.scm
+++ b/expand.scm
@@ -36,6 +36,7 @@
 
 (module chicken.syntax
   (expand
+   expand1
    get-line-number
    read-with-source-info
    strip-syntax
@@ -305,6 +306,9 @@
 	  (loop exp2)
 	  exp2) ) ) )
 
+(define (expand1 exp #!optional (se (##sys#current-environment)) cs?)
+  (nth-value 0 (##sys#expand-0 exp se cs?)) )
+
 
 ;;; Extended (DSSSL-style) lambda lists
 ;
diff --git a/manual/Module (chicken syntax) b/manual/Module (chicken syntax)
index 1f1e44a4..19e550be 100644
--- a/manual/Module (chicken syntax)	
+++ b/manual/Module (chicken syntax)	
@@ -301,6 +301,14 @@ comparison {{(compare sym 'abc)}} should be written as {{(compare sym
 If {{X}} is a macro-form, expand the macro (and repeat expansion
 until expression is a non-macro form).  Returns the resulting expression.
 
+==== expand1
+
+<procedure>(expand1 X)</procedure>
+
+If {{X}} is a macro-form, expand the macro only once.  Unlike
+{{expand}}, does not repeat expansion when the resulting expression is
+itself a macro-form.  Returns the resulting expression.
+
 === Macro helper procedures
 
 ==== begin-for-syntax
diff --git a/manual/Using the interpreter b/manual/Using the interpreter
index de1f3097..14cda247 100644
--- a/manual/Using the interpreter	
+++ b/manual/Using the interpreter	
@@ -117,6 +117,8 @@ The toplevel loop understands a number of special commands:
 
 ; ,x EXP : Pretty-print macroexpanded expression {{EXP}} (the expression is not evaluated).
 
+; ,x1 EXP : Like {{,x}} but {{EXP}} is expanded only one step, using {{expand1}}.
+
 You can define your own toplevel commands using the {{toplevel-command}}
 procedure (see [[Module (chicken csi)]]).
 
-- 
2.44.1

Reply via email to