Created
February 11, 2014 18:45
-
-
Save Orochimarufan/8941357 to your computer and use it in GitHub Desktop.
Lua OOP library (ccobjects)
This file contains 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
-- (c) 2012 Orochimarufan | |
-- Pythonic OOP for Lua | |
-- make table callable. | |
local table = setmetatable({}, {__index=table, __call=function(...)return{...}end}) | |
-- strings | |
local err = { | |
NOATTR = "%s has no attribute '%s'", | |
NOCALL = "%s is not callable.", | |
NOMETH = "Cannot find method %s() on %s", | |
ARG_NOTNIL = "%s() argument #%i: cannot be NIL", | |
ARG_OBJECT = "%s() argument #%i: must be an Object", | |
ARG_TYPE = "%s() argument #%i: must be %s", | |
} | |
-- Declarations | |
local META = table() | |
local MetaType = table() | |
Type = table() | |
Object = table() | |
-- Is* Helpers | |
local function IsObject(o) | |
return type(o)=="table" and getmetatable(o) == META | |
end | |
local function objstring(obj) | |
local id = rawget(obj, "__id__") | |
local cls = rawget(obj, "__class__") | |
if not cls then | |
return ("<? Object at %s>"):format(id) | |
end | |
local name = rawget(cls, "__name__") | |
return ("<'%s' Object at %s>"):format(name, id) | |
end | |
-- Hardcoded Attribute Lookups | |
local function lookup_type(cls, name) | |
--if cls then print(objstring(cls), name) end | |
local res | |
while (cls~=nil and res==nil) do | |
res = rawget(cls, "__dict__")[name] | |
cls = rawget(cls, "__base__") | |
end | |
return res | |
end | |
local function lookup_object(obj, name) | |
local res = rawget(obj, name) | |
if (res ~= nil) then | |
return res | |
else | |
local cls = rawget(obj, "__class__") | |
return lookup_type(cls, name) | |
end | |
end | |
local function lookup_method(obj, name) | |
local cls = rawget(obj, "__class__") | |
return lookup_type(cls, name) | |
end | |
-- get the "table: 0xNNNNNNN" ID of a table | |
local function id(t) | |
local mt = getmetatable(t) | |
if (mt ~= nil and mt.__tostring ~= nil) then | |
setmetatable(t, nil) | |
end | |
local id = tostring(t):sub(8) | |
if (mt ~= nil and mt.__tostring ~= nil) then | |
setmetatable(t, mt) | |
end | |
return id | |
end | |
-- export for use in submodules | |
_util = { | |
isobject = IsObject, | |
objstring = objstring, | |
lookup_type = lookup_type, | |
lookup_object = lookup_object, | |
lookup_method = lookup_method, | |
id = id, | |
table = table, | |
err = err | |
} | |
----------------------------------------------------------------------- | |
-- Metatable | |
----------------------------------------------------------------------- | |
-- Metamethods | |
function META:__index(name) | |
--print(("get %s [%s]"):format(tostring(self), name)) | |
local f = lookup_method(self, "__getattr__") | |
if f ~= nil then | |
return f(self, name) | |
end | |
error(err.NOATTR:format(self, name), 2) | |
end | |
function META:__newindex(name, value) | |
--print(("set %s [%s] = %s"):format(tostring(self), tostring(name), tostring(value))) | |
local f = lookup_method(self, "__setattr__") | |
if f ~= nil then | |
return f(self, name, value) | |
end | |
error(err.NOATTR:format(self, name),2) | |
end | |
function META:__call(...) | |
local f = lookup_method(self, "__call__") | |
if f ~= nil then | |
return f(self, ...) | |
end | |
error(err.NOCALL:format(self), 2) | |
end | |
function META:__tostring() | |
local f --= lookup_method(self, "__str__") | |
if f ~= nil then | |
return f(self) | |
end | |
return objstring(self) | |
end | |
----------------------------------------------------------------------- | |
-- Stock Object Construction | |
----------------------------------------------------------------------- | |
Object.__class__ = Type | |
Object.__name__ = "Object" | |
Object.__base__ = nil | |
Object.__dict__ = table() | |
Object.__id__ = id(Object) | |
setmetatable(Object, META) | |
MetaType.__base__ = Type | |
MetaType.__class__ = {__name__="MetaTypeType (intern)",__base__=Type,__class__=nil,__id__=nil, __dict__=table()} | |
MetaType.__name__ = "MetaType (intern)" | |
MetaType.__id__ = id(MetaType) | |
MetaType.__dict__ = table() | |
setmetatable(MetaType, META) | |
Type.__base__ = Object | |
Type.__class__ = MetaType | |
Type.__name__ = "Type" | |
Type.__dict__ = table() | |
Type.__id__ = id(Type) | |
setmetatable(Type, META) | |
function Type.__dict__.__setattr__(cls, name, value) | |
rawget(cls, "__dict__")[name] = value | |
end | |
----------------------------------------------------------------------- | |
-- Type Type | |
----------------------------------------------------------------------- | |
function MetaType.__dict__:__getattr__(name) | |
return lookup_type(self, name) | |
end | |
function MetaType.__dict__:__str__() | |
return ("<metaclass %s>"):format(self.__name__) | |
end | |
function MetaType.__dict__:__call__(...) | |
local args = {...} | |
if (self == Type and #args == 1) then | |
local ptype = type(args[1]) | |
if (ptype ~= "table") then | |
return ptype | |
elseif (IsObject(args[1])) then | |
return rawget(args[1], "__class__") | |
else | |
return ptype | |
end | |
end | |
return Type.__dict__.__call__(self, unpack(args)) | |
end | |
----------------------------------------------------------------------- | |
-- Type (The type of a class) | |
----------------------------------------------------------------------- | |
-- type attribute access | |
function Type.__dict__.__getattr__(cls, name) | |
-- look in the Metaclass | |
local val = lookup_method(cls, name) | |
if (val ~= nil) then | |
return val | |
end | |
-- look in self | |
val = lookup_type(cls, name) | |
if (val ~= nil) then | |
return val | |
end | |
error(err.NOATTR:format(self, name), 3) | |
end | |
-- tostring | |
function Type.__str__(cls) | |
return ("<class %s>"):format(cls.__name__) | |
end | |
-- Instantiation | |
function Type.__call__(cls, ...) | |
local f = lookup_type(cls, "__new__", true) | |
if f == nil then | |
error(err.NOMETH:format("__new__", cls), 3) end | |
local self = f(cls, ...) | |
f = lookup_type(cls, "__init__") | |
if f == nil then | |
error(err.NOMETH:format("__init__", cls), 3) end | |
f(self, ...) | |
return self | |
end | |
-- Class Creation | |
function Type:__new__(name, base, dict) | |
assert(type(name)=="string", err.ARG_TYPE:format(self, 1, "string")) | |
if base == nil then base = Object end | |
if dics == nil then dict = table() end | |
if not IsObject(base) then error(err.ARG_OBJECT:format(self.__name__, 2), 3) end | |
if not type(dict)=="table" then error(err.ARG_TYPE:format(self.__name__, 3), 3) end | |
if not issubclass(self, rawget(base, "__class__")) then | |
error("Metaclass must be subclass of base metaclass", 3) end | |
local cls = table() | |
cls.__name__ = name | |
cls.__base__ = base | |
cls.__dict__ = dict | |
cls.__class__ = self | |
cls.__id__ = id(cls) | |
setmetatable(cls, META) | |
return cls | |
end | |
function Type:subclass(name, dict) | |
return self.__class__(name, self, dict) | |
end | |
function Type:subclass2(dict) | |
local name = dict.__name__ | |
if not name then | |
error("subclass2 requires __name__ to be in dict!",2) | |
end | |
dict.__name__ = nil | |
return self.__class__(name, self, dict) | |
end | |
----------------------------------------------------------------------- | |
-- Object (the type of an instance) | |
----------------------------------------------------------------------- | |
-- Allocator | |
function Object.__new__(cls) | |
local self = table() | |
self.__class__ = cls | |
self.__id__ = id(self) | |
setmetatable(self, META) | |
return self | |
end | |
-- Constructor/Initializer | |
function Object:__init__() | |
-- Object needs no Initialization | |
end | |
-- getattr | |
function Object:__getattr__(name) | |
-- only called if not in own table! | |
-- look in bases | |
local val = lookup_method(self) | |
if val ~= nil then | |
return val | |
end | |
error(err.NOATTR:format(self, name), 3) | |
end | |
-- setattr | |
function Object:__setattr__(name, value) | |
rawset(self, name, value) | |
end | |
-- inheritance checks | |
function Object:__instancecheck__(instance) | |
return issubclass(Type(instance), self) | |
end | |
function Object:__subclasscheck__(subclass) | |
local this = subclass | |
while this do | |
if this == self then return true end | |
this = rawget(this, "__base__") | |
end | |
return false | |
end | |
----------------------------------------------------------------------- | |
-- Public Functions | |
----------------------------------------------------------------------- | |
function isinstance(inst, cls) | |
if not IsObject(inst) then error(err.ARG_OBJECT:format("isinstance", 1), 2) end | |
if not IsObject(cls) then error(err.ARG_OBJECT:format("isinstance", 2), 2) end | |
local f = lookup_type(cls, "__instancecheck__") | |
return f(cls, inst) | |
end | |
function issubclass(subclass, cls) | |
if not IsObject(subclass) then error(err.ARG_OBJECT:format("issubclass", 1), 2) end | |
if not IsObject(cls) then error(err.ARG_OBJECT:format("issubclass", 2), 2) end | |
local f = lookup_type(cls, "__subclasscheck__") | |
return f(cls, subclass) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment