diff --git a/collects/mzlib/kw.ss b/collects/mzlib/kw.ss index 7697859..a7b5af9 100644 --- a/collects/mzlib/kw.ss +++ b/collects/mzlib/kw.ss @@ -5,20 +5,36 @@ (begin-for-syntax ; -> configuration for lambda/kw ;; must appear at the end, each with exactly one variable (define rest-like-kwds '(#:rest #:body #:rest-keys #:all-keys #:other-keys)) - ;; also in the end, without variable, cannot have contradictions - (define allow-other-keys-kwds '(#:allow-other-keys #:forbid-other-keys)) - (define allow-body-kwds '(#:allow-body #:forbid-body)) - ;; using any of these allows access to additional keys or to body, making the - ;; default be to allow other keys or body - (define other-keys-accessing '(#:rest #:rest-keys #:all-keys #:other-keys)) - (define body-accessing '(#:rest #:body #:rest-keys))) + ;; mode keys are in the end, without variable, cannot have contradictions + ;; each descriptor for #:allow-kwd and #:forbid-kwd is + ;; (kwd-sym (forcer ...) (enabler ...)) + ;; `forcer' is a rest-like keyword that forces the mode, `enabler' is a + ;; rest-like keyword that makes it on by default + (define mode-keyword-specs + '((other-keys (#:other-keys) (#:rest #:rest-keys #:all-keys)) + (duplicate-keys () (#:rest #:all-keys)) + (body (#:body) (#:rest #:rest-keys)))) + ;; precomputed mode keyword stuff + (define processed-keyword-specs + (map (lambda (ks) + (let* ([k (car ks)] + [make (lambda (str) + (string->keyword + (string-append str (symbol->string k))))]) + (list* k (make "allow-") (make "forbid-") (cdr ks)))) + mode-keyword-specs)) + (define mode-keywords + (apply append (map (lambda (ks) (list (cadr ks) (caddr ks))) + processed-keyword-specs)))) (provide lambda/kw) (define-syntax (lambda/kw stx) ;; -------------------------------------------------------------------------- ;; easy syntax errors + (define original-formals #f) (define (serror sub fmt . args) - (raise-syntax-error #f (apply format fmt args) stx sub)) + (raise-syntax-error + #f (apply format fmt args) stx (or sub original-formals))) ;; contents of syntax (define (syntax-e* x) (if (syntax? x) (syntax-e x) x)) ;; turns formals into a syntax list @@ -69,32 +85,29 @@ [var (serror #'var "not a valid ~a spec" #:key)])) ;; -------------------------------------------------------------------------- ;; helpers for process-vars - (define (get-mode mode k k-stx formals keys) - (cond [(null? keys) - (serror k-stx "cannot use without #:key arguments")] - [(pair? (cdar formals)) - (serror (cadar formals) "identifier following mode keyword ~a" k)] - [(and mode (not (eq? k mode))) - (serror k-stx "contradicting mode keywords")] - [else k])) - (define (process-mode mode rests allow enablers) - (if mode - (eq? mode allow) - (ormap (lambda (k) (and (assq k rests) #t)) enablers))) + (define ((process-mode modes rests) processed-spec) + (let ([allow (memq (cadr processed-spec) modes)] + [forbid (memq (caddr processed-spec) modes)]) + (cond + [(and allow forbid) + (serror #f "contradicting #:...-~a keywords" (car processed-spec))] + [(ormap (lambda (k) (assq k rests)) (cadddr processed-spec)) + => ; forced? + (lambda (r) + (when forbid (serror #f "cannot ~s with ~s" (car forbid) (car r))) + #t)] + [allow #t] + [forbid #f] + [else (ormap (lambda (k) (and (assq k rests) #t)) ; suggested? + (car (cddddr processed-spec)))]))) ;; -------------------------------------------------------------------------- ;; test variables - (define (process-vars vars opts keys0 rests other-keys-mode body-mode - . only-vars?) + (define (process-vars vars opts keys0 rests modes . only-vars?) (define (gensym x) (car (generate-temporaries (list x)))) (let*-values ([(only-vars?) (and (pair? only-vars?) (car only-vars?))] [(opts keys0) (values (map process-opt opts) (map process-key keys0))] - [(other-keys-mode body-mode) - (values (process-mode other-keys-mode - rests #:allow-other-keys other-keys-accessing) - (process-mode body-mode - rests #:allow-body body-accessing))] [(rest body rest-keys all-keys other-keys) (apply values (map (lambda (k) (cond [(assq k rests) => cdr] [else #f])) @@ -107,6 +120,9 @@ (values (or rest (gensym #'rest)) (or body (gensym #'body)) (or other-keys (gensym #'other-keys)))] + [(other-keys-mode duplicate-keys-mode body-mode) + (apply values (map (process-mode modes rests) + processed-keyword-specs))] ;; turn ( ) keys to ( ) [(keys) (with-syntax ([r rest*]) @@ -131,7 +147,8 @@ => (lambda (d) (serror d "duplicate argument name"))] [else (values vars opts keys rest rest* body body* body-spec rest-keys all-keys other-keys other-keys* - other-keys-mode body-mode (map cadr keys0))]))) + other-keys-mode duplicate-keys-mode body-mode + (map cadr keys0))]))) ;; -------------------------------------------------------------------------- ;; parses formals, returns list of normal vars, optional var specs, key var ;; specs, an alist of rest-like kw+vars, and a mode for allowing other keys @@ -148,24 +165,20 @@ [opts (pop-formals #:optional)] [keys (pop-formals #:key)]) ;; now get all rest-like vars - (let loop ([formals formals] - [rests '()] - [other-keys-mode #f] - [body-mode #f]) + (let loop ([formals formals] [rests '()] [modes '()]) (if (null? formals) - (apply process-vars vars opts keys rests other-keys-mode body-mode - only-vars?) + (apply process-vars vars opts keys rests modes only-vars?) (let* ([k-stx (caar formals)] [k (syntax-e* k-stx)]) (cond [(memq k '(#:optional #:key)) (serror k-stx "misplaced ~a" k)] - [(memq k allow-other-keys-kwds) - (loop (cdr formals) rests - (get-mode other-keys-mode k k-stx formals keys) - body-mode)] - [(memq k allow-body-kwds) - (loop (cdr formals) rests other-keys-mode - (get-mode body-mode k k-stx formals keys))] + [(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))])] [(not (memq k rest-like-kwds)) (serror k-stx "unknown meta keyword")] [(assq k rests) @@ -178,7 +191,7 @@ (serror k-stx "cannot use without #:key arguments")] [else (loop (cdr formals) (cons (cons k (cadar formals)) rests) - other-keys-mode body-mode)])))))) + modes)])))))) ;; -------------------------------------------------------------------------- ;; generates the actual body (define (generate-body formals expr) @@ -197,8 +210,9 @@ all-keys ; keyword-vals without body other-keys ; unprocessed keyword-vals other-keys* ; always an id - allow-other-keys? ; allowing other keys? - allow-body? ; allowing body after keys? + allow-other-keys? ; allowing other keys? + allow-duplicate-keys? ; allowing duplicate keys? + allow-body? ; allowing body after keys? keywords) ; list of mentioned keywords (parse-formals formals)) (define name @@ -234,43 +248,52 @@ [expr expr] [all-keys* all-keys] [other-keys* other-keys*] - [rest-keys* rest-keys]) - (with-syntax ([loop-vars - #`([body* rest*] - #,@(if all-keys #`([all-keys* '()]) '()) - #,@(if others? #`([other-keys* '()]) '()))] - [next-loop - #`(loop (cddr body*) - #,@(if all-keys - #`((list* (cadr body*) (car body*) - all-keys*)) - '()) - #,@(if others? - #`((if (memq (car body*) 'keywords) - other-keys* - (list* (cadr body*) (car body*) - other-keys*))) - '()))] - [expr - (if (or all-keys others?) - #`(let* (#,@(if all-keys - #'([all-keys* (reverse! all-keys*)]) - '()) - #,@(if others? - #'([other-keys* (reverse! other-keys*)]) - '()) - #,@(cond [(and other-keys rest-keys) - #'([rest-keys* - (append other-keys* body*)])] - [rest-keys ; can destroy other-keys - #'([rest-keys* - (append! other-keys* body*)])] - [else '()])) - expr) - #'expr)]) + [rest-keys* rest-keys] + [seen-keys #'seen-keys]) + (with-syntax + ([loop-vars + #`([body* rest*] + #,@(if all-keys #`([all-keys* '()]) '()) + #,@(if others? #`([other-keys* '()]) '()) + #,@(if allow-duplicate-keys? '() #`([seen-keys '()])))] + [next-loop + #`(loop (cddr body*) + #,@(if all-keys + #`((list* (cadr body*) (car body*) all-keys*)) + '()) + #,@(if others? + #`((if (memq (car body*) 'keywords) + other-keys* + (list* (cadr body*) (car body*) other-keys*))) + '()) + #,@(if allow-duplicate-keys? + '() + #`((if (and (memq (car body*) seen-keys) + (memq (car body*) 'keywords)) + (error* 'name "duplicate keyword: ~e" + (car body*)) + (cons (car body*) seen-keys)))))] + [expr + (if (or all-keys others?) + #`(let* (#,@(if all-keys + #'([all-keys* (reverse! all-keys*)]) + '()) + #,@(if others? + #'([other-keys* (reverse! other-keys*)]) + '()) + #,@(cond [(and other-keys rest-keys) + #'([rest-keys* + (append other-keys* body*)])] + [rest-keys ; can destroy other-keys + #'([rest-keys* + (append! other-keys* body*)])] + [else '()])) + expr) + #'expr)]) (with-syntax ([next-loop (if allow-other-keys? #'next-loop + ;;!!! #'(if (memq (car body*) 'keywords) next-loop (error* 'name "unknown keyword: ~e" @@ -325,7 +348,8 @@ (syntax/loc stx (letrec ([name (case-lambda . clauses)]) name))))])) (syntax-case stx () [(_ formals expr0 expr ...) - (generate-body #'formals #'(let () expr0 expr ...))])) + (begin (set! original-formals #'formals) + (generate-body #'formals #'(let () expr0 expr ...)))])) (provide define/kw) (define-syntax (define/kw stx) @@ -334,25 +358,23 @@ [(_ (name . args) body0 body ...) (syntax/loc stx (_ name (lambda/kw args body0 body ...)))])) -;; raise an proper exception +;; raise an appropriate exception (define (error* who fmt . args) (raise (make-exn:fail:contract (string->immutable-string (apply format (string-append "~a: " fmt) who args)) (current-continuation-marks)))) -;; Keyword searching utility (note: no errors for odd length) +;; keyword searching utility (note: no errors for odd length) (provide getarg) (define (getarg args keyword . not-found) (let loop ([args args]) (cond [(or (null? args) (null? (cdr args)) (not (keyword? (car args)))) - (and (pair? not-found) - (let ([x (car not-found)]) - (if (procedure? x) (x) x)))] + (and (pair? not-found) ((car not-found)))] [(eq? (car args) keyword) (cadr args)] [else (loop (cddr args))]))) -;; a private version of getarg that is always used with simple values +;; a private version of getarg that is used with simple values (define (getarg* args keyword . not-found) (let loop ([args args]) (cond [(or (null? args) (null? (cdr args)) (not (keyword? (car args)))) diff --git a/collects/tests/mzscheme/kw.ss b/collects/tests/mzscheme/kw.ss index 9229ae3..49e6e11 100644 --- a/collects/tests/mzscheme/kw.ss +++ b/collects/tests/mzscheme/kw.ss @@ -5,249 +5,321 @@ (require (lib "kw.ss")) -(let ([t test]) +(let () + (define-syntax t + (syntax-rules (=> <= :rt-err: :st-err:) + [(t E => :rt-err:) (err/rt-test E)] + [(t E => :st-err:) (syntax-test #'E)] + [(t (f x ...) => res) (test res f x ...)] + [(t R => E more ...) (begin (t R => E) (t more ...))] + [(t R <= E more ...) (t E => R more ...)])) ;; make sure that lambda/kw behaves as lambda - (t 1 (lambda/kw () 1)) - (t 1 (lambda/kw (x) 1) 0) - (t '() (lambda/kw x x)) - (t '(1 2) (lambda/kw x x) 1 2) - (t '(1 2) (lambda/kw (x . xs) xs) 0 1 2) + (t ((lambda/kw () 1)) => 1 + ((lambda/kw (x) 1) 0) => 1 + ((lambda/kw x x)) => '() + ((lambda/kw x x) 1 2) => '(1 2) + ((lambda/kw (x . xs) xs) 0 1 2) => '(1 2)) ;; even with keywords - (t #:x (lambda/kw () #:x)) - (t #:x (lambda/kw (x) #:x) #:y) - (t '(#:x #:y) (lambda/kw x x) #:x #:y) - (t '(#:x #:y) (lambda/kw (x . xs) xs) #:z #:x #:y) + (t ((lambda/kw () #:x)) => #:x + ((lambda/kw (x) #:x) #:y) => #:x + ((lambda/kw x x) #:x #:y) => '(#:x #:y) + ((lambda/kw (x . xs) xs) #:z #:x #:y) => '(#:x #:y)) ;; just using #:rest is the same as a dot (let ([f (lambda/kw (#:rest r) r)]) - (t '() f) - (t '(1) f 1) - (t '(1 2) f 1 2)) + (t (f) => '() + (f 1) => '(1) + (f 1 2) => '(1 2))) (let ([f (lambda/kw (x #:rest r) r)]) - (t '() f 0) - (t '(1) f 0 1) - (t '(1 2) f 0 1 2)) + (t (f 0) => '() + (f 0 1) => '(1) + (f 0 1 2) => '(1 2))) ;; using only optionals - (t 0 procedure-arity (lambda/kw (#:optional) 0)) - (t '(3 1 2) procedure-arity (lambda/kw (x #:optional y z) 0)) + (t (procedure-arity (lambda/kw (#:optional) 0)) => 0 + (procedure-arity (lambda/kw (x #:optional y z) 0)) => '(3 1 2)) (let ([f (lambda/kw (x #:optional y) (list x y))]) - (t '(0 #f) f 0) - (t '(0 1) f 0 1)) + (t (f 0) => '(0 #f) + (f 0 1) => '(0 1))) (let ([f (lambda/kw (x #:optional [y 0]) (list x y))]) - (t '(0 0) f 0) - (t '(0 1) f 0 1)) + (t (f 0) => '(0 0) + (f 0 1) => '(0 1))) (let ([f (lambda/kw (x #:optional [y x]) (list x y))]) - (t '(0 0) f 0) - (t '(0 1) f 0 1)) + (t (f 0) => '(0 0) + (f 0 1) => '(0 1))) (let ([f (lambda/kw (x #:optional [y x] [z x]) (list x y z))]) - (t '(0 0 0) f 0) - (t '(0 1 0) f 0 1) - (t '(0 1 2) f 0 1 2)) + (t (f 0) => '(0 0 0) + (f 0 1) => '(0 1 0) + (f 0 1 2) => '(0 1 2))) (let ([f (lambda/kw (x #:optional [y x] [z y]) (list x y z))]) - (t '(0 0 0) f 0) - (t '(0 1 1) f 0 1) - (t '(0 1 2) f 0 1 2)) + (t (f 0) => '(0 0 0) + (f 0 1) => '(0 1 1) + (f 0 1 2) => '(0 1 2))) ;; keywords: basic stuff - (let ([f (lambda/kw (#:key x [y 1] [z #:zz #:z]) (list x y z))]) - (t '(#f 1 #:z) f) - (t '(#:zz 1 #:zzz) f #:zz #:zzz #:zz 123 #:x #:zz)) + (let ([f (lambda/kw (#:key x [y 1] [z #:zz #:z] #:allow-duplicate-keys) + (list x y z))]) + (t (f) => '(#f 1 #:z) + (f #:zz #:zzz #:zz 123 #:x #:zz) => '(#:zz 1 #:zzz))) ;; keywords: default-expr scope - (let ([f (lambda/kw (#:key x y) (list x y))]) - (t '(#f #f) f) - (t '(1 #f) f #:x 1) - (t '(#f 2 ) f #:y 2) - (t '(1 2 ) f #:x 1 #:y 2) - (t '(1 2 ) f #:x 1 #:y 2 #:y 3 #:x 4)) + (let ([f (lambda/kw (#:key x y #:allow-duplicate-keys) (list x y))]) + (t '(#f #f) <= (f) + '(1 #f) <= (f #:x 1) + '(#f 2 ) <= (f #:y 2) + '(1 2 ) <= (f #:x 1 #:y 2) + '(1 2 ) <= (f #:x 1 #:y 2 #:y 3 #:x 4))) (let ([f (lambda/kw (#:key x [y x]) (list x y))]) - (t '(1 1 ) f #:x 1) - (t '(#f 2 ) f #:y 2) - (t '(1 2 ) f #:x 1 #:y 2)) + (t '(1 1 ) <= (f #:x 1) + '(#f 2 ) <= (f #:y 2) + '(1 2 ) <= (f #:x 1 #:y 2))) (let ([f (lambda/kw (#:key x [y x] [z x]) (list x y z))]) - (t '(1 1 1 ) f #:x 1) - (t '(#f 1 #f) f #:y 1) - (t '(#f #f 1 ) f #:z 1)) + (t '(1 1 1 ) <= (f #:x 1) + '(#f 1 #f) <= (f #:y 1) + '(#f #f 1 ) <= (f #:z 1))) (let ([f (lambda/kw (#:key x [y x] [z y]) (list x y z))]) - (t '(1 1 1 ) f #:x 1) - (t '(#f 1 1 ) f #:y 1) - (t '(#f #f 1 ) f #:z 1)) - (t '(1 2) (let ([y 1]) (lambda/kw (#:key [x y] [y (add1 x)]) (list x y)))) - (t '(1 2) (let ([x 1]) (lambda/kw (#:key [x x] [y (add1 x)]) (list x y)))) + (t '(1 1 1 ) <= (f #:x 1) + '(#f 1 1 ) <= (f #:y 1) + '(#f #f 1 ) <= (f #:z 1))) + (t + ((let ([y 1]) (lambda/kw (#:key [x y] [y (add1 x)]) (list x y)))) => '(1 2) + ((let ([x 1]) (lambda/kw (#:key [x x] [y (add1 x)]) (list x y)))) => '(1 2)) ;; keywords: default-expr evaluation - (t 1 (lambda/kw (#:key [x 1]) x)) - (t "1" (lambda/kw (#:key [x "1"]) x)) - (t 1 (lambda/kw (#:key [x '1]) x)) - (t ''1 (lambda/kw (#:key [x ''1]) x)) - (t '(add1 1) (lambda/kw (#:key [x '(add1 1)]) x)) - (t + (lambda/kw (#:key [x +]) x)) + (t ((lambda/kw (#:key [x 1]) x)) => 1 + ((lambda/kw (#:key [x "1"]) x)) => "1" + ((lambda/kw (#:key [x '1]) x)) => 1 + ((lambda/kw (#:key [x ''1]) x)) => ''1 + ((lambda/kw (#:key [x '(add1 1)]) x)) => '(add1 1) + ((lambda/kw (#:key [x +]) x)) => +) (let ([f (lambda () (let ([y 1]) (lambda/kw (#:key [x (begin (set! y 3) 2)]) y)))]) - (t 3 (f)) - (t 1 (f) #:x 1)) + (t ((f)) => 3 + ((f) #:x 1) => 1)) (let ([f (lambda () (let ([y 1]) (let-syntax ([z (syntax-id-rules () [_ (begin (set! y 3) 2)])]) (lambda/kw (#:key [x z]) y))))]) - (t 3 (f)) - (t 1 (f) #:x 1)) + (t ((f)) => 3 + ((f) #:x 1) => 1)) ;; keywords: make sure that getarg stops at end of keyword part (let ([f (lambda/kw (#:key x y #:body b) (list x y b))]) - (t '(#f #f (2 #:x 1)) f 2 #:x 1) - (t '(#f #f (2 3 #:x 1)) f 2 3 #:x 1)) + (t (f) => '(#f #f ()) + (f 2) => '(#f #f (2)) + (f 2 #:x 1) => '(#f #f (2 #:x 1)) + (f 2 3 #:x 1) => '(#f #f (2 3 #:x 1)))) ;; exotic extras - (let ([f (lambda/kw (#:key a b #:rest r) r)]) - (t '(1 2 3) f 1 2 3) - (t '(#:a 1 1 2 3) f #:a 1 1 2 3) - (t '(#:a 1 #:a 2 1 2 3) f #:a 1 #:a 2 1 2 3) - (t '(#:b 2 1 2 3) f #:b 2 1 2 3) - (t '(#:a 1 #:b 2 1 2 3) f #:a 1 #:b 2 1 2 3) - (t '(#:a 1 #:b 2 #:c 3 1 2 3) f #:a 1 #:b 2 #:c 3 1 2 3)) + (let ([f (lambda/kw (#:key a b #:rest r #:allow-duplicate-keys) r)]) + (t (f 1 2 3) => '(1 2 3) + (f #:a 1 1 2 3) => '(#:a 1 1 2 3) + (f #:a 1 #:a 2 1 2 3) => '(#:a 1 #:a 2 1 2 3) + (f #:b 2 1 2 3) => '(#:b 2 1 2 3) + (f #:a 1 #:b 2 1 2 3) => '(#:a 1 #:b 2 1 2 3) + (f #:a 1 #:b 2 #:c 3 1 2 3) => '(#:a 1 #:b 2 #:c 3 1 2 3))) (let ([f (lambda/kw (#:key a b #:body r) r)]) - (t '(1 2 3) f 1 2 3) - (t '(1 2 3) f #:a 1 1 2 3) - (t '(1 2 3) f #:a 1 #:a 2 1 2 3) - (t '(1 2 3) f #:b 2 1 2 3) - (t '(1 2 3) f #:a 1 #:b 2 1 2 3)) + (t (f 1 2 3) => '(1 2 3) + (f #:a 1 1 2 3) => '(1 2 3) + (f #:a 1 #:a 2 1 2 3) => :rt-err: + (f #:b 2 1 2 3) => '(1 2 3) + (f #:a 1 #:b 2 1 2 3) => '(1 2 3))) (let ([f (lambda/kw (#:key a b #:other-keys r) r)]) - (t '() f) - (t '() f #:a 1 #:b 2) - (t '() f #:a 1 #:a 2 #:b 3) - (t '(#:c 3) f #:a 1 #:b 2 #:c 3) - (t '(#:d 4 #:c 3) f #:d 4 #:a 1 #:b 2 #:c 3) - (t '(#:d 4 #:c 3 #:c 33) f #:d 4 #:a 1 #:b 2 #:c 3 #:c 33) - (t '(#:d 4 #:c 3 #:c 33) f #:d 4 #:a 1 #:c 3 #:b 2 #:c 33)) + (t (f) => '() + (f #:a 1 #:b 2) => '() + (f #:a 1 #:b 2 #:c 3) => '(#:c 3) + (f #:d 4 #:a 1 #:b 2 #:c 3) => '(#:d 4 #:c 3) + ;; #:c is not a specified key, so it is allowed to repeat + (f #:d 4 #:a 1 #:b 2 #:c 3 #:c 33) => '(#:d 4 #:c 3 #:c 33) + (f #:d 4 #:a 1 #:c 3 #:b 2 #:c 33) => '(#:d 4 #:c 3 #:c 33))) + (let ([f (lambda/kw (#:key a b #:other-keys r #:allow-duplicate-keys) r)]) + (t (f) => '() + (f #:a 1 #:b 2) => '() + (f #:a 1 #:a 2 #:b 3) => '() + (f #:a 1 #:b 2 #:c 3) => '(#:c 3) + (f #:d 4 #:a 1 #:b 2 #:c 3) => '(#:d 4 #:c 3) + (f #:d 4 #:a 1 #:b 2 #:c 3 #:c 33) => '(#:d 4 #:c 3 #:c 33) + (f #:d 4 #:a 1 #:c 3 #:b 2 #:c 33) => '(#:d 4 #:c 3 #:c 33))) (let ([f (lambda/kw (#:key a b #:rest-keys r) r)]) - (t '() f) - (t '(1 2) f 1 2) - (t '() f #:a 1 #:b 2) - (t '(1 2) f #:a 1 #:b 2 1 2) - (t '() f #:a 1 #:a 2 #:b 3) - (t '(1 2) f #:a 1 #:a 2 #:b 3 1 2) - (t '(#:c 3) f #:a 1 #:b 2 #:c 3) - (t '(#:c 3 1 2) f #:a 1 #:b 2 #:c 3 1 2) - (t '(#:d 4 #:c 3) f #:d 4 #:a 1 #:b 2 #:c 3) - (t '(#:d 4 #:c 3 1 2) f #:d 4 #:a 1 #:b 2 #:c 3 1 2) - (t '(#:d 4 #:c 3 #:c 33) f #:d 4 #:a 1 #:b 2 #:c 3 #:c 33) - (t '(#:d 4 #:c 3 #:c 33 1 2) f #:d 4 #:a 1 #:b 2 #:c 3 #:c 33 1 2) - (t '(#:d 4 #:c 3 #:c 33) f #:d 4 #:a 1 #:c 3 #:b 2 #:c 33) - (t '(#:d 4 #:c 3 #:c 33 1 2) f #:d 4 #:a 1 #:c 3 #:b 2 #:c 33 1 2) - ) + (t (f) => '() + (f 1 2) => '(1 2) + (f #:a 1 #:b 2) => '() + (f #:a 1 #:b 2 1 2) => '(1 2) + (f #:a 1 #:b 2 #:c 3) => '(#:c 3) + (f #:a 1 #:b 2 #:c 3 1 2) => '(#:c 3 1 2) + (f #:d 4 #:a 1 #:b 2 #:c 3) => '(#:d 4 #:c 3) + (f #:d 4 #:a 1 #:b 2 #:c 3 1 2) => '(#:d 4 #:c 3 1 2) + (f #:d 4 #:a 1 #:b 2 #:c 3 #:c 33) => '(#:d 4 #:c 3 #:c 33) + (f #:d 4 #:a 1 #:b 2 #:c 3 #:c 33 1 2) => '(#:d 4 #:c 3 #:c 33 1 2) + (f #:d 4 #:a 1 #:c 3 #:b 2 #:c 33) => '(#:d 4 #:c 3 #:c 33) + (f #:d 4 #:a 1 #:c 3 #:b 2 #:c 33 1 2) => '(#:d 4 #:c 3 #:c 33 1 2))) + (let ([f (lambda/kw (#:key a b #:rest-keys r #:allow-duplicate-keys) r)]) + (t (f) => '() + (f 1 2) => '(1 2) + (f #:a 1 #:b 2) => '() + (f #:a 1 #:b 2 1 2) => '(1 2) + (f #:a 1 #:a 2 #:b 3) => '() + (f #:a 1 #:a 2 #:b 3 1 2) => '(1 2) + (f #:a 1 #:b 2 #:c 3) => '(#:c 3) + (f #:a 1 #:b 2 #:c 3 1 2) => '(#:c 3 1 2) + (f #:d 4 #:a 1 #:b 2 #:c 3) => '(#:d 4 #:c 3) + (f #:d 4 #:a 1 #:b 2 #:c 3 1 2) => '(#:d 4 #:c 3 1 2) + (f #:d 4 #:a 1 #:b 2 #:c 3 #:c 33) => '(#:d 4 #:c 3 #:c 33) + (f #:d 4 #:a 1 #:b 2 #:c 3 #:c 33 1 2) => '(#:d 4 #:c 3 #:c 33 1 2) + (f #:d 4 #:a 1 #:c 3 #:b 2 #:c 33) => '(#:d 4 #:c 3 #:c 33) + (f #:d 4 #:a 1 #:c 3 #:b 2 #:c 33 1 2) => '(#:d 4 #:c 3 #:c 33 1 2))) (let ([f (lambda/kw (x #:key a b #:all-keys r) r)]) - (t '() f 1) - (t '(#:a 1 #:b 2) f 1 #:a 1 #:b 2) - (t '(#:a 1 #:a 2 #:b 3) f 1 #:a 1 #:a 2 #:b 3) - (t '(#:a 1 #:b 2 #:c 3) f 1 #:a 1 #:b 2 #:c 3) - (t '(#:d 4 #:a 1 #:b 2 #:c 3) f 1 #:d 4 #:a 1 #:b 2 #:c 3) - (t '(#:d 4 #:a 1 #:b 2 #:c 3 #:c 33) f 1 #:d 4 #:a 1 #:b 2 #:c 3 #:c 33) - (t '(#:d 4 #:a 1 #:c 3 #:b 2 #:c 33) f 1 #:d 4 #:a 1 #:c 3 #:b 2 #:c 33) - (err/rt-test (f 1 #:a 2 3)) - (err/rt-test (f 1 #:a 2 3 4)) - ) + (t (f 1) => '() + (f 1 #:a 1 #:b 2) => '(#:a 1 #:b 2) + (f 1 #:a 1 #:a 2 #:b 3) => '(#:a 1 #:a 2 #:b 3) + (f 1 #:a 1 #:b 2 #:c 3) => '(#:a 1 #:b 2 #:c 3) + (f 1 #:d 4 #:a 1 #:b 2 #:c 3) => '(#:d 4 #:a 1 #:b 2 #:c 3) + (f 1 #:d 4 #:a 1 #:b 2 #:c 3 #:c 33) => '(#:d 4 #:a 1 #:b 2 #:c 3 #:c 33) + (f 1 #:d 4 #:a 1 #:c 3 #:b 2 #:c 33) => '(#:d 4 #:a 1 #:c 3 #:b 2 #:c 33) + (f 1 #:a 2 3) => :rt-err: + (f 1 #:a 2 3 4) => :rt-err:)) ;; check when other keys are allowed - (err/rt-test ((lambda/kw (#:key a #:body r) r) #:a 1 #:b 2)) - (err/rt-test ((lambda/kw (#:key a) a) #:a 1 #:b 2)) - (t 1 (lambda/kw (#:key a #:rest r) a) #:a 1 #:b 2) - (t 1 (lambda/kw (#:key a #:rest-keys r) a) #:a 1 #:b 2) - (t 1 (lambda/kw (#:key a #:allow-other-keys) a) #:a 1 #:b 2) - (err/rt-test ((lambda/kw (#:key a #:rest r #:forbid-other-keys) a) #:a 1 #:b 2)) + (t :rt-err: <= ((lambda/kw (#:key a #:body r) r) #:a 1 #:b 2) + :rt-err: <= ((lambda/kw (#:key a) a) #:a 1 #:b 2) + 1 <= ((lambda/kw (#:key a #:rest r) a) #:a 1 #:b 2) + 1 <= ((lambda/kw (#:key a #:rest-keys r) a) #:a 1 #:b 2) + 1 <= ((lambda/kw (#:key a #:allow-other-keys) a) #:a 1 #:b 2) + :rt-err: <= ((lambda/kw (#:key a #:rest r #:forbid-other-keys) a) #:a 1 #:b 2)) + ;; check when duplicate keys are allowed + (t :rt-err: <= ((lambda/kw (#:key a #:body r) r) #:a 1 #:a 2) + :rt-err: <= ((lambda/kw (#:key a) a) #:a 1 #:a 2) + 1 <= ((lambda/kw (#:key a #:rest r) a) #:a 1 #:a 2) + :rt-err: <= ((lambda/kw (#:key a #:rest-keys r) a) #:a 1 #:a 2) + 1 <= ((lambda/kw (#:key a #:allow-duplicate-keys) a) #:a 1 #:a 2) + :rt-err: <= ((lambda/kw (#:key a #:rest r #:forbid-duplicate-keys) a) #:a 1 #:a 2)) ;; check when body is allowed - (err/rt-test ((lambda/kw (#:key a #:all-keys r) r) #:a 1 #:b 2 3)) - (err/rt-test ((lambda/kw (#:key a #:all-keys r) r) #:a 1 #:b 2 3 4)) - (err/rt-test ((lambda/kw (#:key a #:other-keys r) r) #:a 1 #:b 2 3)) - (err/rt-test ((lambda/kw (#:key a #:other-keys r) r) #:a 1 #:b 2 3 4)) - (t '(#:a 1 #:b 2 3) (lambda/kw (#:key a #:rest r) r) #:a 1 #:b 2 3) - (t '(#:a 1 #:b 2 3 4) (lambda/kw (#:key a #:rest r) r) #:a 1 #:b 2 3 4) - (t '(3) (lambda/kw (#:key a #:body r) r) #:a 1 3) - (t '(3 4) (lambda/kw (#:key a #:body r) r) #:a 1 3 4) - (t '(3) (lambda/kw (#:key a #:body r) r) #:a 1 #:a 2 3) - (t '(3 4) (lambda/kw (#:key a #:body r) r) #:a 1 #:a 2 3 4) - (err/rt-test ((lambda/kw (#:key a #:body r #:forbid-body) r) #:a 1 3)) - (t '(#:a 1 #:b 2) (lambda/kw (#:key a #:all-keys r #:allow-body) r) #:a 1 #:b 2 3) - (err/rt-test ((lambda/kw (#:key x y) (list x y)) #:x)) - (err/rt-test ((lambda/kw (#:key x y) (list x y)) #:x 1 #:x)) - (err/rt-test ((lambda/kw (#:key x y) (list x y)) #:x #:x #:x)) + (t :rt-err: <= ((lambda/kw (#:key a #:all-keys r) r) #:a 1 #:b 2 3) + :rt-err: <= ((lambda/kw (#:key a #:all-keys r) r) #:a 1 #:b 2 3 4) + :rt-err: <= ((lambda/kw (#:key a #:other-keys r) r) #:a 1 #:b 2 3) + :rt-err: <= ((lambda/kw (#:key a #:other-keys r) r) #:a 1 #:b 2 3 4) + '(#:a 1 #:b 2 3) <= ((lambda/kw (#:key a #:rest r) r) #:a 1 #:b 2 3) + '(#:a 1 #:b 2 3 4) <= ((lambda/kw (#:key a #:rest r) r) #:a 1 #:b 2 3 4)) + (let ([f (lambda/kw (#:key a #:body r) r)]) + (t '(3) <= (f #:a 1 3) + '(3 4) <= (f #:a 1 3 4) + :rt-err: <= (f #:a 1 #:a 2 3) + :rt-err: <= (f #:a 1 #:a 2 3 4))) + (let ([f (lambda/kw (#:key a #:body r #:allow-duplicate-keys) r)]) + (t '(3) <= (f #:a 1 3) + '(3 4) <= (f #:a 1 3 4) + '(3) <= (f #:a 1 #:a 2 3) + '(3 4) <= (f #:a 1 #:a 2 3 4))) + (t '(#:a 1 #:b 2) <= ((lambda/kw (#:key a #:all-keys r #:allow-body) r) + #:a 1 #:b 2 3) + :rt-err: <= ((lambda/kw (#:key x y) (list x y)) #:x) + :rt-err: <= ((lambda/kw (#:key x y) (list x y)) #:x 1 #:x) + :rt-err: <= ((lambda/kw (#:key x y) (list x y)) #:x #:x #:x)) ;; optionals and keys (let ([f (lambda/kw (#:optional a b #:key c d) (list a b c d))]) - (t '(#f #f #f #f) f) - (t '(1 #f #f #f) f 1) - (t '(1 2 #f #f) f 1 2) - (t '(#:c #:d #f #f) f #:c #:d) - (t '(#:c 1 #f #f) f #:c 1) - (t '(1 2 #:d #f) f 1 2 #:c #:d) - (t '(#:c #:d #:d #f) f #:c #:d #:c #:d) - (t '(#:c 1 #:d #f) f #:c 1 #:c #:d)) + (t '(#f #f #f #f) <= (f) + '(1 #f #f #f) <= (f 1) + '(1 2 #f #f) <= (f 1 2) + '(#:c #:d #f #f) <= (f #:c #:d) + '(#:c 1 #f #f) <= (f #:c 1) + '(1 2 #:d #f) <= (f 1 2 #:c #:d) + '(#:c #:d #:d #f) <= (f #:c #:d #:c #:d) + '(#:c 1 #:d #f) <= (f #:c 1 #:c #:d))) ;; multi-level arg lists with #:body specs (let ([f (lambda/kw (#:key x y #:body (z)) (list x y z))]) - (t '(#f #f 3) f 3) - (t '(#f 2 3) f #:y 2 3) - (err/rt-test (f #:y 2)) - (err/rt-test (f #:y 2 3 4))) + (t (f 3) => '(#f #f 3) + (f #:y 2 3) => '(#f 2 3) + (f #:y 2) => :rt-err: + (f #:y 2 3 4) => :rt-err:)) (let ([f (lambda/kw (#:key x y #:body (z . r)) (list x y z r))]) - (t '(#f #f 3 ()) f 3) - (t '(#f 2 3 ()) f #:y 2 3) - (err/rt-test (f #:y 2)) - (t '(#f 2 3 (4)) f #:y 2 3 4)) + (t (f 3) => '(#f #f 3 ()) + (f #:y 2 3) => '(#f 2 3 ()) + (f #:y 2) => :rt-err: + (f #:y 2 3 4) => '(#f 2 3 (4)))) + (let ([f (lambda/kw (#:key x y #:body (a #:key (xx #:x #f) (yy #:y #f) + #:allow-duplicate-keys) + #:allow-duplicate-keys) + (list x y a xx yy))]) + (t '(1 #f 2 3 #f) <= (f #:x 1 2 #:x 3) + '(1 #:x 2 3 #:x) <= (f #:x 1 #:y #:x 2 #:x 3 #:y #:x) + '(1 #:x 2 3 #:x) <= (f #:x 1 #:y #:x #:x 11 2 #:x 3 #:y #:x) + '(1 #:x 2 3 #:x) <= (f #:x 1 #:y #:x 2 #:x 3 #:y #:x #:x 33) + '(1 #:x 2 3 #:x) <= (f #:x 1 #:y #:x #:x 11 2 #:x 3 #:y #:x #:x 33))) + (let ([f (lambda/kw (#:key x y #:body (a #:key (xx #:x #f) (yy #:y #f) + #:allow-duplicate-keys)) + (list x y a xx yy))]) + (t '(1 #f 2 3 #f) <= (f #:x 1 2 #:x 3) + '(1 #:x 2 3 #:x) <= (f #:x 1 #:y #:x 2 #:x 3 #:y #:x) + :rt-err: <= (f #:x 1 #:y #:x #:x 11 2 #:x 3 #:y #:x) + '(1 #:x 2 3 #:x) <= (f #:x 1 #:y #:x 2 #:x 3 #:y #:x #:x 33) + :rt-err: <= (f #:x 1 #:y #:x #:x 11 2 #:x 3 #:y #:x #:x 33))) + (let ([f (lambda/kw (#:key x y #:body (a #:key (xx #:x #f) (yy #:y #f)) + #:allow-duplicate-keys) + (list x y a xx yy))]) + (t '(1 #f 2 3 #f) <= (f #:x 1 2 #:x 3) + '(1 #:x 2 3 #:x) <= (f #:x 1 #:y #:x 2 #:x 3 #:y #:x) + '(1 #:x 2 3 #:x) <= (f #:x 1 #:y #:x #:x 11 2 #:x 3 #:y #:x) + :rt-err: <= (f #:x 1 #:y #:x 2 #:x 3 #:y #:x #:x 33) + :rt-err: <= (f #:x 1 #:y #:x #:x 11 2 #:x 3 #:y #:x #:x 33))) (let ([f (lambda/kw (#:key x y #:body (a #:key (xx #:x #f) (yy #:y #f))) (list x y a xx yy))]) - (t '(1 #f 2 3 #f) f #:x 1 2 #:x 3) - (t '(1 #:x 2 3 #:x) f #:x 1 #:y #:x #:x 11 2 #:x 3 #:y #:x #:x 33)) + (t '(1 #f 2 3 #f) <= (f #:x 1 2 #:x 3)) + (t '(1 #:x 2 3 #:x) <= (f #:x 1 #:y #:x 2 #:x 3 #:y #:x)) + (t :rt-err: <= (f #:x 1 #:y #:x #:x 11 2 #:x 3 #:y #:x)) + (t :rt-err: <= (f #:x 1 #:y #:x 2 #:x 3 #:y #:x #:x 33)) + (t :rt-err: <= (f #:x 1 #:y #:x #:x 11 2 #:x 3 #:y #:x #:x 33))) ;; make sure that internal definitions work (let ([f (lambda/kw (#:key x) (define xx x) xx)]) - (t #f f) - (t 1 f #:x 1)) + (t #f <= (f) + 1 <= (f #:x 1))) + + ;; test syntax errors + (t :st-err: <= (lambda/kw (x #:blah y) 1) + :st-err: <= (lambda/kw (x #:rest) 1) + :st-err: <= (lambda/kw (x #:key k #:key o) 1) + :st-err: <= (lambda/kw (x #:key k #:optional o) 1) + :st-err: <= (lambda/kw (x #:optional k #:optional o) 1) + :st-err: <= (lambda/kw (x #:rest r #:optional o) 1) + :st-err: <= (lambda/kw (x #:rest r #:forbid-other-keys #:allow-other-keys) 1) + :st-err: <= (lambda/kw (x #:rest r #:allow-other-keys #:forbid-other-keys) 1) + :st-err: <= (lambda/kw (x #:rest r1 #:rest r2) 1) + :st-err: <= (lambda/kw (x #:rest) 1) + :st-err: <= (lambda/kw (x #:rest r1 r2) 1) + :st-err: <= (lambda/kw (x #:body b) 1) + :st-err: <= (lambda/kw (x x) 1) + :st-err: <= (lambda/kw (x #:optional [x 1]) 1) + :st-err: <= (lambda/kw (x #:key [x 1]) 1) + :st-err: <= (lambda/kw (x #:rest x) 1) + :st-err: <= (lambda/kw (x #:body x) 1) + :st-err: <= (lambda/kw (x #:optional 3) 1) + :st-err: <= (lambda/kw (x #:optional "3") 1) + :st-err: <= (lambda/kw (x #:optional [(x) 3]) 1) + :st-err: <= (lambda/kw (x #:key 3) 1) + :st-err: <= (lambda/kw (x #:key "3") 1) + :st-err: <= (lambda/kw (x #:key [(y) 3]) 1) + :st-err: <= (lambda/kw (x #:key [x]) 1) + :st-err: <= (lambda/kw (x #:key [y 1 2]) 1) + :st-err: <= (lambda/kw (x #:key [y #:y 1 2]) 1) + :st-err: <= (lambda/kw (x #:rest 3) 1) + :st-err: <= (lambda/kw (x #:rest "3") 1) + :st-err: <= (lambda/kw (x #:rest (x)) 1) + :st-err: <= (lambda/kw (x #:body 3) 1) + :st-err: <= (lambda/kw (x #:key y #:body 3) 1) + :st-err: <= (lambda/kw (x #:body "3") 1) + :st-err: <= (lambda/kw (x #:key y #:body "3") 1) + :st-err: <= (lambda/kw (x #:body (x)) 1) + :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 #:rest-keys z) 1) + :st-err: <= (lambda/kw (x #:all-keys z) 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) + :st-err: <= (lambda/kw (x #:key y #:forbid-other-keys #:rest r #:allow-other-keys) 1) + :st-err: <= (lambda/kw (x #:key y z #:body (x)) x) + :st-err: <= (lambda/kw (#:key a #:body r #:forbid-body) r) + :st-err: <= (lambda/kw (#:key a #:other-keys r #:forbid-other-keys) r)) ) - -;; test syntax errors -(let ([st syntax-test]) - (st #'(lambda/kw (x #:blah y) 1)) - (st #'(lambda/kw (x #:rest) 1)) - (st #'(lambda/kw (x #:key k #:key o) 1)) - (st #'(lambda/kw (x #:key k #:optional o) 1)) - (st #'(lambda/kw (x #:optional k #:optional o) 1)) - (st #'(lambda/kw (x #:rest r #:optional o) 1)) - (st #'(lambda/kw (x #:rest r #:forbid-other-keys #:allow-other-keys) 1)) - (st #'(lambda/kw (x #:rest r #:allow-other-keys #:forbid-other-keys) 1)) - (st #'(lambda/kw (x #:rest r1 #:rest r2) 1)) - (st #'(lambda/kw (x #:rest) 1)) - (st #'(lambda/kw (x #:rest r1 r2) 1)) - (st #'(lambda/kw (x #:body b) 1)) - (st #'(lambda/kw (x x) 1)) - (st #'(lambda/kw (x #:optional [x 1]) 1)) - (st #'(lambda/kw (x #:key [x 1]) 1)) - (st #'(lambda/kw (x #:rest x) 1)) - (st #'(lambda/kw (x #:body x) 1)) - (st #'(lambda/kw (x #:optional 3) 1)) - (st #'(lambda/kw (x #:optional "3") 1)) - (st #'(lambda/kw (x #:optional [(x) 3]) 1)) - (st #'(lambda/kw (x #:key 3) 1)) - (st #'(lambda/kw (x #:key "3") 1)) - (st #'(lambda/kw (x #:key [(y) 3]) 1)) - (st #'(lambda/kw (x #:key [x]) 1)) - (st #'(lambda/kw (x #:key [y 1 2]) 1)) - (st #'(lambda/kw (x #:key [y #:y 1 2]) 1)) - (st #'(lambda/kw (x #:rest 3) 1)) - (st #'(lambda/kw (x #:rest "3") 1)) - (st #'(lambda/kw (x #:rest (x)) 1)) - (st #'(lambda/kw (x #:body 3) 1)) - (st #'(lambda/kw (x #:key y #:body 3) 1)) - (st #'(lambda/kw (x #:body "3") 1)) - (st #'(lambda/kw (x #:key y #:body "3") 1)) - (st #'(lambda/kw (x #:body (x)) 1)) - (st #'(lambda/kw (x #:body x #:allow-other-keys) 1)) - (st #'(lambda/kw (x #:optional ()) 1)) - (st #'(lambda/kw (x #:optional (x y z)) 1)) - (st #'(lambda/kw (x #:other-keys z) 1)) - (st #'(lambda/kw (x #:rest-keys z) 1)) - (st #'(lambda/kw (x #:all-keys z) 1)) - (st #'(lambda/kw (x #:key y #:allow-other-keys z) 1)) - (st #'(lambda/kw (x #:key y #:forbid-body z) 1)) - (st #'(lambda/kw (x #:key y #:allow-body #:rest r #:forbid-body) 1)) - (st #'(lambda/kw (x #:key y #:forbid-other-keys #:rest r #:allow-other-keys) 1)) - (st #'((lambda/kw (x #:key y z #:body (x)) x) 1)))