Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
363 lines (320 sloc) 10.5 KB
/*
** OOP Class implementation
**
** By Samuel D. Crow
**
** See LICENSE file for licensing details
**
*/
;TODO add checking
;TODO add exception handling support
;TODO add static function support
Global _class={} ;namespace
;indicate that this file has been included
Const #CLASS = True
;_parent is deliberately left undefined for root class
_class["_interface"]={} ;global interface table
_class["_error"]={} ;locale specific error functions
_class["_static"]={} ;static members
_class["_name"]="Root Class"
_class["_func"]={} ;static functions
_class["_v"]={} ;vector table
_class["_data"]={} ;fields
_class["_implement"]={} ;interfaces implemented
;*****************************************************************************
;error messages
;*****************************************************************************
Switch(GetSystemLanguage())
Case #LANGUAGE_ENGLISH:
FallThrough
Default:
_class._error["MethodExists"]=Function(name)
Error(name .. " method already exists\nin class " .. self._name)
EndFunction
_class._error["MethodTable"]=Function()
Error("Method parameter defaults must be passed as a table")
EndFunction
_class._error["MethodNotFound"]=Function()
Error("Method not found")
EndFunction
_class._error["NoParent"]=Function()
Error("No parent class for method call")
EndFunction
_class._error["ImpliedTable"]=Function()
Error("Implied interfaces must be passed in a table")
EndFunction
_class._error["InterfaceName"]=Function()
Error("Illegal type name on interface")
EndFunction
_class._error["ProtectedInterface"]=Function()
Error("Protected interfaces not allowed")
EndFunction
_class._error["InterfaceDoesNotExist"]=Function(i, name)
Error("Non-existant interface " .. i .. "\nis implied by interface " .. name)
EndFunction
_class._error["Underscore"]=Function(name)
Error("Underscore indicates illegal protected member\nin " .. name)
EndFunction
_class._error["DuplicateDefinition"]=Function(i, name)
Error("Duplicate definition of method " .. i .. "\nin interface " .. name)
EndFunction
_class._error["InterfaceNoMethod"]=Function(i, name)
Error(i .. " method not implemented\nfrom implementation of interface\n" .. name)
EndFunction
_class._error["InterfaceUndefined"]=Function()
Error("Interface not defined")
EndFunction
_class._error["MissingMethod"]=Function(i)
Error("Does not implement all methods of interface " .. i)
EndFunction
_class._error["ProtectedGetter"]=Function()
Error("Protected field requested by public getter")
EndFunction
_class._error["ProtectedSetter"]=Function()
Error("Public setter attempted to write to a protected field")
EndFunction
_class._error["UnknownClass"]=Function()
Error("Unknown class encountered")
EndFunction
_class._error["UnknownType"]=Function()
Error("Unknown type encountered")
EndFunction
_class._error["TypeOfArgs"]=Function()
Error("Too many arguments in TypeOf function")
EndFunction
EndSwitch
;*****************************************************************************
;helper functions
;*****************************************************************************
;clear parameter number from vararg table to avoid namespace issues
;Function p_RawArg(a)
; RawSet(a, "n", Nil)
; Return(a)
;EndFunction
;*****************************************************************************
;methods
;*****************************************************************************
; method declaration
; params is table containing default values of each parameter key
; body is an actual function definition with self passed as first argument
Function _class:Method(name, params, body)
If RawGet(self, name) Then _class._error.MethodExists(name)
If GetType(params) <> #TABLE Then _class._error.MethodTable()
Local buf={}
buf["_body"]=body
buf["_params"]=params
self._v[name]=buf
self[name]=self._v[name]
EndFunction
; return method and containing class if they are available
Function _class:GetMethod(name)
Local c=self, m
Repeat
m=RawGet(c._v, name)
If m Then Return(m, c)
c=RawGet(c, "_parent") ; check for inheritance
Until IsNil(c)
Return(Nil, Nil)
EndFunction
Function _class:_MethodWorker(name, fn, params)
Local m, c=self.GetMethod(name)
If IsNil(c) Then _class._error.MethodNotFound()
;propogate default entries
For i In m._params
If IsNil(RawGet(params, i)) Then params[i]=m._pararms.i
Next
Return(fn(self, params))
EndFunction
;execute a method
Function _class:DoMethod(name, ...)
Return(self:_MethodWorker(name, self._v.m._body, p_RawArg(arg)))
EndFunction
;inherit from parent method
Function _class:DoParentMethod(name, ...)
Local _p=RawGet(self, "_parent")
If IsNil(_p) Then _class._error.NoMethod()
Return(self:_MethodWorker(_p._name, _p._v.m._body, p_RawArg(arg)))
EndFunction
;*****************************************************************************
;interfaces
;*****************************************************************************
;declare in the global interface table
;implied is a table containing the additional interfaces implied by this one
Function _class:Interface(name, implied, ...)
If GetType(implied)<>#TABLE Then _class._error.ImpliedTable()
If GetType(name)<>#STRING Then _class._error.InterfaceName()
If LeftStr(name, 1) = "_" Then _class._error.ProtectedInterface()
Local iface={}, buf={}, s, s2, i, j
; check for illegal implied interfaces
For s, i In Pairs(implied)
Local ibuf=RawGet(_class._interface, i) ; implementation buffer
If IsNil(ibuf) Then _class._error.InterfaceDoesNotExist(i, name)
; add methods to buf so duplicates can be detected later
For j, s2 In Pairs(ibuf._body)
RawSet(buf, j, True)
Next
Next
For s, i In IPairs(arg)
If LeftStr(i,1)="_" Then _class._error.Underscore("interface " .. name)
If RawGet(buf, i) Then _class._error.DuplicateDefinition(i, name)
RawSet(buf, i, True)
Next
iface["_body"]=buf
iface["_imply"]=implied
_class._interface[name]=iface
EndFunction
; interface implementation access
Function _class:GetImplementation(name)
Local iface = RawGet(_class._interface, name), buf={}, iter, problem
If IsNil(iface) Then _class._error.UndefinedInterface()
buf["_implementation"]=name
buf["_target"]=self
Local iter
For i In iface._body
iter=self.GetMethod(i)
If IsNil(iter) Then _class._error.InterfaceNoMethod(i, name)
;wrap method to fix self pointer
buf[i]=Function(...)
local a=arg
a.n=Nil
a[0]=buf._target
Return(iter(Unpack(a)))
EndFunction
Next
Return(buf)
EndFunction
;check if current class implements an interface
;returns boolean result
Function _class:DoesImplement(interface)
local iface = _class._interface[interface], buf={}, problem
For i In iface._imply
buf, problem=_class._IFaceWorkLoop(name, buf, i)
If problem Then Return(False)
Next
buf, problem=_class._IFaceWorkLoop(name, buf, iface._body)
Return(Not(problem))
EndFunction
;*****************************************************************************
;allocation of new classes
;*****************************************************************************
;inheritance allocation worker method
;iface is a set of names of interfaces implemented
Function _class:_inherit(name, parent, iface)
Local c={}
c["_v"]=CopyTable(parent._v)
c["_data"]=CopyTable(parent._data)
c["_implement"]=CopyTable(parent._implement)
c["_func"]=CopyTable(parent._func)
c["_name"]=name
c["_parent"]=parent
;implement additional interfaces
For i In iface
If IsNil(RawGet(_class._interface, i))
_class._error.InterfaceUndefined()
EndIf
If Not(c.DoesImplement(i)) Then _class._error.MissingMethod(i)
_class._interface[i]=i._body
Next
Return(c)
EndFunction
;inherit from parent and implement additional interfaces
Function _class:Extend(name, ...)
Return(self._inherit(name, self._parent, p_RawArg(arg)))
EndFunction
;new class inherits only from root class
Function New(name, ...)
Return(_class._inherit(name, _class, p_RawArg(arg)))
EndFunction
;*****************************************************************************
;data members
;*****************************************************************************
; member declaration
; value is a default and must be associated with a type
Function _class:Member(name, value)
self._data[name]=value
EndFunction
; static member declaration
Function StaticMember(name, value)
_class._static[name]=value
EndFunction
;*****************************************************************************
;accessor methods
;*****************************************************************************
;default getter method
_class._v["Get"]=Function(self, name)
If (LeftStr(name, 1)="_") Then _class._error.ProtectedGetter()
Local value=RawGet(self._data, name), p=self._parent
While (IsNil(value) And p)
value=RawGet(p, name)
p=RawGet(p, "_parent")
Wend
Return(value)
EndFunction
;default setter method
_class._v["Set"]=Function(self, name, value)
If (LeftStr(name,1)="_") Then _class._error.ProtectedSetter()
self._data[name]=value
EndFunction
;*****************************************************************************
;type checking
;*****************************************************************************
Const #TYPE_NIL=0
Const #TYPE_NUMBER=1
Const #TYPE_STRING=2
Const #TYPE_FUNCTION=3
Const #TYPE_TABLE=4
Const #TYPE_CLASS=5
Const #TYPE_INTERFACE=6
Const #TYPE_UNDEFINED=7
Const #TYPE_METHOD=8
Function _class:TypeOf(...)
Switch (arg.n)
Case 0:
; type of self as class or interface
Local n=RawGet(self, "_name")
If n
Return(#TYPE_CLASS)
Else
n=RawGet(self, "_implementation")
If n Then Return(#TYPE_INTERFACE)
_class._error.UnknownClass()
EndIf
Case 1:
; type of primitive
Local a=arg[0], n=self.Get(a)
If n
Switch (GetType(n))
Case #NUMBER:
Return(#TYPE_NUMBER)
Case #FUNCTION:
Return(#TYPE_FUNCTION)
Case #STRING:
Return(#TYPE_STRING)
Case #TABLE:
Return(#TYPE_TABLE)
Default:
_class._error.UnknownType()
EndSwitch
Else
Local f, c=self.GetMethod(a) ; NOTE: two return codes
If f Then Return(#TYPE_METHOD)
Return(#TYPE_UNDEFINED)
EndIf
Default:
_class._error.TypeOfArgs()
EndSwitch
EndFunction
; subclass type check
Function _class:IsTypeOf(query)
Local i=self
While Not IsNil(i)
If i=query Then Return(True)
i=RawGet(i, "_parent")
Wend
Return(False)
EndFunction
;*****************************************************************************
;standard interface definitions
;*****************************************************************************
;note iterator contents are stored under the "value" key by convention
_class:Interface("Iteratable", {}, "Start", "GetNext")
You can’t perform that action at this time.