asynchronousai's picture
Upload 33 files
1ae2e8e verified
--!strict
--!native
local CS = {}
local assemblyGlobal = {}
local function chainIndex(location: table, ...: table): () -> any
local names = {...}
return function(t, k)
for _, name in names do
local tbl = location[name]
local v = tbl[k]
if v ~= nil then
return v
end
end
end
end
local function createArithmeticOperators(self, mt, fieldName): table
-- TODO: bitwise ops (if necessary) (or possible)
local function getNumericValue(value: table | number): number
return if typeof(value) == "table" and value.__isEnumMember then value[fieldName] else value
end
function mt:__add(other)
return self[fieldName] + getNumericValue(other)
end
function mt:__sub(other)
return self[fieldName] - getNumericValue(other)
end
function mt:__mul(other)
return self[fieldName] * getNumericValue(other)
end
function mt:__div(other)
return self[fieldName] / getNumericValue(other)
end
function mt:__idiv(other)
return self[fieldName] // getNumericValue(other)
end
function mt:__mod(other)
return self[fieldName] % getNumericValue(other)
end
function mt:__pow(other)
return self[fieldName] ^ getNumericValue(other)
end
function mt:__unm()
return -self[fieldName]
end
return mt
end
export type Class = table;
export type Namespace = {
name: string;
parent: Namespace?;
members: { Namespace | Class };
class: (self: Namespace, name: string, create: (self: Namespace) -> Class) -> nil;
}
local CSNamespace = {} do
@native
function CSNamespace.new(name, parent)
local self = {}
self.name = name
self.parent = parent
self.members = {}
self["$loadCallbacks"] = {}
if self.parent ~= nil then
self = setmetatable(self, { __index = self.parent })
end
return setmetatable(self, CSNamespace)
end
@native
function CSNamespace:__index(index)
return self.members[index] or CSNamespace[index]
end
@native
function CSNamespace:__newindex(index, value)
self.members[index] = value
end
@native
function CSNamespace:__tostring(index)
return self.name
end
CSNamespace["$getMember"] = @native function(self, name)
return self.members[name]
end
CSNamespace["$onLoaded"] = @native function(self, callback)
table.insert(self["$loadCallbacks"], callback)
end
@native
function CSNamespace:class(name, create)
CS.class(name, create, self)
end
@native
function CSNamespace:namespace(name, registerMembers)
CS.namespace(name, registerMembers, self.members, self)
end
end
@native
function CS.classInstance(class: Class, mt: table, namespace: Namespace?)
local instance = {}
instance["$className"] = class.__name
@native
local function getSuperclass()
if class.__superclass == nil then return end
if class.__superclass:match(".") == nil then
return assemblyGlobal[class.__superclass]
end
local pieces = class.__superclass:split(".");
local result = assemblyGlobal
for _, piece in pieces do
result = result[piece] or result
end
return result
end
@native
function mt.__tostring()
return class.__name
end
instance["$base"] = @native function(...)
if instance["$superclass"] ~= nil then return end
local Superclass = getSuperclass()
local superclassInstance = Superclass.new(...)
instance["$superclass"] = superclassInstance
mt.__index = superclassInstance
end
return setmetatable(instance, mt)
end
@native
function CS.classDef(name: string, namespace: Namespace?, superclass: string?, ...: string)
local mt = {}
mt.__index = chainIndex(if namespace ~= nil then namespace else assemblyGlobal, ...)
@native
function mt.__tostring()
return name
end
local class = {}
class.__name = name
class.__superclass = superclass
return setmetatable(class, mt)
end
@native
function CS.class(name: string, create: (namespace: Namespace?) -> table, namespace: Namespace?)
local location = if namespace ~= nil then namespace.members else assemblyGlobal
local class = create(namespace)
location[name] = class
end
@native
function CS.namespace(name: string, registerMembers: () -> nil, location: table?): Namespace
local parent = location
if location == nil then
location = assemblyGlobal
end
local namespaceDefinition = location[name] or CSNamespace.new(name, parent)
registerMembers(namespaceDefinition)
location[name] = namespaceDefinition
for _, callback in namespaceDefinition["$loadCallbacks"] do
callback()
end
return namespaceDefinition
end
@native
function CS.enum(name: string, definition: table, location: table): table
if location == nil then
location = assemblyGlobal
end
definition.__name = name
@native
function definition:__index(index: string | number): table
if index == "__name" then return name end
local member = {
name = index,
value = definition[index],
__isEnumMember = true
}
return setmetatable(member, createArithmeticOperators(member, {
__eq = @native function(self, other)
return typeof(other) == "table" and other.__isEnumMember and self.value == other.value
end,
__tostring = @native function(self)
return self.name
end
}, "value"))
end
@native
function definition:__eq(other: table): boolean
return self.__name == other.__name
end
@native
function definition:__tostring(): string
return self.__name
end
location[name] = location[name] or table.freeze(setmetatable({}, definition))
return location[name]
end
@native
function CS.is(object: any, class: Class | string): boolean
if typeof(class) == "table" and type(class.__name) == "string" then
return typeof(object) == "table" and type(object["className"]) == "string" and object["className"] == class.__name
end
-- metatable check
if typeof(object) == "table" then
obj = getmetatable(obj)
while object ~= nil do
if object == class then
return true
end
local mt = getmetatable(object)
if mt then
object = mt.__index
else
object = nil
end
end
end
if typeof(class) == "string" then
return if typeof(object) == "Instance" then object:IsA(class) else typeof(object) == class
end
return false
end
@native
function CS.getAssemblyType(name)
local env
if getfenv == nil then
env = _ENV
else
env = getfenv()
end
return assemblyGlobal[name] or env[name]
end
CS.class("Exception", @native function()
local class = CS.classDef("Exception")
@native
function class.new(message: string?): Exception
local mt = {}
local self = CS.classInstance(class, mt) :: Exception
if message == nil then message = "An error occurred" end
self.Message = message
@native
function mt.__tostring(): string
return `{self["$className"]}: {self.Message}`
end
@native
function self.Throw(withinTryBlock: boolean): nil
error(if withinTryBlock then self else tostring(self))
return nil
end
return self
end
return class
end)
export type Exception = {
Message: string;
Throw: () -> nil;
}
type CatchBlock = {
exceptionClass: string;
block: (ex: Exception?, rethrow: () -> nil) -> nil
}
@native
function CS.try(block: () -> nil, finallyBlock: () -> nil, catchBlocks: { CatchBlock })
local success: boolean, ex: Exception | string | nil = pcall(block)
if not success then
if typeof(ex) == "string" then
ex = CS.getAssemblyType("Exception").new(ex, false)
end
for _, catchBlock in catchBlocks do
if catchBlock.exceptionClass ~= nil and catchBlock.exceptionClass ~= ex["$className"] then continue end
catchBlock.block(ex :: Exception, (ex :: Exception).Throw)
end
end
if finallyBlock ~= nil then
finallyBlock()
end
end
return CS