Upload System Image
This commit is contained in:
648
Program_Files/Common_Files/oop.lua
Normal file
648
Program_Files/Common_Files/oop.lua
Normal file
@@ -0,0 +1,648 @@
|
||||
------------------------------------------------ LevelOOPer Library 2.2.6 ------------------------------------------------
|
||||
function _G.pairs(t)
|
||||
local mt = getmetatable(t)
|
||||
if mt and type(mt.__pairs) == "function" then
|
||||
return mt.__pairs(t)
|
||||
else
|
||||
return next, t, nil
|
||||
end
|
||||
end
|
||||
|
||||
local function fread(file)
|
||||
local f = fs.open(file,"r")
|
||||
local txt = f.readAll()
|
||||
f.close()
|
||||
return txt
|
||||
end
|
||||
|
||||
local env = _ENV
|
||||
local oop = {}
|
||||
|
||||
local classCache = {}
|
||||
local privCache = {}
|
||||
local publicCache = {}
|
||||
local objectCache = {}
|
||||
|
||||
local dir = fs.getDir(({...})[1] or shell.getRunningProgram())
|
||||
|
||||
local function typeRestriction(index, sType, value)
|
||||
if not oop.isType(value, sType) then
|
||||
return false, "cannot convert "..sType.." '"..index.."' to '"..oop.type(value, true).."'"
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function getNumberType(number)
|
||||
if number%1 == 0 then
|
||||
return "int"
|
||||
else
|
||||
return "double"
|
||||
end
|
||||
end
|
||||
|
||||
oop.setEnv = function(newEnv)
|
||||
env = newEnv
|
||||
end
|
||||
|
||||
oop.setDir = function(newDir)
|
||||
oop.expect(1, newDir, "string")
|
||||
dir = newDir
|
||||
end
|
||||
|
||||
oop.getDir = function()
|
||||
return dir
|
||||
end
|
||||
|
||||
local default = {
|
||||
int = 0,
|
||||
double = 0,
|
||||
number = 0,
|
||||
string = "",
|
||||
["function"] = function() end,
|
||||
table = {},
|
||||
boolean = false,
|
||||
}
|
||||
|
||||
local varTypes = {
|
||||
int = "int",
|
||||
double = "double",
|
||||
num = "number",
|
||||
str = "string",
|
||||
func = "function",
|
||||
tbl = "table",
|
||||
bool = "boolean",
|
||||
}
|
||||
|
||||
local newVar = {
|
||||
var = function(name)
|
||||
local tbl = {name=name}
|
||||
setmetatable(tbl, {
|
||||
__call = function(self, value)
|
||||
self.value = value
|
||||
return self
|
||||
end
|
||||
})
|
||||
return tbl
|
||||
end,
|
||||
obj = function(class)
|
||||
oop.expect(1, class, "class")
|
||||
local var = {type=classCache[class].name}
|
||||
setmetatable(var, {
|
||||
__call = function(self, name)
|
||||
self.name = name
|
||||
setmetatable(var, {
|
||||
__call = function(self, value)
|
||||
local ok,err = typeRestriction(name, var.type, value)
|
||||
if not ok then
|
||||
error(err, 2)
|
||||
else
|
||||
self.value = value
|
||||
return self
|
||||
end
|
||||
end,
|
||||
})
|
||||
return self
|
||||
end,
|
||||
})
|
||||
return var
|
||||
end,
|
||||
}
|
||||
|
||||
local const = function(name)
|
||||
if type(name) == "function" then
|
||||
return function(realname)
|
||||
local var = name(realname)
|
||||
var.const = true
|
||||
return var
|
||||
end
|
||||
elseif type(name) == "string" then
|
||||
local var = {name=name, const=true}
|
||||
setmetatable(var, {
|
||||
__call = function(self, value)
|
||||
self.value = value
|
||||
return self
|
||||
end,
|
||||
})
|
||||
return var
|
||||
end
|
||||
end
|
||||
|
||||
for k,v in pairs(varTypes) do
|
||||
newVar[k] = function(name)
|
||||
local tbl = {name=name, type=varTypes[k]}
|
||||
setmetatable(tbl, {
|
||||
__call = function(self, value)
|
||||
local stat,err = typeRestriction(name, tbl.type, value)
|
||||
if not stat then
|
||||
error(err, 2)
|
||||
else
|
||||
self.value = value
|
||||
return self
|
||||
end
|
||||
end,
|
||||
})
|
||||
return tbl
|
||||
end
|
||||
end
|
||||
|
||||
oop.injectEnv = function(env)
|
||||
env.oop = oop
|
||||
env.class = oop.class
|
||||
env.const = const
|
||||
env.import = oop.import
|
||||
env.type = oop.type
|
||||
env.getPublicObject = oop.getPublicObject
|
||||
for k,v in pairs(newVar) do
|
||||
env[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
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,
|
||||
["__index"] = true,
|
||||
["__newindex"] = true,
|
||||
["__call"] = true,
|
||||
["__tostring"] = true,
|
||||
["__metatable"] = true,
|
||||
["__pairs"] = true,
|
||||
["__ipairs"] = true
|
||||
}
|
||||
|
||||
local function sanitizeArgs(...)
|
||||
local args = table.pack(...)
|
||||
for k,v in pairs(args) do
|
||||
if publicCache[v] then
|
||||
args[k] = publicCache[v]
|
||||
end
|
||||
end
|
||||
return table.unpack(args, nil, args.n)
|
||||
end
|
||||
|
||||
local function instantiate(orig)
|
||||
local function deepCopy(o, seen)
|
||||
seen = seen or {}
|
||||
if seen[o] then
|
||||
return seen[o]
|
||||
end
|
||||
local copy
|
||||
if type(o) == 'table' then
|
||||
copy = {}
|
||||
seen[o] = copy
|
||||
for k,v in pairs(o) do
|
||||
copy[deepCopy(k, seen)] = deepCopy(v, seen)
|
||||
end
|
||||
setmetatable(copy, deepCopy(getmetatable(o), seen))
|
||||
else
|
||||
copy = o
|
||||
end
|
||||
return copy
|
||||
end
|
||||
return deepCopy(orig)
|
||||
end
|
||||
|
||||
local function makeClass(name, classTbl, inherit)
|
||||
local class = {
|
||||
static = {
|
||||
},
|
||||
private = {
|
||||
},
|
||||
public = {
|
||||
},
|
||||
properties = {
|
||||
static = {
|
||||
},
|
||||
private = {
|
||||
},
|
||||
public = {
|
||||
},
|
||||
types = {
|
||||
},
|
||||
consts = {
|
||||
},
|
||||
},
|
||||
metatable = {
|
||||
public = {
|
||||
},
|
||||
private = {
|
||||
},
|
||||
},
|
||||
name = name,
|
||||
}
|
||||
|
||||
class.metatable.static = {
|
||||
__index = class.static,
|
||||
__newindex = function(tbl, k, v)
|
||||
if class.properties.static[k] then
|
||||
if class.properties.consts[k] then
|
||||
error("cannot modify const '"..tostring(k).."'", 2)
|
||||
elseif class.properties.types[k] then
|
||||
local stat,err = typeRestriction(k, class.properties.types[k], v)
|
||||
if not stat then
|
||||
error(err, 2)
|
||||
else
|
||||
class.static[k] = v
|
||||
end
|
||||
end
|
||||
elseif class.properties.private[k] then
|
||||
error("cannot modify private property '"..tostring(k).."' outside of class", 2)
|
||||
elseif class.properties.public[k] then
|
||||
error("cannot modify public property '"..tostring(k).."' outside of object", 2)
|
||||
end
|
||||
end,
|
||||
__call = function(self, ...)
|
||||
local privObj = {}
|
||||
local obj = {}
|
||||
|
||||
privCache[obj] = privObj
|
||||
publicCache[privObj] = obj
|
||||
|
||||
local properties = {}
|
||||
local privProperties = {}
|
||||
|
||||
setmetatable(properties, {__index=class.public})
|
||||
setmetatable(privProperties, {
|
||||
__index=function(t, k)
|
||||
return properties[k] or class.private[k]
|
||||
end
|
||||
})
|
||||
|
||||
local mt = {
|
||||
__index = function(t, k)
|
||||
local p = properties
|
||||
if t == privObj then
|
||||
p = privProperties
|
||||
end
|
||||
local v = p[k]
|
||||
if type(v) == "function" then
|
||||
local tEnv = {self=privObj}
|
||||
oop.injectEnv(tEnv)
|
||||
setmetatable(tEnv, {__index=env})
|
||||
setfenv(v, tEnv)
|
||||
end
|
||||
return v
|
||||
end
|
||||
}
|
||||
|
||||
local inConstructor = false
|
||||
|
||||
mt.__pairs = function(tbl)
|
||||
local loopThroughTables = {properties, class.public, class.static}
|
||||
if tbl == privObj then
|
||||
table.insert(loopThroughTables, 2, class.private)
|
||||
end
|
||||
local cls = class
|
||||
while cls.parent do -- this WONT WORK
|
||||
cls = cls.parent
|
||||
if tbl == privObj then
|
||||
table.insert(loopThroughTables, cls.private)
|
||||
end
|
||||
table.insert(loopThroughTables, cls.public)
|
||||
table.insert(loopThroughTables, cls.static)
|
||||
end
|
||||
|
||||
local currentIndex = 1
|
||||
|
||||
local had = {}
|
||||
|
||||
local function customIterator(_, prevKey)
|
||||
local key, value
|
||||
|
||||
while currentIndex <= #loopThroughTables do
|
||||
key, value = next(loopThroughTables[currentIndex], prevKey)
|
||||
while key ~= nil and had[key] do -- skip keys that were already found
|
||||
key, value = next(loopThroughTables[currentIndex], key)
|
||||
end
|
||||
|
||||
if key ~= nil then
|
||||
had[key] = true
|
||||
return key, tbl[key]
|
||||
else
|
||||
prevKey = nil
|
||||
currentIndex = currentIndex + 1
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
return customIterator, tbl, nil
|
||||
end
|
||||
|
||||
mt.__newindex = function(tbl, k, v)
|
||||
if class.properties.consts[k] and not inConstructor then
|
||||
error("cannot modify const '"..tostring(k).."'", 2)
|
||||
end
|
||||
if tbl ~= privObj and class.properties.private[k] then
|
||||
error("cannot modify private property '"..tostring(k).."' outside of class", 2)
|
||||
end
|
||||
if class.properties.types[k] then
|
||||
local stat,err = typeRestriction(k, class.properties.types[k], v)
|
||||
if not stat then
|
||||
error(err, 2)
|
||||
end
|
||||
end
|
||||
if class.properties.static[k] then
|
||||
-- change in whole class
|
||||
class.static[k] = v
|
||||
elseif class.properties.private[k] then
|
||||
privProperties[k] = v
|
||||
else
|
||||
properties[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
local mt2 = {}
|
||||
|
||||
for k,v in pairs(mt) do
|
||||
mt2[k] = v
|
||||
end
|
||||
|
||||
for k,v in pairs(class.metatable.public) do
|
||||
mt[k] = function(...)
|
||||
local tEnv = {self=privObj}
|
||||
oop.injectEnv(tEnv)
|
||||
setmetatable(tEnv, {__index=env})
|
||||
setfenv(v, tEnv)
|
||||
return v(sanitizeArgs(...))
|
||||
end
|
||||
end
|
||||
|
||||
for k,v in pairs(class.metatable.private) do
|
||||
mt2[k] = function(...)
|
||||
local tEnv = {self=privObj}
|
||||
oop.injectEnv(tEnv)
|
||||
setmetatable(tEnv, {__index=env})
|
||||
setfenv(v, tEnv)
|
||||
return v(sanitizeArgs(...))
|
||||
end
|
||||
end
|
||||
|
||||
setmetatable(obj, mt)
|
||||
setmetatable(privObj, mt2)
|
||||
|
||||
local constructors = {class.constructor}
|
||||
local cls = class
|
||||
while cls.parent do
|
||||
cls = cls.parent
|
||||
if cls.constructor then
|
||||
table.insert(constructors, 1, cls.constructor)
|
||||
end
|
||||
end
|
||||
for t=1,#constructors do
|
||||
inConstructor = true
|
||||
local tEnv = {self=privCache[obj] or obj}
|
||||
oop.injectEnv(tEnv)
|
||||
setmetatable(tEnv, {__index=env})
|
||||
setfenv(constructors[t], tEnv)
|
||||
local status,tObj = pcall(constructors[t],sanitizeArgs(...))
|
||||
if not status then
|
||||
error(tObj, 2)
|
||||
end
|
||||
if tObj == false then
|
||||
return false
|
||||
elseif tObj then
|
||||
obj = publicCache[tObj] or tObj
|
||||
end
|
||||
inConstructor = false
|
||||
end
|
||||
|
||||
objectCache[obj] = class
|
||||
objectCache[privObj] = class
|
||||
|
||||
for k,v in pairs(privObj) do
|
||||
if type(v) == "table" and v == class.private[k] then
|
||||
privObj[k] = instantiate(v)
|
||||
end
|
||||
end
|
||||
|
||||
return obj
|
||||
end,
|
||||
}
|
||||
|
||||
local iClass
|
||||
if inherit then
|
||||
iClass = classCache[inherit]
|
||||
class.parent = iClass
|
||||
setmetatable(class.static, {__index=iClass.static})
|
||||
setmetatable(class.public, {
|
||||
__index = function(tbl, key)
|
||||
if iClass.public[key] ~= nil then
|
||||
return iClass.public[key]
|
||||
else
|
||||
return class.static[key]
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
setmetatable(class.private, {
|
||||
__index = function(tbl, key)
|
||||
if iClass.private[key] ~= nil then
|
||||
return iClass.private[key]
|
||||
else
|
||||
return class.public[key]
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
for k,v in pairs(class.properties) do
|
||||
setmetatable(v, {__index=iClass.properties[k]})
|
||||
end
|
||||
|
||||
setmetatable(class, {__index=iClass})
|
||||
else
|
||||
setmetatable(class.public, {__index=class.static})
|
||||
setmetatable(class.private, {__index=class.public})
|
||||
end
|
||||
|
||||
loopThrough = {"static","private","public"}
|
||||
for i,l in ipairs(loopThrough) do
|
||||
if classTbl[l] then
|
||||
for k,v in pairs(classTbl[l]) do
|
||||
if type(k) == "number" then
|
||||
if type(v) == "table" and v.name then
|
||||
if v.const then
|
||||
class.properties.consts[v.name] = true
|
||||
end
|
||||
if v.type then
|
||||
class.properties.types[v.name] = v.type
|
||||
end
|
||||
if v.value or v.type then
|
||||
class[l][v.name] = v.value or default[v.type]
|
||||
end
|
||||
class.properties[l][v.name] = true -- now values can be generic typed starting with nil but still recognized
|
||||
elseif type(v) == "string" then
|
||||
class.properties[l][v] = true
|
||||
end
|
||||
elseif type(k) == "string" then
|
||||
if l == "public" and k == name then -- constructor
|
||||
if type(v) ~= "function" then
|
||||
error("invalid constructor: expected function, got "..type(v),2)
|
||||
end
|
||||
class.constructor = v
|
||||
elseif metamethods[k] then
|
||||
class.metatable[l][k] = v
|
||||
else
|
||||
class[l][k] = v
|
||||
class.properties[l][k] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local classObj = {}
|
||||
|
||||
classCache[classObj] = class
|
||||
class.obj = classObj
|
||||
|
||||
setmetatable(classObj, class.metatable.static)
|
||||
|
||||
env[name] = classObj
|
||||
end
|
||||
|
||||
function oop.class(name)
|
||||
return function(class)
|
||||
if classCache[class] then -- a class object was passed meaning we're gonna inherit
|
||||
return function(realclass)
|
||||
makeClass(name, realclass, class)
|
||||
end
|
||||
else
|
||||
makeClass(name, class)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local oType = type
|
||||
function oop.type(value, numberType)
|
||||
if oType(value) == "table" then
|
||||
if objectCache[value] then
|
||||
return objectCache[value].name
|
||||
elseif classCache[value] then
|
||||
return "class"
|
||||
else
|
||||
return "table"
|
||||
end
|
||||
elseif numberType and oType(value) == "number" then
|
||||
return getNumberType(value)
|
||||
else
|
||||
return oType(value)
|
||||
end
|
||||
end
|
||||
|
||||
function oop.getClassName(class)
|
||||
oop.expect(1, class, "class")
|
||||
return classCache[class].name
|
||||
end
|
||||
|
||||
function oop.isType(value, ...)
|
||||
local types = {oop.type(value), oType(value), oop.type(value, true)}
|
||||
if types[1] ~= types[2] and types[3] ~= "class" then
|
||||
types[3] = "object"
|
||||
end
|
||||
local allowedTypes = {...}
|
||||
for i,t in ipairs(types) do
|
||||
for i=1, #allowedTypes do
|
||||
if allowedTypes[i] == t or (allowedTypes[i] == "double" and t == "number") then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function oop.expect(index, value, ...)
|
||||
local level = 3
|
||||
local allowedTypes = {...}
|
||||
if type(allowedTypes[1]) == "number" then
|
||||
level = allowedTypes[1]
|
||||
table.remove(allowedTypes, 1)
|
||||
end
|
||||
if type(allowedTypes[1]) ~= "string" then
|
||||
error("bad argument #3: expected string", 2)
|
||||
end
|
||||
if oop.isType(value, ...) then
|
||||
return value
|
||||
end
|
||||
|
||||
local expectlist
|
||||
local numbertype = false
|
||||
for t=1,#allowedTypes do
|
||||
if allowedTypes[t] == "int" then
|
||||
numbertype = true
|
||||
end
|
||||
end
|
||||
if #allowedTypes > 1 then
|
||||
local lastType = allowedTypes[#allowedTypes]
|
||||
allowedTypes[#allowedTypes] = nil
|
||||
expectlist = table.concat(allowedTypes, ", ").." or "..lastType
|
||||
else
|
||||
expectlist = allowedTypes[1]
|
||||
end
|
||||
local t = oop.type(value, numbertype)
|
||||
|
||||
error("bad argument #"..index..": expected "..expectlist.." got "..t.." ("..tostring(value)..")", level)
|
||||
end
|
||||
|
||||
function oop.getPublicObject(privateObject)
|
||||
return publicCache[privateObject] or privateObject
|
||||
end
|
||||
|
||||
function oop.import(filepath)
|
||||
oop.expect(1, filepath, "string")
|
||||
|
||||
local makePackage = require("cc.require")
|
||||
local tEnv = setmetatable({shell=shell, multishell=multishell},{__index=env})
|
||||
tEnv.require, tEnv.package = makePackage.make(tEnv, dir)
|
||||
tEnv.oop = oop
|
||||
|
||||
oop.injectEnv(tEnv)
|
||||
|
||||
local pathlog = filepath
|
||||
local filepath2 = filepath..".lua"
|
||||
pathlog = pathlog.."\n"..filepath2
|
||||
|
||||
if filepath:sub(1,1) ~= "/" then
|
||||
pathlog = pathlog.."\n"..fs.combine(dir, filepath)
|
||||
pathlog = pathlog.."\n"..fs.combine(dir, filepath2)
|
||||
if fs.exists(fs.combine(dir, filepath)) then
|
||||
filepath = fs.combine(dir, filepath)
|
||||
elseif fs.exists(fs.combine(dir, filepath2)) then
|
||||
filepath = fs.combine(dir, filepath2)
|
||||
end
|
||||
end
|
||||
|
||||
if fs.exists(filepath2) and not fs.exists(filepath) then
|
||||
filepath = filepath2
|
||||
end
|
||||
|
||||
if not fs.exists(filepath) then
|
||||
error("Could not find file:"..pathlog,2)
|
||||
end
|
||||
|
||||
local f = fread(filepath)
|
||||
|
||||
local func, err = load(f, "@"..filepath, nil, tEnv)
|
||||
|
||||
if not func then
|
||||
error(err, 2)
|
||||
else
|
||||
local ok,err = pcall(func)
|
||||
if not ok then
|
||||
error(err, 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return oop
|
||||
Reference in New Issue
Block a user