Lua hierarchy string to table - lua

Is there a way that I can convert a hierarchy string into table form?
Suppose the input is A.B.C.D
ouput should be a table which traverses above input:
A = {}
A.B = {}
A.B.C = {}
A.B.C.D = {}
Thanks.

The obvious solution would be to parse the string up and construct the hierarchy table from that. But a more clever solution is to let lua do it for you. With a bit of metamagic and function environment manipulation this can be done:
dump = require 'pl.pretty'.dump -- convenient table dumper from penlight
function createtable(str)
local env_mt = {}
env_mt.__index = function(t, k)
rawset(t, k, setmetatable({}, env_mt))
return rawget(t, k)
end
local env = setmetatable({}, env_mt)
local f = loadstring("return "..str)
setfenv(f, env)
f()
return env
end
dump( createtable "A.B.C.D" )
this outputs:
{
A = {
B = {
C = {
D = {
}
}
}
}
}

#greatwolf's answer is right but I prefer the more straightforward approach of "parsing" the string and constructing the table. Less magic, and you do not execute a function loaded from a (possibly) user-defined string, which would be a security issue.
local createtable = function(str)
local top = {}
local cur = top
for i in str:gmatch("[^.]+") do
cur[i] = {}
cur = cur[i]
end
return top
end
(require "pl.pretty").dump(createtable("A.B.C.D"))

Related

Lua: define functions from a table

NativeTable = {
["print"] = {},
["LoadResourceFile"] = {}
}
for k, v in pairs(NativeTable) do
k = function(...)
print("test")
end
end
this would result in defining them in the NativeTable and not as a global function
print = function(...)
print("test")
end
LoadResourceFile = function(...)
print("test")
end
So I'm trying to define a global function with a table name
Guess i could do smth like this
But there must be a better way?
NativeTable = {
["test"] = {},
["LoadResourceFile"] = {}
}
local function OverWriteFunction(FuncName, Func)
local Base = [[ = function(...)
print("jaa")
end
]]
local Final = FuncName .. Base
return Final
end
for k, v in pairs(NativeTable) do
load(OverWriteFunction(k))()
end
In your first example you are redefining the variable k, that is local inside the for loop, so this will not be usable outside the one loop where you define it.
Your second example is something you absolutely should avoid if you can, since defining code inside a string and loading it later means, that even only a syntax error will not be shown on "compiling" it, but only when that exact part is executed. And especially, when you are concatenating string that are meant to be code, you just get the risk of glueing something together wrongly, if you try to have that made generically.
If I understood correctly what you are trying to achieve, I would say, it could be something like this:
NativeTable = {
["test"] = {},
["LoadResourceFile"] = {},
[3] = {}
}
for k, v in pairs(NativeTable) do
if type(k) == "string" then
_G[k] = function(...)
print("output")
end
end
end
test()
LoadResourceFile()
-- _G[3]()
Which outputs:
output
output
What I have done here is to use the _G table which is the global environment of lua, all things you define there with a string in the brackets will be globally available (and what is globally available is inside that table, so be careful, since you can override other functions and variables you have defined!), even when writing it as a normal function call. I did also make sure, to only do this, when the type of k was string, otherwise you could start to define functions that can be called like the last commented out call.
This doesn't make too much sense for me:
NativeTable = {
["print"] = {},
["LoadResourceFile"] = {}
}
for k, v in pairs(NativeTable) do
k = function(...)
print("test")
end
end
First you create a table with table elements print and LoadResourceFile.
Then you iterate over that table and replace the table elements with functions.
Why not simply do this:
myTable = {
a = function() print("I'm function a") end,
b = function() print("I'm function b") end,
}
or
myTable = {}
myTable.a = function () print("I'm function a") end
Then you can call the global function like so: myTable.a()
If you insist on having those functions outside of a table you can simply insert them into the global environment table.
for k, v in pairs(myTable) do _G[k] = v end
then you could call a and b globally. But whatever you're trying to accomplish there's probably a better way than this.

