attaching metatables within tabes - lua

I have parser that parses a config file and produces a table.
The resulting table can look something like:
root = {
global = {
},
section1 = {
subsect1 = {
setting = 1
subsubsect2 = {
}
}
}
}
The goal is to have a table I can read settings from and if the setting doesn't exist, it'll try to grab it from it's parent. At the top level it will grab from global. If it's not in global it'll return nil.
I attach metatables to root like this:
local function attach_mt(tbl, parent)
for k,v in pairs(tbl) do
print(k, v)
if type(v) == 'table' then
attach_mt(v, tbl)
setmetatable(v, {
__index = function(t,k)
print("*****parent=", dump(parent))
if parent then
return tbl[k]
else
if rawget(tbl, k) then
return rawget(tbl, k)
end
end
print(string.format("DEBUG: Request for key: %s: not found", k))
return nil
end
})
end
end
end
attach_mt(root)
However, when requesting keys it doesn't work. What appears to be the case is that is always nil. How do I read from the parent table?

local function attach_mt(tbl, parent)
setmetatable(tbl, {__index = parent or root.global})
for k, v in pairs(tbl) do
if type(v) == 'table' then
attach_mt(v, tbl)
end
end
end
attach_mt(root)
setmetatable(root.global, nil)

Related

Setting multiplication operator through metatable in specific environment in Lua

local names = setmetatable({},
{__mul = function(a,b) return a|b end}
)
names={i=0,j=1}
tr=load("return i*j",nil,"t",names)()
print(tr)
It prints tr as 0. The expected answer is 1 as 0|1 results to 1. Where is the code wrong?
Try:
local mt_obj = {
__tostring = function(o) return tostring(o[1]) end,
}
local function get(o)
if type(o) == "table" then return o[1] else return o end
end
local function new(v)
return setmetatable({v}, mt_obj)
end
function mt_obj.__mul(a,b)
return new(get(a)|get(b))
end
local mt_env = {
__index = function(t,k) return new(t.variables[k]) end,
__newindex = function(t,k,v) t.variables[k] = v end,
}
local names = {i=0,j=1}
local env = setmetatable({variables = names}, mt_env)
tr=get(load("return i*j*8",nil,"t",env)())
print(tr)

Read only iterable table in lua?

I want to have a read only table in my Lua program. If ever a key is removed or a key is associated with a new value, an error must be thrown.
function readonly(table)
local meta = { } -- metatable for proxy
local proxy = { } -- this table is always empty
meta.__index = table -- refer to table for lookups
meta.__newindex = function(t, key, value)
error("You cannot make any changes to this table!")
end
setmetatable(proxy, meta)
return proxy -- user will use proxy instead
end
It works great.
t = { }
t["Apple"] = "Red"
t[true] = "True!"
t[51] = 29
for k,v in pairs(t) do
print(v)
end
t = readonly(t)
t[51] = 30
Prints
Red
True!
29
input:7: You cannot make any changes to this table!
Problem
for k, v in pairs(t) do
print(v)
end
Will print nothing under all circumstances now. That's because the proxy table will never have anything inside of it. pairs apparently never calls index and thus cannot retrieve anything from the actual table.
What can I do to make this readonly table iterable?
I'm on Lua 5.1 and have access to these metamethods:
Lua 5.1 Manual
You can modify standard Lua function pairs to work correctly with your read-only tables.
local function readonly_newindex(t, key, value)
error("You cannot make any changes to this table!")
end
function readonly(tbl)
return
setmetatable({}, {
__index = tbl,
__newindex = readonly_newindex
})
end
local original_pairs = pairs
function pairs(tbl)
if next(tbl) == nil then
local mt = getmetatable(tbl)
if mt and mt.__newindex == readonly_newindex then
tbl = mt.__index
end
end
return original_pairs(tbl)
end
Usage:
t = { }
t["Apple"] = "Red"
t[true] = "True!"
t[51] = 29
for k,v in pairs(t) do
print(k, v)
end
t = readonly(t)
for k,v in pairs(t) do
print(k, v)
end
t[51] = 30
One solution is to create a wholly custom iterator for the table.
function readonly(table)
local meta = { } -- metatable for proxy
local proxy = { } -- this table is always empty
meta.__index = table -- refer to table for lookups
meta.__newindex = function(t, key, value)
error("You cannot make any changes to this table!")
end
local function iter()
return next, table
end
setmetatable(proxy, meta)
return proxy, iter -- user will use proxy instead
end
Usage:
t = { }
t["Apple"] = "Red"
t[true] = "True!"
t[51] = 29
for k,v in pairs(t) do
print(v)
end
t, tIter = readonly(t)
t[51] = 30
for k, v in tIter do
print(v)
end

