~ chicken-core (chicken-5) df0eb601874c78131fb6a61af4f053729ff1888f


commit df0eb601874c78131fb6a61af4f053729ff1888f
Author:     felix <felix@call-with-current-continuation.org>
AuthorDate: Mon Jul 7 22:46:00 2014 +0200
Commit:     Peter Bex <peter.bex@xs4all.nl>
CommitDate: Sat Jul 12 17:59:21 2014 +0200

    Allow functor-arguments to be optional and having defaults, and use the correct export-lists when matching functor arguments.
    
    Signed-off-by: Peter Bex <peter.bex@xs4all.nl>

diff --git a/NEWS b/NEWS
index 6efec544..a9c7732a 100644
--- a/NEWS
+++ b/NEWS
@@ -29,6 +29,11 @@
     ##sys#zap-strings, ##sys#round, ##sys#foreign-number-vector-argument,
     ##sys#check-port-mode, ##sys#check-port*
 
+- Module system
+  - Allow functor arguments to be optional, with default implementations.
+  - Fixed a bug that prevented functors from being instantiated with
+     built-in modules.
+
 - Syntax expander
   - define-values, set!-values and letrec-values now support full lambda
     lists as binding forms
diff --git a/chicken-syntax.scm b/chicken-syntax.scm
index c815bc80..448ebf4d 100644
--- a/chicken-syntax.scm
+++ b/chicken-syntax.scm
@@ -1133,10 +1133,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
@@ -1145,8 +1146,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 72e246a4..1b5a74e3 100644
--- a/expand.scm
+++ b/expand.scm
@@ -1475,7 +1475,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 758cd80f..b4048fc3 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 913d4489..439c7091 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 1b307fd5..1a05266c 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)
Trap