macrotypes/old/stlc.rkt
2016-03-23 12:05:51 -04:00

57 lines
2.1 KiB
Racket

#lang racket/base
(require
(for-syntax racket/base syntax/parse syntax/parse/experimental/template
syntax/stx racket/syntax
"stx-utils.rkt")
"typecheck.rkt")
(provide (rename-out [λ/tc λ] [app/tc #%app] [datum/tc #%datum]))
(provide #%module-begin #%top-interaction)
;; Simply-Typed Lambda Calculus
;; - lam, app, var, +, and int literals only
;; - implemented in racket
(define-and-provide-builtin-types Int)
(provide (for-syntax assert-Int-type))
(define-for-syntax (assert-Int-type e) (assert-type e #'Int))
;; typed forms ----------------------------------------------------------------
;; datum
(define-syntax (datum/tc stx)
(syntax-parse stx
[(_ . n:integer) ( (syntax/loc stx (#%datum . n)) #'Int)]
[(_ . x)
#:when (type-error #:src #'x #:msg "Literal ~a has unknown type." #'x)
(syntax/loc stx (#%datum . x))]))
;; op
(define-primop + : (Int Int Int))
;; lambda
(define-syntax (λ/tc stx)
(syntax-parse stx #:datum-literals (:)
[(_ ([x:id : τ] ...) e)
;; the with-extended-type-env must be outside the expand/df (instead of
;; around just the body) bc ow the parameter will get restored to the old
;; value before the local-expand happens
#:with (lam xs+ e+) (with-extended-type-env #'([x τ] ...)
(expand/df #'(λ (x ...) e)))
;; manually handle identifiers here
;; - since Racket has no #%var hook, ids didn't get "expanded" in the previous line
;; and thus didn't get a type
;; TODO: can I put this somewhere else where it's more elegant?
#:with e++ (if (identifier? #'e+)
(with-extended-type-env #'([x τ] ...) (expand/df #'e+))
#'e+)
#:with τ_body (typeof #'e++)
( (syntax/loc stx (lam xs+ e++)) #'(τ ... τ_body))]))
;; #%app
(define-syntax (app/tc stx)
(syntax-parse stx #:literals ()
[(_ e_fn e_arg ...)
#:with (e_fn+ e_arg+ ...) (stx-map expand/df #'(e_fn e_arg ...))
#:with (τ ... τ_res) (typeof #'e_fn+)
#:when (stx-andmap assert-type #'(e_arg+ ...) #'(τ ...))
( (syntax/loc stx (#%app e_fn+ e_arg+ ...)) #'τ_res)]))