fix multi inheritance
authorChristian Thaeter <ct@pipapo.org>
Tue, 7 Mar 2017 23:25:03 +0000 (00:25 +0100)
committerChristian Thaeter <ct@pipapo.org>
Tue, 7 Mar 2017 23:25:03 +0000 (00:25 +0100)
Additional inheritance paths must now be given at clone time.
proto_inherit() got removed. This prevents cycles in the hierarchy.

proto.lua
prototest.lua

index 6ec8da6..9572045 100644 (file)
--- a/proto.lua
+++ b/proto.lua
@@ -1,12 +1,8 @@
 --- Prototype based Objectsysten
 ---   Objects are `cloned` from existing objects ,`Object` is the mother of all objects.
----   One can introduce additional inheritance paths. Lookup order is
----   first the prototype chain from `clone` and then all parent chains in the order
----   they where added.
----
----   Multiple inheritance
----     Apart from the object where cloned from, objects can have ordered multiple
----     inheritance. Parents can be appended dynamically at later time.
+---   One can pass additional inheritance paths. Lookup order is first the prototype chain
+--    from which the object is cloned and then the ohter objects in the order
+---   they where given.
 ---
 ---   Differential inheritance
 ---     Dynamic changes in prototypes and parent objects are reflected in children unless
@@ -14,7 +10,7 @@
 ---
 ---   Member lookup is child first then for each parent depth first.
 ---
----
+--- Slots can be dynamically added and removed, inheritance is fixed at clone time (to prevent circular inheritance)
 
 
 -- Object is the root object for all objects to be created
@@ -36,35 +32,36 @@ local function indexfn (self, key)
     end
 end
 
+
 --- Object:proto_clone ()
 --- Object:proto_clone {...}
 ---   takes an optional table for constructing the cloned object
----     - associative mebers define new slots
----     - Objects define objects to inherit from
+---     - Associative members define new slots
+---     - Object references define objects to inherit from
 ---     - Strings define members to copy up
 function Object:proto_clone(newobject)
-    assert(not newobject or type(newobject) == 'table')
+    assert(not newobject or type(newobject) == 'table' and newobject.__index == nil, "Wrong argument, pass {...} not (...)")
     newobject = newobject or {}
     setmetatable(newobject, newobject)
     newobject.__index = self
 
-    -- copy up members
+    -- inherit from objects, copy up members
+    local parents
     for i=1,#newobject do
-        if type(newobject[i]) == 'string' then
-            newobject[newobject[i]] = self[newobject[i]]
-        elseif type(newobject[i]) == 'table' then
-            local parents = rawget(newobject, "__proto_parents")
+        local subject = newobject[i]
+        newobject[i] = nil
+        if type(subject) == 'string' then
+            newobject[subject] = self[subject]
 
+        elseif type(subject) == 'table' then
             if not parents then
                 parents = {self}
                 rawset(newobject, "__proto_parents",  parents)
                 newobject.__index = indexfn
             end
 
-            assert((pcall(function () return newobject[i].indexable end)), "object not indexable")
-            parents[#parents + 1] = newobject[i]
+            parents[#parents + 1] = subject
         end
-        newobject[i] = nil
     end
 
     return newobject
@@ -73,14 +70,20 @@ end
 
 
 --- Object:proto_erase (name)
----   marks member 'name' as delered, removed from inheritance
+---   marks member 'name' as deleted, even if inherited. access to erased slots it will return 'nil'
 function Object:proto_erase (key)
-    local parents = rawget(self,"__proto_parents") or rawset(self,"__proto_parents", {self.__index} or {}) and rawget(self,"__proto_parents")
-    if type(self.__index) == 'table' then
+    local parents = rawget(self, "__proto_parents")
+    if not parents then
+        parents = {self.__index}
+        rawset(self, "__proto_parents",  parents)
         self.__index = indexfn
     end
 
-    local erased = rawget(self,"__proto_erased") or rawset(self,"__proto_erased", {}) and rawget(self,"__proto_erased")
+    local erased = rawget(self, "__proto_erased")
+    if not erased then
+        erased = {}
+        rawset(self, "__proto_erased",  erased)
+    end
 
     erased[key] = true
     return self
@@ -88,29 +91,6 @@ end
 
 
 
---- Object:proto_inherit (...)
----   adds some more Objects to the inheritance list
-function Object:proto_inherit (...)
-    local args = {...}
-    if #args > 0 then
-        local parents = rawget(self,"__proto_parents")
-        if not parents then
-            parents = {self.__index}
-            rawset(self, "__proto_parents",  parents)
-            self.__index = indexfn
-        end
-
-        for i=1,#args do
-            assert((pcall(function () return args[i].indexable end)), "object not indexable")
-            parents[#parents + 1] = args[i]
-        end
-    end
-    return self
-end
-
-
-
-
 --- Object:proto_prototypes ()
 ---   iterator over all prototypes of an object in search order (depth first, no duplicates, including self)
 ---    for prototype in object:prototypes()
@@ -209,7 +189,7 @@ end
 
 
 
---- Object:proto_object (name)
+--- Object:proto_objectof (name)
 ---   return the object where a member is actually defined
 function Object:proto_objectof(name)
     local erased = rawget(self,"__proto_erased")
index 4b70128..1973927 100644 (file)
@@ -8,9 +8,8 @@ assert( O2.o2 == "o2")
 assert( Object.o2 == nil)
 
 
-O3 = Object:proto_clone {o3 = "o3"}
+O3 = Object:proto_clone {O2, o3 = "o3"}
 assert( O3.o3 == "o3")
-O3:proto_inherit(O2)
 assert( O3.o2 == "o2")
 assert( O3:proto_objectof('o2') == O2)
 
@@ -67,7 +66,6 @@ do
       proto_members=true,
       proto_findall=true,
       proto_objectof=true,
-      proto_inherit=true,
       proto_clone=true,
       proto_erase=true,
       proto_pcall=true,
@@ -173,28 +171,44 @@ local Duck = Waterfowl:proto_clone {
 
 print(Duck:proto_pcall('canQuack'))
 
-
 -- multiple inheritance
 
 local A = Object:proto_clone {
   sayName = function (self) print(self.myName) end
 }
 
+
 local B = Object:proto_clone {
   myName = "I am B"
 }
 
-local AB1 = Object:proto_clone ()
-AB1:proto_inherit (A,B)
-AB1:sayName()
+print(1)
+do
+  local AB = Object:proto_clone {A,B}
+  AB:sayName()
+end
+
+print(2)
+do
+  local AB = A:proto_clone {B}
+  AB:sayName()
+end
 
-local AB1_1 = Object:proto_clone {A,B}
-AB1_1:sayName()
 
-local AB2 = A:proto_clone ()
-AB2:proto_inherit (B)
-AB2:sayName()
+print(3)
+do
+  local AB = B:proto_clone {A}
+  AB:sayName()
+end
+
+
+print(4)
+do
+  local AB1 = Object:proto_clone {A,B}
+  AB1:sayName()
+  local AB2 = A:proto_clone {B}
+  AB2:sayName()
+  local AB3 = B:proto_clone {A}
+  AB3:sayName()
+end
 
-local AB3 = B:proto_clone ()
-AB3:proto_inherit (A)
-AB3:sayName()