Advertisement
TetraSource

Moon

Feb 26th, 2019
192
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.58 KB | None | 0 0
  1.  
  2. --[[
  3. moon API by TetraSource
  4. Adds OOP to OC Lua, based on python's OOP.
  5. dependencies: none
  6. ]]--
  7.  
  8. ---------------
  9. -- variables --
  10. ---------------
  11.  
  12. local moon = {
  13.     version = "v1.0"
  14. }
  15.  
  16. local checkArg = checkArg or function(n, arg, ...)
  17.     arg = type(arg)
  18.     local i = 1
  19.     local t = select(1, ...)
  20.     while t do
  21.         if arg == t then
  22.             return
  23.         end
  24.         i = i+1
  25.         t = select(i, ...)
  26.     end
  27.     error(string.format("bad argument #%d, (%s expected, got %s)",
  28.      n, select(1, ...), arg), 3)
  29. end
  30.  
  31. -- for backward compatibility with Lua 5.2
  32. local unpack = unpack or table.unpack
  33.  
  34. ------------------------
  35. -- linearization algo --
  36. ------------------------
  37.  
  38. local function revert(list)
  39.     local j, cpy = 1, {}
  40.     for i = #list, 1, -1 do
  41.         j, cpy[j] = j+1, list[i]
  42.     end
  43.     return cpy
  44. end
  45.  
  46. local function isGoodHead(mros, goodHead)
  47.     for i = 1, #mros do
  48.         local mro = mros[i]
  49.         for j = 1, #mro-1 do
  50.             if rawequal(mro[j], goodHead) then
  51.                 return false
  52.             end
  53.         end
  54.     end
  55.     return true
  56. end
  57.  
  58. local function removeGoodHead(mros, goodHead)
  59.     local i = 1
  60.     while mros[i] do
  61.         local mro = mros[i]
  62.         if rawequal(mro[#mro], goodHead) then
  63.             if #mro > 1 then
  64.                 mro[#mro] = nil
  65.             else
  66.                 table.remove(mros, i)
  67.                 i = i-1
  68.             end
  69.         end
  70.         i = i+1
  71.     end
  72. end
  73.  
  74. local function merge(bases)
  75.     local mro, mros = {}, {}
  76.     for i = 1, #bases do
  77.         mros[i] = revert(bases[i].__mro)
  78.     end
  79.     mros[#mros+1] = revert(bases)
  80.  
  81.     while #mros > 0 do
  82.         for k = 1, math.huge do
  83.             if not mros[k] then
  84.                 return nil
  85.             end
  86.  
  87.             local goodHead = mros[k][ #mros[k] ]
  88.             if isGoodHead(mros, goodHead) then
  89.                 table.insert(mro, goodHead)
  90.                 removeGoodHead(mros, goodHead)
  91.                 break
  92.             end
  93.         end
  94.     end
  95.     return mro
  96. end
  97.  
  98. ---------------
  99. -- metatable --
  100. ---------------
  101.  
  102. local metatable = {}
  103.  
  104. local function lookup(mro, idx)
  105.     for i = 1, #mro do
  106.         if mro[i].__table[idx] ~= nil then
  107.             return mro[i].__table[idx]
  108.         end
  109.     end
  110.     return nil
  111. end
  112.  
  113. function metatable.__index(obj, idx)
  114.     local method = lookup(obj.__class.__mro, "__getfield")
  115.     if type(method) == "function" then
  116.         return method(obj, idx)
  117.     end
  118.     return nil
  119. end
  120.  
  121. function metatable.__newindex(obj, idx, val)
  122.     local method = lookup(obj.__class.__mro, "__setfield")
  123.     if type(method) == "function" then
  124.         method(obj, idx, val)
  125.     end
  126. end
  127.  
  128. local function compErr()
  129.     error("invalid types for comparison", 2)
  130. end
  131.  
  132. local function arithErr()
  133.     error("attempt to perform arithmetic on a table value", 2)
  134. end
  135.  
  136. local function binErr()
  137.     error("attempt to perform bitwise operation on a table value", 2)
  138. end
  139.  
  140. local defaults = {
  141.     __call = function()
  142.         error("attempt to call a table value", 2)
  143.     end,
  144.     __tostring = function(self)
  145.         local name = rawget(self, "__name")
  146.         if name then
  147.             name = (" name: '%s'"):format(name)
  148.         else
  149.             name = ""
  150.         end
  151.         return ("<%s type: '%s'%s>"):format(
  152.             tostring(self.__table), self.__class.__name, name)
  153.     end,
  154.     __len = function(self)
  155.         return #self.__table
  156.     end,
  157.     __pairs = function(self)
  158.         return pairs(self.__table)
  159.     end,
  160.     -- might get deprecated
  161.     __ipairs = function(self)
  162.         return ipairs(self.__table)
  163.     end,
  164.     __gc = function()
  165.     end,
  166.     __unm = arithErr,
  167.     __bnot = binErr,
  168. }
  169. for name, operation in next, defaults do
  170.     metatable[name] = function(obj, ...)
  171.         local method = lookup(obj.__class.__mro, name)
  172.         if type(method) == "function" then
  173.             return method(obj, ...)
  174.         else
  175.             return operation(obj, ...)
  176.         end
  177.     end
  178. end
  179.  
  180. defaults = {
  181.     __concat = function()
  182.         error("attempt to concatenate a table value", 2)
  183.     end,
  184.     __eq = rawequal,
  185.     __lt = compErr,
  186.     __le = compErr,
  187.     __add = arithErr,
  188.     __sub = arithErr,
  189.     __mul = arithErr,
  190.     __div = arithErr,
  191.     __idiv = arithErr,
  192.     __mod = arithErr,
  193.     __pow = arithErr,
  194.     __band = binErr,
  195.     __bor = binErr,
  196.     __bxor = binErr,
  197.     __shl = binErr,
  198.     __shr = binErr,
  199. }
  200. for name, operation in next, defaults do
  201.     metatable[name] = function(obj, other)
  202.         local meta = getmetatable(obj)
  203.         local method = lookup((meta and type(meta) == "table" and
  204.             type(meta[name]) == "function" and obj or other).__class.__mro, name)
  205.         if type(method) == "function" then
  206.             return method(obj, other)
  207.         else
  208.             return operation(obj, other)
  209.         end
  210.     end
  211. end
  212.  
  213. -------------
  214. -- classes --
  215. -------------
  216.  
  217. local function namespaceGet(obj, idx)
  218.     if rawget(obj, "__mro") then
  219.         -- check namespace of class and its bases
  220.         return lookup(obj.__mro, idx)
  221.     else
  222.         -- check namespace of object
  223.         return obj.__table[idx]
  224.     end
  225. end
  226.  
  227. local function getfield(obj, idx, descriptor)
  228.     if descriptor and type(descriptor) == "table" and
  229.         type(descriptor.__get) == "function" then
  230.         -- call data descriptor
  231.         return descriptor:__get(obj)
  232.     end
  233.  
  234.     local field = namespaceGet(obj, idx)
  235.     if field == nil then
  236.         field = descriptor
  237.     end
  238.     if field ~= nil then
  239.         return field
  240.     end
  241.  
  242.     field = lookup(obj.__class.__mro, "__index")
  243.     if type(field) == "function" then
  244.         -- call __index
  245.         return field(obj, idx)
  246.     end
  247.  
  248.     -- default
  249.     return nil
  250. end
  251.  
  252. local function setfield(obj, idx, val, descriptor)
  253.     if descriptor and type(descriptor) == "table" and
  254.         type(descriptor.__set) == "function" then
  255.         -- call data descriptor
  256.         descriptor:__set(obj, val)
  257.         return
  258.     end
  259.  
  260.     if namespaceGet(obj, idx) == nil then
  261.         local field = lookup(obj.__class.__mro, "__newindex")
  262.         if type(field) == "function" then
  263.             -- call __newindex
  264.             field(obj, idx, val)
  265.             return
  266.         end
  267.     end
  268.  
  269.     -- default
  270.     obj.__table[idx] = val
  271. end
  272.  
  273. do
  274.     local Class = {
  275.         __getfield = function(self, idx)
  276.             return getfield(self, idx, lookup(self.__class.__mro, idx))
  277.         end;
  278.  
  279.         __setfield = function(self, idx, val)
  280.             setfield(self, idx, val, lookup(self.__class.__mro, idx))
  281.         end;
  282.  
  283.         __new = function(self, name, bases, namespace)
  284.             checkArg(1, name, "string")
  285.             checkArg(2, bases, "table")
  286.             checkArg(3, namespace, "table")
  287.  
  288.             local cls = {
  289.                 __class = self,
  290.                 __name = name,
  291.                 __table = namespace,
  292.                 __bases = bases,
  293.                 __mro = merge(bases),
  294.             }
  295.             if not cls.__mro then
  296. error("Cannot create a consistent method resolution order (MRO) for bases", 3)
  297.             end
  298.             table.insert(cls.__mro, 1, cls)
  299.             return setmetatable(cls, getmetatable(self))
  300.         end;
  301.  
  302.         __init = function(self)
  303.         end;
  304.  
  305.         __call = function(self, ...)
  306.             local obj = self:__new(...)
  307.             self.__init(obj, ...)
  308.             return obj
  309.         end;
  310.  
  311.         -- classmethod
  312.         __prepare = function(cls)
  313.             return {}
  314.         end
  315.     }
  316.  
  317.     Class = Class:__call("Class", {}, Class)
  318.     rawset(Class, "__class", Class)
  319.     setmetatable(Class, metatable)
  320.     moon.Class = Class
  321. end
  322.  
  323. moon.Object = moon.Class("Object", {}, {
  324.     __getfield = moon.Class.__getfield;
  325.  
  326.     __setfield = moon.Class.__setfield;
  327.  
  328.     -- classmethod
  329.     __new = function(cls)
  330.         return setmetatable({
  331.             __class = cls,
  332.             __table = {},
  333.         }, getmetatable(cls))
  334.     end;
  335.  
  336.     __init = function()
  337.     end;
  338. })
  339.  
  340. local function superLookup(self, idx)
  341.     local mro = self.__native.__class.__mro
  342.     for i = self.__lvl, #mro do
  343.         if mro[i].__table[idx] ~= nil then
  344.             return mro[i].__table[idx]
  345.         end
  346.     end
  347.     return nil
  348. end
  349.  
  350. moon.Super = moon.Class("Super", {}, {
  351.     __getfield = function(self, idx)
  352.         return getfield(self.__native, idx, superLookup(self, idx))
  353.     end;
  354.  
  355.     __setfield = function(self, idx, val)
  356.         setfield(self.__native, idx, val, superLookup(self, idx))
  357.     end;
  358.  
  359.     __new = function(self, cls, obj)
  360.         return setmetatable({
  361.             __class = self,
  362.             __table = true,
  363.             __native = obj,
  364.             __lvl = 1,
  365.         }, getmetatable(obj))
  366.     end;
  367.  
  368.     __init = function(self, cls, obj)
  369.         checkArg(1, cls, "table")
  370.         checkArg(2, obj, "table")
  371.  
  372.         local mro = obj.__class.__mro
  373.         for i = 1, #mro do
  374.             if rawequal(mro[i], cls) then
  375.                 self.__lvl = i+1
  376.                 break
  377.             end
  378.         end
  379.         self.__table = obj.__table
  380.     end;
  381. })
  382.  
  383. moon.Property = moon.Class("Property", {moon.Object}, {
  384.     __init = function(self, fget, fset)
  385.         checkArg(1, fget, "function")
  386.         if fset ~= nil then
  387.             checkArg(2, fset, "function")
  388.             self.fset = fset
  389.         end
  390.         self.fget = fget
  391.     end;
  392.  
  393.     __get = function(self, obj)
  394.         return self.fget(obj)
  395.     end;
  396.  
  397.     __set = function(self, obj, val)
  398.         if not self.fset then
  399.             error("can't set attribute", 2)
  400.         end
  401.         self.fset(obj, val)
  402.     end;
  403. })
  404.  
  405. ---------
  406. -- API --
  407. ---------
  408.  
  409. function moon.class(name, ...)
  410.     local arg, cls, bases = 1, moon.Class, {...}
  411.     if type(name) == "table" then
  412.         arg, cls, name = 2, name, table.remove(bases, 1)
  413.     end
  414.     checkArg(arg, name, "string")
  415.  
  416.     for i = 1, #bases do
  417.         checkArg(arg+i, bases[i], "table")
  418.     end
  419.     bases[1] = bases[1] or moon.Object
  420.  
  421.     return cls(name, bases, cls:__prepare(name, bases))
  422. end
  423.  
  424. function moon.super(cls, obj)
  425.     checkArg(1, cls, "table")
  426.     checkArg(2, obj, "table")
  427.     return moon.Super(cls, obj)
  428. end
  429.  
  430. function moon.issubclass(cls1, cls2)
  431.     if type(cls1) == "table" and type(cls1.__mro) == "table" then
  432.         local mro = cls1.__mro
  433.         for i = 1, #mro do
  434.             if rawequal(mro[i], cls2) then
  435.                 return true
  436.             end
  437.         end
  438.     end
  439.     return false
  440. end
  441. local issubclass = moon.issubclass
  442.  
  443. function moon.isinstance(inst, cls)
  444.     if type(inst) ~= "table" then
  445.         return false
  446.     end
  447.     return issubclass(inst.__class, cls)
  448. end
  449.  
  450. function moon.rawget(obj, idx)
  451.     checkArg(1, obj, "table")
  452.     return namespaceGet(obj, idx)
  453. end
  454.  
  455. function moon.rawset(obj, idx, val)
  456.     checkArg(1, obj, "table")
  457.     obj.__table[idx] = val
  458. end
  459.  
  460. function moon.classmethod(method)
  461.     checkArg(1, method, "function")
  462.     return function(self, ...)
  463.         if not rawget(self, "__mro") then
  464.             -- self is object
  465.             self = self.__class
  466.         end
  467.         return method(self, ...)
  468.     end
  469. end
  470.  
  471. function moon.staticmethod(method)
  472.     checkArg(1, method, "function")
  473.     return function(_, ...)
  474.         return method(...)
  475.     end
  476. end
  477.  
  478. return moon
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement