fix type constructor pattern matching and extraction

- tests passing up to stlc+reco+var tup and proj (not but variants)
This commit is contained in:
Stephen Chang 2015-07-31 17:33:45 -04:00
parent b687cffb0a
commit a12e85d4bc
9 changed files with 279 additions and 190 deletions

View File

@ -78,7 +78,7 @@
(define-syntax (begin/tc stx)
(syntax-parse stx
[(_ e_unit ... e)
#:with ((e_unit- τ_unit) ...) (infers+erase #'(e_unit ...))
#:with ([e_unit- τ_unit] ...) (infers+erase #'(e_unit ...))
#:with (e- τ) (infer+erase #'e)
#:fail-unless (stx-andmap Unit? #'(τ_unit ...))
(string-append

View File

@ -1,3 +1,21 @@
2015-07-31
Problem: pattern-expander not being recognized
Solution: syntax-parse pattern directives must begin with ~ prefix
2015-07-30:
Problem: How to match against an "expanded" type when I have an unexpanded pat?
- use the 'orig syntax?
- this would probably work but now 'orig is used for more than debugging/msgs
- so dont do this
- also, wont work because you're only matching part of the type
- use pattern expanders!
- a declared literal in define-type-constructor is defined as *both*:
- and macro that applies a temporary id (defined as void)
- a pattern-expander that expands into the expanded form:
((~literal #%plain-app) (~literal tmp-id) . args)
Note to self: when getting weird macro pattern matching errors, always check if you're accidentally using a pattern variable!
2015-07-28
Problem: How to handle mixed types, ie combining expanded and unexpanded types.
Problem: When to eval, ie expand, types into canonical form

View File

@ -1,13 +1,13 @@
#lang racket/base
(require "typecheck.rkt")
(require (prefix-in stlc: (only-in "stlc+tup.rkt" #%app begin tup proj let type=?))
(except-in "stlc+tup.rkt" #%app begin tup proj let type=?))
(except-in "stlc+tup.rkt" #%app begin tup proj let type=? ×))
(provide (rename-out [stlc:#%app #%app] [stlc:let let] [stlc:begin begin]
[define/tc define]))
(provide (except-out (all-from-out "stlc+tup.rkt")
stlc:#%app stlc:let stlc:begin stlc:tup stlc:proj
(for-syntax stlc:type=?)))
(provide tup proj var case)
(provide tup proj); var case)
(provide (for-syntax type=?))
@ -30,44 +30,65 @@
; extend to:
; 1) accept strings (ie, record labels)
(define (type=? τ1 τ2)
; (printf "t1 = ~a\n" (syntax->datum τ1))
; (printf "t2 = ~a\n" (syntax->datum τ2))
(syntax-parse (list τ1 τ2)
[(s1:str s2:str) (string=? (syntax-e #'s1) (syntax-e #'s2))]
[_ (stlc:type=? τ1 τ2)]))
(current-type=? type=?)
(current-typecheck-relation (current-type=?)))
(current-typecheck-relation (current-type=?))
(define (same-types? τs)
(define τs-lst (syntax->list τs))
(or (null? τs-lst)
(andmap (λ (τ) ((current-type=?) (car τs-lst) τ)) (cdr τs-lst)))))
(provide define-type-alias)
(define-syntax define-type-alias
(syntax-parser
[(_ alias:id τ:type)
; must eval, otherwise undefined types will be allowed
#'(define-syntax alias (syntax-parser [x:id #'τ.norm]))]))
;#'(define-syntax alias (syntax-parser [x:id #'τ.norm]))]))
#'(define-syntax alias (syntax-parser [x:id #'τ]))]))
(define-type-constructor : #:arity 2)
;(define-type-constructor [: str τ] #:lits (:))
; re-define tuples as records
(define-type-constructor
(× [~$ label τ_fld] ...) #:lits (~$)
#:declare label str
#:declare τ_fld type
)
; (× τ ...)
; #:declare τ type)
;; records
(define-syntax (tup stx)
(syntax-parse stx #:datum-literals (=)
[(_ [l:str = e] ...)
#:with ((e- τ) ...) (infers+erase #'(e ...))
( #'(list (list l e-) ...) #'(× [: l τ] ...))]
[(_ e ...)
#:with ([e- τ] ...) (infers+erase #'(e ...))
( (list (list l e-) ...) : (× [~$ l τ] ...))]
#;[(_ e ...)
#'(stlc:tup e ...)]))
(define-syntax (proj stx)
(syntax-parse stx #:literals (quote)
[(_ rec l:str ~!)
#:with (rec- τ_rec) (infer+erase #'rec)
#:fail-unless (×? #'τ_rec) (format "not record type: ~a" (syntax->datum #'τ_rec))
#:with (['l_τ:str τ] ...) (stx-map :-args (×-args #'τ_rec))
#:with [rec- τ_rec] (infer+erase #'rec)
; #:when (printf "inferred type: ~a\n" (syntax->datum #'τ_rec))
; #:when (printf "inferred type eval ~a\n" (syntax->datum ((current-type-eval) #'τ_rec)))
#:with ('l_τ:str ...) (×-get label from τ_rec)
#:with (τ ...) (×-get τ_fld from τ_rec)
; #:fail-unless (×? #'τ_rec) (format "not record type: ~a" (syntax->datum #'τ_rec))
; #:with (['l_τ:str τ] ...) (stx-map :-args (×-args #'τ_rec))
#:with (l_match:str τ_match) (str-stx-assoc #'l #'([l_τ τ] ...))
( #'(cadr (assoc l rec)) #'τ_match)]
[(_ e ...) #'(stlc:proj e ...)]))
( (cadr (assoc l rec)) : τ_match)]
#;[(_ e ...) #'(stlc:proj e ...)]))
(define-type-constructor )
#;(define-type-constructor ( [: label τ] ...))
(define-syntax (var stx)
#;(define-syntax (var stx)
(syntax-parse stx #:datum-literals (as =) #:literals (quote)
[(_ l:str = e as τ:type)
#:when (? #'τ.norm)
@ -76,7 +97,7 @@
#:with (e- τ_e) (infer+erase #'e)
#:when (typecheck? #'τ_e #'τ_match)
( #'(list l e) #'τ.norm)]))
(define-syntax (case stx)
#;(define-syntax (case stx)
(syntax-parse stx #:datum-literals (of =>) #:literals (quote)
[(_ e [l:str x => e_l] ...)
#:fail-when (null? (syntax->list #'(l ...))) "no clauses"

View File

@ -14,18 +14,17 @@
;; - terms from ext-stlc.rkt
;; - tup and proj
(define-type-constructor ×)
(define-type-constructor (× τ ...) #:declare τ type)
(define-syntax (tup stx)
(syntax-parse stx
[(_ e ...)
#:with ((e- τ) ...) (infers+erase #'(e ...))
( #'(list e- ...) #'(× τ ...))]))
#:with ([e- τ] ...) (infers+erase #'(e ...))
( (list e- ...) : (× τ ...))]))
(define-syntax (proj stx)
(syntax-parse stx
[(_ tup n:integer)
#:with (tup- τ_tup) (infer+erase #'tup)
#:fail-unless (×? #'τ_tup) "not tuple type"
#:fail-unless (< (syntax-e #'n) (×-num-args #'τ_tup)) "proj index too large"
( #'(list-ref tup n) (×-ref #'τ_tup (syntax-e #'n)))]))
[(_ e_tup n:integer)
#:with [e_tup- τs_tup] (×-match+erase #'e_tup)
#:fail-unless (< (syntax-e #'n) (stx-length #'τs_tup)) "proj index too large"
( (list-ref e_tup- n) : #,(stx-list-ref #'τs_tup (syntax-e #'n)))]))

View File

@ -61,6 +61,9 @@
(define-syntax (app/tc stx)
(syntax-parse stx
[(_ e_fn e_arg ...)
; #:with [e_fn- τ_fn] (infer+erase #'e_fn)
; #:with (τ_in ...) (→-get τ_in from #'τ_fn)
; #:with τ_out (→-get τ_out from #'τ_fn)
#:with [e_fn- (τ_in ... τ_out)] (→-match+erase #'e_fn)
#:with ([e_arg- τ_arg] ...) (infers+erase #'(e_arg ...))
#:fail-unless (typechecks? #'(τ_arg ...) #'(τ_in ...))

View File

@ -112,9 +112,10 @@
#:with-msg
"Arguments to function.+have wrong type.+Given:.+Int.+Expected:.+Bool")
;(typecheck-fail (λ ([x : Bool]) x)) ; Bool is now valid type
(typecheck-fail (λ ([f : Int]) (f 1 2))
#:with-msg
"Expected type with pattern: \\(→ τ_in ... τ_out\\), got: Int")
(typecheck-fail
(λ ([f : Int]) (f 1 2))
#:with-msg
"Expected type of expression f to match pattern \\(→ τ_in ... τ_out\\), got: Int")
(check-type (λ ([f : ( Int Int Int)] [x : Int] [y : Int]) (f x y))
: ( ( Int Int Int) Int Int Int))

View File

@ -12,10 +12,13 @@
(check-type + : ArithBinOp)
(check-type (λ ([f : ArithBinOp]) (f 1 2)) : ( ( Int Int Int) Int))
;; records (ie labeled tuples)
; records (ie labeled tuples)
(check-type "Stephen" : String)
(check-type (tup) : (×))
(check-type (tup ["name" = "Stephen"]) : (× [~$ "name" String]))
(check-type (proj (tup ["name" = "Stephen"]) "name") : String "Stephen")
(check-type (tup ["name" = "Stephen"] ["phone" = 781] ["male?" = #t]) :
(× [: "name" String] [: "phone" Int] [: "male?" Bool]))
(× [~$ "name" String] [~$ "phone" Int] [~$ "male?" Bool]))
(check-type (proj (tup ["name" = "Stephen"] ["phone" = 781] ["male?" = #t]) "name")
: String "Stephen")
(check-type (proj (tup ["name" = "Stephen"] ["phone" = 781] ["male?" = #t]) "name")
@ -25,157 +28,157 @@
(check-type (proj (tup ["name" = "Stephen"] ["phone" = 781] ["male?" = #t]) "male?")
: Bool #t)
(check-not-type (tup ["name" = "Stephen"] ["phone" = 781] ["male?" = #t]) :
(× [: "my-name" String] [: "phone" Int] [: "male?" Bool]))
(× [~$ "my-name" String] [~$ "phone" Int] [~$ "male?" Bool]))
(check-not-type (tup ["name" = "Stephen"] ["phone" = 781] ["male?" = #t]) :
(× [: "name" String] [: "my-phone" Int] [: "male?" Bool]))
(× [~$ "name" String] [~$ "my-phone" Int] [~$ "male?" Bool]))
(check-not-type (tup ["name" = "Stephen"] ["phone" = 781] ["male?" = #t]) :
(× [: "name" String] [: "phone" Int] [: "is-male?" Bool]))
(× [~$ "name" String] [~$ "phone" Int] [~$ "is-male?" Bool]))
;; variants
(check-type (var "coffee" = (void) as ( [: "coffee" Unit])) : ( [: "coffee" Unit]))
(check-not-type (var "coffee" = (void) as ( [: "coffee" Unit])) : ( [: "coffee" Unit] [: "tea" Unit]))
(typecheck-fail ((λ ([x : ( [: "coffee" Unit] [: "tea" Unit])]) x)
(var "coffee" = (void) as ( [: "coffee" Unit]))))
(check-type (var "coffee" = (void) as ( [: "coffee" Unit] [: "tea" Unit])) : ( [: "coffee" Unit] [: "tea" Unit]))
(check-type (var "coffee" = (void) as ( [: "coffee" Unit] [: "tea" Unit] [: "coke" Unit]))
: ( [: "coffee" Unit] [: "tea" Unit] [: "coke" Unit]))
;(check-type (var "coffee" = (void) as ( [: "coffee" Unit])) : ( [: "coffee" Unit]))
;(check-not-type (var "coffee" = (void) as ( [: "coffee" Unit])) : ( [: "coffee" Unit] [: "tea" Unit]))
;(typecheck-fail ((λ ([x : ( [: "coffee" Unit] [: "tea" Unit])]) x)
; (var "coffee" = (void) as ( [: "coffee" Unit]))))
;(check-type (var "coffee" = (void) as ( [: "coffee" Unit] [: "tea" Unit])) : ( [: "coffee" Unit] [: "tea" Unit]))
;(check-type (var "coffee" = (void) as ( [: "coffee" Unit] [: "tea" Unit] [: "coke" Unit]))
; : ( [: "coffee" Unit] [: "tea" Unit] [: "coke" Unit]))
;
;(typecheck-fail
; (case (var "coffee" = (void) as ( [: "coffee" Unit] [: "tea" Unit]))
; ["coffee" x => 1])) ; not enough clauses
;(typecheck-fail
; (case (var "coffee" = (void) as ( [: "coffee" Unit] [: "tea" Unit]))
; ["coffee" x => 1]
; ["teaaaaaa" x => 2])) ; wrong clause
;(typecheck-fail
; (case (var "coffee" = (void) as ( [: "coffee" Unit] [: "tea" Unit]))
; ["coffee" x => 1]
; ["tea" x => 2]
; ["coke" x => 3])) ; too many clauses
;(typecheck-fail
; (case (var "coffee" = (void) as ( [: "coffee" Unit] [: "tea" Unit]))
; ["coffee" x => "1"]
; ["tea" x => 2])) ; mismatched branch types
;(check-type
; (case (var "coffee" = 1 as ( [: "coffee" Int] [: "tea" Unit]))
; ["coffee" x => x]
; ["tea" x => 2]) : Int ⇒ 1)
;(define-type-alias Drink ( [: "coffee" Int] [: "tea" Unit] [: "coke" Bool]))
;(check-type ((λ ([x : Int]) (+ x x)) 10) : Int ⇒ 20)
;(check-type (λ ([x : Int]) (+ (+ x x) (+ x x))) : (→ Int Int))
;(check-type
; (case ((λ ([d : Drink]) d)
; (var "coffee" = 1 as ( [: "coffee" Int] [: "tea" Unit] [: "coke" Bool])))
; ["coffee" x => (+ (+ x x) (+ x x))]
; ["tea" x => 2]
; ["coke" y => 3])
; : Int ⇒ 4)
;
;(check-type
; (case ((λ ([d : Drink]) d) (var "coffee" = 1 as Drink))
; ["coffee" x => (+ (+ x x) (+ x x))]
; ["tea" x => 2]
; ["coke" y => 3])
; : Int ⇒ 4)
(typecheck-fail
(case (var "coffee" = (void) as ( [: "coffee" Unit] [: "tea" Unit]))
["coffee" x => 1])) ; not enough clauses
(typecheck-fail
(case (var "coffee" = (void) as ( [: "coffee" Unit] [: "tea" Unit]))
["coffee" x => 1]
["teaaaaaa" x => 2])) ; wrong clause
(typecheck-fail
(case (var "coffee" = (void) as ( [: "coffee" Unit] [: "tea" Unit]))
["coffee" x => 1]
["tea" x => 2]
["coke" x => 3])) ; too many clauses
(typecheck-fail
(case (var "coffee" = (void) as ( [: "coffee" Unit] [: "tea" Unit]))
["coffee" x => "1"]
["tea" x => 2])) ; mismatched branch types
(check-type
(case (var "coffee" = 1 as ( [: "coffee" Int] [: "tea" Unit]))
["coffee" x => x]
["tea" x => 2]) : Int 1)
(define-type-alias Drink ( [: "coffee" Int] [: "tea" Unit] [: "coke" Bool]))
(check-type ((λ ([x : Int]) (+ x x)) 10) : Int 20)
(check-type (λ ([x : Int]) (+ (+ x x) (+ x x))) : ( Int Int))
(check-type
(case ((λ ([d : Drink]) d)
(var "coffee" = 1 as ( [: "coffee" Int] [: "tea" Unit] [: "coke" Bool])))
["coffee" x => (+ (+ x x) (+ x x))]
["tea" x => 2]
["coke" y => 3])
: Int 4)
(check-type
(case ((λ ([d : Drink]) d) (var "coffee" = 1 as Drink))
["coffee" x => (+ (+ x x) (+ x x))]
["tea" x => 2]
["coke" y => 3])
: Int 4)
;; previous tests: ------------------------------------------------------------
;; tests for tuples -----------------------------------------------------------
(check-type (tup 1 2 3) : (× Int Int Int))
(check-type (tup 1 "1" #f +) : (× Int String Bool ( Int Int Int)))
(check-not-type (tup 1 "1" #f +) : (× Unit String Bool ( Int Int Int)))
(check-not-type (tup 1 "1" #f +) : (× Int Unit Bool ( Int Int Int)))
(check-not-type (tup 1 "1" #f +) : (× Int String Unit ( Int Int Int)))
(check-not-type (tup 1 "1" #f +) : (× Int String Bool ( Int Int Unit)))
(check-type (proj (tup 1 "2" #f) 0) : Int 1)
(check-type (proj (tup 1 "2" #f) 1) : String "2")
(check-type (proj (tup 1 "2" #f) 2) : Bool #f)
(typecheck-fail (proj (tup 1 "2" #f) 3)) ; index too large
(typecheck-fail (proj 1 2)) ; not tuple
;; previous tuple tests: ------------------------------------------------------------
;; wont work anymore
;(check-type (tup 1 2 3) : (× Int Int Int))
;(check-type (tup 1 "1" #f +) : (× Int String Bool (→ Int Int Int)))
;(check-not-type (tup 1 "1" #f +) : (× Unit String Bool (→ Int Int Int)))
;(check-not-type (tup 1 "1" #f +) : (× Int Unit Bool (→ Int Int Int)))
;(check-not-type (tup 1 "1" #f +) : (× Int String Unit (→ Int Int Int)))
;(check-not-type (tup 1 "1" #f +) : (× Int String Bool (→ Int Int Unit)))
;
;(check-type (proj (tup 1 "2" #f) 0) : Int ⇒ 1)
;(check-type (proj (tup 1 "2" #f) 1) : String ⇒ "2")
;(check-type (proj (tup 1 "2" #f) 2) : Bool ⇒ #f)
;(typecheck-fail (proj (tup 1 "2" #f) 3)) ; index too large
;(typecheck-fail (proj 1 2)) ; not tuple
;; ext-stlc.rkt tests ---------------------------------------------------------
;; should still pass
;; new literals and base types
(check-type "one" : String) ; literal now supported
(check-type #f : Bool) ; literal now supported
(check-type (λ ([x : Bool]) x) : ( Bool Bool)) ; Bool is now valid type
;; Unit
(check-type (void) : Unit)
(check-type void : ( Unit))
(typecheck-fail ((λ ([x : Unit]) x) 2))
(typecheck-fail ((λ ([x : Unit])) void))
(check-type ((λ ([x : Unit]) x) (void)) : Unit)
;; begin
(typecheck-fail (begin))
(check-type (begin 1) : Int)
(typecheck-fail (begin 1 2 3))
(check-type (begin (void) 1) : Int 1)
;;ascription
(typecheck-fail (ann 1 : Bool))
(check-type (ann 1 : Int) : Int 1)
(check-type ((λ ([x : Int]) (ann x : Int)) 10) : Int 10)
; let
(check-type (let () (+ 1 1)) : Int 2)
(check-type (let ([x 10]) (+ 1 2)) : Int)
(typecheck-fail (let ([x #f]) (+ x 1)))
(check-type (let ([x 10] [y 20]) ((λ ([z : Int] [a : Int]) (+ a z)) x y)) : Int 30)
(typecheck-fail (let ([x 10] [y (+ x 1)]) (+ x y))) ; unbound identifier
(check-type (let* ([x 10] [y (+ x 1)]) (+ x y)) : Int 21)
(typecheck-fail (let* ([x #t] [y (+ x 1)]) 1))
; letrec
(typecheck-fail (letrec ([(x : Int) #f] [(y : Int) 1]) y))
(typecheck-fail (letrec ([(y : Int) 1] [(x : Int) #f]) x))
(check-type (letrec ([(x : Int) 1] [(y : Int) (+ x 1)]) (+ x y)) : Int 3)
;; recursive
(check-type
(letrec ([(countdown : ( Int String))
(λ ([i : Int])
(if (= i 0)
"liftoff"
(countdown (- i 1))))])
(countdown 10)) : String "liftoff")
;; mutually recursive
(check-type
(letrec ([(is-even? : ( Int Bool))
(λ ([n : Int])
(or (zero? n)
(is-odd? (sub1 n))))]
[(is-odd? : ( Int Bool))
(λ ([n : Int])
(and (not (zero? n))
(is-even? (sub1 n))))])
(is-odd? 11)) : Bool #t)
;; tests from stlc+lit-tests.rkt --------------------------
; most should pass, some failing may now pass due to added types/forms
(check-type 1 : Int)
;(check-not-type 1 : (Int → Int))
;(typecheck-fail "one") ; literal now supported
;(typecheck-fail #f) ; literal now supported
(check-type (λ ([x : Int] [y : Int]) x) : ( Int Int Int))
(check-not-type (λ ([x : Int]) x) : Int)
(check-type (λ ([x : Int]) x) : ( Int Int))
(check-type (λ ([f : ( Int Int)]) 1) : ( ( Int Int) Int))
(check-type ((λ ([x : Int]) x) 1) : Int 1)
(typecheck-fail ((λ ([x : Bool]) x) 1)) ; Bool now valid type, but arg has wrong type
;(typecheck-fail (λ ([x : Bool]) x)) ; Bool is now valid type
(typecheck-fail (λ ([f : Int]) (f 1 2))) ; applying f with non-fn type
(check-type (λ ([f : ( Int Int Int)] [x : Int] [y : Int]) (f x y))
: ( ( Int Int Int) Int Int Int))
(check-type ((λ ([f : ( Int Int Int)] [x : Int] [y : Int]) (f x y)) + 1 2) : Int 3)
(typecheck-fail (+ 1 (λ ([x : Int]) x))) ; adding non-Int
(typecheck-fail (λ ([x : ( Int Int)]) (+ x x))) ; x should be Int
(typecheck-fail ((λ ([x : Int] [y : Int]) y) 1)) ; wrong number of args
(check-type ((λ ([x : Int]) (+ x x)) 10) : Int 20)
;(check-type "one" : String) ; literal now supported
;(check-type #f : Bool) ; literal now supported
;
;(check-type (λ ([x : Bool]) x) : (→ Bool Bool)) ; Bool is now valid type
;
;;; Unit
;(check-type (void) : Unit)
;(check-type void : (→ Unit))
;(typecheck-fail ((λ ([x : Unit]) x) 2))
;(typecheck-fail ((λ ([x : Unit])) void))
;(check-type ((λ ([x : Unit]) x) (void)) : Unit)
;
;;; begin
;(typecheck-fail (begin))
;(check-type (begin 1) : Int)
;(typecheck-fail (begin 1 2 3))
;(check-type (begin (void) 1) : Int ⇒ 1)
;
;;;ascription
;(typecheck-fail (ann 1 : Bool))
;(check-type (ann 1 : Int) : Int ⇒ 1)
;(check-type ((λ ([x : Int]) (ann x : Int)) 10) : Int ⇒ 10)
;
;; let
;(check-type (let () (+ 1 1)) : Int ⇒ 2)
;(check-type (let ([x 10]) (+ 1 2)) : Int)
;(typecheck-fail (let ([x #f]) (+ x 1)))
;(check-type (let ([x 10] [y 20]) ((λ ([z : Int] [a : Int]) (+ a z)) x y)) : Int ⇒ 30)
;(typecheck-fail (let ([x 10] [y (+ x 1)]) (+ x y))) ; unbound identifier
;
;(check-type (let* ([x 10] [y (+ x 1)]) (+ x y)) : Int ⇒ 21)
;(typecheck-fail (let* ([x #t] [y (+ x 1)]) 1))
;
;; letrec
;(typecheck-fail (letrec ([(x : Int) #f] [(y : Int) 1]) y))
;(typecheck-fail (letrec ([(y : Int) 1] [(x : Int) #f]) x))
;
;(check-type (letrec ([(x : Int) 1] [(y : Int) (+ x 1)]) (+ x y)) : Int ⇒ 3)
;
;;; recursive
;(check-type
; (letrec ([(countdown : (→ Int String))
; (λ ([i : Int])
; (if (= i 0)
; "liftoff"
; (countdown (- i 1))))])
; (countdown 10)) : String ⇒ "liftoff")
;
;;; mutually recursive
;(check-type
; (letrec ([(is-even? : (→ Int Bool))
; (λ ([n : Int])
; (or (zero? n)
; (is-odd? (sub1 n))))]
; [(is-odd? : (→ Int Bool))
; (λ ([n : Int])
; (and (not (zero? n))
; (is-even? (sub1 n))))])
; (is-odd? 11)) : Bool ⇒ #t)
;
;;; tests from stlc+lit-tests.rkt --------------------------
;; most should pass, some failing may now pass due to added types/forms
;(check-type 1 : Int)
;;(check-not-type 1 : (Int → Int))
;;(typecheck-fail "one") ; literal now supported
;;(typecheck-fail #f) ; literal now supported
;(check-type (λ ([x : Int] [y : Int]) x) : (→ Int Int Int))
;(check-not-type (λ ([x : Int]) x) : Int)
;(check-type (λ ([x : Int]) x) : (→ Int Int))
;(check-type (λ ([f : (→ Int Int)]) 1) : (→ (→ Int Int) Int))
;(check-type ((λ ([x : Int]) x) 1) : Int ⇒ 1)
;(typecheck-fail ((λ ([x : Bool]) x) 1)) ; Bool now valid type, but arg has wrong type
;;(typecheck-fail (λ ([x : Bool]) x)) ; Bool is now valid type
;(typecheck-fail (λ ([f : Int]) (f 1 2))) ; applying f with non-fn type
;(check-type (λ ([f : (→ Int Int Int)] [x : Int] [y : Int]) (f x y))
; : (→ (→ Int Int Int) Int Int Int))
;(check-type ((λ ([f : (→ Int Int Int)] [x : Int] [y : Int]) (f x y)) + 1 2) : Int ⇒ 3)
;(typecheck-fail (+ 1 (λ ([x : Int]) x))) ; adding non-Int
;(typecheck-fail (λ ([x : (→ Int Int)]) (+ x x))) ; x should be Int
;(typecheck-fail ((λ ([x : Int] [y : Int]) y) 1)) ; wrong number of args
;(check-type ((λ ([x : Int]) (+ x x)) 10) : Int ⇒ 20)
;

View File

@ -12,8 +12,11 @@
(check-type (proj (tup 1 "2" #f) 0) : Int 1)
(check-type (proj (tup 1 "2" #f) 1) : String "2")
(check-type (proj (tup 1 "2" #f) 2) : Bool #f)
(typecheck-fail (proj (tup 1 "2" #f) 3)) ; index too large
(typecheck-fail (proj 1 2)) ; not tuple
(typecheck-fail (proj (tup 1 "2" #f) 3) #:with-msg "index too large")
(typecheck-fail
(proj 1 2)
#:with-msg
"Expected type of expression 1 to match pattern \\(× τ ...\\), got: Int")
;; ext-stlc.rkt tests ---------------------------------------------------------
;; should still pass

View File

@ -2,8 +2,10 @@
(require
(for-syntax (except-in racket extends)
syntax/parse racket/syntax syntax/stx
"stx-utils.rkt")
"stx-utils.rkt"
syntax/parse/debug)
(for-meta 2 racket/base syntax/parse racket/syntax)
(for-meta 3 racket/base syntax/parse racket/syntax)
racket/provide)
(provide
(for-syntax (all-defined-out)) (all-defined-out)
@ -118,11 +120,13 @@
(format "Type error: expected ~a type, got ~a"
(type->str #'τ) (type->str ty))
#'args]
[_ #f])])))
[_ #f])]))
)
(define-syntax define-type-constructor
(syntax-parser
[(_ (τ:id . pat)
; lits must have ~ prefix (for syntax-parse compat) for now
(~optional (~seq #:lits (lit ...)) #:defaults ([(lit 1) null]))
decls ...
#;(~optional (~seq (~seq #:decl tvar (~datum as) cls) ...)
@ -133,15 +137,43 @@
#:with τ-match+erase (format-id #'τ "~a-match+erase" #'τ)
#:with pat-class (generate-temporary #'τ) ; syntax-class name
#:with tycon (generate-temporary #'τ) ; need a runtime id for expansion
#:with (lit-tmp ...) (generate-temporaries #'(lit ...))
#`(begin
(define lit void) ...
;; list can't be function, ow it will use typed #%app
;; define lit as macro that expands into #%app,
;; so it will use the #%app here (racket's #%app)
(define lit-tmp void) ...
(define-syntax lit (syntax-parser [(_ . xs) #'(lit-tmp . xs)])) ...
(provide lit ...)
(provide τ)
(begin-for-syntax
(define-syntax-class pat-class #:literals (lit ...)
(define-syntax lit
(pattern-expander
(syntax-parser
[(_ . xs)
;#:when (displayln "pattern expanding")
#'((~literal #%plain-app) (~literal lit-tmp) . xs)]))) ...
(define-syntax-class pat-class
;; dont check is-type? here; should already be types
;; only check is-type? for user-entered types, eg tycon call
;; thus, dont include decls here, only want shape
(pattern pat))
#;(pattern (~and pre (~do (printf "trying to match: ~a\n"(syntax->datum #'pre))) pat (~do (displayln "no")))) ; uses "lit" pattern expander
(pattern pat)
#;(pattern any
#:when (printf "trying to match: ~a\n" (syntax->datum #'any))
#:when (printf "orig: ~a\n" (type->str #'any))
#:when (printf "against pattern: ~a\n" (syntax->datum (quote-syntax pat)))
#:when (displayln #`(#,(stx-cdr (stx-car #'any))))
#:when (pretty-print (debug-parse #`(#,(stx-cdr (stx-car #'any))) pat))
#:with pat #'any) ;#`(#,(stx-cdr (stx-car #'any))))
#;(pattern any
#:when (displayln "match failed")
#:when (displayln "pat: ")
#:when (displayln (quote-syntax pat))
#:when (displayln #'any)
#:when (displayln "orig")
#:when (displayln (type->str #'any))
#:with pat #'any))
(define (τ-match ty)
(or (match-type ty tycon pat-class)
;; error msg should go in specific macro def?
@ -167,12 +199,12 @@
[(_ attr from ty)
#:with args (generate-temporary)
#:with args.attr (format-id #'args "~a.~a" #'args #'attr)
#'(syntax-parse ((current-type-eval) ty)
[((~literal #%plain-type) ((~literal #%plain-app) t . args))
#'(syntax-parse ((current-type-eval) #'ty)
[((~literal #%plain-type) ((~literal #%plain-app) (~literal tycon) . args))
#:declare args pat-class ; check shape of arguments
#:fail-unless (typecheck? #'t #'tycon) ; check tycons match
(format "Type error: expected ~a type, got ~a"
(type->str #'τ) (type->str ty))
; #:fail-unless (typecheck? #'t #'tycon) ; check tycons match
; (format "Type error: expected ~a type, got ~a"
; (type->str #'τ) (type->str #'ty))
(attribute args.attr)])])))
(define tycon (λ _ (raise (exn:fail:type:runtime
(format "~a: Cannot use type at run time" 'τ)
@ -182,12 +214,21 @@
[(_ . (~and pat !~ args)) ; first check shape
; this inner syntax-parse gets the ~! to register
; otherwise, apparently #:declare's get subst into pat (before ~!)
(syntax-parse #'args
(syntax-parse #'args #:literals (lit ...)
[pat #,@#'(decls ...) ; then check declarations (eg, valid type)
#'(#%type (tycon . args))])]
[_ (type-error #:src stx
#:msg "Improper usage of type constructor ~a: ~a, expected pattern ~a"
#'τ stx (quote-syntax (τ . pat)))])))]))
[_
(type-error #:src stx
#:msg (string-append
"Improper usage of type constructor ~a: ~a, expected pattern ~a, "
#;(format
"where: ~a"
(string-join
(stx-map
(λ (typ clss) (format "~a is a ~a" typ clss))
#'(ty (... ...)) #'(cls (... ...)))
", ")))
#'τ stx (quote-syntax (τ . pat)))])))]))
;; TODO: refine this to enable specifying arity information
;; type constructors currently must have 1+ arguments