forgot to add a file to the last commit
This commit is contained in:
parent
e28b63e056
commit
06598dd5c7
230
racket/collects/racket/contract/private/arity-checking.rkt
Normal file
230
racket/collects/racket/contract/private/arity-checking.rkt
Normal file
|
@ -0,0 +1,230 @@
|
|||
#lang racket/base
|
||||
(require "blame.rkt"
|
||||
"kwd-info-struct.rkt")
|
||||
|
||||
(provide do-arity-checking
|
||||
|
||||
;; for test suites
|
||||
arity-as-string
|
||||
raw-arity-as-string)
|
||||
|
||||
(define (do-arity-checking blame val
|
||||
->stct-doms
|
||||
->stct-rest
|
||||
->stct-min-arity
|
||||
->stct-kwd-infos)
|
||||
(let/ec k
|
||||
(unless (procedure? val)
|
||||
(maybe-err
|
||||
k blame
|
||||
(λ (neg-party)
|
||||
(raise-blame-error blame #:missing-party neg-party val
|
||||
'(expected: "a procedure" given: "~e")
|
||||
val))))
|
||||
(define-values (actual-mandatory-kwds actual-optional-kwds) (procedure-keywords val))
|
||||
(define arity (if (list? (procedure-arity val))
|
||||
(procedure-arity val)
|
||||
(list (procedure-arity val))))
|
||||
(define expected-number-of-non-keyword-args (length ->stct-doms))
|
||||
(define matching-arity?
|
||||
(and (for/or ([a (in-list arity)])
|
||||
(or (equal? expected-number-of-non-keyword-args a)
|
||||
(and (arity-at-least? a)
|
||||
(>= expected-number-of-non-keyword-args (arity-at-least-value a)))))
|
||||
(if ->stct-rest
|
||||
(let ([lst (car (reverse arity))])
|
||||
(and (arity-at-least? lst)
|
||||
(<= (arity-at-least-value lst) ->stct-min-arity)))
|
||||
#t)))
|
||||
(unless matching-arity?
|
||||
(maybe-err
|
||||
k blame
|
||||
(λ (neg-party)
|
||||
(raise-blame-error blame #:missing-party neg-party val
|
||||
'(expected:
|
||||
"a procedure that accepts ~a non-keyword argument~a~a"
|
||||
given: "~e"
|
||||
"\n ~a")
|
||||
expected-number-of-non-keyword-args
|
||||
(if (= expected-number-of-non-keyword-args 1) "" "s")
|
||||
(if ->stct-rest
|
||||
" and arbitrarily many more"
|
||||
"")
|
||||
val
|
||||
(arity-as-string val)))))
|
||||
|
||||
(define (should-have-supplied kwd)
|
||||
(maybe-err
|
||||
k blame
|
||||
(λ (neg-party)
|
||||
(raise-blame-error blame #:missing-party neg-party val
|
||||
'(expected:
|
||||
"a procedure that accepts the ~a keyword argument"
|
||||
given: "~e"
|
||||
"\n ~a")
|
||||
kwd
|
||||
val
|
||||
(arity-as-string val)))))
|
||||
|
||||
(define (should-not-have-supplied kwd)
|
||||
(maybe-err
|
||||
k blame
|
||||
(λ (neg-party)
|
||||
(raise-blame-error blame #:missing-party neg-party val
|
||||
'(expected:
|
||||
"a procedure that does not require the ~a keyword argument"
|
||||
given: "~e"
|
||||
"\n ~a")
|
||||
kwd
|
||||
val
|
||||
(arity-as-string val)))))
|
||||
|
||||
(when actual-optional-kwds ;; when all kwds are okay, no checking required
|
||||
(let loop ([mandatory-kwds actual-mandatory-kwds]
|
||||
[all-kwds actual-optional-kwds]
|
||||
[kwd-infos ->stct-kwd-infos])
|
||||
(cond
|
||||
[(null? kwd-infos)
|
||||
(unless (null? mandatory-kwds)
|
||||
(should-not-have-supplied (car mandatory-kwds)))]
|
||||
[else
|
||||
(define kwd-info (car kwd-infos))
|
||||
(define-values (mandatory? kwd new-mandatory-kwds new-all-kwds)
|
||||
(cond
|
||||
[(null? all-kwds)
|
||||
(should-have-supplied (kwd-info-kwd kwd-info))]
|
||||
[else
|
||||
(define mandatory?
|
||||
(and (pair? mandatory-kwds)
|
||||
(equal? (car mandatory-kwds) (car all-kwds))))
|
||||
(values mandatory?
|
||||
(car all-kwds)
|
||||
(if mandatory?
|
||||
(cdr mandatory-kwds)
|
||||
mandatory-kwds)
|
||||
(cdr all-kwds))]))
|
||||
(cond
|
||||
[(equal? kwd (kwd-info-kwd kwd-info))
|
||||
(when (and (not (kwd-info-mandatory? kwd-info))
|
||||
mandatory?)
|
||||
(maybe-err
|
||||
k blame
|
||||
(λ (neg-party)
|
||||
(raise-blame-error
|
||||
blame #:missing-party neg-party val
|
||||
'(expected:
|
||||
"a procedure that optionally accepts the keyword ~a (this one is mandatory)"
|
||||
given: "~e"
|
||||
"\n ~a")
|
||||
val
|
||||
kwd
|
||||
(arity-as-string val)))))
|
||||
(loop new-mandatory-kwds new-all-kwds (cdr kwd-infos))]
|
||||
[(keyword<? kwd (kwd-info-kwd kwd-info))
|
||||
(when mandatory?
|
||||
(should-not-have-supplied kwd))
|
||||
(loop new-mandatory-kwds new-all-kwds kwd-infos)]
|
||||
[else
|
||||
(loop new-mandatory-kwds new-all-kwds kwd-infos)])])))
|
||||
|
||||
#f))
|
||||
|
||||
|
||||
(define (arity-as-string v)
|
||||
(define prefix (if (object-name v)
|
||||
(format "~a accepts: " (object-name v))
|
||||
(format "accepts: ")))
|
||||
(string-append prefix (raw-arity-as-string v)))
|
||||
|
||||
(define (raw-arity-as-string v)
|
||||
(define ar (procedure-arity v))
|
||||
(define (plural n) (if (= n 1) "" "s"))
|
||||
(define-values (man-kwds all-kwds) (procedure-keywords v))
|
||||
(define opt-kwds (if all-kwds (remove* man-kwds all-kwds) #f))
|
||||
(define normal-str (if (null? all-kwds) "" "normal "))
|
||||
(define normal-args
|
||||
(cond
|
||||
[(null? ar) "no arguments"]
|
||||
[(number? ar) (format "~a ~aargument~a" ar normal-str (plural ar))]
|
||||
[(arity-at-least? ar) (format "~a or arbitrarily many more ~aarguments"
|
||||
(arity-at-least-value ar)
|
||||
normal-str)]
|
||||
[else
|
||||
(define comma
|
||||
(if (and (= (length ar) 2)
|
||||
(not (arity-at-least? (list-ref ar 1))))
|
||||
""
|
||||
","))
|
||||
(apply
|
||||
string-append
|
||||
(let loop ([ar ar])
|
||||
(cond
|
||||
[(null? (cdr ar))
|
||||
(define v (car ar))
|
||||
(cond
|
||||
[(arity-at-least? v)
|
||||
(list
|
||||
(format "~a, or arbitrarily many more ~aarguments"
|
||||
(arity-at-least-value v)
|
||||
normal-str))]
|
||||
[else
|
||||
(list (format "or ~a ~aarguments" v normal-str))])]
|
||||
[else
|
||||
(cons (format "~a~a " (car ar) comma)
|
||||
(loop (cdr ar)))])))]))
|
||||
(cond
|
||||
[(and (null? man-kwds) (null? opt-kwds))
|
||||
normal-args]
|
||||
[(and (null? man-kwds) (not opt-kwds))
|
||||
(string-append normal-args " and optionally any keyword")]
|
||||
[(and (null? man-kwds) (pair? opt-kwds))
|
||||
(string-append normal-args
|
||||
" and the optional keyword"
|
||||
(plural (length opt-kwds))
|
||||
" "
|
||||
(kwd-list-as-string opt-kwds))]
|
||||
[(and (pair? man-kwds) (not opt-kwds))
|
||||
(string-append normal-args
|
||||
", the mandatory keyword"
|
||||
(plural (length man-kwds))
|
||||
" "
|
||||
(kwd-list-as-string man-kwds)
|
||||
", and optionally any keyword")]
|
||||
[(and (pair? man-kwds) (null? opt-kwds))
|
||||
(string-append normal-args
|
||||
" and the mandatory keyword"
|
||||
(plural (length man-kwds))
|
||||
" "
|
||||
(kwd-list-as-string man-kwds))]
|
||||
[(and (pair? man-kwds) (pair? opt-kwds))
|
||||
(string-append normal-args
|
||||
", the mandatory keyword"
|
||||
(plural (length man-kwds))
|
||||
" "
|
||||
(kwd-list-as-string man-kwds)
|
||||
", and the optional keyword"
|
||||
(plural (length opt-kwds))
|
||||
" "
|
||||
(kwd-list-as-string opt-kwds))]))
|
||||
|
||||
(define (kwd-list-as-string kwds)
|
||||
(cond
|
||||
[(null? (cdr kwds))
|
||||
(format "~a" (list-ref kwds 0))]
|
||||
[(null? (cddr kwds))
|
||||
(format "~a and ~a" (list-ref kwds 0) (list-ref kwds 1))]
|
||||
[else
|
||||
(apply
|
||||
string-append
|
||||
(let loop ([kwds kwds])
|
||||
(cond
|
||||
[(null? (cdr kwds))
|
||||
(list (format "and ~a" (car kwds)))]
|
||||
[else
|
||||
(cons (format "~a, " (car kwds))
|
||||
(loop (cdr kwds)))])))]))
|
||||
|
||||
(define (maybe-err k blame neg-accepter)
|
||||
(if (blame-original? blame)
|
||||
(neg-accepter #f)
|
||||
(k neg-accepter)))
|
Loading…
Reference in New Issue
Block a user