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 --------------------------------------------------------------------------------
7 setmetatable (types, { __index = getfenv(0)})
9 function types.error (fmt, ...)
10 error(string.format("Runtime type-checking failure: "..fmt, ...))
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
22 --------------------------------------------------------------------------------
24 --------------------------------------------------------------------------------
25 for typename in values{ "number", "string", "boolean", "function", "thread" } do
28 if type(val) ~= typename then types.error ("%s expected", typename) end
32 function types.integer(val)
33 if type(val)~='number' or val%1~=0 then types.error 'integer expected' end
36 --------------------------------------------------------------------------------
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 (...)
45 local key_type, val_type, range_from, range_to
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
53 if type_key then types.error "Invalid type: too many types"
54 elseif type_val then type_key, type_val = type_val, x
58 if not range2 then range2=range1 end
59 if not type_key then type_key = types.integer end
61 if type(val) ~= "table" then types.error "table expected" end
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)
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, ...)
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
85 --------------------------------------------------------------------------------
86 -- [range(a,b)] checks that number [val] is between [a] and [b]. [a] and [b]
88 --------------------------------------------------------------------------------
89 function types.range (a,b)
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")
99 --------------------------------------------------------------------------------
100 -- [inter (x, y)] checks that the term has both types [x] and [y].
101 --------------------------------------------------------------------------------
102 function types.inter (...)
105 for t in values(args) do nest_error ("in inter type", t, args) end
109 --------------------------------------------------------------------------------
110 -- [inter (x, y)] checks that the term has type either [x] or [y].
111 --------------------------------------------------------------------------------
112 function types.union (...)
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"
120 --------------------------------------------------------------------------------
121 -- [optional(t)] accepts values of types [t] or [nil].
122 --------------------------------------------------------------------------------
123 function types.optional(t)
125 if val~=nil then nest_error("In optional type", t, val) end
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])
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)
153 --------------------------------------------------------------------------------
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