#lang racket/base (require (for-syntax racket/base racket/syntax) (for-meta 2 racket/base) racket/contract/base racket/contract/combinator racket/dict (rename-in (except-in "private/id-table.rkt" make-free-id-table make-immutable-free-id-table make-bound-id-table make-immutable-bound-id-table mutable-free-id-table? immutable-free-id-table? mutable-bound-id-table? immutable-bound-id-table? free-id-table-set free-id-table-remove bound-id-table-set bound-id-table-remove) [mutable-free-id-table mutable-free-id-table*] [immutable-free-id-table immutable-free-id-table*] [mutable-bound-id-table mutable-bound-id-table*] [immutable-bound-id-table immutable-bound-id-table*])) ;; ======== (define dict-contract-methods (vector-immutable identifier? any/c id-table-iter? #f #f #f)) (begin-for-syntax (define (replace old new template) (datum->syntax new (string->symbol (regexp-replace (regexp-quote old) (symbol->string template) (regexp-replace-quote (symbol->string (syntax-e new))))))) (define-syntax (define-templates stx) (syntax-case stx () [(_ old new (template ...)) #`(begin (define/with-syntax template (replace old new 'template)) ...)]))) ;; ======== (define-struct base-id-table/c (dom rng immutable)) (define-values (id-table/c-dom-pos-proj id-table/c-dom-neg-proj id-table/c-rng-pos-proj id-table/c-rng-neg-proj) (let () (define (proj acc location swap) (lambda (ctc blame) ((contract-projection (acc ctc)) (blame-add-context blame location #:swap? swap)))) (values (proj base-id-table/c-dom "the keys of" #f) (proj base-id-table/c-dom "the keys of" #t) (proj base-id-table/c-rng "the values of" #f) (proj base-id-table/c-rng "the values of" #t)))) (define (make-id-table/c idtbl/c-symbol idtbl? mutable-idtbl? immutable-idtbl? immutable-idtbl) (define (id-table/c-name ctc) (apply build-compound-type-name idtbl/c-symbol (base-id-table/c-dom ctc) (base-id-table/c-rng ctc) (case (base-id-table/c-immutable ctc) [(dont-care) null] [(#t) (list '#:immutable #t)] [(#f) (list '#:immutable #f)]))) (define (id-table/c-first-order ctc) (define dom-ctc (base-id-table/c-dom ctc)) (define rng-ctc (base-id-table/c-rng ctc)) (define immutable (base-id-table/c-immutable ctc)) (λ (val) (and (idtbl? val) (case immutable [(#t) (immutable-idtbl? val)] [(#f) (mutable-idtbl? 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-id-table/c ctc val blame) (define immutable (base-id-table/c-immutable ctc)) (case immutable [(#t) (unless (immutable-idtbl? val) (raise-blame-error blame val '(expected "an immutable ~a," given: "~e") 'idtbl val))] [(#f) (unless (mutable-idtbl? val) (raise-blame-error blame val '(expected "a mutable ~a," given: "~e") 'idtbl val))] [(dont-care) (unless (idtbl? val) (raise-blame-error blame val '(expected "a ~a," given: "~e") 'idtbl val))])) (define (fo-projection ctc) (λ (blame) (define dom-proj (id-table/c-dom-pos-proj ctc blame)) (define rng-proj (id-table/c-rng-pos-proj ctc blame)) (λ (val) (check-id-table/c ctc val blame) (for ([(k v) (in-dict val)]) (dom-proj k) (rng-proj v)) val))) (define (ho-projection ctc) (lambda (blame) (define pos-dom-proj (id-table/c-dom-pos-proj ctc blame)) (define neg-dom-proj (id-table/c-dom-neg-proj ctc blame)) (define pos-rng-proj (id-table/c-rng-pos-proj ctc blame)) (define neg-rng-proj (id-table/c-rng-neg-proj ctc blame)) (lambda (tbl) (check-id-table/c ctc tbl blame) ;;TODO for immutable hash tables optimize this chaperone to a flat ;;check if possible (if (immutable-idtbl? tbl) (chaperone-immutable-id-table tbl pos-dom-proj pos-rng-proj impersonator-prop:contracted ctc) (chaperone-mutable-id-table tbl neg-dom-proj pos-dom-proj neg-rng-proj pos-rng-proj impersonator-prop:contracted ctc))))) (struct flat-id-table/c base-id-table/c () #:omit-define-syntaxes #:property prop:flat-contract (build-flat-contract-property #:name id-table/c-name #:first-order id-table/c-first-order #:projection fo-projection)) (struct chaperone-id-table/c base-id-table/c () #:omit-define-syntaxes #:property prop:chaperone-contract (build-chaperone-contract-property #:name id-table/c-name #:first-order id-table/c-first-order #:projection ho-projection)) ;; Note: impersonator contracts not currently supported. (struct impersonator-id-table/c base-id-table/c () #:omit-define-syntaxes #:property prop:contract (build-contract-property #:name id-table/c-name #:first-order id-table/c-first-order #:projection ho-projection)) (define (id-table/c key/c value/c #:immutable [immutable 'dont-care]) (define key/ctc (coerce-contract idtbl/c-symbol key/c)) (define value/ctc (coerce-contract idtbl/c-symbol value/c)) (cond [(and (eq? immutable #t) (flat-contract? key/ctc) (flat-contract? value/ctc)) (flat-id-table/c key/ctc value/ctc immutable)] [(chaperone-contract? value/ctc) (chaperone-id-table/c key/ctc value/ctc immutable)] [else (impersonator-id-table/c key/ctc value/ctc immutable)])) (procedure-rename id-table/c idtbl/c-symbol)) ;; ======== (define-syntax (make-code stx) (syntax-case stx () [(_ idtbl) (let () (define-templates "idtbl" #'idtbl (mutable-idtbl* immutable-idtbl* mutable-idtbl immutable-idtbl make-idtbl make-mutable-idtbl make-immutable-idtbl idtbl? immutable-idtbl? mutable-idtbl? idtbl-hash idtbl-phase idtbl-ref idtbl-set! idtbl-set idtbl-remove! idtbl-remove idtbl-set/constructor idtbl-remove/constructor idtbl-count idtbl-iterate-first idtbl-iterate-next idtbl-iterate-key idtbl-iterate-value idtbl-map idtbl-for-each idtbl-mutable-methods idtbl-immutable-methods idtbl/c)) #'(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)]) (let ([t (mutable-idtbl (make-hasheq) phase)]) (for ([(k v) (in-dict init-dict)]) (unless (identifier? k) (raise-type-error 'make-idtbl "dictionary with identifier keys" init-dict)) (idtbl-set! t k v)) t)) (define (make-immutable-idtbl [init-dict null] #:phase [phase (syntax-local-phase-level)]) (for/fold ([t (immutable-idtbl '#hasheq() phase)]) ([(k v) (in-dict init-dict)]) (unless (identifier? k) (raise-type-error 'make-immutable-idtbl "dictionary with identifier keys" init-dict)) (idtbl-set t k v))) ;; Replace to use new constructor (define (idtbl-set d id v) (idtbl-set/constructor d id v immutable-idtbl)) (define (idtbl-remove d id) (idtbl-remove/constructor d id immutable-idtbl)) (define idtbl-immutable-methods (vector-immutable idtbl-ref #f idtbl-set #f idtbl-remove idtbl-count idtbl-iterate-first idtbl-iterate-next idtbl-iterate-key idtbl-iterate-value)) (struct mutable-idtbl mutable-idtbl* () #:property prop:dict/contract (list idtbl-mutable-methods dict-contract-methods)) (struct immutable-idtbl immutable-idtbl* () #:property prop:dict/contract (list idtbl-immutable-methods dict-contract-methods)) (define idtbl/c (make-id-table/c 'idtbl/c idtbl? mutable-idtbl? immutable-idtbl? immutable-idtbl)) (provide/contract [make-idtbl (->* () (dict? #:phase (or/c #f exact-integer?)) mutable-idtbl?)] [make-immutable-idtbl (->* () (dict? #:phase (or/c #f exact-integer?)) immutable-idtbl?)] [idtbl? (-> any/c boolean?)] [mutable-idtbl? (-> any/c boolean?)] [immutable-idtbl? (-> any/c boolean?)] [idtbl-ref (->* (idtbl? identifier?) (any/c) any)] [idtbl-set! (-> mutable-idtbl? identifier? any/c void?)] [idtbl-set (-> immutable-idtbl? identifier? any/c immutable-idtbl?)] [idtbl-remove! (-> mutable-idtbl? identifier? void?)] [idtbl-remove (-> immutable-idtbl? identifier? immutable-idtbl?)] [idtbl-count (-> idtbl? exact-nonnegative-integer?)] [idtbl-iterate-first (-> idtbl? (or/c #f id-table-iter?))] [idtbl-iterate-next (-> idtbl? id-table-iter? (or/c #f id-table-iter?))] [idtbl-iterate-key (-> idtbl? id-table-iter? identifier?)] [idtbl-iterate-value (-> idtbl? id-table-iter? any)] [idtbl-map (-> idtbl? (-> identifier? any/c any) list?)] [idtbl-for-each (-> idtbl? (-> identifier? any/c any) any)] [idtbl/c (->* (flat-contract? chaperone-contract?) (#:immutable (or/c 'dont-care #t #f)) contract?)])))])) (make-code bound-id-table) (make-code free-id-table) (provide/contract [id-table-iter? (-> any/c boolean?)])