Converting Python function to Lua function

I am trying to convert existing python function into lua function. But my lua function is not producing same result as python function. Any help is appreciated.
Python function:
import json
test = '{"http://localhost:8080/":{"phone":{"-detail/phone detail.template.html":"5167n,a,7,2","s/motorola-xoom-with-wifi.json":"516a0,5,4,3"},"favicon.ico":"016ad,3,3,2","img/phones/motorola-xoom-with-wi-fi.":{"1.jpg":"*02s,2s,4v,h3|116da,o,l,6","2.jpg":"*02s,2s,4v,kp|116da,j,i,8","3.jpg":"*02s,2s,4v,ob|116da,o,m,8,7,,7,7,7","4.jpg":"*02s,2s,4v,rx|116da,o,m,9,8,,7,7,7","5.jpg":"*02s,2s,4v,vj|116da,p,m,a,8,,7,7,7"}}}'
def tri(param):
t = {}
for key in param:
if key not in param:
continue
if isinstance(param[key], dict) and param[key] is not None:
flat = tri(param[key])
for x in flat:
if x not in flat:
continue
t[key + x] = flat[x]
else:
t[key] = param[key]
return t
print(tri(json.loads(test)))
Lua code ( which is not producing same result as python function)
local json = require('cjson')
local test = '{"http://localhost:8080/":{"phone":{"-detail/phone-detail.template.html":"5167n,a,7,2","s/motorola-xoom-with-wi-fi.json":"516a0,5,4,3"},"favicon.ico":"016ad,3,3,2","img/phones/motorola-xoom-with-wi-fi.":{"1.jpg":"*02s,2s,4v,h3|116da,o,l,6","2.jpg":"*02s,2s,4v,kp|116da,j,i,8","3.jpg":"*02s,2s,4v,ob|116da,o,m,8,7,,7,7,7","4.jpg":"*02s,2s,4v,rx|116da,o,m,9,8,,7,7,7","5.jpg":"*02s,2s,4v,vj|116da,p,m,a,8,,7,7,7"}}}'
local function tri(param)
t = {}
for key in pairs(param) do
if param[key] == nil then end
if type(param[key]) == "table" then
flat = tri(param[key])
for k in pairs(flat) do
t[key .. k] = flat[k]
end
else
t[key] = param[key]
end
end
return t
end
print(json.encode(tri(json.decode(test))))
local function tri(param)
t = {} -- every time we call tri t will be "reset" to an empty table
for key in pairs(param) do
if param[key] == nil then end
if type(param[key]) == "table" then
flat = tri(param[key]) -- here we call tri, but we still need t!
for k in pairs(flat) do
t[key .. k] = flat[k]
end
else
t[key] = param[key]
end
end
return t
end
Making at least t global should solve that problem. But there is also no reason for flat to be global so we make it local too.
local function tri(param)
local t = {}
for key in pairs(param) do
if param[key] == nil then end
if type(param[key]) == "table" then
local flat = tri(param[key])
for k in pairs(flat) do
t[key .. k] = flat[k]
end
else
t[key] = param[key]
end
end
return t
end
Your task could be done a bit easier using json.traverse() function from this Lua JSON module.
Traversing lets you perform arbitrary operations with JSON elements on-the-fly.
This code concatenates element's path (for every JSON element except JSON containers: arrays/objects) and uses it as a key for Lua table.
local json = require'json'
local t = {}
local function callback(path, json_type, value)
if value ~= nil then -- value == nil for containers (arrays/objects)
t[table.concat(path)] = value
end
end
local test = '{"http://localhost:8080/":{"phone":{"-detail/phone detail.template.html":"5167n,a,7,2","s/motorola-xoom-with-wifi.json":"516a0,5,4,3"},"favicon.ico":"016ad,3,3,2","img/phones/motorola-xoom-with-wi-fi.":{"1.jpg":"*02s,2s,4v,h3|116da,o,l,6","2.jpg":"*02s,2s,4v,kp|116da,j,i,8","3.jpg":"*02s,2s,4v,ob|116da,o,m,8,7,,7,7,7","4.jpg":"*02s,2s,4v,rx|116da,o,m,9,8,,7,7,7","5.jpg":"*02s,2s,4v,vj|116da,p,m,a,8,,7,7,7"}}}'
json.traverse(test, callback)
-- Now t == {
-- ["http://localhost:8080/favicon.ico"] = "016ad,3,3,2",
-- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.1.jpg"] = "*02s,2s,4v,h3|116da,o,l,6",
-- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.2.jpg"] = "*02s,2s,4v,kp|116da,j,i,8",
-- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.3.jpg"] = "*02s,2s,4v,ob|116da,o,m,8,7,,7,7,7",
-- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.4.jpg"] = "*02s,2s,4v,rx|116da,o,m,9,8,,7,7,7",
-- ["http://localhost:8080/img/phones/motorola-xoom-with-wi-fi.5.jpg"] = "*02s,2s,4v,vj|116da,p,m,a,8,,7,7,7",
-- ["http://localhost:8080/phone-detail/phone detail.template.html"] = "5167n,a,7,2",
-- ["http://localhost:8080/phones/motorola-xoom-with-wifi.json"] = "516a0,5,4,3"
-- }

