~ chicken-core (chicken-5) d4048c61a4c4a1652c01b51eb3efdfac7eb0dab0


commit d4048c61a4c4a1652c01b51eb3efdfac7eb0dab0
Author:     felix <felix@call-with-current-continuation.org>
AuthorDate: Mon Oct 2 18:25:29 2023 +0200
Commit:     Peter Bex <peter@more-magic.net>
CommitDate: Tue Oct 17 09:16:06 2023 +0200

    add "export/rename" for renaming identifiers on export
    
    Module structures get an additional rename a-list, and renaming of value/syntax
    bindings is done on import.
    
    This patch also renames some loop variables to make the code less
    confusing and drops a redundant check.
    
    Signed-off-by: Peter Bex <peter@more-magic.net>

diff --git a/NEWS b/NEWS
index 19686e11..55b4cad1 100644
--- a/NEWS
+++ b/NEWS
@@ -29,6 +29,8 @@
     from the (chicken syntax) module.  read/source-info is still exported
     from the undocumented internal (chicken compiler support) module, but
     using it from there is deprecated.
+  - Added "export/rename" to (chicken module) for renaming identifiers on
+    export.
 
 - Tools
   - The -R option for csi and csc now accepts list-notation like
diff --git a/eval.scm b/eval.scm
index 929acc38..4562a506 100644
--- a/eval.scm
+++ b/eval.scm
@@ -851,6 +851,7 @@
 		   define-interface
 		   delay-force
 		   export
+                   export/rename
 		   functor
 		   import
 		   import-for-syntax
diff --git a/expand.scm b/expand.scm
index 13a7f553..ba4737b5 100644
--- a/expand.scm
+++ b/expand.scm
@@ -1198,6 +1198,25 @@
 	(##sys#add-to-export-list mod exps))
       '(##core#undefined)))))
 
+(##sys#extend-macro-environment
+ 'export/rename '()
+ (##sys#er-transformer
+  (lambda (x r c)
+    (let ((exps (map (lambda (ren)
+                       (if (and (pair? ren) 
+                                (symbol? (car ren))
+                                (pair? (cdr ren))
+                                (symbol? (cadr ren))
+                                (null? (cddr ren)))
+                           (cons (car ren) (cadr ren))
+                           (##sys#syntax-error-hook "invalid item in export rename list" 
+                                                    ren)))
+                  (strip-syntax (cdr x))))
+          (mod (##sys#current-module)))
+      (when mod
+	(##sys#add-to-export/rename-list mod exps))
+      '(##core#undefined)))))
+
 (##sys#extend-macro-environment
  'reexport '()
  (##sys#er-transformer
diff --git a/manual/Modules b/manual/Modules
index e562f460..3f327aa5 100644
--- a/manual/Modules
+++ b/manual/Modules
@@ -121,6 +121,17 @@ Allows augmenting module-exports from inside the module-body.
 
 If used outside of a module, then this form does nothing.
 
+==== export/rename
+
+<macro>(export/rename (NAME EXPORT) ...)</macro>
+
+Allows augmenting module-exports from inside the module-body.
+Each argument should be a two-element list containing the name
+of the local value- or syntax-definition (NAME) and the name under which the
+definition should be exported (EXPORT).
+
+If used outside of a module, then this form does nothing.
+
 ==== import
 
 <macro>(import IMPORT ...)</macro>
diff --git a/modules.scm b/modules.scm
index 61556fef..c6b77acd 100644
--- a/modules.scm
+++ b/modules.scm
@@ -90,12 +90,13 @@
 	module-meta-expressions set-module-meta-expressions!
 	module-defined-syntax-list set-module-defined-syntax-list!
 	module-saved-environments set-module-saved-environments!
-	module-iexports set-module-iexports!))
+	module-iexports set-module-iexports!
+        module-rename-list set-module-rename-list!))
 
 (define-record-type module
   (%make-module name library export-list defined-list exist-list defined-syntax-list
 		undefined-list import-forms meta-import-forms meta-expressions 
-		vexports sexports iexports saved-environments) 
+		vexports sexports iexports saved-environments rename-list) 
   module?
   (name module-name)			; SYMBOL
   (library module-library)		; SYMBOL
@@ -111,7 +112,8 @@
   (sexports module-sexports set-module-sexports!)	      ; ((SYMBOL SE TRANSFORMER) ...)
   (iexports module-iexports set-module-iexports!)	      ; ((SYMBOL . SYMBOL) ...)
   ;; for csi's ",m" command, holds (<env> . <macroenv>)
-  (saved-environments module-saved-environments set-module-saved-environments!))
+  (saved-environments module-saved-environments set-module-saved-environments!)
+  (rename-list module-rename-list set-module-rename-list!))
 
 (define ##sys#module-name module-name)
 
@@ -121,8 +123,9 @@
    (module-vexports m)
    (module-sexports m)))
 
-(define (make-module name lib explist vexports sexports iexports)
-  (%make-module name lib explist '() '() '() '() '() '() '() vexports sexports iexports #f))
+(define (make-module name lib explist vexports sexports iexports #!optional (renames '()))
+  (%make-module name lib explist '() '() '() '() '() '() '() vexports sexports iexports #f
+                renames))
 
 (define (##sys#register-module-alias alias name)
   (##sys#module-alias-environment
@@ -181,6 +184,11 @@
 	  (set-module-exist-list! mod (append el exps)))
 	(set-module-export-list! mod (append xl exps)))))
 
+(define (##sys#add-to-export/rename-list mod renames)
+  (let ((rl (module-rename-list mod)))
+    (set-module-rename-list! mod (append rl renames))
+    (##sys#add-to-export-list mod (map car renames))))
+
 (define (##sys#toplevel-definition-hook sym renamed exported?) #f)
 
 (define (##sys#register-meta-expression exp)
@@ -303,8 +311,7 @@
 	    ((or (eq? last-se (car ses)) (null? (car ses)))
 	     (loop (cdr ses) last-se se2))
 	    ((not last-se)
-	     (unless (null? ses)
-	       (for-each (lambda (e) (hash-table-set! seen (car e) #t)) se2))
+             (for-each (lambda (e) (hash-table-set! seen (car e) #t)) se2)
 	     (loop ses se2 se2))
 	    (else (let lp ((se (car ses)) (se2 se2))
 		    (cond ((null? se) (loop (cdr ses) (car ses) se2))
@@ -369,14 +376,18 @@
                             (else
                               (let ((name (caar sd)))
                                 (cons `(scheme#cons ',(caar sd) ,(strip-syntax (cdar sd)))
-                                      (loop (cdr sd)))))))))))))))
+                                      (loop (cdr sd)))))))))
+            (scheme#list   ; renames
+              ,@(map (lambda (ren)
+                       `(scheme#cons ',(car ren) ',(cdr ren)))
+                  (module-rename-list mod)))))))))
 
 ;; iexports = indirect exports (syntax dependencies on value idents, explicitly included in module export list)
 ;; vexports = value (non-syntax) exports
 ;; sexports = syntax exports
 ;; sdefs = unexported definitions from syntax environment used by exported macros (not in export list)
 (define (##sys#register-compiled-module name lib iexports vexports sexports #!optional
-					(sdefs '()))
+					(sdefs '()) (renames '()))
   (define (find-reexport name)
     (let ((a (assq name (##sys#macro-environment))))
       (if (and a (pair? (cdr a)))
@@ -396,7 +407,8 @@
 	  (map (lambda (ne)
 		 (list (car ne) #f (##sys#ensure-transformer (cdr ne) (car ne))))
 	       sdefs))
-	 (mod (make-module name lib '() vexports (append sexps reexp-sexps) iexports))
+	 (mod (make-module name lib '() vexports (append sexps reexp-sexps) iexports
+                           renames))
 	 (senv (if (or (not (null? sexps))  ; Only macros have an senv
 		       (not (null? nexps))) ; which must be patched up
 		   (merge-se
@@ -660,6 +672,16 @@
 	    ((symbol? x) (##sys#symbol->string x))
 	    ((number? x) (number->string x))
 	    (else (##sys#syntax-error-hook loc "invalid prefix" ))))
+    (define (export-rename mod lst)
+      (let ((ren (module-rename-list mod)))
+        (if (null? ren)
+            lst
+            (map (lambda (a)
+                   (cond ((assq (car a) ren) =>
+                          (lambda (b) 
+                            (cons (cdr b) (cdr a))))
+                         (else a)))
+              lst))))
     (call-with-current-continuation
      (lambda (k)
        (define (module-imports name)
@@ -670,10 +692,10 @@
 	       (values (module-name mod)
 		       (module-library mod)
 		       (module-name mod)
-		       (module-vexports mod)
-		       (module-sexports mod)
+		       (export-rename mod (module-vexports mod))
+		       (export-rename mod (module-sexports mod))
 		       (module-iexports mod)))))
-       (let loop ((x x))
+       (let outer ((x x))
 	 (cond ((symbol? x)
 		(module-imports (strip-syntax x)))
 	       ((not (pair? x))
@@ -682,7 +704,7 @@
 		(let ((head (car x)))
 		  (cond ((c %only head)
 			 (##sys#check-syntax loc x '(_ _ . #(symbol 0)))
-			 (let-values (((name lib spec impv imps impi) (loop (cadr x)))
+			 (let-values (((name lib spec impv imps impi) (outer (cadr x)))
 				      ((imports) (strip-syntax (cddr x))))
 			   (let loop ((ids imports) (v '()) (s '()) (missing '()))
 			     (cond ((null? ids)
@@ -701,11 +723,11 @@
 				    (loop (cdr ids) v s (cons (car ids) missing)))))))
 			((c %except head)
 			 (##sys#check-syntax loc x '(_ _ . #(symbol 0)))
-			 (let-values (((name lib spec impv imps impi) (loop (cadr x)))
+			 (let-values (((name lib spec impv imps impi) (outer (cadr x)))
 				      ((imports) (strip-syntax (cddr x))))
-			   (let loop ((impv impv) (v '()) (ids imports))
+			   (let loopv ((impv impv) (v '()) (ids imports))
 			     (cond ((null? impv)
-				    (let loop ((imps imps) (s '()) (ids ids))
+				    (let loops ((imps imps) (s '()) (ids ids))
 				      (cond ((null? imps)
 					     (for-each
 					      (lambda (id)
@@ -714,21 +736,21 @@
 					     (values name lib `(,head ,spec ,@imports) v s impi))
 					    ((memq (caar imps) ids) =>
 								    (lambda (id)
-								      (loop (cdr imps) s (delete (car id) ids eq?))))
+								      (loops (cdr imps) s (delete (car id) ids eq?))))
 					    (else
-					     (loop (cdr imps) (cons (car imps) s) ids)))))
+					     (loops (cdr imps) (cons (car imps) s) ids)))))
 				   ((memq (caar impv) ids) =>
 							   (lambda (id)
-							     (loop (cdr impv) v (delete (car id) ids eq?))))
+							     (loopv (cdr impv) v (delete (car id) ids eq?))))
 				   (else
-				    (loop (cdr impv) (cons (car impv) v) ids))))))
+				    (loopv (cdr impv) (cons (car impv) v) ids))))))
 			((c %rename head)
 			 (##sys#check-syntax loc x '(_ _ . #((symbol symbol) 0)))
-			 (let-values (((name lib spec impv imps impi) (loop (cadr x)))
+			 (let-values (((name lib spec impv imps impi) (outer (cadr x)))
 				      ((renames) (strip-syntax (cddr x))))
-			   (let loop ((impv impv) (v '()) (ids renames))
+			   (let loopv ((impv impv) (v '()) (ids renames))
 			     (cond ((null? impv)
-				    (let loop ((imps imps) (s '()) (ids ids))
+				    (let loops ((imps imps) (s '()) (ids ids))
 				      (cond ((null? imps)
 					     (for-each
 					      (lambda (id)
@@ -737,21 +759,21 @@
 					     (values name lib `(,head ,spec ,@renames) v s impi))
 					    ((assq (caar imps) ids) =>
 					     (lambda (a)
-					       (loop (cdr imps)
+					       (loops (cdr imps)
 						     (cons (cons (cadr a) (cdar imps)) s)
 						     (delete a ids eq?))))
 					    (else
-					     (loop (cdr imps) (cons (car imps) s) ids)))))
+					     (loops (cdr imps) (cons (car imps) s) ids)))))
 				   ((assq (caar impv) ids) =>
 				    (lambda (a)
-				      (loop (cdr impv)
-					    (cons (cons (cadr a) (cdar impv)) v)
-					    (delete a ids eq?))))
+				      (loopv (cdr impv)
+					     (cons (cons (cadr a) (cdar impv)) v)
+					     (delete a ids eq?))))
 				   (else
-				    (loop (cdr impv) (cons (car impv) v) ids))))))
+				    (loopv (cdr impv) (cons (car impv) v) ids))))))
 			((c %prefix head)
 			 (##sys#check-syntax loc x '(_ _ _))
-			 (let-values (((name lib spec impv imps impi) (loop (cadr x)))
+			 (let-values (((name lib spec impv imps impi) (outer (cadr x)))
 				      ((prefix) (strip-syntax (caddr x))))
 			   (define (rename imp)
 			     (cons
diff --git a/tests/module-tests.scm b/tests/module-tests.scm
index ec447e45..4d15c88f 100644
--- a/tests/module-tests.scm
+++ b/tests/module-tests.scm
@@ -380,6 +380,28 @@
    (import (scheme) (chicken module))
    (eq? (current-module) 'm33)))
 
+(module m34 ((syn bar) alias)
+  (import scheme (chicken base) (chicken module))
+  (export/rename (bar baz) (syn syn2))
+  (define bar 123)
+  (assert (equal? bar 123))
+  (define-syntax alias 
+    (syntax-rules () 
+      ((_) (syn))))
+  (define-syntax syn
+    (syntax-rules ()
+      ((_) (list bar)))))
+
+(module m35 ()
+  (import scheme (chicken base) (chicken module))
+  (import (only (rename m34 (syn2 syn3)) syn3 alias))
+  (import (rename m34 (baz bax)))
+  (define bar 99)
+  (assert (equal? bax 123))
+  (assert (equal? (syn3) '(123)))
+  (assert (equal? (alias) '(123)))
+  (assert (equal? bar 99)))
+
 (test-end "modules")
 
 (test-exit)
Trap