(module tc-structs mzscheme
(require (lib "struct.ss" "syntax")
(lib "etc.ss")
"type-rep.ss" "type-effect-convenience.ss" "type-env.ss" "parse-type.ss" "type-environments.ss" "type-name-env.ss" "utils.ss"
"union.ss"
(lib "trace.ss")
(lib "kw.ss")
(lib "plt-match.ss"))
(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-F (syntax-e #'nm)))]
[(nm par) (let ([parent (parse-type #'par)])
(values #'nm parent (syntax-e #'nm) (make-F (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)
[(list _ 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) (member name s)) (map fv types))])
(values types rec?)))
(define (get-parent-flds p)
(match p
[(Struct: _ _ flds _) flds]
[#f null]))
(define (mk/register-sty nm flds parent parent-field-types types rec? wrapper setters? proc-ty)
(let* ([name (syntax-e nm)]
[fld-types (append parent-field-types types)]
[sty-initial (make-Struct name parent fld-types proc-ty)]
[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-F 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
(if (Poly? parent)
(instantiate-poly parent new-tvars)
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
#f))
(define tc/struct
(opt-lambda (nm/par flds tys [proc-ty #f])
(define-values (nm parent name name-tvar) (parse-parent nm/par))
(define-values (types rec?) (parse-types/rec name name-tvar tys))
(define-values (proc-ty-parsed proc-rec?)
(if proc-ty
(parse-types/rec name name-tvar (list proc-ty))
(values (list #f) #f)))
(mk/register-sty nm flds parent (get-parent-flds parent) types (or rec? proc-rec?)
(lambda (t) t)
#t
(car proc-ty-parsed))))
(define (tc/builtin-struct nm parent flds tys parent-tys)
(let ([parent* (if parent (lookup-type-name parent) #f)])
(mk/register-sty nm flds parent* parent-tys tys #f (lambda (t) t) #t #f)))
(define (tc/define-type parent-nm top-pred variants)
(define parent-sym (syntax-e parent-nm))
(define parent-tvar (make-F 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 (syntax-e nm) #f fld-tys #f))
(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))
(match-define (Struct: _ _ fld-types _) 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))
)