forked from alex-eg/sex
implement type inference
Two things out of one mechanism. `_' as a type means "work it out from
the initializer", so (var n _ (strlen s)) stops needing size-t spelled
out; `type-of' hands a macro the type of an expression, so a macro can
dispatch on what it was handed rather than on what was declared. Both
read the same answers from two sides.
Algorithm W's core, intra-procedural, with the extensions C forces:
- an unknown type, since (include stdio.h) brings in names we never
parsed. Unification is consistency rather than equality, so
anything touching an unparsed declaration stops constraining
instead of rejecting a program that compiled yesterday;
- the usual arithmetic conversions, since `+' is not a function of
one type;
- checking mode for initializers, since #(0 0) has no type of its own
and takes one from its context. #(T : ...) is the way out of that.
What it wanted on the way:
- what type a *name* has, which neither the typedef nor the tag
database recorded. One table serves functions and variables, since
a function type already has a surface spelling;
- a scope chain, so a (var c int 9) inside a do ends with the block;
- form-type, keyed by cons cell, so one form has one type;
- macros expanded during the walk rather than before it, so type-of
is answered in the scope the macro was written in.
Closures take the same machinery: a receiver whose type comes from a
call, captures written (name expr) and typed from the expression, and
conversion from a bare function wherever a closure is expected.
type-match grew `_' on the pattern side, since (closure ((int)) int)
and (closure ((float)) int) were separate clauses for one case.
This commit is contained in:
83
tests/sex-programs/wildcards.sex
Normal file
83
tests/sex-programs/wildcards.sex
Normal file
@@ -0,0 +1,83 @@
|
||||
(input)
|
||||
(output "literals: 42 3.5 hello"
|
||||
"calls: 12"
|
||||
"members: 1 2.5"
|
||||
"pointers: 1 2.5"
|
||||
"arrays: 30"
|
||||
"loop: 0 1 2"
|
||||
"shadowed: 9 then 42"
|
||||
"partial: 1 2.5"
|
||||
"joined: 43.5 84 49 1"
|
||||
"from elements: 4 9 0")
|
||||
(return 0)
|
||||
|
||||
;;; `_' as a type means "work it out from the initializer". What the
|
||||
;;; pass can answer comes from declarations -- Sex writes a type at
|
||||
;;; every binding site -- and from the signature of whatever a call
|
||||
;;; names. A partial type like `(* _)' is solved by unifying what was
|
||||
;;; written against what the initializer gives, so only the wildcard
|
||||
;;; inside the spelling is filled in.
|
||||
|
||||
(include stdio.h)
|
||||
|
||||
(struct point ((x int) (y float)))
|
||||
|
||||
(fn area ((w int) (h int)) int
|
||||
(return (* w h)))
|
||||
|
||||
(pub fn main () int
|
||||
(var n _ 42)
|
||||
(var f _ 3.5)
|
||||
(var s _ "hello")
|
||||
(printf "literals: %d %g %s\n" n f s)
|
||||
|
||||
(var a _ (area 3 4))
|
||||
(printf "calls: %d\n" a)
|
||||
|
||||
(var p (struct point) #((struct point) : .x 1 .y 2.5))
|
||||
(var px _ (. p x))
|
||||
(var py _ (. p y))
|
||||
(printf "members: %d %g\n" px py)
|
||||
|
||||
(var pp _ (& p))
|
||||
(printf "pointers: %d %g\n" (-> pp x) (-> pp y))
|
||||
|
||||
(var table (¤ int 3))
|
||||
(= (¤ table 0) 10)
|
||||
(= (¤ table 1) 20)
|
||||
(var first _ (¤ table 0))
|
||||
(var second _ (¤ table 1))
|
||||
(printf "arrays: %d\n" (+ first second))
|
||||
|
||||
;; a for opens a scope, and its initializer is declared inside it
|
||||
(printf "loop:")
|
||||
(for (var i _ 0) (< i 3) (++ i)
|
||||
(printf " %d" i))
|
||||
(printf "\n")
|
||||
|
||||
;; a block's declarations end with it
|
||||
(do (var n _ 9)
|
||||
(printf "shadowed: %d then " n))
|
||||
(printf "%d\n" n)
|
||||
|
||||
;; a wildcard inside a written type: only it is solved
|
||||
(var pp2 (* _) (& p))
|
||||
(printf "partial: %d %g\n" (-> pp2 x) (-> pp2 y))
|
||||
|
||||
;; C's usual arithmetic conversions, far enough to answer `_'
|
||||
(var d double 1.5)
|
||||
(var l long 7)
|
||||
(var g float 0.5)
|
||||
(var mixed _ (+ n d))
|
||||
(var same _ (+ n n))
|
||||
(var wider _ (+ n l))
|
||||
(var single _ (+ g g))
|
||||
(printf "joined: %g %d %ld %g\n" mixed same wider single)
|
||||
|
||||
;; a brace initializer has no type of its own, but its elements solve
|
||||
;; the hole in the array type around it -- and the length stays as
|
||||
;; written, whether or not every slot is initialized
|
||||
(var squares (¤ _ 4) #(0 1 4 9))
|
||||
(var sparse (¤ _ 8) #(0 1))
|
||||
(printf "from elements: %d %d %d\n" (¤ squares 2) (¤ squares 3) (¤ sparse 7))
|
||||
(return 0))
|
||||
Reference in New Issue
Block a user