Skip to content

Instantly share code, notes, and snippets.

@eric-wieser
Created September 26, 2012 07:26
Show Gist options
  • Save eric-wieser/3786582 to your computer and use it in GitHub Desktop.
Save eric-wieser/3786582 to your computer and use it in GitHub Desktop.
Python-style classes in lua
--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