macrotypes/tapl/typed-lang-builder/typed-lang-builder.rkt
AlexKnauth 3bc035fde1 start on typed-lang-builder
with stlc and stlc+lit
2016-06-20 16:10:01 -04:00

292 lines
13 KiB
Racket

#lang racket/base
(provide (except-out (all-from-out "../typecheck.rkt") -define-typed-syntax)
define-typed-syntax
(for-syntax syntax-parse/typed-syntax))
(require (rename-in
"../typecheck.rkt"
[define-typed-syntax -define-typed-syntax]
))
(module syntax-classes racket/base
(provide (all-defined-out))
(require (for-meta -1 (except-in "../typecheck.rkt" #%module-begin))
(for-meta -2 (except-in "../typecheck.rkt" #%module-begin)))
(define-syntax-class ---
[pattern (~datum --------)])
(define-syntax-class elipsis
[pattern (~literal ...)])
(define-splicing-syntax-class props
[pattern (~and (~seq stuff ...) (~seq (~seq k:id v) ...))])
(define-splicing-syntax-class id+props+≫
#:datum-literals ()
#:attributes ([x- 1] [ctx 1])
[pattern (~seq [x:id props:props x--:id])
#:with [x- ...] #'[x--]
#:with [ctx ...] #'[[x props.stuff ...]]]
[pattern (~seq [x:id props:props x--:id] ooo:elipsis)
#:with [x- ...] #'[x-- ooo]
#:with [ctx ...] #'[[x props.stuff ...] ooo]])
(define-splicing-syntax-class id-props+≫*
#:attributes ([x- 1] [ctx 1])
[pattern (~seq ctx1:id+props+≫ ...)
#:with [x- ...] #'[ctx1.x- ... ...]
#:with [ctx ...] #'[ctx1.ctx ... ...]])
(define-splicing-syntax-class inf
#:datum-literals ( :)
#:attributes ([pre 1] [e-stx 1] [e-pat 1] τ-tagss [τ-pats 1] k-tagss [k-pats 1] [post 1])
[pattern (~seq [[e-stx* e-pat*] τ-props*])
#:with [:inf] #'[[[e-stx* e-pat*] τ-props* ()]]]
[pattern (~seq [[e-stx* e-pat*] τ-props*] ooo:elipsis)
#:with [:inf] #'[[[e-stx* e-pat*] τ-props* ()] ooo]]
[pattern (~seq [[e-stx* e-pat*] (τ-props:props) (k-props:props)])
#:with [pre ...] #'[]
#:with [e-stx ...] #'[e-stx*]
#:with [e-pat ...] #'[e-pat*]
#:with τ-tagss #'(list (list 'τ-props.k ...))
#:with [τ-pats ...] #'[[τ-props.v ...]]
#:with k-tagss #'(list (list 'k-props.k ...))
#:with [k-pats ...] #'[[k-props.v ...]]
#:with [post ...] #'[]]
[pattern (~seq [[e-stx* e-pat*] (τ-props:props) (k-props:props)] ooo:elipsis)
#:with [pre ...] #'[]
#:with [e-stx ...] #'[e-stx* ooo]
#:with [e-pat ...] #'[e-pat* ooo]
#:with τ-tagss #'(map cdr (syntax->datum #'[[e-stx* τ-props.k ...] ooo]))
#:with [τ-pats ...] #'[[τ-props.v ...] ooo]
#:with k-tagss #'(map cdr (syntax->datum #'[[e-stx* k-props.k ...] ooo]))
#:with [k-pats ...] #'[[k-props.v ...] ooo]
#:with [post ...] #'[]]
[pattern (~seq [[e-stx* e-pat*] (: τ-stx*)])
#:with τ-tmp (generate-temporary 'τ)
#:with τ-stx-tmp (generate-temporary #'τ-stx*)
#:with [pre ...] #'[#:with τ-stx-tmp ((current-type-eval) #'τ-stx*)]
#:with [e-stx ...] #'[(add-expected e-stx* τ-stx-tmp)]
#:with [e-pat ...] #'[e-pat*]
#:with τ-tagss #'(list (list ':))
#:with [τ-pats ...] #'[[τ-tmp]]
#:with k-tagss #'(list (list))
#:with [k-pats ...] #'[[]]
#:with [post ...]
#'[#:with
(~and _
(~post
(~post
(~fail
#:unless (typecheck? #'τ-tmp #'τ-stx-tmp)
(format "type mismatch: expected ~a, given ~a\n expression: ~s"
(type->str #'τ-stx-tmp)
(type->str #'τ-tmp)
(syntax->datum #'e-stx*))))))
this-syntax]]
[pattern (~seq [[e-stx* e-pat*] (: τ-stx*)] ooo:elipsis)
#:with τ-tmp (generate-temporary 'τ)
#:with τ-stx-tmp (generate-temporary #'τ-stx*)
#:with [pre ...] #'[#:with [τ-stx-tmp ooo]
(stx-map (current-type-eval) #'[τ-stx* ooo])]
#:with [e-stx ...] #'[(add-expected e-stx* τ-stx-tmp) ooo]
#:with [e-pat ...] #'[e-pat* ooo]
#:with τ-tagss #'(map cdr (syntax->datum #'[[τ-stx-tmp :] ooo]))
#:with [τ-pats ...] #'[[τ-tmp] ooo]
#:with k-tagss #'(list)
#:with [k-pats ...] #'[]
#:with [post ...]
#'[#:with
(~and _
(~post
(~post
(~fail
#:unless (typechecks? #'[τ-tmp ooo] #'[τ-stx-tmp ooo])
(format (string-append "type mismatch\n"
" expected: ~a\n"
" given: ~a\n"
" expressions: ~a")
(string-join (stx-map type->str #'[τ-stx-tmp ooo]) ", ")
(string-join (stx-map type->str #'[τ-tmp ooo]) ", ")
(string-join (map ~s (stx-map syntax->datum #'[e-stx* ooo])) ", "))))))
this-syntax]]
)
(define-splicing-syntax-class inf*
[pattern (~seq inf:inf ...)
#:with [pre ...] #'[inf.pre ... ...]
#:with [e-stx ...] #'[inf.e-stx ... ...]
#:with [e-pat ...] #'[inf.e-pat ... ...]
#:with τ-tagss #'(append inf.τ-tagss ...)
#:with [τ-pats ...] #'[inf.τ-pats ... ...]
#:with k-tagss #'(append inf.k-tagss ...)
#:with [k-pats ...] #'[inf.k-pats ... ...]
#:with [post ...] #'[inf.post ... ...]])
(define-splicing-syntax-class clause
#:attributes ([stuff 1])
#:datum-literals ( τ⊑ :)
[pattern [ (~and (~seq inf-stuff ...) (~seq inf:inf ...))]
#:with [:clause] #'[[() () inf-stuff ...]]]
[pattern (~seq [ (~and (~seq inf-stuff ...) (~seq inf:inf ...))] ooo:elipsis)
#:with [:clause] #'[[() () inf-stuff ...] ooo]]
[pattern [(tvctx:id-props+≫*) (ctx:id-props+≫*) inf:inf*]
#:with e:id (generate-temporary)
#:with τ:id (generate-temporary)
#:with ooo (quote-syntax ...)
#:with [stuff ...]
#'[inf.pre ...
#:with [(tvctx.x- ...) (ctx.x- ...) (e ooo) (τ ooo)]
(infer #:tvctx #'(tvctx.ctx ...) #:ctx #'(ctx.ctx ...) #'(inf.e-stx ...))
#:with [inf.e-pat ...] #'[e ooo]
#:with [inf.τ-pats ...]
(for/list ([e (in-list (syntax->list #'[e ooo]))]
[tags (in-list inf.τ-tagss)])
(for/list ([tag (in-list tags)])
(typeof e #:tag tag)))
#:with [inf.k-pats ...]
(for/list ([τ (in-list (syntax->list #'[τ ooo]))]
[tags (in-list inf.k-tagss)])
(for/list ([tag (in-list tags)])
(typeof τ #:tag tag)))
inf.post ...]]
[pattern (~seq [(tvctx:id-props+≫*) (ctx:id-props+≫*) inf:inf*] ooo:elipsis)
#:with e:id (generate-temporary)
#:with τ:id (generate-temporary)
;TODO: What should these do?
#:fail-unless (stx-null? #'[inf.pre ...]) "this type of clause does not support elipses"
#:fail-unless (stx-null? #'[inf.post ...]) "this type of clause does not support elipses"
#:with tvctxss (cond [(stx-null? #'[tvctx.ctx ...]) #'(in-cycle (in-value '()))]
[else #'(in-list (syntax->list #'[(tvctx.ctx ...) ooo]))])
#:with ctxss (cond [(stx-null? #'[ctx.ctx ...]) #'(in-cycle (in-value '()))]
[else #'(in-list (syntax->list #'[(ctx.ctx ...) ooo]))])
#:with [stuff ...]
#'[#:with [[(tvctx.x- ...) (ctx.x- ...) (e ooo) (τ ooo)] ooo]
(for/list ([tvctxs tvctxss]
[ctxs ctxss]
[es (in-list (syntax->list #'[(inf.e-stx ...) ooo]))])
(infer #:tvctx tvctxs #:ctx ctxs es))
#:with [(inf.e-pat ...) ooo] #'[(e ooo) ooo]
#:with [(inf.τ-pats ...) ooo]
(for/list ([es (in-list (syntax->list #'[(e ooo) ooo]))])
(for/list ([e (in-list (syntax->list es))]
[tags (in-list inf.τ-tagss)])
(for/list ([tag (in-list tags)])
(typeof e #:tag tag))))
#:with [(inf.k-pats ...) ooo]
(for/list ([τs (in-list (syntax->list #'[(τ ooo) ooo]))])
(for/list ([τ (in-list (syntax->list τs))]
[tags (in-list inf.k-tagss)])
(for/list ([tag (in-list tags)])
(typeof τ #:tag tag))))]]
[pattern [a τ⊑ b]
#:with [stuff ...]
#'[#:fail-unless (typecheck? #'a #'b)
(format "type mismatch: expected ~a, given ~a"
(type->str #'b)
(type->str #'a))]]
[pattern (~seq [a τ⊑ b] ooo:elipsis)
#:with [stuff ...]
#'[#:fail-unless (typechecks? #'[a ooo] #'[b ooo])
(format (string-append "type mismatch\n"
" expected: ~a\n"
" given: ~a")
(string-join (stx-map type->str #'[b ooo]) ", ")
(string-join (stx-map type->str #'[a ooo]) ", "))]]
[pattern [#:when condition:expr]
#:with [stuff ...]
#'[#:when condition]]
[pattern [#:with pat:expr expr:expr]
#:with [stuff ...]
#'[#:with pat expr]]
[pattern [#:fail-unless condition:expr message:expr]
#:with [stuff ...]
#'[#:fail-unless condition message]]
)
(define-syntax-class last-clause
#:datum-literals ( :)
[pattern [ [[pat e-stx] (τ-props:props)]]
#:with [pre ...]
#'[]
#:with [stuff ...]
#'[(for/fold ([result (quasisyntax/loc this-syntax e-stx)])
([tag (in-list (list 'τ-props.k ...))]
[τ (in-list (list #`τ-props.v ...))])
(assign-type result τ #:tag tag))]]
[pattern [ [[pat e-stx] (: τ-pat)]]
#:with τ
(generate-temporary #'τ-pat)
#:with [pre ...]
#'[#:with τ (get-expected-type this-syntax)
#:with (~and _ (~post (~fail #:unless (syntax-e #'τ)
"no expected type, add annotations")))
this-syntax
#:with τ-pat #'τ]
#:with [stuff ...]
#'[(assign-type (quasisyntax/loc this-syntax e-stx) #`τ)]]
[pattern [pat e-stx]
#:with [pre ...]
#'[]
#:with [stuff ...]
#'[(quasisyntax/loc this-syntax e-stx)]]
[pattern [pat #:error msg:expr]
#:with [pre ...]
#'[]
#:with [stuff ...]
#'[#:fail-unless #f msg
;; should never get here
(error msg)]])
(define-splicing-syntax-class pat #:datum-literals ( :)
[pattern (~seq pat)
#:with [stuff ...] #'[]]
[pattern (~seq pat (: τ-pat))
#:with τ (generate-temporary #'τ-pat)
#:with [stuff ...]
#'[#:with τ (get-expected-type this-syntax)
#:with (~and _ (~post (~fail #:unless (syntax-e #'τ)
"no expected type, add annotations")))
this-syntax
#:with τ-pat #'τ]])
(define-syntax-class rule #:datum-literals ()
[pattern [pat:pat
clause:clause ...
:---
last-clause:last-clause]
#:with norm
#'[(~and pat.pat last-clause.pat)
pat.stuff ...
last-clause.pre ...
clause.stuff ... ...
last-clause.stuff ...]])
)
(require (for-meta 1 'syntax-classes)
(for-meta 2 'syntax-classes))
(define-syntax define-typed-syntax
(lambda (stx)
(syntax-parse stx
[(def name:id
(~and (~seq stuff ...) (~or (~seq :keyword _)
(~seq :keyword)))
...
rule:rule
...)
#'(-define-typed-syntax
name
stuff ... ...
rule.norm
...)])))
(begin-for-syntax
(define-syntax syntax-parse/typed-syntax
(lambda (stx)
(syntax-parse stx
[(stxparse
stx-id:id
(~and (~seq stuff ...) (~or (~seq :keyword _)
(~seq :keyword)))
...
rule:rule
...)
#'(syntax-parse
stx-id
stuff ... ...
rule.norm
...)]))))