1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
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