Module:Class

local function debugPrintTable(t) local sb = {} for k, v in pairs(t) do   sb[#sb+1] = tostring(k)..' = '..tostring(v)..'\n' end return table.concat(sb) end

local function syntaxErrFn error "Syntax error" end local function syntaxSetMethodFn error "Attempting to set a field on a method" end

local __metatable = {} setmetatable(__metatable, { __metatable = {},  __index = { __tostring = function(t) return t.toStr end },  __newindex = function end, })

local function makeState(meta, state) if state == nil then state = {} end local get = meta.get local set = meta.set local call = meta.call meta = { __metatable = __metatable, __index = get and function(t, k) return get(t, state, k) end or syntaxErrFn, __newindex = set and function(t, k, v) return set(t, state, k, v) end or syntaxErrFn, __call = call and function(t, ...) return call(t, state, ...) end or syntaxErrFn, } local obj = {} setmetatable(obj, meta) return obj end

- -- Objects -- - local objMeta = {}

local function objGetFor(self, objState, key, class) local slot = class.slots[key] if slot == nil then local parent = class.parent if parent == nil then error("No slot "..key.." found on class "..objState.class.name) end -- TODO self.typeof return objMeta.get(self, objState, key, parent) else if slot.new then error("Constructors must be called on the class, not objects") end if slot.static then return slot.args and slot.body or slot.state else return slot.args and function(...) return slot.body(self, ...) end or objState.storage[key] end end end

function objMeta:get(objState, key) local class = objState.class return key == "typeof" and function return class.typeObj end or objGetFor(self, objState, key, class) end

function objMeta:set(objState, key, val, class) local curClass = class or objState.class local slot = curClass.slots[key] if slot == nil then local parent = curClass.parent if parent == nil then error("No slot "..key.." found on class "..objState.class.name) end -- TODO self.typeof return objMeta.set(self, objState, key, val, parent) else if slot.args then error("Attempting to assign a value to the method "..key) end if slot.static then slot.state = val -- TODO Type checking? else objState.storage[key] = val end end end

- -- Classes -- - local classMeta = {}

local classGetFor function classGetFor(self, classState, key, class) local slot = (class or classState).slots[key] if slot == nil then local parent = (class or classState).parent if parent == nil then error("No slot "..key.." found on class "..classState.name) end -- TODO self.typeof return classGetFor(self, classState, key, parent) else if slot.new and class then error("No slot "..key.." found on class "..classState.name) end if slot.static then return slot.args and slot.body or slot.state elseif slot.new then return function(...) local objState = {class = classState, storage = {}} local obj = makeState(objMeta, objState) -- TODO Super? slot.body(obj, ...) return obj end else error("Attempting to access "..key.." from a static context") end end end

function classMeta:get(classState, key) if key == "toStr" then return function return classState.name.."#" end end return classGetFor(self, classState, key) end

function classMeta:set(classState, key, val, isSuper) local slot = classState.slots[key] if slot == nil then local parent = classState.parent if parent == nil then error("No slot "..key.." found on class "..self) end -- TODO self.typeof return classMeta.set(self, parent, key, val, true) else if slot.args then error("Attempting to assign a value to the method "..key) end if slot.static then slot.state = val -- TODO Type checking? else error("Attempting to access "..key.." from a static context") end end end

- -- Slot Definition -- - local defMeta = {} local defBuilder = {} local slotModifiers = {}

function slotModifiers.static(state) if state.static ~= nil then syntaxErrFn end state.static = true end

function slotModifiers.new(state) if state.new ~= nil then syntaxErrFn end state.new = true state.type = "This" state.args = {} end

-- Initializing a slot declaration function defMeta:get(state, key) local subState = {parent = state} return makeState(defBuilder, subState)[key] end

-- A step in slot declaration function defBuilder:get(state, key) local modifier = slotModifiers[key] if modifier ~= nil then modifier(state) elseif state.type == nil then state.type = key elseif state.name ~= nil then syntaxErrFn else state.name = key end return self end

-- Finalizing a slot declaration function defBuilder:call(state, ...) local args = { ... } if #args > 0 then for i, arg in ipairs(args) do args[i] = state.parent.argStates[arg] end state.args = args end local slots, name = state.parent.slots, state.name if slots[name] ~= nil then error( "Slot "..name.." already defined" ) end slots[name] = state state.parent = nil end

-- Assigning a method body function defMeta:set(state, key, val) if type( val ) ~= "function" then syntaxErrFn end local slot = state.slots[key] if slot == nil then error("Attempted to define the body of the method "..key.." without declaring it first") end if slot.body ~= nil then error("Method "..key.." already has been defined") end if slot.args == nil then slot.args = {} end slot.body = val end

- -- Arg Builder -- - local argMeta = {} local argBuilder = {} local argModifiers = {}

function argMeta:get(state, key) local argState = {} local arg = makeState(argBuilder, argState) state.argStates[arg] = argState return arg[key] end

function argBuilder:get(state, key) local modifier = argModifiers[key] if modifier ~= nil then modifier(state) elseif state.type == nil then state.type = key elseif state.name ~= nil then syntaxErrFn else state.name = key end return self end

--- -- Class Builder -- --- local classBuilder = {} local classModifiers = {}

-- Declare some functions to be replaced once we have the base types defined. local function buildReflection(state) end -- Define the actual functionality once we have the reflection types local function getClassState return nil end -- We use a nil reference for "Obj"

-- Class modifiers function classModifiers.from(state, key) if key == nil then state.expects = "from" elseif state.parent ~= nil then syntaxErrFn else state.parent = getClassState(key) state.expects = nil end end

-- Declare the class function classBuilder:get(state, key) local modifier = classModifiers[key] if state.expects then if modifier ~= nil then syntaxErrFn end classModifiers[state.expects](state, key) return self elseif modifier ~= nil then modifier(state) elseif state.name == nil then state.name = key else syntaxErrFn end return self end

-- Build the class function classBuilder:call(state, cb) -- Parse the definitions cb(makeState(defMeta, state), makeState(argMeta, state))

-- Register the class definition local classes = state.static.classes if classes[state.name] ~= nil then error("Class "..state.name.." already defined in:\n"..state.loc) end classes[state.name] = state

-- Build references if state.parent == nil then state.parent = getClassState"Obj" end buildReflection(state)

-- Clean up state.argStates = nil state.static = nil

-- Now build the class local classObj = makeState(classMeta, state) state.classObj = classObj return classObj end

-- -- Class Static -- --

local ClassMeta = {} local ClassState = { classes = {} }

function ClassMeta:get(state, key) if key == "toStr" then return function return "Class definition object. To use, please read documentation on Module:Class" end end local traceIterator = debug.traceback:gmatch("[^\r\n]+") traceIterator; traceIterator; traceIterator return makeState(classBuilder, {   static = state,    slots = {},    argStates = {},    loc = traceIterator,  })[key] end

Class = makeState(ClassMeta, ClassState)

-- -- Global Table -- -- local globalMeta = {} local arg

function globalMeta:__index(key) if key == "arg" then return arg end return getClassState(key).classObj end

function globalMeta:__newindex(key, val) if key == "arg" then arg = val else error("Setting of global variables is prohibited: ("..key.." = "..tostring(val)..")") end end

setmetatable(_G, globalMeta)

-- Define Obj --

local Obj = Class .Obj (function(def,arg) def .new .make  function def:make  end

def .Str .toStr function def :toStr return "Object" end end)

local objState = ClassState.classes.Obj -- We'll use this later for injecting definitions

-- Now we can define getClassState (since there's a root class) function getClassState(name) local class = ClassState.classes[name] if class ~= nil then return class end if not pcall(require, "Module:Class/"..name) then error("Unknown type: "..name) end class = ClassState.classes[name] if class == nil then error("Unknown type: "..name) end return class end

- -- Define Type -- - local Type = Class .Type (function(def,arg) def .Type .base  def .Str .name

def .new .make(arg.Type.base, arg.Str.name) -- TODO super constructor function def:make(base, name) self.base = base self.name = name end

def .Str .toStr function def:toStr return self.name.."#" end end)

-- Make types for the two classes we have and inject them objState.slots.typeof = { type = "Type", name = "typeof", args = {} } local objType = Type.make(nil, "Obj") objState.typeObj = objType

local typeState = ClassState.classes.Type typeState.typeObj = Type.make(objType, "Type")

-- Make an intermediary buildReflection function before we have the rest of the types function buildReflection(state) state.typeObj = Type.make(state.parent.typeObj, state.name) end

- -- Define Slot -- - Class .Slot (function(def,arg) def .Type .parent  def .Str .name

def .new .make(arg.Type.parent, arg.Str.name) function def:make(parent, name) self.parent = parent self.name = name end

def .Str .toStr function def :toStr return self.parent.."#"..self.name end end)

Class .Field .from .Slot (function(def,arg) def .new .make(arg.Type.parent, arg.Str.name) -- TODO super constructor  function def:make(parent, name)    self.parent = parent    self.name = name  end end)

Class .Method .from .Slot (function(def,arg) def .new .make(arg.Type.parent, arg.Str.name) -- TODO super constructor  function def:make(parent, name)    self.parent = parent    self.name = name  end end)

return Class

- -- Example -- - --[=[ -- class Point Class .Point (function(def,arg) def .static .This .defVal  def .Num .x  def .Num .y

def .new .make(arg.Num.x, arg.Num.y) function def:make(x, y)    self.x = x or 0 self.y = y or 0 end

def .This .translate(arg.Point.p) function def:translate(p) return Point.make(self.x + p.x, self.y + p.y) end

def .static .Str .foobar function def.foobar return "asdf" end

def .Str .toStr function def:toStr return "Point("..self.x..","..self.y..")" end end)

-- class SpecialPoint Class .SpecialPoint .from .Point (function(def,arg) def .new .make(arg.Num.x, arg.Num.y)  function def:make(x, y)    self.x = x or 0    self.y = y or 0  end

def .Num .sum function def:sum return self.x + self.y end end)

Point.defVal = Point.make(0, 0)

return SpecialPoint --]=]