8dfc300629e6eae6015ce5a80d4d4f9949e5fc24
[luaproto] / proto.lua
1 --- Prototype based Objectsysten
2 ---   Objects are `cloned` from existing objects ,`Object` is the mother of all objects.
3 ---   One can introduce additional inheritance paths with 'inherit'. Lookup order is
4 ---   first the prototype chain from `clone` and then all parent chains in the order
5 ---   they where added.
6 ---
7 ---   Multiple inheritance
8 ---     Objects can have an ordered list of other objects where they inhterit from
9 ---
10 ---   Differential inheritance
11 ---     Dynamic changes in prototypes and parent objects are reflected in childs unless a
12 ---     child overidden a slot.
13 ---
14 ---   Normal member lookup is child first then for each parent depth first.
15 ---
16 ---
17 ---
18
19
20 -- Object is the root object for all objects to be created
21 local Object = {}
22 Object.__metatable = Object
23 setmetatable(Object, Object)
24
25 --- Object:proto_clone ()
26 --- Object:proto_clone {...}
27 ---   takes an optional table for new mebers of the cloned object
28 ---   Sole strings on this table (aka, the integer part) define which members to copy up
29 function Object:proto_clone(newobject)
30     assert(not newobject or type(newobject) == 'table')
31     newobject = newobject or {}
32     newobject.__metatable = newobject
33     setmetatable(newobject, newobject)
34     newobject.__index = self
35
36     -- copy up members
37     for i=1,#newobject do
38         assert(type(newobject[i]) == 'string')
39         newobject[newobject[i]] = self[newobject[i]]
40         newobject[i] = nil
41     end
42
43     return newobject
44 end
45
46
47 --- Object:proto_inherit (...)
48 ---   adds some more Objects to the inheritance list
49 function Object:proto_inherit (...)
50     local args = {...}
51     if #args > 0 then
52         local parents = rawget(self,"__proto_parents") or rawset(self,"__proto_parents", {self.__index} or {}) and rawget(self,"__proto_parents")
53
54         for i=1,#args do
55             assert((pcall(function () return args[i].indexable end)), "object not indexable")
56             parents[#parents + 1] = args[i]
57         end
58
59         if type(self.__index) == 'table' then
60             self.__index = function(self, key)
61                                local parents = rawget(self,"__proto_parents")
62                                for i=1,#parents do
63                                    local value = parents[i][key]
64                                    if value then
65                                        return value
66                                    end
67                                end
68                            end
69         end
70     end
71     return self
72 end
73
74
75
76
77 --- Object:proto_prototypes ()
78 ---   iterator over all prototypes of an object in search order (depth first, no duplicates, including self)
79 ---    for parent in object:prototypes()
80 local function next_prototype(state)
81     local object, mystate
82     repeat
83         mystate = state.state
84
85         repeat
86             mystate.pos = (mystate.pos or 0) +1
87             object = mystate.parents[mystate.pos]
88         until not object or not state.objects_done[object]
89
90         if object then
91             state.objects_done[object] = true
92             state.state = {oldstate = mystate, parents= rawget(object, "__proto_parents") or {object.__index} or {}}
93             return object
94         end
95
96         state.state = state.state.oldstate
97     until not state.state
98 end
99
100 function Object:proto_prototypes()
101     local state = {objects_done = {}, state={parents = {self}}}
102     return next_prototype, state
103 end
104
105
106 --- Object:proto_members ()
107 ---   iterate over members including inherited ones of a object, no duplicates
108 ---   returns member, value
109 local function next_member(state)
110     local value, mystate
111
112     repeat
113         mystate = state.state
114
115         repeat
116             mystate.mpos,value = next(state.object, mystate.mpos)
117         until not value or not state.members_done[mystate.mpos]
118
119         if value then
120             state.members_done[mystate.mpos] = true
121             return mystate.mpos, value
122         end
123
124         state.object = next_prototype(state)
125     until not state.object
126 end
127
128
129 function Object:proto_members()
130     local state = {members_done = {}, objects_done = {}, state={parents = {self}}}
131     state.object = next_prototype(state)
132     return next_member, state
133 end
134
135
136
137
138 --- Object:proto_findall (name)
139 ---   finds all members including inherited ones of a given name
140 ---   returns object, value
141 local function next_value(state)
142     local mystate
143
144     repeat
145         mystate = state.state
146         state.object = next_prototype(state)
147         if state.object then
148             local value = rawget(state.object, state.name)
149             if value then
150                 return state.object, value
151             end
152         end
153     until not state.object
154 end
155
156 function Object:proto_findall(name)
157     local state = {objects_done = {}, name=name, state={parents = {self}}}
158     return next_value, state
159 end
160
161
162
163 --- Object:proto_object (name)
164 ---   return the object where a member is actually defined
165 function Object:proto_objectof(name)
166     if rawget(self, name) then
167         return self
168     else
169         local parents = rawget(self, "__proto_parents") or {self.__index} or {}
170         for i=1,#parents do
171             local obj = parents[i]:proto_objectof(name)
172             if obj then
173                 return obj
174             end
175         end
176     end
177 end
178
179
180 --- Object:proto_isa (prototype)
181 ---   check if self is inherited from prototype
182 function Object:proto_isa(prototype)
183     if self == prototype then
184         return true
185     else
186         local parents = rawget(self, "__proto_parents") or {self.__index} or {}
187         for i=1,#parents do
188             if parents[i]:proto_isa(prototype) then
189                 return true
190             end
191         end
192     end
193     return false
194 end
195
196
197 return Object
198
199 -- Local Variables:
200 -- mode: lua
201 -- lua-indent-level: 4
202 -- indent-tabs-mode: nil
203 -- End: