Hello!

Here another (and proper) patch: it allows functor arguments to be
optional, giving the author or a functor a way to provide a default,
which seems to be quite useful. I think this patch was submitted
before, but seems to have been forgotten.

This patch also fixes a bug in the functor-argument matching code:
when verifying whether a module given as argument exports the required
binding, the export-list was previously checked (that's the list given
in a module declaration, specifying the exports). But that was
incorrect, as, for example, builtin modules (like "scheme") do not
have export lists. This change uses the "vexports"/"sexports" lists
of a module instead, that is, the "real" exports.


felix
>From 409f2add49b6ccec225a766c457b5982ed3bb1f9 Mon Sep 17 00:00:00 2001
From: felix <fe...@call-with-current-continuation.org>
Date: Mon, 7 Jul 2014 22:46:00 +0200
Subject: [PATCH] Allow functor-arguments to be optional and having defaults,
 and use the correct export-lists when matching functor
 arguments.

---
 chicken-syntax.scm      |   11 +++++++--
 expand.scm              |    2 +-
 manual/Modules          |    5 ++++
 modules.scm             |   27 ++++++++++++++++++---
 tests/functor-tests.scm |   62 +++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 100 insertions(+), 7 deletions(-)

diff --git a/chicken-syntax.scm b/chicken-syntax.scm
index 7a28158..0120dda 100644
--- a/chicken-syntax.scm
+++ b/chicken-syntax.scm
@@ -1147,10 +1147,11 @@
  'functor '()
  (##sys#er-transformer
   (lambda (x r c)
-    (##sys#check-syntax 'functor x '(_ (symbol . #((symbol _) 0)) _ . _))
+    (##sys#check-syntax 'functor x '(_ (symbol . #((_ _) 0)) _ . _))
     (let* ((x (##sys#strip-syntax x))
 	   (head (cadr x))
 	   (name (car head))
+	   (args (cdr head))
 	   (exps (caddr x))
 	   (body (cdddr x))
 	   (registration
@@ -1159,8 +1160,14 @@
 	      ',(map (lambda (arg)
 		       (let ((argname (car arg))
 			     (exps (##sys#validate-exports (cadr arg) 'functor)))
+			 (unless (or (symbol? argname)
+				     (and (list? argname) 
+					  (= 2 (length argname))
+					  (symbol? (car argname))
+					  (symbol? (cadr argname))))
+			   (##sys#syntax-error-hook "invalid functor argument" name arg))
 			 (cons argname exps)))
-		     (cdr head))
+		     args)
 	      ',(##sys#validate-exports exps 'functor)
 	      ',body)))
       `(##core#module
diff --git a/expand.scm b/expand.scm
index 40f0c50..ecfddc9 100644
--- a/expand.scm
+++ b/expand.scm
@@ -1459,7 +1459,7 @@
 			     '(##core#undefined))))
 		     (else
 		      (##sys#check-syntax 
-		       'module x '(_ symbol _ (symbol . #(_ 1))))
+		       'module x '(_ symbol _ (symbol . #(_ 0))))
 		      (##sys#instantiate-functor
 		       name
 		       (car app)	; functor name
diff --git a/manual/Modules b/manual/Modules
index 758cd80..b4048fc 100644
--- a/manual/Modules
+++ b/manual/Modules
@@ -460,6 +460,11 @@ requirement that a specific export of an argument-module must be
 syntax or non-syntax - it can be syntax in one instantiation and a
 procedure definition in another.
 
+{{ARGUMENTMODULE}} may also be a list of the form {{(ALIAS DEFAULT)}}
+to allow specifying a default- or optional functor argument in case
+the instanation doesn't provide one. Optional functor
+arguments may only be followed by non-optional functor arguments.
+
 The common case of using a functor with a single argument module
 that is not used elsewhere can be expressed in the following way:
 
diff --git a/modules.scm b/modules.scm
index 913d448..b79259c 100644
--- a/modules.scm
+++ b/modules.scm
@@ -823,15 +823,33 @@
 	     (cons name args) (cons fname (map car fargs))))
       `(##core#let-module-alias
 	,(let loop ((as args) (fas fargs))
-	   (cond ((null? as) (if (null? fas) '() (merr)))
+	   (cond ((null? as) 
+		  ;; use default arguments (if available) or bail out
+		  (let loop2 ((fas fas))
+		    (if (null? fas)
+			'()
+			(let ((p (car fas)))
+			  (if (pair? (car p)) ; has default argument?
+			      (let ((alias (caar p))
+				    (mname (cadar p))
+				    (exps (cdr p)))
+				(##sys#match-functor-argument alias name mname exps fname)
+				(cons (list alias mname) (loop2 (cdr fas))))
+			      ;; no default argument, we have too few argument modules
+			      (merr))))))
+		 ;; more arguments given as defined for the functor
 		 ((null? fas) (merr))
 		 (else
+		  ;; otherwise match provided argument to functor argument
 		  (let* ((p (car fas))
-			 (alias (car p))
+			 (p1 (car p))
+			 (def? (pair? p1))
+			 (alias (if def? (car p1) p1))
 			 (mname (car as))
 			 (exps (cdr p)))
 		    (##sys#match-functor-argument alias name mname exps fname)
-		    (cons (list alias mname) (loop (cdr as) (cdr fas)))))))
+		    (cons (list alias mname) 
+			  (loop (cdr as) (cdr fas)))))))
 	(##core#module
 	 ,name
 	 ,(if (eq? '* exports) #t exports)
@@ -844,7 +862,8 @@
 	(for-each
 	 (lambda (exp)
 	   (let ((sym (if (symbol? exp) exp (car exp))))
-	     (unless (##sys#find-export sym mod #f)
+	     (unless (or (assq sym (module-vexports mod))
+			 (assq sym (module-sexports mod)))
 	       (set! missing (cons sym missing)))))
 	 exps)
 	(when (pair? missing)
diff --git a/tests/functor-tests.scm b/tests/functor-tests.scm
index 1b307fd..1a05266 100644
--- a/tests/functor-tests.scm
+++ b/tests/functor-tests.scm
@@ -132,6 +132,68 @@
  99)
 
 
+;; Test optional functor arguments
+
+(functor (greet ((X default-writer) (write-greeting))) *
+  (import scheme X)
+  (define (greetings) (write-greeting 'Hello!)))
+
+(module default-writer (write-greeting)
+  (import scheme)
+  (define write-greeting list))
+
+(module writer (write-greeting)
+  (import scheme)
+  (define write-greeting vector))
+
+(module greet1 = (greet writer))
+(module greet2 = (greet))
+
+(test-equal
+ "optional functor argument #1"
+ (module m2 ()
+	 (import greet1)
+	 (greetings))
+ '#(Hello!))
+
+(test-equal
+ "optional functor argument #2"
+ (module m3 ()
+	 (import greet2)
+	 (greetings))
+ '(Hello!))
+
+
+;; Optional functor syntax with builtin ("primitive") modules:
+
+(functor (wrapper ((X scheme) (vector))) *
+  (import (except scheme vector) X)
+  (define (wrap x) (vector x)))
+
+(module default-wrapper (vector)
+  (import scheme))
+
+(module list-wrapper (vector)
+  (import (rename (only scheme list) (list vector))))
+
+(module lwrap = (wrapper list-wrapper))
+(module vwrap = (wrapper))
+
+(test-equal
+ "primitive optional functor argument #1"
+ (module m4 ()
+	 (import lwrap)
+	 (wrap 99))
+ '(99))
+
+(test-equal
+ "primitive optional functor argument #2"
+ (module m5 ()
+	 (import vwrap)
+	 (wrap 99))
+ '#(99))
+
+
 ;;
 
 (test-end)
-- 
1.7.9.5

_______________________________________________
Chicken-hackers mailing list
Chicken-hackers@nongnu.org
https://lists.nongnu.org/mailman/listinfo/chicken-hackers

Reply via email to