diff --git a/collects/mzlib/kw.ss b/collects/mzlib/kw.ss index 70c62a1aa0..6a8e187867 100644 --- a/collects/mzlib/kw.ss +++ b/collects/mzlib/kw.ss @@ -188,7 +188,7 @@ '()))] [opts (pop-formals #:optional)] [keys (pop-formals #:key)]) - ;; now get all rest-like vars + ;; now get all rest-like vars and modes (let loop ([formals formals] [rests '()] [modes '()]) (if (null? formals) (apply process-vars vars opts keys rests modes only-vars?) @@ -197,12 +197,16 @@ (cond [(memq k '(#:optional #:key)) (serror k-stx "misplaced ~a" k)] [(memq k mode-keywords) - (cond [(null? keys) - (serror k-stx "cannot use without #:key arguments")] - [(pair? (cdar formals)) - (serror (cadar formals) - "identifier following mode keyword ~a" k)] - [else (loop (cdr formals) rests (cons k modes))])] + (cond + #; ;(*) + ;; don't throw an error here, it still fine if used with + ;; #:allow-other-keys (explicit or implicit), also below + [(null? keys) + (serror k-stx "cannot use without #:key arguments")] + [(pair? (cdar formals)) + (serror (cadar formals) + "identifier following mode keyword ~a" k)] + [else (loop (cdr formals) rests (cons k modes))])] [(not (memq k rest-like-kwds)) (serror k-stx "unknown meta keyword")] [(assq k rests) @@ -211,6 +215,9 @@ (serror k-stx "missing variable name")] [(not (null? (cddar formals))) (serror k-stx "too many variable names")] + #; ;(*) + ;; same as above: don't throw an error here, still fine if + ;; used with #:allow-other-keys (explicit or implicit) [(and (null? keys) (not (eq? #:rest k))) (serror k-stx "cannot use without #:key arguments")] [else (loop (cdr formals) @@ -367,14 +374,24 @@ (with-syntax ([body (make-rest-body expr)] [keys keys]) #'(let* keys body))) ;; ------------------------------------------------------------------------ + ;; more sanity tests (see commented code above -- search for "(*)") + (let ([r (or all-keys other-keys other-keys+body body rest)]) + (when (and (not allow-other-keys?) (null? keys)) + (when r (serror r "cannot use without #:key or #:allow-other-keys")) + (when allow-duplicate-keys? + (serror #f (string-append "cannot allow duplicate keys without" + " #:key or #:allow-other-keys")))) + (when (and allow-other-keys? (null? keys) (not r)) + (serror #f "cannout allow other keys without using them in some way"))) + ;; ------------------------------------------------------------------------ ;; body generation starts here (cond - ;; no optionals or keys => plain lambda - [(and (null? opts) (null? keys)) + ;; no optionals or keys (or other-keys) => plain lambda + [(and (null? opts) (null? keys) (not allow-other-keys?)) (with-syntax ([vars (append! vars (or rest '()))] [expr expr]) (syntax/loc stx (lambda vars expr)))] ;; no keys => make a case-lambda for optionals - [(null? keys) + [(and (null? keys) (not allow-other-keys?)) (let ([clauses (make-opt-clauses expr (or rest '()))]) (with-syntax ([name name] [clauses clauses]) (syntax/loc stx (letrec ([name (case-lambda . clauses)]) name))))] diff --git a/collects/tests/mzscheme/kw.ss b/collects/tests/mzscheme/kw.ss index fe8993ddf8..99a94d6332 100644 --- a/collects/tests/mzscheme/kw.ss +++ b/collects/tests/mzscheme/kw.ss @@ -375,9 +375,11 @@ :st-err: <= (lambda/kw (x #:body x #:allow-other-keys) 1) :st-err: <= (lambda/kw (x #:optional ()) 1) :st-err: <= (lambda/kw (x #:optional (x y z)) 1) - :st-err: <= (lambda/kw (x #:other-keys z) 1) - :st-err: <= (lambda/kw (x #:other-keys+body z) 1) - :st-err: <= (lambda/kw (x #:all-keys z) 1) + ;; :st-err: <= (lambda/kw (x #:other-keys z) 1) <-- these are all + ;; :st-err: <= (lambda/kw (x #:other-keys+body z) 1) <-- fine! + ;; :st-err: <= (lambda/kw (x #:all-keys z) 1) <-- (see below) + :st-err: <= (lambda/kw (x #:other-keys z #:forbid-other-keys) 1) + :st-err: <= (lambda/kw (x #:all-keys z #:forbid-other-keys) 1) :st-err: <= (lambda/kw (x #:key y #:allow-other-keys z) 1) :st-err: <= (lambda/kw (x #:key y #:forbid-body z) 1) :st-err: <= (lambda/kw (x #:key y #:allow-body #:rest r #:forbid-body) 1) @@ -387,4 +389,12 @@ :st-err: <= (lambda/kw (#:key a #:body r #:forbid-body) r) :st-err: <= (lambda/kw (#:key a #:other-keys r #:forbid-other-keys) r)) + ;; it is ok to have no keys, and still specify all-keys etc + (let ([f (lambda/kw (x #:all-keys ak) (list x ak))]) + (t (f 1) => '(1 ()) + (f 1 #:a 2) => '(1 (#:a 2)) + (f 1 #:a 2 #:b 3) => '(1 (#:a 2 #:b 3)) + (f 1 #:a 2 #:a 3) => '(1 (#:a 2 #:a 3)) + (f 1 #:a 2 #:a 3) => '(1 (#:a 2 #:a 3)))) + )