-
Notifications
You must be signed in to change notification settings - Fork 28
/
class.lua
165 lines (134 loc) · 3.89 KB
/
class.lua
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
--[[
barebones oop basics
construction
call the class object to construct a new instance
this will construct a new table, assign it as a class
instance, and call `new`
if you are defining a subclass, you will need to call
`self:super(...)` as part of `new` to complete superclass
construction - if done correctly this will propagate
up the chain and you wont have to think about it
classes are used as metatables directly so that
metamethods "just work" - except for index, which is
used to hook up instance methods
classes do use a prototype chain for inheritance, but
also copy their interfaces (including superclass)
we copy interfaces in classes rather than relying on
a prototype chain, so that pairs on the class gets
all the methods when implemented as an interface
class properties are not copied and should likely
be accessed through the concrete class object so
that everything refers to the same object
arguments (all optional):
name (string):
the name to use for type()
extends (class):
superclass for basic inheritance
implements (ordered table of classes):
mixins/interfaces
default_tostring (boolean):
whether or not to provide a default tostring function
]]
--generate unique increasing class ids
local class_id_gen = 0
local function next_class_id()
class_id_gen = class_id_gen + 1
return class_id_gen
end
--implement an interface into c
local function implement(c, interface)
c.__is[interface] = true
for k, v in pairs(interface) do
if c[k] == nil and type(v) == "function" then
c[k] = v
end
end
end
--build a new class
local function class(config)
local class_id = next_class_id()
config = config or {}
local extends = config.extends
local implements = config.implements
local src_location = "call location not available"
if debug and debug.getinfo then
local dinfo = debug.getinfo(2)
local src = dinfo.short_src
local line = dinfo.currentline
src_location = ("%s:%d"):format(src, line)
end
local name = config.name or ("unnamed class %d (%s)"):format(
class_id,
src_location
)
local c = {}
--prototype
c.__index = c
--unique generated id per-class
c.__id = class_id
--the class name for type calls
c.__type = name
--return the name of the class
function c:type()
return self.__type
end
if config.default_tostring then
function c:__tostring()
return name
end
end
--class metatable to set up constructor call
setmetatable(c, {
__call = function(self, ...)
local instance = setmetatable({}, self)
instance:new(...)
return instance
end,
__index = extends,
})
--checking class membership for probably-too-dynamic code
--returns true for both extended classes and implemented interfaces
--(implemented with a hashset for fast lookups)
c.__is = {}
c.__is[c] = true
function c:is(t)
return self.__is[t] == true
end
--get the inherited class for super calls if/as needed
--allows overrides that still refer to superclass behaviour
c.__super = extends
--perform a (partial) super construction for an instance
--for any nested super calls, it'll call the relevant one in the
--heirarchy, assuming no super calls have been missed
function c:super(...)
if not c.__super then return end
--hold reference so we can restore
local current_super = c.__super
--push next super
c.__super = c.__super.__super
--call
current_super.new(self, ...)
--restore
c.__super = current_super
end
if c.__super then
--implement superclass interface
implement(c, c.__super)
end
--implement all the passed interfaces/mixins
--in order provided
if implements then
for _, interface in ipairs(implements) do
implement(c, interface)
end
end
--default constructor, just proxy to the super constructor
--override it and use to set up the properties of the instance
--but don't forget to call the super constructor!
function c:new(...)
self:super(...)
end
--done
return c
end
return class