Code and test fixes for new chaperone-based hash/c contracts.
This commit is contained in:
parent
f5b62ececd
commit
d2b3ee8892
|
@ -2,107 +2,130 @@
|
||||||
|
|
||||||
(require "guts.ss")
|
(require "guts.ss")
|
||||||
|
|
||||||
(provide hash/c)
|
(provide (rename-out [build-hash/c hash/c]))
|
||||||
|
|
||||||
(define (hash/c dom rng #:immutable [immutable 'dont-care])
|
(define (build-hash/c dom rng #:immutable [immutable 'dont-care] #:flat? [flat? #f])
|
||||||
(unless (memq immutable '(#t #f dont-care))
|
(unless (memq immutable '(#t #f dont-care))
|
||||||
(error 'hash/c "expected #:immutable argument to be either #t, #f, or 'dont-care, got ~s" immutable))
|
(error 'hash/c "expected #:immutable argument to be either #t, #f, or 'dont-care, got ~s" immutable))
|
||||||
(cond
|
(let ([dom-ctc (if flat?
|
||||||
[(eq? immutable #t)
|
(coerce-flat-contract 'hash/c dom)
|
||||||
(make-immutable-hash/c (coerce-contract 'hash/c dom)
|
(coerce-contract 'hash/c dom))]
|
||||||
(coerce-contract 'hash/c rng))]
|
[rng-ctc (if flat?
|
||||||
[else
|
|
||||||
(make-hash/c (coerce-flat-contract 'hash/c dom)
|
|
||||||
(coerce-flat-contract 'hash/c rng)
|
(coerce-flat-contract 'hash/c rng)
|
||||||
immutable)]))
|
(coerce-contract 'hash/c rng))])
|
||||||
|
(if (or flat?
|
||||||
|
(and (eq? immutable #t)
|
||||||
|
(flat-contract? dom-ctc)
|
||||||
|
(flat-contract? rng-ctc)))
|
||||||
|
(make-flat-hash/c dom-ctc rng-ctc immutable)
|
||||||
|
(make-ho-hash/c dom-ctc rng-ctc immutable))))
|
||||||
|
|
||||||
;; hash-test : hash/c -> any -> bool
|
(define (hash/c-first-order ctc)
|
||||||
(define (hash-test ctc)
|
(let ([dom-ctc (hash/c-dom ctc)]
|
||||||
(let ([dom-proc (flat-contract-predicate (hash/c-dom ctc))]
|
[rng-ctc (hash/c-rng ctc)]
|
||||||
[rng-proc (flat-contract-predicate (hash/c-rng ctc))]
|
[immutable (hash/c-immutable ctc)]
|
||||||
[immutable (hash/c-immutable ctc)])
|
[flat? (flat-hash/c? ctc)])
|
||||||
(λ (val)
|
(λ (val #:blame [blame #f])
|
||||||
(and (hash? val)
|
(let/ec return
|
||||||
|
(define (fail . args)
|
||||||
|
(if blame
|
||||||
|
(apply raise-blame-error blame val args)
|
||||||
|
(return #f)))
|
||||||
|
(unless (hash? val)
|
||||||
|
(fail "expected a hash, got ~a" val))
|
||||||
(case immutable
|
(case immutable
|
||||||
[(#t) (immutable? val)]
|
[(#t)
|
||||||
[(#f) (not (immutable? val))]
|
(unless (immutable? val)
|
||||||
[(dont-care) #t])
|
(fail "expected an immutable hash, got ~a" val))]
|
||||||
(let/ec k
|
[(#f)
|
||||||
(hash-for-each
|
(when (immutable? val)
|
||||||
val
|
(fail "expected an mutable hash, got ~a" val))]
|
||||||
(λ (dom rng)
|
[(dont-care) (void)])
|
||||||
(unless (dom-proc dom) (k #f))
|
(when (or flat? (immutable? val))
|
||||||
(unless (rng-proc rng) (k #f))))
|
(for ([(k v) (in-hash val)])
|
||||||
#t)))))
|
(if blame
|
||||||
|
(begin (((contract-projection dom-ctc) blame) k)
|
||||||
|
(((contract-projection rng-ctc) blame) v)
|
||||||
|
(void))
|
||||||
|
(unless (and (contract-first-order-passes? dom-ctc k)
|
||||||
|
(contract-first-order-passes? rng-ctc v))
|
||||||
|
(fail)))))
|
||||||
|
#t))))
|
||||||
|
|
||||||
(define-struct hash/c (dom rng immutable)
|
(define (hash/c-name ctc)
|
||||||
|
(apply
|
||||||
|
build-compound-type-name
|
||||||
|
'hash/c (hash/c-dom ctc) (hash/c-rng ctc)
|
||||||
|
(append
|
||||||
|
(if (and (flat-hash/c? ctc)
|
||||||
|
(not (eq? (hash/c-immutable ctc) #t)))
|
||||||
|
(list '#:flat? #t)
|
||||||
|
null)
|
||||||
|
(case (hash/c-immutable ctc)
|
||||||
|
[(dont-care) null]
|
||||||
|
[(#t)
|
||||||
|
(list '#:immutable #t)]
|
||||||
|
[(#f)
|
||||||
|
(list '#:immutable #f)]))))
|
||||||
|
|
||||||
|
(define-struct hash/c (dom rng immutable))
|
||||||
|
|
||||||
|
(define-struct (flat-hash/c hash/c) ()
|
||||||
#:omit-define-syntaxes
|
#:omit-define-syntaxes
|
||||||
|
|
||||||
#:property prop:flat-contract
|
#:property prop:flat-contract
|
||||||
(build-flat-contract-property
|
(build-flat-contract-property
|
||||||
#:first-order hash-test
|
#:name hash/c-name
|
||||||
|
#:first-order hash/c-first-order
|
||||||
|
|
||||||
|
#:projection
|
||||||
|
(λ (ctc)
|
||||||
|
(λ (blame)
|
||||||
|
(λ (val)
|
||||||
|
((hash/c-first-order ctc) val #:blame blame)
|
||||||
|
val)))))
|
||||||
|
|
||||||
|
(define-struct (ho-hash/c hash/c) ()
|
||||||
|
#:omit-define-syntaxes
|
||||||
|
|
||||||
|
#:property prop:contract
|
||||||
|
(build-contract-property
|
||||||
|
#:name hash/c-name
|
||||||
|
#:first-order hash/c-first-order
|
||||||
|
|
||||||
#:projection
|
#:projection
|
||||||
(λ (ctc)
|
(λ (ctc)
|
||||||
(let ([dom-proc (contract-projection (hash/c-dom ctc))]
|
(let ([dom-proc (contract-projection (hash/c-dom ctc))]
|
||||||
[rng-proc (contract-projection (hash/c-rng ctc))]
|
[rng-proc (contract-projection (hash/c-rng ctc))]
|
||||||
[immutable (hash/c-immutable ctc)])
|
[immutable (hash/c-immutable ctc)])
|
||||||
(λ (blame)
|
(λ (blame)
|
||||||
(let ([partial-dom-contract (dom-proc blame)]
|
(let ([pos-dom-proj (dom-proc blame)]
|
||||||
[partial-rng-contract (rng-proc blame)])
|
[neg-dom-proj (dom-proc (blame-swap blame))]
|
||||||
|
[pos-rng-proj (rng-proc blame)]
|
||||||
|
[neg-rng-proj (rng-proc (blame-swap blame))])
|
||||||
(λ (val)
|
(λ (val)
|
||||||
(unless (hash? val)
|
((hash/c-first-order ctc) val #:blame blame)
|
||||||
(raise-blame-error blame val "expected a hash, got ~a" val))
|
|
||||||
(case immutable
|
|
||||||
[(#t) (unless (immutable? val)
|
|
||||||
(raise-blame-error blame val
|
|
||||||
"expected an immutable hash, got ~a" val))]
|
|
||||||
[(#f) (when (immutable? val)
|
|
||||||
(raise-blame-error blame val
|
|
||||||
"expected a mutable hash, got ~a" val))]
|
|
||||||
[(dont-care) (void)])
|
|
||||||
|
|
||||||
(hash-for-each
|
(if (immutable? val)
|
||||||
|
(let ([hash-maker
|
||||||
|
(cond
|
||||||
|
[(hash-equal? val) make-immutable-hash]
|
||||||
|
[(hash-eqv? val) make-immutable-hasheqv]
|
||||||
|
[(hash-eq? val) make-immutable-hasheq])])
|
||||||
|
(hash-maker
|
||||||
|
(for/list ([(k v) (in-hash val)])
|
||||||
|
(cons (pos-dom-proj k)
|
||||||
|
(pos-rng-proj v)))))
|
||||||
|
(proxy-hash
|
||||||
val
|
val
|
||||||
(λ (key val)
|
(λ (h k)
|
||||||
(partial-dom-contract key)
|
(values (neg-dom-proj k)
|
||||||
(partial-rng-contract val)))
|
(λ (h k v)
|
||||||
|
(pos-rng-proj v))))
|
||||||
val)))))
|
(λ (h k v)
|
||||||
|
(values (neg-dom-proj k)
|
||||||
#:name
|
(neg-rng-proj v)))
|
||||||
(λ (ctc) (apply
|
(λ (h k)
|
||||||
build-compound-type-name
|
(neg-dom-proj k))
|
||||||
'hash/c (hash/c-dom ctc) (hash/c-rng ctc)
|
(λ (h k)
|
||||||
(if (eq? 'dont-care (hash/c-immutable ctc))
|
(pos-dom-proj k)))))))))))
|
||||||
'()
|
|
||||||
(list '#:immutable (hash/c-immutable ctc)))))))
|
|
||||||
|
|
||||||
(define-struct immutable-hash/c (dom rng)
|
|
||||||
#:omit-define-syntaxes
|
|
||||||
|
|
||||||
#:property prop:contract
|
|
||||||
(build-contract-property
|
|
||||||
#:first-order (λ (ctc) (λ (val) (and (hash? val) (immutable? val))))
|
|
||||||
#:projection
|
|
||||||
(λ (ctc)
|
|
||||||
(let ([dom-proc (contract-projection (immutable-hash/c-dom ctc))]
|
|
||||||
[rng-proc (contract-projection (immutable-hash/c-rng ctc))])
|
|
||||||
(λ (blame)
|
|
||||||
(let ([partial-dom-contract (dom-proc blame)]
|
|
||||||
[partial-rng-contract (rng-proc blame)])
|
|
||||||
(λ (val)
|
|
||||||
(unless (and (hash? val)
|
|
||||||
(immutable? val))
|
|
||||||
(raise-blame-error blame val
|
|
||||||
"expected an immutable hash"))
|
|
||||||
(make-immutable-hash
|
|
||||||
(hash-map
|
|
||||||
val
|
|
||||||
(λ (k v)
|
|
||||||
(cons (partial-dom-contract k)
|
|
||||||
(partial-rng-contract v))))))))))
|
|
||||||
|
|
||||||
#:name
|
|
||||||
(λ (ctc) (build-compound-type-name
|
|
||||||
'hash/c (immutable-hash/c-dom ctc) (immutable-hash/c-rng ctc)
|
|
||||||
'#:immutable #t))))
|
|
||||||
|
|
|
@ -3531,6 +3531,46 @@
|
||||||
'pos
|
'pos
|
||||||
'neg))
|
'neg))
|
||||||
|
|
||||||
|
(test/spec-passed
|
||||||
|
'hash/c1b
|
||||||
|
'(contract (hash/c symbol? boolean? #:flat? #t)
|
||||||
|
(make-hash)
|
||||||
|
'pos
|
||||||
|
'neg))
|
||||||
|
|
||||||
|
(test/spec-passed
|
||||||
|
'hash/c1c
|
||||||
|
'(let ([h (contract (hash/c symbol? boolean?)
|
||||||
|
(make-hash)
|
||||||
|
'pos
|
||||||
|
'neg)])
|
||||||
|
(hash-set! h 'x #t)
|
||||||
|
(hash-ref h 'x)))
|
||||||
|
|
||||||
|
(test/neg-blame
|
||||||
|
'hash/c1d
|
||||||
|
'(let ([h (contract (hash/c symbol? boolean?)
|
||||||
|
(make-hash)
|
||||||
|
'pos
|
||||||
|
'neg)])
|
||||||
|
(hash-set! h 3 #t)))
|
||||||
|
|
||||||
|
(test/neg-blame
|
||||||
|
'hash/c1e
|
||||||
|
'(let ([h (contract (hash/c symbol? boolean?)
|
||||||
|
(make-hash)
|
||||||
|
'pos
|
||||||
|
'neg)])
|
||||||
|
(hash-set! h 'x 3)))
|
||||||
|
|
||||||
|
(test/neg-blame
|
||||||
|
'hash/c1f
|
||||||
|
'(let ([h (contract (hash/c symbol? boolean?)
|
||||||
|
(make-hash)
|
||||||
|
'pos
|
||||||
|
'neg)])
|
||||||
|
(hash-ref h 3)))
|
||||||
|
|
||||||
(test/spec-passed
|
(test/spec-passed
|
||||||
'hash/c2
|
'hash/c2
|
||||||
'(contract (hash/c symbol? boolean?)
|
'(contract (hash/c symbol? boolean?)
|
||||||
|
@ -8781,9 +8821,23 @@ so that propagation occurs.
|
||||||
(define-struct s (a b))
|
(define-struct s (a b))
|
||||||
(struct/c s any/c any/c)))
|
(struct/c s any/c any/c)))
|
||||||
|
|
||||||
(ctest #t flat-contract? (hash/c any/c any/c #:immutable #f))
|
(ctest #t contract? (hash/c any/c any/c #:immutable #f))
|
||||||
(ctest #f flat-contract? (hash/c any/c any/c #:immutable #t))
|
(ctest #t flat-contract? (hash/c any/c any/c #:immutable #f #:flat? #t))
|
||||||
(ctest #t flat-contract? (hash/c any/c any/c))
|
|
||||||
|
(ctest #t flat-contract? (hash/c any/c any/c #:immutable #t))
|
||||||
|
(ctest #t flat-contract? (hash/c any/c any/c #:immutable #t #:flat? #t))
|
||||||
|
|
||||||
|
(ctest #t contract? (hash/c any/c any/c))
|
||||||
|
(ctest #t flat-contract? (hash/c any/c any/c #:flat? #t))
|
||||||
|
|
||||||
|
(ctest #t contract? (hash/c number? (-> number? number?) #:immutable #f))
|
||||||
|
(ctest #f flat-contract? (hash/c number? (-> number? number?) #:immutable #f))
|
||||||
|
|
||||||
|
(ctest #t contract? (hash/c number? (-> number? number?) #:immutable #t))
|
||||||
|
(ctest #f flat-contract? (hash/c number? (-> number? number?) #:immutable #t))
|
||||||
|
|
||||||
|
(ctest #t contract? (hash/c number? (-> number? number?)))
|
||||||
|
(ctest #f flat-contract? (hash/c number? (-> number? number?)))
|
||||||
|
|
||||||
(ctest #t contract? 1)
|
(ctest #t contract? 1)
|
||||||
(ctest #t contract? (-> 1 1))
|
(ctest #t contract? (-> 1 1))
|
||||||
|
@ -8887,15 +8941,15 @@ so that propagation occurs.
|
||||||
'(1 2 3 4)
|
'(1 2 3 4)
|
||||||
'(1 2 3))
|
'(1 2 3))
|
||||||
|
|
||||||
(test-flat-contract '(hash/c symbol? boolean?) (make-hash) 1)
|
(test-flat-contract '(hash/c symbol? boolean? #:flat? #t) (make-hash) 1)
|
||||||
(test-flat-contract '(hash/c symbol? boolean?)
|
(test-flat-contract '(hash/c symbol? boolean? #:flat? #t)
|
||||||
(let ([ht (make-hash)])
|
(let ([ht (make-hash)])
|
||||||
(hash-set! ht 'x #t)
|
(hash-set! ht 'x #t)
|
||||||
ht)
|
ht)
|
||||||
(let ([ht (make-hash)])
|
(let ([ht (make-hash)])
|
||||||
(hash-set! ht 'x 1)
|
(hash-set! ht 'x 1)
|
||||||
ht))
|
ht))
|
||||||
(test-flat-contract '(hash/c symbol? boolean?)
|
(test-flat-contract '(hash/c symbol? boolean? #:flat? #t)
|
||||||
(let ([ht (make-hash)])
|
(let ([ht (make-hash)])
|
||||||
(hash-set! ht 'x #t)
|
(hash-set! ht 'x #t)
|
||||||
ht)
|
ht)
|
||||||
|
@ -9497,12 +9551,16 @@ so that propagation occurs.
|
||||||
|
|
||||||
(ctest #t contract-first-order-passes? (hash/c any/c any/c) (make-hash))
|
(ctest #t contract-first-order-passes? (hash/c any/c any/c) (make-hash))
|
||||||
(ctest #f contract-first-order-passes? (hash/c any/c any/c) #f)
|
(ctest #f contract-first-order-passes? (hash/c any/c any/c) #f)
|
||||||
(ctest #f contract-first-order-passes? (hash/c symbol? boolean?) (let ([ht (make-hash)])
|
(ctest #t contract-first-order-passes? (hash/c symbol? boolean?) (let ([ht (make-hash)])
|
||||||
(hash-set! ht 'x 1)
|
(hash-set! ht 'x 1)
|
||||||
ht))
|
ht))
|
||||||
(ctest #f contract-first-order-passes? (hash/c symbol? boolean?) (let ([ht (make-hash)])
|
(ctest #f contract-first-order-passes? (hash/c symbol? boolean? #:flat? #t)
|
||||||
|
(let ([ht (make-hash)]) (hash-set! ht 'x 1) ht))
|
||||||
|
(ctest #t contract-first-order-passes? (hash/c symbol? boolean?) (let ([ht (make-hash)])
|
||||||
(hash-set! ht 1 #f)
|
(hash-set! ht 1 #f)
|
||||||
ht))
|
ht))
|
||||||
|
(ctest #f contract-first-order-passes? (hash/c symbol? boolean? #:flat? #t)
|
||||||
|
(let ([ht (make-hash)]) (hash-set! ht 1 #f) ht))
|
||||||
(ctest #t contract-first-order-passes? (hash/c symbol? boolean?) (let ([ht (make-hash)])
|
(ctest #t contract-first-order-passes? (hash/c symbol? boolean?) (let ([ht (make-hash)])
|
||||||
(hash-set! ht 'x #t)
|
(hash-set! ht 'x #t)
|
||||||
ht))
|
ht))
|
||||||
|
|
Loading…
Reference in New Issue
Block a user