Split Lua string into tables with subtables

All the examples for splitting strings generate arrays. I want the following
Given string like x.y.z e.g. storage.clusters.us-la-1
How do I generate a table from that resembling
x = {
y = {
z = {
}
}
}
Below is a function that should do what you want.
function gen_table(str, existing)
local root = existing or {}
local tbl = root
for p in string.gmatch(str, "[^.]+") do
local new = tbl[p] or {}
tbl[p] = new
tbl = new
end
return root
end
Usage:
local t = gen_table("x.y.z")
local u = gen_table("x.y.w", t)
t.x.y.z.field = "test1"
t.x.y.w.field = "test2"

Unable to figure out lua table inheritence

Hope someone can make sense of what I'm attempting to figure out, Just don't seem to understand Lua enough to achieve this.
--[[
tbl.a.test("moo") returns "Table A moo appears"
tbl.b.test("moo") returns "moo appears"
]]
tbl = {
a = { ID = "Table A" },
b = {
test = function(...) print(... .. " appears") end,
},
}
tbl.a__index = function(self, ...) tbl.b[self](tbl.a.ID .. ...) end
What I'm attempting to do is I could create several tables a, c, d, e and not have to copy test to each one. When tbl.a.test, tbl.c.test, tbl.d.test is used, It'll retrieve the tbl.a.ID var, then call tbl.b.test(ID, "moo")
So far all I'm finding out is it's not able to find .test on anything other than tbl.b
** EDIT **
Thank's to support so far the code is now;
tbl = {
a = { ID = "Table A " },
b = { test = function(...) local id, rest = ... print(id .. ": " .. rest) end },
}
setmetatable(tbl.a, {__index=function(self, k, ...) local rest = ... return tbl.b[k](tbl.a.ID, rest) end})
However, the ... is not being progressed for some odd reason :|
You're missing a period between tbl.a and __index.
__index needs to be on a's metatable, not the table itself.
You don't return anything from your __index function
self in the __index function is the table being indexed, not the key (which is the second argument)
This should work:
setmetatable(tbl.a, {__index=function(self, k) return tbl.b[k](tbl.a.ID) end})
--------------------------------------------------------------------------------
-- [Sub]Class creation
--------------------------------------------------------------------------------
function newclass(new_obj,old_obj)
old_obj = old_obj or {} --use passed-in object (if any)
new_obj = new_obj or {}
assert(type(new_obj) == 'table','New Object/Class is not a table')
assert(type(old_obj) == 'table','Old Object/Class is not a table')
old_obj.__index = old_obj --store __index in parent object (optimization)
return setmetatable(new_obj,old_obj) --create 'new_obj' inheriting 'old_obj'
end
--------------------------------------------------------------------------------
prototype = {
test = function(self,s) print('Table ' .. self.id .. ' ' .. s .. ' appears') end
}
tbl = {}
tbl.a = newclass({id = 'A'},prototype)
tbl.b = newclass({id = 'B'},prototype)
tbl.a:test('moo')
tbl.b:test('moo')
The distinction between class and object in Lua is only theoretical. In practice they are implemented exactly the same way.
Anytime you need to do inheritance, you can use my general-purpose newclass() function to either create a new class/object, or inherit from an existing one.
Any common code & data you would like to have passed on should go into the 'prototype' table (whatever you'd like to call it for each case).
Also, you seem to forget to use the method calling syntax (that uses a colon instead of a dot) when calling methods. Without it, the self parameter is not automatically recognized.

Division of metatable

got some problem with metatable. This is my simple metatable:
local mt = {}
function mt:add(n)
return setmetatable({n = n}, {__index = mt})
end
function mt:get() return self.n end
Now I want to add some division like:
mt.math
mt.effect
Which each one has some own methods like:
mt.math:floor() return math.floor(self:get()) end
mt.effect:show(args) onMapShowEffect(self:get(), {x = x + (args[1] ~= nil or 0), ...) end
mt.effect:get() return getCurrentPos() end
Any ideas?
OK, trying make all details to share my problem.
Player = {}
function Player:add(this)
return setmetatable({this = this}, {__index = Player})
end
Player:get() return self.this end
Above code works perfectly on this example
function enterToGame(player1, player2)
local p1 = Player:add(player1)
local p2 = Player:add(player2)
print(p1:get()) -- ID1
print(p2:get()) -- ID2
Now I want to create some helpfully methods(functions) for table Player. I want to make it more flexible, so I want divide it for classes. Example:
Player.info = {
id = function() return Player:get() end,
}
Player.pos = {
get = function() return getPosition(Player:get()) end,
set = function(args) setPosition(Player:get(), args) end,
}
Player.speed = {
get = function() return getSpeed(Player:get()) end,
set = function(value) setSpeed(value) end,
improve = function(value) setSpeed(Player.speed.get() + value) end,
}
But its not work exactly what I want:
function enterToGame(player1, player2)
local p1 = Player:add(player1)
local p2 = Player:add(player2)
print(p1:get()) -- ID1
print(p2:get()) -- ID2
print(p1.info.id()) -- ID2 instead of ID1
print(p2.info.id()) -- ID2
When I put Player:get() in my methods its return last object declaration.
Based on what you state, if you do
mt.math = mt:add(123)
You don't need themt:get() because mt is the metatable for mt.math. Then
mt.math.floor = function(self) return math.floor(self.n) end
will work as expected. For example,
print(mt.math:floor())
prints 123.
EDIT 1: So now that I have a better understanding of what you are trying to do: normally you would do
p1:id()
p1:getPos()
p1:setPos()
p1:getSpeed()
p1:improveSpeed()
Note the colon, this is important, so that each method gets a "self" as first parameter, thereby given them the table instance to operate on (p1, in the above example). Instead you want to group methods so
p1.info:id()
p1.pos:get()
p1.pos:set()
p1.speed:improve()
p1.speed:get()
These methods will get a self that points to p1.info, p1.pos, etc. But those sub-tables have no knowledge of the container table (p1). The info and pos tables are in the Player class: they are shared by all instances of Player (p1, p2 etc). You have to make the info and pos tables non-shared:
function Player:add(player)
local pN= setmetatable( {n = player, info={}, pos={}}, {__index = Player})
pN.info.id = function() return pN.n end
pN.pos.set = function(x) return setPosition(pN, x) end
return pN
end
Then you get
> p1=mt:add(player1)
> p2=mt:add(player2)
> print(player1)
table: 0024D390
> print(p1.info.id())
table: 0024D390
> print(player2)
table: 0024D250
> print(p2.info.id())
table: 0024D250
All that said, I don't really like the idea of having to use closures like this, perhaps there are gotchas since not everything will be in Player.

How can I change every index into a table using a metatable?

I'm trying to write a metatable so that all indexes into the table are shifted up one position (i.e. t[i] should return t[i+1]). I need to do this because the table is defined using index 1 as the first element, but I have to interface with a program that uses index 0 as the first element. Since reading Programming in Lua, I think that I can accomplish what I want with a proxy table, but I can't seem to get it working. So far, I have this:
t = {"foo", "bar"}
local _t = t
t = {}
local mt = {
__index = function(t, i)
return _t[i+1]
end
}
setmetatable(t, mt)
However, this does not create the expected result. In fact, it doesn't return any values at all (every lookup is nil). Is there a better way to do this, or am I just missing something?
t = {"foo", "bar"}
local _t = t
t = {}
local mt = {
__index = function(t, i)
return _t[i+1]
end
}
setmetatable(t, mt)
print(t[0])
outputs "foo" for me when run here: http://www.lua.org/cgi-bin/demo

Resources