-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathlight-class.lua
More file actions
246 lines (209 loc) · 6.12 KB
/
Copy pathlight-class.lua
File metadata and controls
246 lines (209 loc) · 6.12 KB
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
---@class LightClassFactory
---@overload table<string, LightClass>
class = {};
local container = {}; ---@type table<string, LightClass>
local base_cls = {}; ---@class LightClass
local meta_methods = {
"__mode",
"__metatable",
"__tostring",
"__gc",
"__add",
"__sub",
"__mul",
"__div",
"__mod",
"__pow",
"__unm",
"__idiv",
"__band",
"__bor",
"__bxor",
"__bnot",
"__shl",
"__shr",
"__concat",
"__len",
"__eq",
"__lt",
"__le",
"__newindex",
"__call",
"__pairs",
"__close"
};
---@class lc_metatable: metatable
---@field public __name string The meta name of the class
---@field public __cls boolean Indicates that this is a class metatable
---@field public __clsi boolean Indicates that this is an instance metatable
--- Get Lua version as major and minor numbers
---@return number major, number minor
local function get_lua_version()
local major, minor = _VERSION:match("Lua (%d+)%.(%d+)");
return tonumber(major), tonumber(minor);
end
--- Lua < 5.4 compatibility
--- Make __tostring method returns class name and address
---@return fun(self: table): string
local function __tostring(self)
local metatable = getmetatable(self);
local name = metatable.__name;
return string.format("%s: %p", name, self);
end
---@generic T: LightClass
---@generic U: LightClass
---@param name `T`
---@param super? U
local function new_class(name, super)
local cls = {};
local metatable = {};
super = type(super) == "table" and super or base_cls;
local super_metatable = getmetatable(super) or {};
metatable.__name = name;
metatable.__tostring = super_metatable.__tostring ~= __tostring
and super_metatable.__tostring or __tostring;
metatable.__index = super;
metatable.__cls = true;
metatable.__clsi = false;
for i = 1, #meta_methods do
local method_name = meta_methods[i];
if (metatable[method_name] == nil) then
local super_metamethod = rawget(super_metatable, method_name);
if (super_metamethod ~= nil) then
rawset(metatable, method_name, super_metamethod);
end
end
end
container[name] = cls;
return setmetatable(cls, metatable);
end
---@generic T: LightClass
---@param cls T
---@param ... any
---@return T
local function new_instance(cls, ...)
local obj = {};
local cls_metatable = getmetatable(cls) or {};
local metatable = {};
for i = 1, #meta_methods do
local name = meta_methods[i];
local method = rawget(cls, name) or rawget(cls_metatable, name);
if (method ~= nil) then
rawset(metatable, name, method);
end
end
metatable.__name = cls_metatable.__name;
metatable.__tostring = cls_metatable.__tostring ~= __tostring
and cls_metatable.__tostring or __tostring;
metatable.__call = function()
error("Cannot call instance directly. Create a new instance from the class.");
end
metatable.__index = cls;
metatable.__cls = false;
metatable.__clsi = true;
setmetatable(obj, metatable);
local __init = rawget(cls, "__init"); ---@type fun(self: T, ...: any): void
if (type(__init) == "function") then
__init(obj, ...);
end
return obj;
end
--- Create a new class
---@generic T: LightClass
---@param name `T`
---@return T
function class.new(name)
assert(type(name) == "string", "Class name must be a string.");
assert(container[name] == nil, "Class '" .. name .. "' is already defined.");
return new_class(name);
end
--- Create a new class that extends a superclass
---@generic T: LightClass
---@generic U: LightClass
---@param name `T`
---@param super U
---@return T
function class.extend(name, super)
assert(type(name) == "string", "Class name must be a string.");
assert(container[name] == nil, "Class '" .. name .. "' is already defined.");
return new_class(name, super);
end
--- Get the metatable (prototype) of a class or instance
---@generic T: LightClass
---@param obj T
---@return lc_metatable
function class.prototype(obj)
return getmetatable(obj);
end
--- Check if the object is a class
---@param obj any
---@return boolean
function class.is_class(obj)
local mt = getmetatable(obj);
return mt ~= nil and mt.__cls == true;
end
--- Check if the object is an instance of a class
---@param obj any
---@return boolean
function class.is_instance(obj)
local mt = getmetatable(obj);
return mt ~= nil and mt.__clsi == true;
end
--- Check if the object is an instance of the specified class or its subclasses
---@generic T: LightClass
---@generic U: LightClass
---@param obj T
---@param cls U
---@return boolean
function class.is_instance_of(obj, cls)
if (not class.is_instance(obj) or not class.is_class(cls)) then
return false;
end
local obj_mt = class.prototype(obj);
local cls_mt = class.prototype(cls);
while (obj_mt ~= nil) do
if (obj_mt == cls_mt) then
return true;
end
local index = rawget(obj_mt, "__index");
obj_mt = (type(index) == "table") and class.prototype(index) or nil;
end
return false;
end
--- Get the type of the object: "class", "instance", or the standard Lua type
---@param obj any
---@return type | "class" | "instance"
function class.type(obj)
local mt = getmetatable(obj);
if (mt == nil) then
return type(obj);
end
if (mt.__cls == true) then
return "class";
elseif (mt.__clsi == true) then
return "instance";
else
return type(obj);
end
end
--- Get the name of the class or instance
---@param obj any
---@return string | nil
function class.name(obj)
local mt = class.prototype(obj);
return type(mt) == "table" and mt.__name or nil;
end
setmetatable(class, {
__index = function(_, name)
assert(type(name) == "string", "Class name must be a string.");
assert(container[name] ~= nil, "Class '" .. name .. "' is not defined.");
return container[name];
end
});
setmetatable(base_cls, {
__name = "LightClass",
__call = function(cls, ...)
return new_instance(cls, ...);
end
});
return class;