]> git.lizzy.rs Git - metalua.git/blob - src/lib/metalua/extension/types-runtime.mlua
Merge remote branch 'origin/master'
[metalua.git] / src / lib / metalua / extension / types-runtime.mlua
1 --------------------------------------------------------------------------------
2 -- Initialize the types table. It has an __index metatable entry,
3 -- so that if a symbol is not found in it, it is looked for in the current
4 -- environment. It allows to write things like [ n=3; x :: vector(n) ].
5 --------------------------------------------------------------------------------
6 types = { }
7 setmetatable (types, { __index = getfenv(0)})
8
9 function types.error (fmt, ...)
10    error(string.format("Runtime type-checking failure: "..fmt, ...))
11 end
12
13 --------------------------------------------------------------------------------
14 -- Add a prefix to an error message, if an error occurs.
15 -- Useful for type checkers that call sub-type-checkers.
16 --------------------------------------------------------------------------------
17 local function nest_error (prefix, ...)
18    local status, msg = pcall(...)
19    if not status then types.error("%s:\n%s", prefix, msg) end
20 end
21
22 --------------------------------------------------------------------------------
23 -- Built-in types
24 --------------------------------------------------------------------------------
25 for typename in values{ "number", "string", "boolean", "function", "thread" } do
26    types[typename] = 
27       function (val)           
28          if type(val) ~= typename then types.error ("%s expected", typename) end
29       end
30 end
31
32 function types.integer(val)
33    if type(val)~='number' or val%1~=0 then types.error 'integer expected' end
34 end
35
36 --------------------------------------------------------------------------------
37 -- table(foo) checks
38 -- table(foo, bar) checks
39 -- table(i) where i is an integer checks
40 -- table(i, j) where i and j are integers checks
41 -- Integers and key/value types can be combined
42 --------------------------------------------------------------------------------
43 function types.table (...)
44
45    local key_type, val_type, range_from, range_to
46    -- arguments parsing
47    for x in values{...} do
48       if type(x) == "number" then
49          if range2    then types.error "Invalid type: too many numbers in table type"
50          elseif range1 then range2 = x
51          else   range1 = x end
52       else
53          if     type_key  then types.error "Invalid type: too many types"
54          elseif type_val  then type_key, type_val = type_val, x
55          else   type_val = x end
56       end
57    end
58    if not range2 then range2=range1 end
59    if not type_key then type_key = types.integer end
60    return function (val)
61       if type(val) ~= "table" then types.error "table expected" end
62       local s = #val
63       if range2 and range2 > s then types.error "Not enough table elements" end
64       if range1 and range1 < s then types.error "Too many elements table elements" end
65       for k,v in pairs(val) do 
66          nest_error ("in table key",   type_key, k)
67          nest_error ("in table value", type_val, v)
68       end
69    end
70 end
71
72 --------------------------------------------------------------------------------
73 -- [list (subtype)] checks that the term is a table, and all of its 
74 -- integer-indexed elements are of type [subtype].
75 --------------------------------------------------------------------------------
76 types.list = |...| types.table (types.integer, ...)
77
78 --------------------------------------------------------------------------------
79 -- Check that [x] is an integral number
80 --------------------------------------------------------------------------------
81 function types.int (x)
82    if type(x)~="number" or x%1~=0 then types.error "Integer number expected" end
83 end
84
85 --------------------------------------------------------------------------------
86 -- [range(a,b)] checks that number [val] is between [a] and [b]. [a] and [b]
87 -- can be omitted.
88 --------------------------------------------------------------------------------
89 function types.range (a,b)
90    return function (val)
91       if type(val)~="number" or a and val<a or b and val>b then 
92          types.error ("Number between %s and %s expected",
93                       a and tostring(a) or "-infty",
94                       b and tostring(b) or "+infty")
95       end
96    end
97 end
98
99 --------------------------------------------------------------------------------
100 -- [inter (x, y)] checks that the term has both types [x] and [y].
101 --------------------------------------------------------------------------------
102 function types.inter (...)
103    local args={...}
104    return function(val)
105       for t in values(args) do nest_error ("in inter type", t, args) end
106    end
107 end      
108
109 --------------------------------------------------------------------------------
110 -- [inter (x, y)] checks that the term has type either [x] or [y].
111 --------------------------------------------------------------------------------
112 function types.union (...)
113    local args={...}
114    return function(val)
115       for t in values(args) do if pcall(t, val) then return end end
116       types.error "None of the types in the union fits"
117    end
118 end      
119
120 --------------------------------------------------------------------------------
121 -- [optional(t)] accepts values of types [t] or [nil].
122 --------------------------------------------------------------------------------
123 function types.optional(t)
124    return function(val) 
125              if val~=nil then nest_error("In optional type", t, val) end 
126    end
127 end  
128
129 --------------------------------------------------------------------------------
130 -- A call to this is done on litteral tables passed as types, i.e.
131 -- type {1,2,3} is transformed into types.__table{1,2,3}.
132 --------------------------------------------------------------------------------
133 function types.__table(s_type)
134    return function (s_val)
135       if type(s_val) ~= "table" then types.error "Struct table expected" end
136       for k, field_type in pairs (s_type) do
137          nest_error ("in struct field "..k, field_type, s_val[k])
138       end
139    end
140 end
141
142 --------------------------------------------------------------------------------
143 -- Same as __table, except that it's called on literal strings.
144 --------------------------------------------------------------------------------
145 function types.__string(s_type)
146    return function (s_val)
147       if s_val ~= s_type then
148          types.error("String %q expected", s_type)
149       end
150    end
151 end
152
153 --------------------------------------------------------------------------------
154 -- Top and Bottom:
155 --------------------------------------------------------------------------------
156 function types.any() end
157 function types.none() types.error "Empty type" end
158 types.__or  = types.union
159 types.__and = types.inter