Advertisement
JackMacWindows

class.lua

Nov 29th, 2019 (edited)
258
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 5.08 KB | None | 0 0
  1. --[[
  2. class.lua
  3. Copyright 2019-2020 JackMacWindows
  4.  
  5. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  6.  
  7. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  8.  
  9. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  10. ]]
  11.  
  12. --[[
  13.     * Define a class with `class "<name>" {<contents>}`
  14.         * This class will be available in the global namespace
  15.     * Set inherited classes by adding `{extends = <class>}` or `{extends = {<classes...>}}` between the name and contents
  16.     * To create a new object, call the class by name: `object = <name>()`
  17.     * The initializer is named __init, and it will be called after creating the object
  18.     * Metamethods are available as class methods or static methods
  19.     * Static properties and methods may be defined in a static block inside the class definition
  20.         * These are not synced to an object's namespace: when `class.a {static {foo = "bar"}}`, `a.foo` is nil
  21.     * The super variable can be used to access methods/properties of the parent class(es)
  22.     * Methods will automatically get self/super variables in their environment
  23.         * Methods should not have a self argument in their definition
  24.         * Do not call methods with the : operator, as self is already handled
  25.     * Multiple inheritance follows the order given in the table
  26.     * Initializer chaining is available by calling the name of the parent class in the initializer
  27.         * `class.a {__init = function(name) self.name = name end}; class.b {__init = function(name, type) a(name); self.type = type end}`
  28. ]]
  29. local metamethods = {__add=true, __sub=true, __mul=true, __div=true, __mod=true, __pow=true, __unm=true, __concat=true, __len=true, __eq=true, __lt=true, __le=true, __newindex=true, __call=true, __metatable=true}
  30. local function _wrap_method(obj, func, ...)
  31.     local env = getfenv(func)
  32.     env.self = obj
  33.     env.super = setmetatable({}, {__index = getmetatable(obj).__index})
  34.     setfenv(func, env)
  35.     return func(...)
  36. end
  37. local function defineClass(name, meta, def)
  38.     local c, cmt = {__class = name}, {}
  39.     if def.static then for k,v in pairs(def.static) do if metamethods[k] then cmt[k] = v else c[k] = v end end end
  40.     def.static = nil
  41.     if meta.extends then if meta.extends[1] then cmt.__index = function(self, name) for i,v in ipairs(meta.extends) do if v[name] then return v[name] end end end else cmt.__index = meta.extends end end
  42.     local __init = def.__init
  43.     def.__init = nil
  44.     cmt.__call = function(self, ...)
  45.         local omt, supers = {}, {}
  46.         local obj = setmetatable({__class = name}, omt)
  47.         if meta.extends and not __init then if meta.extends[1] then for i,v in ipairs(meta.extends) do supers[i] = v() end omt.__index = function(self, name) for i,v in ipairs(supers) do if v[name] then return v[name] end end end else omt.__index = meta.extends(...) end end
  48.         for k,v in pairs(def) do if type(v) == "function" then obj[k] = function(...) return _wrap_method(obj, v, ...) end elseif k ~= "__class" then obj[k] = v end if metamethods[k] then omt[k] = obj[k]; obj[k] = nil end end
  49.         if __init then
  50.             local env = getfenv(__init)
  51.             env.self = obj
  52.             env.super = setmetatable({}, {__index = omt.__index})
  53.             if meta.extends then if meta.extends[1] then omt.__index = function(self, name) if #supers < #meta.extends then for i,v in ipairs(meta.extends) do supers[i] = v() end end for i,v in ipairs(supers) do if v[name] then return v[name] end end end for i,v in ipairs(meta.extends) do env[v.__class] = function(...) supers[i] = v(...) end end else omt.__index = function(self, name) omt.__index = meta.extends(); return omt.__index[name] end env[meta.extends.__class] = function(...) omt.__index = meta.extends(...) end end end
  54.             setfenv(__init, env)
  55.             __init(...)
  56.             if meta.extends and meta.extends[1] then omt.__index = function(self, name) for i,v in ipairs(supers) do if v[name] then return v[name] end end end end
  57.         end
  58.         return obj
  59.     end
  60.     _G[name] = setmetatable(c, cmt)
  61.     return c
  62. end
  63. return setmetatable({}, {__call = function(self, name) return function(tab) if tab.extends or tab.implements then return function(impl) return defineClass(name, tab, impl) end else return defineClass(name, {}, tab) end end end})
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement