Add idtbl-chaperone and implement contracts on top of that.

This commit is contained in:
Eric Dobson 2012-06-23 20:20:11 -07:00 committed by Ryan Culpepper
parent 54b82871ab
commit 8f8bc76e39
2 changed files with 221 additions and 54 deletions

View File

@ -2,6 +2,7 @@
(require (for-syntax racket/base
racket/syntax)
racket/contract/base
racket/contract/combinator
racket/dict
(rename-in (except-in "private/id-table.rkt"
make-free-id-table
@ -49,7 +50,9 @@
[mutable-idtbl?
(format-id #'idtbl "mutable-~a?" (syntax-e #'idtbl))]
[immutable-idtbl?
(format-id #'idtbl "immutable-~a?" (syntax-e #'idtbl))])
(format-id #'idtbl "immutable-~a?" (syntax-e #'idtbl))]
[chaperone-idtbl
(format-id #'idtbl "chaperone-~a" (syntax-e #'idtbl))])
(define (s x) (format-id #'idtbl "~a~a" (syntax-e #'idtbl) x))
(with-syntax ([idtbl? (s '?)]
[idtbl-hash (s '-hash)]
@ -111,6 +114,131 @@
idtbl-iterate-key
idtbl-iterate-value))
(define-struct base-idtbl/c (dom rng immutable))
(define (idtbl/c-name ctc)
(apply
build-compound-type-name
'idtbl/c (base-idtbl/c-dom ctc) (base-idtbl/c-rng ctc)
(append
(if (flat-idtbl/c? ctc)
(list '#:flat? #t)
null)
(case (base-idtbl/c-immutable ctc)
[(dont-care) null]
[(#t)
(list '#:immutable #t)]
[(#f)
(list '#:immutable #f)]))))
(define-values (idtbl/c-dom-pos-proj
idtbl/c-dom-neg-proj
idtbl/c-rng-pos-proj
idtbl/c-rng-neg-proj)
(let ()
(define (proj acc location swap)
(lambda (ctc blame)
((contract-projection (acc ctc))
(blame-add-context blame "the keys of" #:swap swap))))
(values
(proj base-idtbl/c-dom "the keys of" #f)
(proj base-idtbl/c-dom "the keys of" #t)
(proj base-idtbl/c-rng "the values of" #f)
(proj base-idtbl/c-rng "the values of" #t))))
(define (idtbl/c-first-order ctc)
(define dom-ctc (base-idtbl/c-dom ctc))
(define rng-ctc (base-idtbl/c-rng ctc))
(define immutable (base-idtbl/c-immutable ctc))
(λ (val)
(and (idtbl? val)
(case immutable
[(#t) (immutable? val)]
[(#f) (not (immutable? val))]
[else #t])
(for/and ([(k v) (in-dict val)])
(and (contract-first-order-passes? dom-ctc k)
(contract-first-order-passes? rng-ctc v))))))
(define (check-idtbl/c ctc val blame)
(define immutable (base-idtbl/c-immutable ctc))
(unless (idtbl? val)
(raise-blame-error blame val
'(expected "a ~a," given: "~e") 'idtbl val))
(case immutable
[(#t)
(unless (immutable? val)
(raise-blame-error blame val
'(expected "an immutable ~a," given: "~e") 'idtbl val))]
[(#f)
(when (immutable? val)
(raise-blame-error blame val
'(expected "a mutable ~a," given: "~e") 'idtbl val))]
[(dont-care) (void)]))
(define ho-projection
(lambda (ctc)
(lambda (blame)
(lambda (b)
(define pos-dom-proj (idtbl/c-dom-pos-proj ctc blame))
(define neg-dom-proj (idtbl/c-dom-pos-proj ctc blame))
(define pos-rng-proj (idtbl/c-dom-pos-proj ctc blame))
(define neg-rng-proj (idtbl/c-dom-pos-proj ctc blame))
(lambda (tbl)
(check-idtbl/c ctc tbl blame)
(if (immutable? tbl)
(error 'idtbl/c "Not Yet implemented")
(chaperone-idtbl tbl
(λ (t k)
(values (neg-dom-proj k)
(λ (h k v)
(pos-rng-proj v))))
(λ (t k v)
(values (neg-dom-proj k)
(neg-rng-proj v)))
(λ (t k)
(neg-dom-proj k))
(λ (t k)
(pos-dom-proj k))
impersonator-prop:contracted ctc)))))))
(struct flat-idtbl/c base-idtbl/c ()
#:omit-define-syntaxes
#:property prop:flat-contract
(build-flat-contract-property
#:name idtbl/c-name
#:first-order idtbl/c-first-order
#:projection
(λ (ctc)
(λ (blame)
(λ (val)
(check-idtbl/c ctc val blame)
(define dom-proj (idtbl/c-dom-pos-proj ctc))
(define rng-proj (idtbl/c-rng-pos-proj ctc))
(for ([(k v) (in-dict val)])
(dom-proj k)
(rng-proj v))
val)))))
(struct chaperone-idtbl/c base-idtbl/c ()
#:omit-define-syntaxes
#:property prop:chaperone-contract
(build-chaperone-contract-property
#:name idtbl/c-name
#:first-order idtbl/c-first-order
#:projection ho-projection))
(struct impersonator-idtbl/c base-idtbl/c ()
#:omit-define-syntaxes
#:property prop:contract
(build-contract-property
#:name idtbl/c-name
#:first-order idtbl/c-first-order
#:projection ho-projection))
(struct mutable-idtbl mutable-idtbl* ()
#:property prop:dict/contract
(list idtbl-mutable-methods
@ -120,28 +248,18 @@
(list idtbl-immutable-methods
dict-contract-methods))
(define (mutable-idtbl/c value/c)
(struct/c mutable-idtbl
(hash/c any/c
(listof (cons/c any/c value/c))
#:immutable #f)
any/c))
(define (immutable-idtbl/c value/c)
(struct/c immutable-idtbl
(hash/c any/c
(listof (cons/c any/c value/c))
#:immutable #t)
any/c))
(define (idtbl/c value/c #:immutable (immutable 'dont-care))
(case immutable
((dont-care) (or/c (mutable-idtbl/c value/c)
(immutable-idtbl/c value/c)))
((#t) (immutable-idtbl/c value/c))
((#f) (mutable-idtbl/c value/c))))
(define (idtbl/c key/c value/c #:immutable (immutable 'dont-care))
(define key/ctc (coerce-contract 'idtbl/c key/c))
(define value/ctc (coerce-contract 'idtbl/c value/c))
(cond
((and (eq? immutable #t)
(flat-contract? key/ctc)
(flat-contract? value/ctc))
(flat-idtbl/c key/ctc value/ctc immutable))
((chaperone-contract? value/ctc)
(chaperone-idtbl/c key/ctc value/ctc immutable))
(else
(impersonator-idtbl/c key/ctc value/ctc immutable))))
(provide/contract
[make-idtbl
@ -179,7 +297,7 @@
[idtbl-for-each
(-> idtbl? (-> identifier? any/c any) any)]
[idtbl/c
(->* (contract?)
(->* (chaperone-contract? contract?)
(#:immutable (or/c 'dont-care #t #f))
contract?)]))))]))

View File

@ -9,6 +9,24 @@
;; where hash maps symbol => (listof (cons identifier value))
;; phase is a phase-level (integer or #f)
(define-values (prop:id-table-impersonator
id-table-impersonator?
id-table-impersonator-value)
(make-impersonator-property 'id-table-impersonator))
(define-values (id-table-imp-wrapped
id-table-imp-ref-wrapper
id-table-imp-set!-wrapper
id-table-imp-remove!-wrapper
id-table-imp-key-wrapper)
(let ((extractor (lambda (i)
(lambda (d)
(vector-ref (id-table-impersonator-value d) i)))))
(apply values (build-list 5 extractor))))
(define (make-id-table/constructor who init-dict phase make identifier->symbol identifier=?)
(let ([t (make (make-hasheq) phase)])
(for ([(k v) (in-dict init-dict)])
@ -25,34 +43,47 @@
(id-table-set/constructor who t k v make identifier->symbol identifier=?)))
(define (id-table-ref who d id default identifier->symbol identifier=?)
(let ([phase (id-table-phase d)])
(let ([i (for/first ([i (in-list (hash-ref (id-table-hash d)
(identifier->symbol id phase)
null))]
#:when (identifier=? (car i) id phase))
i)])
(if i
(cdr i)
(cond [(eq? default not-given)
(error who "no mapping for ~e" id)]
[(procedure? default) (default)]
[else default])))))
(if (id-table-impersonator? d)
(let-values (((new-id return-wrapper)
((id-table-imp-ref-wrapper d) d id)))
(return-wrapper
(id-table-ref (id-table-imp-wrapped d) new-id default)))
(let ([phase (id-table-phase d)])
(let ([i (for/first ([i (in-list (hash-ref (id-table-hash d)
(identifier->symbol id phase)
null))]
#:when (identifier=? (car i) id phase))
i)])
(if i
(cdr i)
(cond [(eq? default not-given)
(error who "no mapping for ~e" id)]
[(procedure? default) (default)]
[else default]))))))
(define (id-table-set! who d id v identifier->symbol identifier=?)
(let* ([phase (id-table-phase d)]
[sym (identifier->symbol id phase)]
[l (hash-ref (id-table-hash d) sym null)]
[new-l (alist-set identifier=? phase l id v)])
(hash-set! (id-table-hash d) sym new-l)))
(if (id-table-impersonator? d)
(let-values (((new-id new-v)
((id-table-imp-set!-wrapper d) d id v)))
(id-table-set! (id-table-imp-wrapped d) new-id new-v))
(let* ([phase (id-table-phase d)]
[sym (identifier->symbol id phase)]
[l (hash-ref (id-table-hash d) sym null)]
[new-l (alist-set identifier=? phase l id v)])
(hash-set! (id-table-hash d) sym new-l))))
(define (id-table-remove! who d id identifier->symbol identifier=?)
(let* ([phase (id-table-phase d)]
[sym (identifier->symbol id phase)]
[l (hash-ref (id-table-hash d) sym null)]
[newl (alist-remove identifier=? phase l id)])
(if (pair? newl)
(hash-set! (id-table-hash d) sym newl)
(hash-remove! (id-table-hash d) sym))))
(if (id-table-impersonator? d)
(let ((new-id ((id-table-imp-remove!-wrapper d) d id)))
(id-table-remove! (id-table-imp-wrapped d) new-id))
(let* ([phase (id-table-phase d)]
[sym (identifier->symbol id phase)]
[l (hash-ref (id-table-hash d) sym null)]
[newl (alist-remove identifier=? phase l id)])
(if (pair? newl)
(hash-set! (id-table-hash d) sym newl)
(hash-remove! (id-table-hash d) sym)))))
(define (id-table-set/constructor who d id v constructor identifier->symbol identifier=?)
(let* ([phase (id-table-phase d)]
@ -120,12 +151,19 @@ Notes (FIXME?):
(make-id-table-iter d a2 b2 b2))))))))
(define (id-table-iterate-key who d pos)
(let-values ([(h a br b) (rebase-iter who d pos)])
(caar b)))
(if (id-table-impersonator? d)
(let ((wrapper (id-table-imp-key-wrapper d)))
(wrapper (id-table-iterate-key (id-table-imp-wrapped d) pos)))
(let-values ([(h a br b) (rebase-iter who d pos)])
(caar b))))
;; TODO figure out how to provide API compatibility with hashes with regards
;; to iterate-key and provide the checking from rebase-iter
(define (id-table-iterate-value who d pos)
(let-values ([(h a br b) (rebase-iter who d pos)])
(cdar b)))
(if (id-table-impersonator? d)
(id-table-ref d (id-table-iterate-key d pos))
(let-values ([(h a br b) (rebase-iter who d pos)])
(cdar b))))
(define (rebase-iter who d pos)
(unless (eq? d (id-table-iter-d pos))
@ -206,7 +244,9 @@ Notes (FIXME?):
[mutable-idtbl?
(format-id #'idtbl "mutable-~a?" (syntax-e #'idtbl))]
[immutable-idtbl?
(format-id #'idtbl "immutable-~a?" (syntax-e #'idtbl))])
(format-id #'idtbl "immutable-~a?" (syntax-e #'idtbl))]
[chaperone-idtbl
(format-id #'idtbl "chaperone-~a" (syntax-e #'idtbl))])
(define (s x) (format-id #'idtbl "~a~a" (syntax-e #'idtbl) x))
(with-syntax ([idtbl? (s '?)]
[idtbl-ref (s '-ref)]
@ -228,7 +268,6 @@ Notes (FIXME?):
#'(begin
;; Struct defs at end, so that dict methods can refer to earlier procs
(define (make-idtbl [init-dict null]
#:phase [phase (syntax-local-phase-level)])
(make-id-table/constructor 'make-idtbl init-dict phase mutable-idtbl
@ -239,6 +278,14 @@ Notes (FIXME?):
(make-immutable-id-table/constructor 'make-immutable-idtbl init-dict phase immutable-idtbl
identifier->symbol identifier=?))
(define (chaperone-idtbl d ref set! remove! key . args)
(apply chaperone-struct d
id-table-phase (lambda (d p) p)
prop:id-table-impersonator
(vector d ref set! remove! key)
args))
(define (idtbl-ref d id [default not-given])
(id-table-ref 'idtbl-ref d id default identifier->symbol identifier=?))
(define (idtbl-set! d id v)
@ -249,6 +296,7 @@ Notes (FIXME?):
(idtbl-set/constructor d id v immutable-idtbl))
(define (idtbl-remove! d id)
(id-table-remove! 'idtbl-remove! d id identifier->symbol identifier=?))
(define (idtbl-remove/constructor d id constructor)
(id-table-remove/constructor 'idtbl-remove d id constructor identifier->symbol identifier=?))
(define (idtbl-remove d id)
@ -317,6 +365,7 @@ Notes (FIXME?):
idtbl-for-each
;; just for use/extension by syntax/id-table
chaperone-idtbl
idtbl-set/constructor
idtbl-remove/constructor
idtbl-mutable-methods