implement ext-stlc and stlc+tup with typed-lang-builder

This commit is contained in:
AlexKnauth 2016-06-20 15:07:38 -04:00
parent 3bc035fde1
commit c75f79c0db
4 changed files with 204 additions and 21 deletions

View File

@ -1,4 +1,4 @@
#lang s-exp "../ext-stlc.rkt"
#lang s-exp "../typed-lang-builder/ext-stlc.rkt"
(require "rackunit-typechecking.rkt")
;; tests for stlc extensions
@ -14,10 +14,10 @@
(typecheck-fail
((λ ([x : Unit]) x) 2)
#:with-msg (expected "Unit" #:given "Int"))
#:with-msg "expected: +Unit\n *given: +Int")
(typecheck-fail
((λ ([x : Unit]) x) void)
#:with-msg (expected "Unit" #:given "(→ Unit)"))
#:with-msg "expected: +Unit\n *given: +\\(→ Unit\\)")
(check-type ((λ ([x : Unit]) x) (void)) : Unit)
@ -41,12 +41,14 @@
;;ascription
(check-type (ann 1 : Int) : Int 1)
(check-type ((λ ([x : Int]) (ann x : Int)) 10) : Int 10)
(typecheck-fail (ann 1 : Bool) #:with-msg "ann: 1 does not have type Bool")
(typecheck-fail (ann 1 : Bool)
#:with-msg "ann: type mismatch: expected Bool, given Int\n *expression: 1")
;ann errs
(typecheck-fail (ann 1 : Complex) #:with-msg "unbound identifier")
(typecheck-fail (ann 1 : 1) #:with-msg "not a valid type")
(typecheck-fail (ann 1 : (λ ([x : Int]) x)) #:with-msg "not a valid type")
(typecheck-fail (ann Int : Int) #:with-msg "does not have type Int")
(typecheck-fail (ann Int : Int)
#:with-msg "ann: type mismatch: expected Int, given #%type\n *expression: Int")
; let
(check-type (let () (+ 1 1)) : Int 2)
@ -54,24 +56,24 @@
(check-type (let ([x 10] [y 20]) ((λ ([z : Int] [a : Int]) (+ a z)) x y)) : Int 30)
(typecheck-fail
(let ([x #f]) (+ x 1))
#:with-msg (expected "Int, Int" #:given "Bool, Int"))
#:with-msg "expected: +Int, Int\n *given: +Bool, Int\n *expressions: x, 1")
(typecheck-fail (let ([x 10] [y (+ x 1)]) (+ x y))
#:with-msg "x: unbound identifier")
(check-type (let* ([x 10] [y (+ x 1)]) (+ x y)) : Int 21)
(typecheck-fail
(let* ([x #t] [y (+ x 1)]) 1)
#:with-msg (expected "Int, Int" #:given "Bool, Int"))
#:with-msg "expected: +Int, Int\n *given: +Bool, Int\n *expressions: x, 1")
; letrec
(typecheck-fail
(letrec ([(x : Int) #f] [(y : Int) 1]) y)
#:with-msg
"letrec: type check fail, args have wrong type:\n#f has type Bool, expected Int")
"letrec: type mismatch\n *expected: +Int, Int\n *given: +Bool, Int\n *expressions: #f, 1")
(typecheck-fail
(letrec ([(y : Int) 1] [(x : Int) #f]) x)
#:with-msg
"letrec: type check fail, args have wrong type:.+#f has type Bool, expected Int")
"letrec: type mismatch\n *expected: +Int, Int\n *given: +Int, Bool\n *expressions: 1, #f")
(check-type (letrec ([(x : Int) 1] [(y : Int) (+ x 1)]) (+ x y)) : Int 3)
@ -99,19 +101,20 @@
;; check some more err msgs
(typecheck-fail
(and "1" #f)
#:with-msg "Expected expression \"1\" to have Bool type, got: String")
#:with-msg
"and: type mismatch: expected Bool, given String\n *expression: \"1\"")
(typecheck-fail
(and #t "2")
#:with-msg
"Expected expression \"2\" to have Bool type, got: String")
"and: type mismatch: expected Bool, given String\n *expression: \"2\"")
(typecheck-fail
(or "1" #f)
#:with-msg
"Expected expression \"1\" to have Bool type, got: String")
"or: type mismatch\n *expected: +Bool, Bool\n *given: +String, Bool\n *expressions: \"1\", #f")
(typecheck-fail
(or #t "2")
#:with-msg
"Expected expression \"2\" to have Bool type, got: String")
"or: type mismatch\n *expected: +Bool, Bool\n *given: +Bool, String\n *expressions: #t, \"2\"")
;; 2016-03-10: change if to work with non-false vals
(check-type (if "true" 1 2) : Int -> 1)
(typecheck-fail
@ -133,12 +136,12 @@
(typecheck-fail
((λ ([x : Bool]) x) 1)
#:with-msg (expected "Bool" #:given "Int"))
#:with-msg "expected: +Bool\n *given: +Int\n *expressions: +1")
;(typecheck-fail (λ ([x : Bool]) x)) ; Bool is now valid type
(typecheck-fail
(λ ([f : Int]) (f 1 2))
#:with-msg
"Expected expression f to have → type, got: Int")
"Expected → type, got: Int")
(check-type (λ ([f : ( Int Int Int)] [x : Int] [y : Int]) (f x y))
: ( ( Int Int Int) Int Int Int))
@ -147,14 +150,13 @@
(typecheck-fail
(+ 1 (λ ([x : Int]) x))
#:with-msg (expected "Int, Int" #:given "Int, (→ Int Int)"))
#:with-msg "expected: +Int, Int\n *given: +Int, \\(→ Int Int\\)")
(typecheck-fail
(λ ([x : ( Int Int)]) (+ x x))
#:with-msg (expected "Int, Int" #:given "(→ Int Int), (→ Int Int)"))
#:with-msg "expected: +Int, Int\n *given: +\\(→ Int Int\\), \\(→ Int Int\\)\n *expressions: x, x")
(typecheck-fail
((λ ([x : Int] [y : Int]) y) 1)
#:with-msg (expected "Int, Int" #:given "Int"
#:note "Wrong number of arguments"))
#:with-msg "wrong number of arguments: expected 2, given 1")
(check-type ((λ ([x : Int]) (+ x x)) 10) : Int 20)

View File

@ -1,4 +1,4 @@
#lang s-exp "../stlc+tup.rkt"
#lang s-exp "../typed-lang-builder/stlc+tup.rkt"
(require "rackunit-typechecking.rkt")
;; tests for tuples
@ -17,7 +17,7 @@
(typecheck-fail
(proj 1 2)
#:with-msg
"Expected expression 1 to have × type, got: Int")
"proj: Expected × type, got: Int")
;; ext-stlc.rkt tests ---------------------------------------------------------
;; should still pass

View File

@ -0,0 +1,146 @@
#lang macrotypes/tapl/typed-lang-builder
(extends "stlc+lit.rkt" #:except #%datum)
(provide (for-syntax current-join))
;; Simply-Typed Lambda Calculus, plus extensions (TAPL ch11)
;; Types:
;; - types from stlc+lit.rkt
;; - Bool, String
;; - Unit
;; Terms:
;; - terms from stlc+lit.rkt
;; - literals: bool, string
;; - boolean prims, numeric prims
;; - if
;; - prim void : (→ Unit)
;; - begin
;; - ascription (ann)
;; - let, let*, letrec
(define-base-type Bool)
(define-base-type String)
(define-base-type Float)
(define-base-type Char)
(define-typed-syntax #%datum
[(#%datum . b:boolean)
--------
[ [[_ (#%datum- . b)] (: Bool)]]]
[(#%datum . s:str)
--------
[ [[_ (#%datum- . s)] (: String)]]]
[(#%datum . f)
[#:when (flonum? (syntax-e #'f))]
--------
[ [[_ (#%datum- . f)] (: Float)]]]
[(#%datum . c:char)
--------
[ [[_ (#%datum- . c)] (: Char)]]]
[(#%datum . x)
--------
[_ (stlc+lit:#%datum . x)]])
(define-primop zero? : ( Int Bool))
(define-primop = : ( Int Int Bool))
(define-primop - : ( Int Int Int))
(define-primop add1 : ( Int Int))
(define-primop sub1 : ( Int Int))
(define-primop not : ( Bool Bool))
(define-typed-syntax and
[(and e1 e2)
[ [[e1 e1-] (: Bool)]]
[ [[e2 e2-] (: Bool)]]
--------
[ [[_ (and- e1- e2-)] (: Bool)]]])
(define-typed-syntax or
[(or e ...)
[#:with [Bool* ...] (make-list (stx-length #'[e ...]) #'Bool)]
[ [[e e-] (: Bool*)] ...]
--------
[ [[_ (or- e- ...)] (: Bool)]]])
(begin-for-syntax
(define current-join
(make-parameter
(λ (x y)
(unless (typecheck? x y)
(type-error
#:src x
#:msg "branches have incompatible types: ~a and ~a" x y))
x))))
(define-syntax
(syntax-parser
[( τ1 τ2 ...)
(for/fold ([τ ((current-type-eval) #'τ1)])
([τ2 (in-list (stx-map (current-type-eval) #'[τ2 ...]))])
((current-join) τ τ2))]))
(define-typed-syntax if
[(if e_tst e1 e2) (: τ-expected)
[ [[e_tst e_tst-] (: _)]] ; Any non-false value is truthy.
[ [[e1 e1-] (: τ-expected)]]
[ [[e2 e2-] (: τ-expected)]]
--------
[ [[_ (if- e_tst- e1- e2-)] (: _)]]]
[(if e_tst e1 e2)
[ [[e_tst e_tst-] (: _)]] ; Any non-false value is truthy.
[ [[e1 e1-] (: τ1)]]
[ [[e2 e2-] (: τ2)]]
--------
[ [[_ (if- e_tst- e1- e2-)] (: ( τ1 τ2))]]])
(define-base-type Unit)
(define-primop void : ( Unit))
(define-typed-syntax begin
[(begin e_unit ... e) (: τ_expected)
[ [[e_unit e_unit-] (: _)] ...]
[ [[e e-] (: τ_expected)]]
--------
[ [[_ (begin- e_unit- ... e-)] (: _)]]]
[(begin e_unit ... e)
[ [[e_unit e_unit-] (: _)] ...]
[ [[e e-] (: τ_e)]]
--------
[ [[_ (begin- e_unit- ... e-)] (: τ_e)]]])
(define-typed-syntax let
[(let ([x e] ...) e_body) (: τ_expected)
[ [[e e-] (: τ_x)] ...]
[() ([x : τ_x x-] ...) [[e_body e_body-] (: τ_expected)]]
--------
[ [[_ (let- ([x- e-] ...) e_body-)] (: _)]]]
[(let ([x e] ...) e_body)
[ [[e e-] (: τ_x)] ...]
[() ([x : τ_x x-] ...) [[e_body e_body-] (: τ_body)]]
--------
[ [[_ (let- ([x- e-] ...) e_body-)] (: τ_body)]]])
; dont need to manually transfer expected type
; result template automatically propagates properties
; - only need to transfer expected type when local expanding an expression
; - see let/tc
(define-typed-syntax let*
[(let* () e_body)
--------
[_ e_body]]
[(let* ([x e] [x_rst e_rst] ...) e_body)
--------
[_ (let ([x e]) (let* ([x_rst e_rst] ...) e_body))]])
(define-typed-syntax letrec
[(letrec ([b:type-bind e] ...) e_body) (: τ_expected)
[() ([b.x : b.type x-] ...)
[[e e-] (: b.type)] ... [[e_body e_body-] (: τ_expected)]]
--------
[ [[_ (letrec- ([x- e-] ...) e_body-)] (: _)]]]
[(letrec ([b:type-bind e] ...) e_body)
[() ([b.x : b.type x-] ...)
[[e e-] (: b.type)] ... [[e_body e_body-] (: τ_body)]]
--------
[ [[_ (letrec- ([x- e-] ...) e_body-)] (: τ_body)]]])

View File

@ -0,0 +1,35 @@
#lang macrotypes/tapl/typed-lang-builder
(extends "ext-stlc.rkt")
(require (for-syntax racket/list))
;; Simply-Typed Lambda Calculus, plus tuples
;; Types:
;; - types from ext-stlc.rkt
;; - ×
;; Terms:
;; - terms from ext-stlc.rkt
;; - tup and proj
(define-type-constructor × #:arity >= 0
#:arg-variances (λ (stx)
(make-list (stx-length (stx-cdr stx)) covariant)))
(define-typed-syntax tup
[(tup e ...) (: (~× τ ...))
[#:when (stx-length=? #'[e ...] #'[τ ...])]
[ [[e e-] (: τ)] ...]
--------
[ [[_ (list- e- ...)] (: _)]]]
[(tup e ...)
[ [[e e-] (: τ)] ...]
--------
[ [[_ (list- e- ...)] (: (× τ ...))]]])
(define-typed-syntax proj
[(proj e_tup n:nat)
[ [[e_tup e_tup-] (: (~× τ ...))]]
[#:fail-unless (< (syntax-e #'n) (stx-length #'[τ ...])) "index too large"]
--------
[ [[_ (list-ref- e_tup- n)] (: #,(stx-list-ref #'[τ ...] (syntax-e #'n)))]]])