add proto_erase() for whiteout inherited members
authorChristian Thaeter <ct@pipapo.org>
Mon, 6 Mar 2017 11:06:25 +0000 (12:06 +0100)
committerChristian Thaeter <ct@pipapo.org>
Mon, 6 Mar 2017 11:06:25 +0000 (12:06 +0100)
proto.lua
prototest.lua

index 1dc20d3..12a293c 100644 (file)
--- a/proto.lua
+++ b/proto.lua
@@ -8,10 +8,10 @@
 ---     Objects can have an ordered list of other objects where they inhterit from
 ---
 ---   Differential inheritance
----     Dynamic changes in prototypes and parent objects are reflected in childs unless a
----     child overidden a slot.
+---     Dynamic changes in prototypes and parent objects are reflected in children unless
+---     a child overidden a slot. proto_erase() can whiteout an inherited slot.
 ---
----   Normal member lookup is child first then for each parent depth first.
+---   Member lookup is child first then for each parent depth first.
 ---
 ---
 ---
@@ -19,9 +19,9 @@
 
 -- Object is the root object for all objects to be created
 local Object = {}
-Object.__metatable = Object
 setmetatable(Object, Object)
 
+
 --- Object:proto_clone ()
 --- Object:proto_clone {...}
 ---   takes an optional table for new mebers of the cloned object
@@ -29,7 +29,6 @@ setmetatable(Object, Object)
 function Object:proto_clone(newobject)
     assert(not newobject or type(newobject) == 'table')
     newobject = newobject or {}
-    newobject.__metatable = newobject
     setmetatable(newobject, newobject)
     newobject.__index = self
 
@@ -44,6 +43,38 @@ function Object:proto_clone(newobject)
 end
 
 
+local function indexfn (self, key)
+    local erased = rawget(self,"__proto_erased")
+    if erased and erased[key] then
+        return nil
+    end
+
+    local parents = rawget(self,"__proto_parents") or {}
+    for i=1,#parents do
+        local value = parents[i][key]
+        if value then
+            return value
+        end
+    end
+end
+
+
+--- Object:proto_erase (name)
+---   marks member 'name' as delered, removed from inheritance
+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
+        self.__index = indexfn
+    end
+
+    local erased = rawget(self,"__proto_erased") or rawset(self,"__proto_erased", {}) and rawget(self,"__proto_erased")
+
+    erased[key] = true
+    return self
+end
+
+
+
 --- Object:proto_inherit (...)
 ---   adds some more Objects to the inheritance list
 function Object:proto_inherit (...)
@@ -57,15 +88,7 @@ function Object:proto_inherit (...)
         end
 
         if type(self.__index) == 'table' then
-            self.__index = function(self, key)
-                               local parents = rawget(self,"__proto_parents")
-                               for i=1,#parents do
-                                   local value = parents[i][key]
-                                   if value then
-                                       return value
-                                   end
-                               end
-                           end
+            self.__index = indexfn
         end
     end
     return self
@@ -110,6 +133,12 @@ local function next_member(state)
     local value, mystate
 
     repeat
+        local erased = rawget(state.object,"__proto_erased") or {}
+
+        for e,_ in pairs(erased) do
+            state.members_done[e] = true
+        end
+
         mystate = state.state
 
         repeat
@@ -136,7 +165,7 @@ end
 
 
 --- Object:proto_findall (name)
----   finds all members including inherited ones of a given name
+---   finds all members including inherited and erased ones of a given name
 ---   returns object, value
 local function next_value(state)
     local mystate
@@ -145,6 +174,12 @@ local function next_value(state)
         mystate = state.state
         state.object = next_prototype(state)
         if state.object then
+
+            local erased = rawget(state.object,"__proto_erased") or {}
+            if erased[state.name] then
+                return state.object, nil
+            end
+
             local value = rawget(state.object, state.name)
             if value then
                 return state.object, value
@@ -163,6 +198,11 @@ end
 --- Object:proto_object (name)
 ---   return the object where a member is actually defined
 function Object:proto_objectof(name)
+    local erased = rawget(self,"__proto_erased")
+    if erased and erased[name] then
+        return nil
+    end
+
     if rawget(self, name) then
         return self
     else
index 1e26fa5..9c3bce6 100644 (file)
@@ -25,8 +25,19 @@ assert( O4.name == "O4")
 assert( Object.name == "Still Object")
 
 
+assert(O4:proto_objectof('o2') == O2)
 assert(O4:proto_objectof ("name") == O4)
 
+O4:proto_erase('o2')
+
+assert(O4:proto_objectof ("name") == O4)
+assert(O4.o2 == nil)
+
+assert(O4:proto_objectof ("o2") == nil)
+
+
+--O4.o2 = "override"
+--assert(O4.o2 == "override")
 
 do
    local expected_prototypes = {}
@@ -47,9 +58,8 @@ do
    local expected_members = {
       __index=true,
       __proto_parents=true,
-      __metatable=true,
+      __proto_erased=true,
       name=true,
-      o2=true,
       o3=true,
       o4=true,
       proto_isa=true,
@@ -59,10 +69,11 @@ do
       proto_objectof=true,
       proto_inherit=true,
       proto_clone=true,
+      proto_erase=true,
    }
 
    for name,value in O4:proto_members() do
-      assert(expected_members[name])
+      assert(expected_members[name], name)
       expected_members[name] = nil
    end
 
@@ -72,12 +83,15 @@ end
 do
    local expected_names = {}
 
-   expected_names["O4"] = true
    expected_names["Still Object"] = true
 
+   O4:proto_erase('name')
+
    for object,value in O4:proto_findall("name") do
-      assert(expected_names[value])
-      expected_names[value] = nil
+     if value then
+       assert(expected_names[value])
+       expected_names[value] = nil
+     end
    end
 
    assert(not next(expected_names))