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