www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 9e3e2478bfa4e56acc4f8369ae206c3254578778
parent 92f18c3978111863419f9e624823940f6b0ad93b
Author: Georges Dupéron <georges.duperon@gmail.com>
Date:   Tue,  9 May 2017 20:20:42 +0200

Moved the-case-dispatch to ids.rkt

Diffstat:
Mmain.rkt | 119+++----------------------------------------------------------------------------
Mprivate/ids.rkt | 131++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 134 insertions(+), 116 deletions(-)

diff --git a/main.rkt b/main.rkt @@ -14,8 +14,6 @@ ;; Defines a literal which can be renamed, without conflicting with other ;; poly literals, or identifiers with other meanings. define-poly-literal - ;; TODO: move this to ids.rkt - the-case-dispatch ;; Defines a static overload for a polysemic method define-poly-case) @@ -34,21 +32,6 @@ racket/syntax) (for-meta 2 racket/base)) -(begin-for-syntax - (define/contract all-meanings (set/c symbol? #:kind 'mutable) (mutable-set)) - (define/contract (register-meanings-end syms) - (-> (listof symbol?) void?) - (for ([meaning (in-list syms)]) - (set-add! all-meanings meaning))) - - (define/contract (register-meanings syms) - (-> (listof symbol?) void?) - (for ([meaning (in-list syms)]) - (set-add! all-meanings meaning)) - (syntax-local-lift-module-end-declaration - #`(begin-for-syntax - (register-meanings-end '#,syms))))) - ;; Require transformers ;; _____________________________________________________________________________ @@ -196,9 +179,6 @@ ; 'initial-id) (pattern {~poly _ meaning}))))) -(begin-for-syntax - (struct a-case (f-id pred-id) #:transparent)) - ;; TODO: multimethods (define-syntax (define-poly-case stx) (syntax-case stx () @@ -219,106 +199,15 @@ #,@(if (identifier-binding #'generated-normal-macro) #'{} #'{(local-require - (only-in polysemy + (only-in polysemy/private/ids [the-case-dispatch generated-normal-macro]))}) #,@(if (identifier-binding #'generated-identifier-macro) #'{} #'{(local-require - (only-in polysemy - [the-case-dispatch generated-identifier-macro]))}) + (only-in polysemy/private/ids + [the-case-dispatch + generated-identifier-macro]))}) (define/contract (tmp-f arg₀ argᵢ ...) (-> pred? (or/c 'argᵢ any/c) ... any) . body) (define-syntax generated-name (a-case #'tmp-f #'pred?)))))])) - -(define-for-syntax contracts-supertypes #f) -(define-for-syntax contracts-expand #f) -(define-for-syntax (detect-overlap stx pred-ids) - ;; Lazily fill in the supertypes hash table, to avoid compile-time costs - ;; when the module is later required. - (unless contracts-supertypes - (set! contracts-supertypes - (make-free-id-table - `((,#'any/c . ()) - (,#'string? . (,#'any/c)) - (,#'exact-positive-integer? . (,#'exact-integer? ,#'positive?)) - (,#'exact-integer . (,#'integer? ,#'exact?)) - (,#'integer? . (,#'number?)) - (,#'exact? . (,#'number?)) ;; not quite right - (,#'number? . (,#'any/c)) - (,#'zero? . ,#'integer?) - #;…)))) - ;; Lazily fill in the "expansion" hash table, to avoid compile-time costs - ;; when the module is later required. - (unless contracts-expand - (set! contracts-expand - (make-free-id-table - `((,#'exact-nonnegative-integer? . (,#'zero? - ,#'exact-positive-integer?)) - #;…)))) - ;; Build the set of covered contracts. When a contract is a union of two - ;; disjoint contracts, it is replaced by these - ;; (e.g. exact-nonnegative-integer? is replaced by zero? and - ;; exact-positive-integer?) - (define covered-ids (mutable-free-id-set)) - (for/list ([pred-id (in-list pred-ids)]) - (define expanded* - (free-id-table-ref contracts-expand - pred-id - (λ () (list pred-id)))) - (for ([expanded (in-list expanded*)]) - (when (free-id-set-member? covered-ids expanded) - (raise-syntax-error 'polysemy - "some available function cases overlap" - stx - #f - pred-ids)) - (free-id-set-add! covered-ids expanded))) - ;; Move up the inheritance DAG, and see if any of the ancestors - ;; is covered. Since we start with the parents of the user-supplied contract, - ;; there will be no self-detection. - (define already-recur (mutable-free-id-set)) - (define (recur pred-id) - (unless (free-id-set-member? already-recur pred-id) - (free-id-set-add! already-recur pred-id) - (when (free-id-set-member? covered-ids pred-id) - (raise-syntax-error 'polysemy - "some available function cases overlap" - stx - #f - pred-ids)) - (for-each recur (free-id-table-ref contracts-supertypes pred-id)))) - (for ([pred-id (in-list pred-ids)]) - (apply recur (free-id-table-ref contracts-supertypes - pred-id)))) - -(define-for-syntax (the-case-dispatch-impl stx) - (syntax-case stx () - [(id . args) - (identifier? #'id) - #`(#%app #,(the-case-dispatch-impl #'id) . args)] - [id - (identifier? #'id) - (let () - (define/with-syntax ((f-id pred-id) ...) - (for*/list ([meaning (in-set all-meanings)] - [generated-name (in-value (gen-id #'id meaning))] - [slv (in-value - (syntax-local-value generated-name (λ () #f)))] - #:when (and slv (a-case? slv))) - (list (a-case-f-id slv) - (a-case-pred-id slv)))) - ;; Detect if there is overlap among the predicates, and raise an error - ;; in that case. - (detect-overlap #'id (syntax->list #'(pred-id ...))) - ;; TODO: for now, this only supports a single argument. - ;; we should generalize it to support case-λ, and dispatch on - ;; multiple arguments - ;; TODO: use syntax-local-lift-module-end-declaration to cache - ;; the generated dispatch functions. - #`(λ (arg) - (cond - [(pred-id arg) (f-id arg)] - ...)))])) - -(define-syntax the-case-dispatch the-case-dispatch-impl) diff --git a/private/ids.rkt b/private/ids.rkt @@ -2,13 +2,26 @@ (require racket/match (for-syntax racket/base + racket/contract + racket/set + syntax/id-table + syntax/id-set "utils.rkt")) (provide ;; The only polysemic id (all others are renamings of this one) the-polysemic-id ;; The only safeguard id (all others are renamings of this one) - the-safeguard-id) + the-safeguard-id + ;; The only case-dispatch macro (all others are renamings of this one) + the-case-dispatch + ;; Records all known meanings + (for-syntax all-meanings + register-meanings)) +(begin-for-syntax + (provide + ;; Represents a single overload of a function + (struct-out a-case))) ;; We can have a safeguard identifier to detect uses of rename-in, rename-out ;; and only-in, instead of their poly- counterparts. The safeguard @@ -89,3 +102,119 @@ ;; The only polysemic id (all others are renamings of this one) (define-syntax the-polysemic-id (polysemic)) + +;; Record all known meanigns, so that the-case-dispatch-impl can perform some +;; sanity checks. +(begin-for-syntax + (define/contract all-meanings (set/c symbol? #:kind 'mutable) (mutable-set)) + (define/contract (register-meanings-end syms) + (-> (listof symbol?) void?) + (for ([meaning (in-list syms)]) + (set-add! all-meanings meaning))) + + (define/contract (register-meanings syms) + (-> (listof symbol?) void?) + (for ([meaning (in-list syms)]) + (set-add! all-meanings meaning)) + (syntax-local-lift-module-end-declaration + #`(begin-for-syntax + (register-meanings-end '#,syms))))) + +(begin-for-syntax + ;; Represents a single overload of a function (function-id + predicate-id) + (struct a-case (f-id pred-id) #:transparent)) + +;; (FreeIdTable Id (Listof Id)) +(define-for-syntax contracts-supertypes #f) +;; (FreeIdTable Id (Listof Id)) +(define-for-syntax contracts-expand #f) +(define-for-syntax (detect-overlap stx pred-ids) + ;; Lazily fill in the supertypes hash table, to avoid compile-time costs + ;; when the module is later required. + (unless contracts-supertypes + (set! contracts-supertypes + (make-free-id-table + `((,#'any/c . ()) + (,#'string? . (,#'any/c)) + (,#'exact-positive-integer? . (,#'exact-integer? ,#'positive?)) + (,#'exact-integer . (,#'integer? ,#'exact?)) + (,#'integer? . (,#'number?)) + (,#'exact? . (,#'number?)) ;; not quite right + (,#'number? . (,#'any/c)) + (,#'zero? . (,#'integer?)) + #;…)))) + ;; Lazily fill in the "expansion" hash table, to avoid compile-time costs + ;; when the module is later required. + (unless contracts-expand + (set! contracts-expand + (make-free-id-table + `((,#'exact-nonnegative-integer? . (,#'zero? + ,#'exact-positive-integer?)) + #;…)))) + ;; Build the set of covered contracts. When a contract is a union of two + ;; disjoint contracts, it is replaced by these + ;; (e.g. exact-nonnegative-integer? is replaced by zero? and + ;; exact-positive-integer?) + (define covered-ids (mutable-free-id-set)) + (for/list ([pred-id (in-list pred-ids)]) + (define expanded* + (free-id-table-ref contracts-expand + pred-id + (λ () (list pred-id)))) + (for ([expanded (in-list expanded*)]) + (when (free-id-set-member? covered-ids expanded) + (raise-syntax-error 'polysemy + "some available function cases overlap" + stx + #f + pred-ids)) + (free-id-set-add! covered-ids expanded))) + ;; Move up the inheritance DAG, and see if any of the ancestors + ;; is covered. Since we start with the parents of the user-supplied contract, + ;; there will be no self-detection. + (define already-recur (mutable-free-id-set)) + (define (recur pred-id) + (unless (free-id-set-member? already-recur pred-id) + (free-id-set-add! already-recur pred-id) + (when (free-id-set-member? covered-ids pred-id) + (raise-syntax-error 'polysemy + "some available function cases overlap" + stx + #f + pred-ids)) + (for-each recur (free-id-table-ref contracts-supertypes pred-id)))) + (for ([pred-id (in-list pred-ids)]) + (apply recur (free-id-table-ref contracts-supertypes + pred-id)))) + +(define-for-syntax (the-case-dispatch-impl stx) + (syntax-case stx () + [(id . args) + (identifier? #'id) + #`(#%app #,(the-case-dispatch-impl #'id) . args)] + [id + (identifier? #'id) + (with-syntax + ([((f-id pred-id) ...) + (for*/list ([meaning (in-set all-meanings)] + [generated-name (in-value (gen-id #'id meaning))] + [slv (in-value + (syntax-local-value generated-name (λ () #f)))] + #:when (and slv (a-case? slv))) + (list (a-case-f-id slv) + (a-case-pred-id slv)))]) + ;; Detect if there is overlap among the predicates, and raise an error + ;; in that case. + (detect-overlap #'id (syntax->list #'(pred-id ...))) + ;; TODO: for now, this only supports a single argument. + ;; we should generalize it to support case-λ, and dispatch on + ;; multiple arguments + ;; TODO: use syntax-local-lift-module-end-declaration to cache + ;; the generated dispatch functions. + #`(λ (arg) + (cond + [(pred-id arg) (f-id arg)] + ...)))])) + +;; The only case-dispatch macro (all others are renamings of this one) +(define-syntax the-case-dispatch the-case-dispatch-impl)