Check if a Lua table member exists at any level

I need to check if a member exists in a table that isn't at the next level, but along a path of members.
foo = {}
if foo.bar.joe then
print(foo.bar.joe)
end
this will cast an attempt to index field 'bar' (a nil value) because bar isn't defined.
My usual solution is to test the chain, piece-by-piece.
foo = {}
if foo.bar and foo.bar.joe then
print(foo.bar.joe)
end
but this can be very tedious when there are many nested tables. Are there a better way to do this test than piece-by-piece?
I don't understand what you try to mean by "along a path of members". From the example, I assume you are trying to find a value in a "subtable"?
local function search(master, target) --target is a string
for k,v in next, master do
if type(v)=="table" and v[target] then return true end
end
end
A simple example. If you use such a function, you can pass the foo table and the joe string to see if foo.*.joe exists. Hope this helps.
debug.setmetatable(nil, {__index = {}})
foo = {}
print(foo.bar.baz.quux)
print(({}).prd.krt.skrz.drn.zprv.zhlt.hrst.zrn) -- sorry ))
To search for an element that is at any level of a table, I would use a method such as this one:
function exists(tab, element)
local v
for _, v in pairs(tab) do
if v == element then
return true
elseif type(v) == "table" then
return exists(v, element)
end
end
return false
end
testTable = {{"Carrot", {"Mushroom", "Lettuce"}, "Mayonnaise"}, "Cinnamon"}
print(exists(testTable, "Mushroom")) -- true
print(exists(testTable, "Apple")) -- false
print(exists(testTable, "Cinnamon")) -- true
I think you're looking for something along these lines:
local function get(Obj, Field, ...)
if Obj == nil or Field == nil then
return Obj
else
return get(Obj[Field], ...)
end
end
local foo = {x = {y = 7}}
assert(get() == nil)
assert(get(foo) == foo)
assert(get(foo, "x") == foo.x)
assert(get(foo, "x", "y") == 7)
assert(get(foo, "x", "z") == nil)
assert(get(foo, "bar", "joe") == nil)
assert(get(foo, "x", "y") or 41 == 7)
assert(get(foo, "bar", "joe") or 41 == 41)
local Path = {foo, "x", "y"}
assert(get(table.unpack(Path)) == 7)
get simply traverses the given path until a nil is encountered. Seems to do the job. Feel free to think up a better name than "get" though.
As usual, exercise care when combining with or.
I'm impressed by Egor's clever answer, but in general I think we ought to not rely on such hacks.
See also
The 'Safe Table Navigation' patch for Lua 5.2 : http://lua-users.org/wiki/LuaPowerPatches
Lengthy discussion on this matter : http://lua-users.org/lists/lua-l/2010-08/threads.html#00519
Related technique : http://lua-users.org/wiki/AutomagicTables
I suspect something relevant has been implemented in MetaLua, but I can't find at the moment.
If I understood your problem correctly, here's one possibility:
function isField(s)
local t
for key in s:gmatch('[^.]+') do
if t == nil then
if _ENV[ key ] == nil then return false end
t = _ENV[ key ]
else
if t[ key ] == nil then return false end
t = t[ key ]
end
--print(key) --for DEBUGGING
end
return true
end
-- To test
t = {}
t.a = {}
t.a.b = {}
t.a.b.c = 'Found me'
if isField('t.a.b.c') then print(t.a.b.c) else print 'NOT FOUND' end
if isField('t.a.b.c.d') then print(t.a.b.c.d) else print 'NOT FOUND' end
UPDATE: As per cauterite's suggestion, here's a version that also works with locals but has to take two arguments :(
function isField(t,s)
if t == nil then return false end
local t = t
for key in s:gmatch('[^.]+') do
if t[ key ] == nil then return false end
t = t[ key ]
end
return true
end
-- To test
local
t = {}
t.a = {}
t.a.b = {}
t.a.b.c = 'Found me'
if isField(t,'a.b.c') then print(t.a.b.c) else print 'NOT FOUND' end
if isField(t,'a.b.c.d') then print(t.a.b.c.d) else print 'NOT FOUND' end
foo = {}
foo.boo = {}
foo.boo.jeo = {}
foo.boo.joe is foo['boo']['joe'] and so
i make next function
function exist(t)
local words = {}
local command
for i,v in string.gmatch(t, '%w+') do words[#words+1] = i end
command = string.format('a = %s', words[1])
loadstring(command)()
if a == nil then return false end
for count=2, #words do
a = a[words[count]]
if a == nil then return false end
end
a = nil
return true
end
foo = {}
foo.boo = {}
foo.boo.joe = {}
print(exist('foo.boo.joe.b.a'))
using loadstring to make temp variable. my lua ver is 5.1
remove loadstring at 5.2 5.3, instead using load

time complexity of metatable in Lua when accessing

local cls = {base = "base"}
local ins = {}
cls.__index = cls
setmetatable(ins, cls)
What is the time complexity of accesssing ins.base?
You can expect O(1) time complexity from the official Lua implementation.
The code for __index is roughly equivalent to this Lua code, taken from the manual:
function gettable_event (table, key)
local h
if type(table) == "table" then
local v = rawget(table, key)
if v ~= nil then return v end
h = metatable(table).__index
if h == nil then return nil end
else
h = metatable(table).__index
if h == nil then
error(ยทยทยท)
end
end
if type(h) == "function" then
return (h(table, key)) -- call the handler
else return h[key] -- or repeat operation on it
end
end
The __index lookup itself has no loops, and since Lua tables are backed by hash tables, table lookups are usually a constant-time operation.

Resources