From b47b9a55b316dcb6324f92bfd6ae01afb99766f1 Mon Sep 17 00:00:00 2001 From: Tobias Ulmer Date: Fri, 23 Nov 2012 18:44:28 +0100 Subject: [PATCH] strict: change API to support fields with nil values Previously, locked tables could not contain members set to nil, since Lua is a bit confused about setting something to nil versus it not existing at all. Also all members are renamed fields, since "member" is an OO concept, but the strict module doesn't particularily deal with objects. The documentation was also slightly improved. strict.unlock is now implemented, but still untested. Signed-off-by: Tobias Ulmer --- generic/plugin.lua | 5 ++-- generic/strict.lua | 73 ++++++++++++++++++++++++++-------------------- local/scm.lua | 5 ++-- 3 files changed, 47 insertions(+), 36 deletions(-) diff --git a/generic/plugin.lua b/generic/plugin.lua index 111952a..396dcd7 100644 --- a/generic/plugin.lua +++ b/generic/plugin.lua @@ -84,7 +84,7 @@ local function load_plugin(dir, p, ctx) local e = err.new("loading plugin failed: %s", p) local plugin_file = string.format("%s/%s", dir, p) - strict.declare(_G, {plugin_descriptor = {}}) + strict.declare(_G, {"plugin_descriptor"}) local chunk, msg = loadfile(plugin_file) if not chunk then @@ -92,12 +92,13 @@ local function load_plugin(dir, p, ctx) end chunk() if not plugin_descriptor then + strict.undeclare(_G, {"plugin_descriptor"}) return false, e:append("no plugin descriptor in plugin: %s", plugin_file) end local pd = plugin_descriptor - strict.undeclare(_G, {plugin_descriptor = {}}) + strict.undeclare(_G, {"plugin_descriptor"}) if type(pd.description) ~= "string" then e:append("description missing in plugin descriptor") diff --git a/generic/strict.lua b/generic/strict.lua index 402254a..2d8be78 100644 --- a/generic/strict.lua +++ b/generic/strict.lua @@ -33,7 +33,7 @@ local function what() return d and d.what or "C" end ---- Lock a table against adding members explicitly or by implicit assignment. +--- Lock a table against adding fields explicitly or by implicit assignment. -- @param t table to lock function strict.lock(t) assert(type(t) == "table") @@ -48,7 +48,7 @@ function strict.lock(t) local mt = getmetatable(t) if not mt.__declared[k] and what() ~= "C" then error("assignment in "..tostring(t).. - " to undeclared variable '"..tostring(k).."'") + " to undeclared field '"..tostring(k).."'") end -- error("assignment to "..tostring(t).."[".. @@ -70,15 +70,24 @@ function strict.lock(t) return t end ---- Unlock a table that was protected against adding and assigning to members. --- Not implemented yet. --- @param t table to unlock +--- Unlock a table that was protected against adding and assigning to fields. +-- @param t table to unlock. +-- @return the unlocked table. function strict.unlock(t) assert(type(t) == "table") - error("strict.unlock() is not implemented yet") + + if not strict.islocked(t) then + error("table is not locked") + end + + setmetatable(t, nil) + + return t end --- Test whether a table is locked. +-- The implementation determines this by looking at certain keys, it's therefore +-- not 100% accurate. -- @param t table to check -- @return true if it's locked, false if not function strict.islocked(t) @@ -92,45 +101,47 @@ function strict.islocked(t) return false end ---- Declare new members of a table and assign them a value. --- Note that declaring already declared members causes an error message to be --- raised. --- @param t table to declare members in --- @param values table of member (key) / value pairs. -function strict.declare(t, values) +--- Declare new fields of a table. +-- Note that declaring existing fields raise a fatal error. +-- @param t table to declare fields in +-- @param fields array of field strings to declare. +-- @return the modified table +function strict.declare(t, fields) assert(type(t) == "table") - assert(type(values) == "table") + assert(type(fields) == "table") local mt = getmetatable(t) - for k, v in pairs(values) do - if mt.__declared[k] then - error("variable '"..k.."' is already declared") + for _,f in ipairs(fields) do + assert(type(f) == "string") + + if mt.__declared[f] then + error("field '"..f.."' is already declared") end - mt.__declared[k] = true - rawset(t, k, v) + mt.__declared[f] = true end return t end ---- Remove members from a table. --- Note that the members must be declared, otherwise a fatal error is raised. --- Also note that values in the values table are ignored, only keys are --- considered. --- @param t table to remove members from --- @param values table of members to delete. Must be in key/value form. -function strict.undeclare(t, values) +--- Remove fields from a table. +-- Note that the fields must be declared, otherwise a fatal error is raised. +-- @param t table to remove fields from +-- @param fields array of field strings to remove. +-- @return the modified table +function strict.undeclare(t, fields) assert(type(t) == "table") - assert(type(values) == "table") + assert(type(fields) == "table") local mt = getmetatable(t) - for k, v in pairs(values) do - if not mt.__declared[k] then - error("variable '"..k.."' was not declared in the first place") + for _,f in ipairs(fields) do + assert(type(f) == "string") + + if not mt.__declared[f] then + error("field '"..f.."' was not declared") end - mt.__declared[k] = nil - rawset(t, k, nil) + mt.__declared[f] = nil + rawset(t, f, nil) end return t diff --git a/local/scm.lua b/local/scm.lua index 5a195aa..caba4cf 100644 --- a/local/scm.lua +++ b/local/scm.lua @@ -89,10 +89,9 @@ function scm.register_interface(name) -- we have lots of calls like scm.(...). Register the interface -- function in the scm module to support those calls. if strict.islocked(scm) then - strict.declare(scm, {[name]=func}) - else - scm[name] = func + strict.declare(scm, {name}) end + scm[name] = func return true, nil end -- 2.39.5