Created
September 26, 2012 07:26
-
-
Save eric-wieser/3786582 to your computer and use it in GitHub Desktop.
Python-style classes in lua
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--quick implementation of python's id function. Fails for built in roblox objects | |
id = function(o) | |
return type(o) == "table" and o.__id__ or tonumber(tostring(o):sub(-8, -1). 16) | |
end | |
class = setmetatable({}, { | |
__call = function(_, name, implementer) | |
local cls = {__init__ = function() end, __name__ = name} | |
local instancemt = {} | |
--implement method lookups and descriptors (such as properties) | |
function instancemt:__index(k) | |
local member = cls[k] | |
if type(member) == "table" and member.__get__ then | |
return member:__get__(self) | |
else | |
return member | |
end | |
end | |
function instancemt:__newindex(k, v) | |
local member = cls[k] | |
if member then | |
if type(member) == "table" and member.__set__ then | |
member:__set__(self, v) | |
else | |
error("Cannot set static member from non-static context") | |
end | |
else | |
rawset(self, k, v) | |
end | |
end | |
--tostring method | |
function instancemt:__tostring(k, v) | |
local str = cls.__str__ or cls.__repr__ | |
if str then | |
return str(self) | |
else | |
return ("<%s instance at 0x%X>"):format(name, self.__id__) | |
end | |
end | |
--Add binary operators | |
for _, binop in ipairs({'add', 'sub', 'mul', 'div', 'mod', 'pow'}) do | |
local luaname = ("__%s"):format(binop) | |
local newname = ("__%s__"):format(binop) | |
local rname = ("__r%s__"):format(binop) | |
instancemt[luaname] = function(a, b) | |
if getmetatable(a) == instancemt then | |
if cls[newname] then | |
return cls[newname](a, b) | |
else | |
error() | |
end | |
elseif getmetatable(b) == instancemt then | |
if cls[rname] then | |
return cls[rname](b, a) | |
else | |
error() | |
end | |
end | |
end | |
end | |
--define the constructor | |
setmetatable(cls, { | |
__call = function(self, ...) | |
local instance = {__class__ = cls} | |
instance.__id__ = id(instance) | |
setmetatable(instance, instancemt) | |
instance:__init__(...) | |
return instance | |
end | |
}) | |
--call oldenv in an environment that proxies to the class object | |
local oldenv = getfenv(implementer) | |
setfenv(implementer, setmetatable({}, { | |
__index = function(_, k) | |
if cls[k] ~= nil then | |
return cls[k] | |
else | |
return oldenv[k] | |
end | |
end, | |
__newindex = cls | |
}))(cls) | |
return cls | |
end, | |
--syntactic sugar | |
__index = function(self, name) | |
return function(_, f) | |
getfenv()[name] = self(name, f) | |
end | |
end | |
}); | |
class: property(function() | |
function __get__(self, instance) | |
if self.getter then return self.getter(instance) else error() end | |
end | |
function __set__(self, instance, value) | |
if self.setter then self.setter(instance, value) else error() end | |
end | |
end) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment