local T = {} -- types
|
|
-- istype[o] iff o represents a type (i.e. set of values)
|
T.istype = {}
|
|
-- iserror[o] iff o represents an error type (created via T.error).
|
T.iserror = {}
|
|
-- istabletype[o] iff o represents a table type (created by T.table).
|
T.istabletype = {}
|
|
-- Number type
|
T.number = {}
|
setmetatable(T.number, T.number)
|
function T.number.__tostring(self)
|
return 'number'
|
end
|
T.istype[T.number] = true
|
|
-- String type
|
T.string = {}
|
setmetatable(T.string, T.string)
|
function T.string.__tostring(self)
|
return 'string'
|
end
|
T.istype[T.string] = true
|
|
-- Boolean type
|
T.boolean = {}
|
setmetatable(T.boolean, T.boolean)
|
function T.boolean.__tostring(self)
|
return 'boolean'
|
end
|
T.istype[T.boolean] = true
|
|
-- Table type
|
function T.table(t)
|
T.istype[t] = true
|
T.istabletype[t] = true
|
return t
|
end
|
|
-- Universal type. This is a superset of all other types.
|
T.universal = {}
|
setmetatable(T.universal, T.universal)
|
function T.universal.__tostring(self)
|
return 'unknown'
|
end
|
T.istype[T.universal] = true
|
|
-- nil type. Represents `nil` but can be stored in tables.
|
T['nil'] = {}
|
setmetatable(T['nil'], T['nil'])
|
T['nil'].__tostring = function(self)
|
return 'nil'
|
end
|
T.istype[T['nil']] = true
|
|
-- None type. Represents a non-existent value, in a similar way
|
-- that `none` is used differently from `nil` in the Lua C API.
|
T.none = {}
|
setmetatable(T.none, T.none)
|
function T.none.__tostring(self)
|
return 'none'
|
end
|
T.istype[T.none] = true
|
|
-- Error type
|
local CError = {}; CError.__index = CError
|
function CError.__tostring(self) return "error:" .. tostring(self.value) end
|
function T.error(val)
|
local self = setmetatable({value=val}, CError)
|
T.istype[self] = true
|
T.iserror[self] = true
|
return self
|
end
|
|
|
-- Gets a type that is a superset of the two given types.
|
function T.superset_types(a, b)
|
if T.iserror[a] then return a end
|
if T.iserror[b] then return b end
|
if rawequal(a, b) then -- note: including nil == nil
|
return a
|
elseif type(a) == 'string' or a == T.string then
|
if type(b) == 'string' or b == T.string then
|
return T.string
|
else
|
return T.universal
|
end
|
elseif type(a) == 'number' or a == T.number then
|
if type(b) == 'number' or b == T.number then
|
return T.number
|
else
|
return T.universal
|
end
|
elseif type(a) == 'boolean' or a == T.boolean then
|
if type(b) == 'boolean' or b == T.boolean then
|
return T.boolean
|
else
|
return T.universal
|
end
|
else
|
return T.universal -- IMPROVE
|
end
|
end
|
--[[TESTS:
|
assert(T.superset_types(2, 2) == 2)
|
assert(T.superset_types(2, 3) == T.number)
|
assert(T.superset_types(2, T.number) == T.number)
|
assert(T.superset_types(T.number, T.string) == T.universal)
|
print 'DONE'
|
--]]
|
|
-- Determines whether type `o` certainly evaluates to true (true),
|
-- certainly evaluates to false (false) or could evaluate to either
|
-- true of false ('?').
|
function T.boolean_cast(o)
|
if T.iserror[o] then -- special case
|
return '?'
|
elseif o == nil or o == false or o == T['nil'] then -- all subsets of {nil, false}
|
return false
|
elseif o == T.universal or o == T.boolean then -- all supersets of boolean
|
return '?'
|
else -- all subsets of universal - {nil, false}
|
return true
|
end
|
end
|
|
return T
|