(module tc-structs mzscheme
(require (lib "struct.ss" "syntax")
"subst.ss" "types.ss" "infer.ss" "types-aux.ss" "planet-requires.ss" "type-env.ss" "parse-type.ss" "type-environments.ss" "type-name-env.ss" "utils.ss"
(lib "kw.ss")
(lib "match.ss"))
(require-libs)
(require (planet "environment.ss" ("cobbe" "environment.plt" 3 0)))
(require-for-template mzscheme)
(provide (all-defined))
(define (parse-parent nm/par)
(syntax-case nm/par ()
[nm (identifier? #'nm) (values #'nm #f (syntax-e #'nm) (make-tvar (syntax-e #'nm)))]
[(nm par) (let ([parent (parse-type #'par)])
(values #'nm parent (syntax-e #'nm) (make-tvar (syntax-e #'nm))))]))
(define (struct-names nm flds setters?)
(define (split l)
(let loop ([l l] [getters '()] [setters '()])
(if (null? l)
(values (reverse getters) (reverse setters))
(loop (cddr l) (cons (car l) getters) (cons (cadr l) setters)))))
(match (build-struct-names nm flds #f (not setters?) nm)
[(_ maker pred . getters/setters)
(if setters?
(let-values ([(getters setters) (split getters/setters)])
(values maker pred getters setters))
(values maker pred getters/setters #f))]))
(define (parse-types/rec name dummy-type tys)
(let* ( [types (parameterize
([current-tvars
(extend-env (list name) (list dummy-type) (current-tvars))])
(map parse-type tys))]
[rec? (ormap (lambda (s) (set:member? name s)) (map fv types))])
(values types rec?)))
(define (get-parent-flds p)
(if p (struct-ty-flds p) null))
(define (mk/register-sty nm flds parent parent-field-types types rec? wrapper setters?)
(let* ([name (syntax-e nm)]
[fld-types (append parent-field-types types)]
[sty-initial (make-struct-ty name parent fld-types)]
[sty (if rec? (make-mu name sty-initial) sty-initial)]
[external-fld-types/no-parent (map (lambda (t) (subst name sty t)) types)]
[external-fld-types (map (lambda (t) (subst name sty t)) fld-types)])
(register-struct-types nm sty flds external-fld-types external-fld-types/no-parent setters? #:wrapper wrapper)))
(define/kw (register-struct-types nm sty flds external-fld-types external-fld-types/no-parent setters?
#:key
[wrapper (lambda (x) x)]
[maker* #:maker #f])
(define-values (maker pred getters setters) (struct-names nm flds setters?))
(register-type-name nm (wrapper sty))
(register-type (or maker* maker) (wrapper (->* external-fld-types sty)))
(register-types getters
(map (lambda (t) (wrapper (->* (list sty) t))) external-fld-types/no-parent))
(when setters?
(printf "setters: ~a~n" (syntax-object->datum setters))
(register-types setters
(map (lambda (t) (wrapper (->* (list sty t) -Void))) external-fld-types/no-parent)))
(register-type pred (make-pred-ty (wrapper sty))))
(define (tc/poly-struct vars nm/par flds tys)
(define-values (nm parent name name-tvar) (parse-parent nm/par))
(define tvars (map syntax-e vars))
(define new-tvars (map make-tvar tvars))
(define-values (types rec?)
(parameterize ([current-tvars (extend-env tvars new-tvars (current-tvars))])
(let*-values ([(types-init rec?) (parse-types/rec name (make-poly tvars name-tvar) tys)])
(if rec? (register-type-name nm (make-poly tvars name-tvar)))
(values (if rec? (map parse-type tys) types-init) rec?))))
(define concrete-parent
(cond [(and parent (poly? parent))
(subst-all (map list (poly-var parent) new-tvars) (poly-type parent))]
[else parent]))
(define parent-field-types (get-parent-flds concrete-parent))
(mk/register-sty nm flds parent parent-field-types types rec?
(lambda (t) (make-poly tvars t))
#f))
(define (tc/struct nm/par flds tys)
(define-values (nm parent name name-tvar) (parse-parent nm/par))
(define-values (types rec?) (parse-types/rec name name-tvar tys))
(mk/register-sty nm flds parent (get-parent-flds parent) types rec?
(lambda (t) t)
#t))
(define (tc/define-type parent-nm top-pred variants)
(define parent-sym (syntax-e parent-nm))
(define parent-tvar (make-tvar parent-sym))
(define (mk-initial-variant nm fld-tys-stx)
(define-values (fld-tys _) (parse-types/rec parent-sym parent-tvar fld-tys-stx))
(make-struct-ty (syntax-e nm) #f fld-tys))
(define (mk-un-ty parent-sym variant-struct-tys)
(make-mu parent-sym (apply Un variant-struct-tys)))
(define (mk-variant nm maker-name fld-names un-ty variant-struct-ty parent-nm)
(define variant-ty (subst parent-nm un-ty variant-struct-ty))
(define fld-types (struct-ty-flds variant-ty))
(register-struct-types nm variant-ty fld-names fld-types fld-types #f #:maker maker-name))
(define variant-names (map car variants))
(define variant-makers (map cadr variants))
(define variant-flds (map caddr variants))
(define variant-struct-tys (map (lambda (n flds) (mk-initial-variant n (map car flds))) variant-names variant-flds))
(define variant-fld-names (map (lambda (x) (map cdr x)) variant-flds))
(define un-ty (mk-un-ty parent-sym variant-struct-tys))
(register-type top-pred (make-pred-ty un-ty))
(register-type-name parent-nm un-ty)
(for-each (lambda (nm mk fld-names sty) (mk-variant nm mk fld-names un-ty sty parent-sym))
variant-names variant-makers variant-fld-names variant-struct-tys))
)