]> git.lizzy.rs Git - metalua.git/blob - src/samples/typecheck.mlua
fix CRLF
[metalua.git] / src / samples / typecheck.mlua
1 -- static partial checkings for Lua.
2 --
3 -- This program checks some metalua or plain lua source code for most common
4 -- mistakes. Its design focuses on the ability to check plain lua code: there is
5 -- no need to load any extension in the module.
6 --
7 -- The current checkings include:
8 --
9 -- * Listing all free variables, and make sure they are declared.
10 -- * For free vars known as modules, check that indexings in them are also
11 --   declared.
12 -- * When the type of something is known, do some basic type checkings. These
13 --   checkings are by no means exhaustive; however, when a parameter function
14 --   is constant or statically declared, it's checked.
15
16
17
18 --[[
19 Type grammar:
20
21 t ::=
22 | t and t
23 | t or  t
24 | function (t, ...) return t, ... end
25 | { (k=t)... }
26 | table(t, t)
27 | string
28 | number
29 | integer
30 | boolean
31 | userdata
32 | nil
33 | multi(t, ...)
34 | _
35
36 --]]
37
38
39 match function get_type
40 | `Number{...} -> return +{number}
41 | `String{...} -> return +{string}
42 | `True|`False -> return +{boolean}
43 | `Nil         -> return +{nil}
44 | `Dots        -> return +{_}
45 | `Stat{_,v}   -> return get_type(v)
46 | `Paren{t}    -> return get_one_type(t)
47 | `Call{f, ...} -> 
48    local ftype = get_type(f)
49    match ftype with
50    | `Function{ _, {`Return{result}} } -> return get_type(result)
51    | `Function{ _, {`Return{...} == results} } ->
52       local r2 = +{ multi() }
53       for r in ivalues(results) table.insert(r2, get_type(r)) end
54       return r2
55    | `And{...} -> return +{_} -- not implemented
56    | `Or{ a, b } -> match get_one_type(a), get_one_type(b) with
57      | `Function{...}==f1, `Function{...}==f2 ->
58        return `Op{ 'or', get_type(`Call{f1}), get_type(`Call{f2})}
59      | `Function{...}==f, _ | _, `Function{...}==f ->
60        return get_type(`Call{f})
61    | _ -> return +{_}
62    end
63 | `Invoke{o, m, ... } ==  x -> return get_type(`Call{`Index{o, m}})
64 | `Op{...}==o -> return get_op_type(o)
65 | `Table{...}==t ->
66    local r = `Table{ }
67    for x in ivalues(t) do
68       match x with
69       | `Pair{ `String{...}==k, v } -> table.insert(r, `Pair{k, get_one_type(v)})
70       | t -> table.insert(r, get_one_type(t))
71       end
72    end
73    return r
74 | `Function{...}==f ->
75 | `Id{v} ->
76 | `Index{t, k} -> match get_one_type(t), get_one_type(k) with
77    | `Call{`Id 'table', tk, tv }, _ -> return tv
78    | `Table{...}==tt, `Id 'string' ->
79
80
81
82 local types_rt = require 'extension.types'
83
84
85
86
87 function check_function(f, term)
88    match get_type(term) with
89    | `Function{ params, {`Return{...} == results}}, args ->
90    | `And{ a, b }, args ->
91       check_function(a, args)
92       check_function(b, args)
93    | `Or{ a, b }, args ->
94       if not pcall(check_function, a, args) then check_function(b, args) end
95    | `Id '_' -> -- pass
96    | _ -> error ("Call to a non-function")
97    end
98 end
99
100 function check_index(a, b, term)
101    match get_type(term) with
102    | `Table{}
103
104 match function cfg.id.up
105 | `Call{ f, ... } == x -> check_function (f, x)
106 | `Index{ a, b }  == x -> check_index (a, b, x)
107 end   
108
109
110 -- List free vars
111 cfg.id.