Can I extract stack values from a parent function in Lua? - lua

My game engine pushes a value on to the lua stack as a parameter to a function and then invokes it using lua_pcall. The lua code will run and call additional lua functions. Eventually this lua code will invoke a C function. Is it possible for this function to retrieve the value that was originally pushed on to the stack?
Its like this:
<engine function A>
pushes parameter value X on to stack for lua
<lua func>
<lua func>
<lua func>
<engine function B>
can I extract the values X that was pushed by function A here?

Yes, with a combination of getinfo, getlocal and getupvalue you can get all that information (you can even change those values using set* functions).
Here is a fragment from MobDebug that returns stack information along with a table of locals and upvalues at each level. The variables at each level will be indexed in the same order they appear in the code (starting from parameters). For each get* function you can use their C equivalents (lua_getinfo, lua_getlocal, and lua_getupvalue), but the logic should be exactly the same.
local function stack(start)
local function vars(f)
local func = debug.getinfo(f, "f").func
local i = 1
local locals = {}
while true do
local name, value = debug.getlocal(f, i)
if not name then break end
if string.sub(name, 1, 1) ~= '(' then locals[name] = {value, tostring(value)} end
i = i + 1
end
i = 1
local ups = {}
while func and true do -- check for func as it may be nil for tail calls
local name, value = debug.getupvalue(func, i)
if not name then break end
ups[name] = {value, tostring(value)}
i = i + 1
end
return locals, ups
end
local stack = {}
for i = (start or 0), 100 do
local source = debug.getinfo(i, "Snl")
if not source then break end
table.insert(stack, {
{source.name, source.source, source.linedefined,
source.currentline, source.what, source.namewhat, source.short_src},
vars(i+1)})
if source.what == 'main' then break end
end
return stack
end

Related

LUA: Looking for a specific table by its variable

I'm currently starting work on a text adventure game in Lua--no addons, just pure Lua for my first project. In essence, here is my problem; I'm trying to find out how I can do a "reverse lookup" of a table using one of its variables. Here's an example of what I've tried to do:
print("What are you trying to take?")
bag = {}
gold = {name="Gold",ap=3}
x = io.read("*l")
if x == "Gold" then
table.insert(bag,gold)
print("You took the " .. gold.name .. ".")
end
Obviously, writing a line like this with every single object in the game would be very... exhausting--especially since I think I'll be able to use this solution for not just taking items but movement from room to room using a reverse lookup with each room's (x,y) coordinates. Anyone have any ideas on how to make a more flexible system that can find a table by the player typing in one of its variables? Thanks in advance!
-blockchainporter
This doesn't directly answer your question as you asked it, but I think it would serve the purpose of what you are trying to do. I create a table called 'loot' which can hold many objects, and the player can place any of these in their 'bag' by typing the name.
bag = {}
loot = {
{name="Gold", qty=3},
{name="Axe", qty=1},
}
print("What are you trying to take?")
x = io.read("*l")
i = 1
while loot[i] do
if (x == loot[i].name) then
table.insert(bag, table.remove(loot,i))
else
i = i + 1
end
end
For bonus points, you could check 'bag' to see if the player has some of that item already and then just update the quantity...
while loot[i] do
if (x == loot[i].name) then
j, found = 1, nil
while bag[j] do
if (x == bag[j].name) then
found = true
bag[j].qty = bag[j].qty + loot[i].qty
table.remove(loot,i)
end
j = j + 1
end
if (not found) then
table.insert(bag, table.remove(loot,i))
end
else
i = i + 1
end
end
Again, this isn't a 'reverse lookup' solution like you asked for... but I think it is closer to what you are trying to do by letting a user choose to loot something.
My disclaimer is that I don't use IO functions in my own lua usage, so I have to assume that your x = io.read("*l") is correct.
PS. If you only ever want objects to have a name and qty, and never any other properties (like condition, enchantment, or whatever) then you could also simplify my solution by using key/val pairs:
bag = {}
loot = { ["Gold"] = 3, ["Axe"] = 1 }
print("What are you trying to take?")
x = io.read("*l")
for name, qty in pairs(loot) do
if x == name then
bag.name = (bag.name or 0) + qty
loot.name = nil
end
end
I have a few notes to start before I specifically address your question. (I just want to do this before I forget, so please bear with me!)
I recommend printing to the terminal using stderr instead of stdout--the Lua function print uses the latter. When I am writing a Lua script, I often create a C-style function called eprintf to print formatted output to stderr. I implement it like this:
local function eprintf(fmt, ...)
io.stderr:write(string.format(fmt, ...))
return
end
Just be aware that, unlike print, this function does not automatically append a newline character to the output string; to do so, remember to put \n at the end of your fmt string.
Next, it may be useful to define a helper function that calls io.read("*l") to get an entire line of input. In writing some example code to help answer your question, I called my function getline--like the C++ function that has similar behavior--and defined it like this:
local function getline()
local read = tostring(io.read("*l"))
return read
end
If I correctly understand what it is you are trying to do, the player will have an inventory--which you have called bag--and he can put items into it by entering item names into stdin. So, for instance, if the player found a treasure chest with gold, a sword, and a potion in it and he wanted to take the gold, he would type Gold into stdin and it would be placed in his inventory.
Based on what you have so far, it looks like you are using Lua tables to create these items: each table has a name index and another called ap; and, if a player's text input matches an item's name, the player picks that up item.
I would recommend creating an Item class, which you could abstract nicely by placing it in its own script and then loading it as needed with require. This is a very basic Item class module I wrote:
----------------
-- Item class --
----------------
local Item = {__name = "Item"}
Item.__metatable = "metatable"
Item.__index = Item
-- __newindex metamethod.
function Item.__newindex(self, k, v)
local err = string.format(
"type `Item` does not have member `%s`",
tostring(k)
)
return error(err, 2)
end
-- Item constructor
function Item.new(name_in, ap_in)
assert((name_in ~= nil) and (ap_in ~= nil))
local self = {
name = name_in,
ap = ap_in
}
return setmetatable(self, Item)
end
return Item
From there, I wrote a main driver to encapsulate some of the behavior you described in your question. (Yes, I know my Lua code looks more like C.)
#!/usr/bin/lua
-------------
-- Modules --
-------------
local Item = assert(require("Item"))
local function eprintf(fmt, ...)
io.stderr:write(string.format(fmt, ...))
return
end
local function printf(fmt, ...)
io.stdout:write(string.format(fmt, ...))
return
end
local function getline()
local read = tostring(io.read("*l"))
return read
end
local function main(argc, argv)
local gold = Item.new("Gold", 3)
printf("gold.name = %s\ngold.ap = %i\n", gold.name, gold.ap)
return 0
end
main(#arg, arg)
Now, as for the reverse search which you described, at this point all you should have to do is check the user's input against an Item's name. Here it is in the main function:
local function main(argc, argv)
local gold = Item.new("Gold", 3)
local bag = {}
eprintf("What are you trying to take? ")
local input = getline()
if (input == gold.name) then
table.insert(bag, gold)
eprintf("You took the %s.\n", gold.name)
else
eprintf("Unrecognized item `%s`.\n", input)
end
return 0
end
I hope this helps!

how to understand ":" in the following code? [duplicate]

I am confused about the difference between function calls via . and via :
> x = {foo = function(a,b) return a end, bar = function(a,b) return b end, }
> return x.foo(3,4)
3
> return x.bar(3,4)
4
> return x:foo(3,4)
table: 0x10a120
> return x:bar(3,4)
3
What is the : doing ?
The colon is for implementing methods that pass self as the first parameter. So x:bar(3,4)should be the same as x.bar(x,3,4).
For definition it is exactly the same as specifying self manually - it will even produce same bytecode on compilation. I.e. function object:method(arg1, arg2) is same as function object.method(self, arg1, arg2).
On use : is almost the same as . - a special kind of call will be used internally to make sure object and any possible side-effects of calculations/access are calculated only once. Calling object:method(arg1, arg2) is otherwise same as object.method(object, arg1, arg2).
To be completely precise, obj:method(1, 2, 3) is the same as
do
local _obj = obj
_obj.method(_obj, 1, 2, 3)
end
Why the local variable? Because, as many have pointed out, obj:method() only indexes _ENV once to get obj. This normally just important when considering speed, but consider this situation:
local tab do
local obj_local = { method = function(self, n) print n end }
tab = setmetatable({}, {__index = function(idx)
print "Accessing "..idx
if idx=="obj" then return obj_local end
end})
end
tab.obj.method(tab.obj, 20)
--> Accessing obj
--> Accessing obj
--> 20
tab.obj:method(10)
--> Accessing obj
--> 10
Now imagine the __index metamethod did more than just printing something. Imagine it increased a counter, logged something to a file or deleted a random user from your database. There's a big difference between doing that twice or only once. In this case, there's a clear difference between obj.method(obj, etc) and obj:method(etc).

Load Lua function chunk, modify environment in 5.2 with sandbox

This question is similar to Modify Lua Chunk Environment, but with a twist.
I want to load strings as reusable Lua functions, where they all share the same sandbox for evaluation, but where each invocation I can pass in different values for the variables.
For example, say I want to evaluate the equation round(10*w/h) + a * b, with round, w, and h all values in a common sandbox environment, but where a and b are values that I want to modify each time I evaluate the equation.
This question shows how to dynamically change the environment for a compiled function. However, it does so setting the ENTIRE environment for the function, without the fallback sandbox that I have.
What's an efficient way to achieve my goals? Note that I almost only care about time needed to evaluate the function, not the setup time.
The way I have things currently, users write CSS expressions like:
box {
left: #x;
width: viewwidth - #w;
}
...where #x and #w are attributes of the box element (the 'local' variables) and viewwidth is a sheet-level variable set up elsewhere (my sandbox). Each of the property values—the portion after the :—is parsed out as a string to be compiled into a Lua function. They use normal Lua syntax, except where I currently swap the # with _el. in order to dereference the element table.
For answers to this question it is acceptable to keep this same syntax and require a differentiation between the local and sheet variables, BUT it is also acceptable to have a solution the gets rid of the # symbols and treats all variables the same.
I came up with 6 techniques for accomplishing the goal. The bottom of this post benchmarks their performance for evaluation speed. The fastest techniques require differentiating the local variables from sandbox variables in code:
local SANDBOX = {
w=1920,
h=1080,
round=function(n) return math.floor(n+0.5) end
}
local CODE = "round(10*w/h) + #a * #b"
function compile(code, sandbox)
local munged = 'local _el=... return '..code:gsub('#', '_el.')
return load(munged, nil, nil, sandbox)
end
local f = compile(CODE, SANDBOX)
print(f{a=1, b=2}) --> 20
print(f{a=3, b=4}) --> 30
If you don't want to differentiate the changing variables from those in the sandbox, or don't want to use a fragile sub() like the above, the next fastest is to mutate the __index of your locals and then pass it as the environment. You can wrap this in a helper function to make it easier:
local CODE = "round(10*w/h) + a * b"
function compile(code, sandbox)
local meta = {__index=sandbox}
return {meta=meta, f=load('_ENV=... return '..code)}
end
function eval(block, locals)
return block.f(setmetatable(locals, block.meta))
end
local f = compile(CODE, SANDBOX)
print(eval(f, {a=1, b=2})) --> 20
print(eval(f, {a=3, b=4})) --> 30
Here is the benchmark results of all the techniques below. Note that the fastest one can be even faster because unlike all other techniques the compile function returns a function that can be invoked directly, instead of wrapped in an evaluation helper script:
scope as a table, 2 : 0.9s (0.22µs per eval)
scope as a table : 1.1s (0.27µs per eval)
Use __ENV, change scope meta : 1.3s (0.32µs per eval)
blank env, change meta of scope : 1.6s (0.41µs per eval)
copy values over environment : 2.8s (0.70µs per eval)
setfenv, change scope meta : 3.0s (0.74µs per eval)
local SANDBOX = {
w = 1920,
h = 1080,
round = function(n) return math.floor(n+0.5) end
}
local TESTS = {
{env={a=1, b=2}, expected=18+2},
{env={a=4, b=3}, expected=18+12},
{env={a=9, b=7}, expected=18+63},
{env={a=4, b=5}, expected=18+20},
}
-- https://leafo.net/guides/setfenv-in-lua52-and-above.html
local function setfenv(fn, env)
local i = 1
while true do
local name = debug.getupvalue(fn, i)
if name == "_ENV" then
debug.upvaluejoin(fn, i, (function() return env end), 1)
break
elseif not name then
break
end
i = i + 1
end
return fn
end
local techniques = {
["copy values over environment"]={
code="round(10*w/h) + a*b",
setup=function(code, fallback)
local env = setmetatable({},{__index=fallback})
return {env=env,func=load("return "..code,nil,nil,env)}
end,
call=function(block, kvs)
for k,v in pairs(block.env) do block.env[k]=nil end
for k,v in pairs(kvs) do block.env[k]=v end
return block.func()
end
},
["blank env, change meta of scope"]={
code="round(10*w/h) + a*b",
setup=function(code, fallback)
local kvsmeta = {__index=fallback}
local envmeta = {}
local env = setmetatable({},envmeta)
return {envmeta=envmeta,meta=meta,kvsmeta=kvsmeta,func=load("return "..code,nil,nil,env)}
end,
call=function(block, kvs)
block.envmeta.__index=kvs
setmetatable(kvs, block.kvsmeta)
return block.func()
end
},
["setfenv, change scope meta"]={
code="round(10*w/h) + a*b",
setup=function(code, fallback)
return {meta={__index=fallback}, func=load("return "..code)}
end,
call=function(block, kvs)
setmetatable(kvs,block.meta)
setfenv(block.func, kvs)
return block.func()
end
},
["Use __ENV, change scope meta"]={
code="round(10*w/h) + a*b",
setup=function(code, fallback)
local meta = {__index=fallback}
return {meta=meta, func=load("_ENV=... return "..code)}
end,
call=function(block, kvs)
return block.func(setmetatable(kvs, block.meta))
end
},
["scope as a table"]={
-- NOTE: requires different code than all other techniques!
code="round(10*w/h) + _el.a * _el.b",
setup=function(code, fallback)
local env = setmetatable({},{__index=fallback})
return {env=env,func=load("return "..code,nil,nil,env)}
end,
call=function(block, kvs)
block.env._el=kvs
return block.func()
end
},
["scope as a table, 2"]={
-- NOTE: requires different code than all other techniques!
code="round(10*w/h) + _el.a * _el.b",
setup=function(code, fallback)
return load("local _el=... return "..code,nil,nil,fallback)
end,
call=function(func, kvs)
return func(kvs)
end
},
}
function validate()
for name,technique in pairs(techniques) do
local f = technique.setup(technique.code, SANDBOX)
for i,test in ipairs(TESTS) do
local actual = technique.call(f, test.env)
if actual~=test.expected then
local err = ("%s test #%d expected %d but got %s\n"):format(name, i, test.expected, tostring(actual))
io.stderr:write(err)
error(-1)
end
end
end
end
local N = 1e6
function benchmark(setup, call)
for name,technique in pairs(techniques) do
local f = technique.setup(technique.code, SANDBOX)
local start = os.clock()
for i=1,N do
for i,test in ipairs(TESTS) do
technique.call(f, test.env)
end
end
local elapsed = os.clock() - start
print(("%-33s: %.1fs (%.2fµs per eval)"):format(name, elapsed, 1e6*elapsed/#TESTS/N))
end
end
validate()
benchmark()

OOP Help - How do I get this code block to recognize one of its arguments?

I'm learning Lua and how to implement OOP. Trying out a test example of an object seems to return one of the argument of the object as 'null' despite being assigned one.
function Character(Name, Level, Class) --Constructor
return {GetName = T.GetName, GetLevel = T.GetLevel, GetClass = T.GetClass}
end
end
-- Snippets
Player = Character("Bob", 1, "Novice")
When I try printing Player.GetName() it returns null instead of Bob. Where have I gone wrong?
Here is the full code.
OOP in Lua takes a bit more than what you have done there, you will want to make use of metatables and upvalues.
-- How you could define your character structure.
local Character = {}
function Character.GetName(self)
return self.name
end
function Character.new(Name, Level, Class)
local _meta = {}
local _private = {}
_private.name = Name
_private.level = Level
_private.class = Class
_meta.__index = function(t, k) -- This allows access to _private
return rawget(_private, k) or rawget(Character, k)
end
_meta.__newindex = function(t, k, v) -- This prevents the value from being shaded
if rawget(_private, k) or rawget(Character, k) then
error("this field is protected")
else
rawset(t, k, v)
end
end
return setmetatable({}, _meta) --return an empty table with our meta methods implemented
end
This creates a local table _private when you create a new instance of a Character. That local table is an upvalue to the _meta.__index and it cannot be accessed outside the scope of the Character.new function. _private can be accessed when __index is called because it is an upvalue.
-- How to use the character structure
player = Character.new("Bob", 10, "Novice")
npc = Character.new("Alice", 11, "Novice")
print(player:GetName())
I use player:GetName(), but in all honesty you can just do player.name as well.
Resources for more on this topic:
http://tutorialspoint.com/lua/lua_metatables.htm
http://lua-users.org/wiki/ObjectOrientationTutorial

What does [{n,{}}] do in lua?

As you can tell I'm a beginner in lua. I am trying to understand a function I'm stuck at what the following code segment does?
It is used in the following code snippet in the last line:
function classify(txt_dir, img_dir, cls_list)
local acc = 0.0
local total = 0.0
local fea_img = {}
local fea_txt = {}
for fname in io.lines(cls_list) do
local imgpath = img_dir .. '/' .. fname .. '.t7'
local txtpath = txt_dir .. '/' .. fname .. '.t7'
fea_img[#fea_img + 1] = extract_img(imgpath)
fea_txt[#fea_txt + 1] = extract_txt(txtpath)
end
for i = 1,#fea_img do
-- loop over individual images.
for k = 1,fea_img[i]:size(1) do
local best_match = 1
local best_score = -math.huge
for j = 1,#fea_txt do
local cur_score = torch.dot(fea_img[i][{k,{}}], fea_txt[j])
From my understanding, fea_img is a lua table. Is the line fea_img[i][{k,{}}] some sort of slicing for the value for the key 'i' in the table fea_img?
I tried searching for more examples and found this being used here too (last line):
for i = 1,nsamples,batchsize do
-- indices
local lasti = math.min(i+batchsize-1,nsamples)
local m = lasti - i + 1
-- k-means step, on minibatch
local batch = x[{ {i,lasti},{} }]
Any help on this would be really appreciated. Thank you!
In lua you can access a specific index on a table in multiple ways. Like these two examples
local myValue = someTable.theIndex
-- or
local myOtherValue = someTable[2]
So the construct you see here is to access some values from a (nested) table.
Also in lua you can use anything except nil as a index, so even tables are possible.
The line
fea_img[i][{k,{}}]
Can be extended to this:
local index1 = i -- i in this case is your loop variable
local index2 = { k , { } } -- This creates a table with 2 values, the first one will be the vaule of the var k, the second one is an empty table
local value1 = fea_img[index1] -- This will get you a table
local value2 = value1[index2] -- This will get the same as: fea_img[i][{k,{}}]
Correction and Addition:
As Nicol Bolas already said in the comments: The index must be an exact match. Which means it literally has to be the same table, which is not the case for the presented code from you. Either you dropped code you thought is unnecessary or fea_img has some some kind of metatable on it.
In the case of
local k = 2
local table1 = {k, { } }
local table2 = {k, { } }
table2 and table1 do have the exact same content. But they are not the same table. Which will lead to nil always being retrieved if one is used to store data in a table and the other is used to get it back.
Syntactically, t[k] is indexing a table with a key. Normally, if there is a record in the table with the key k, its value is returned. Nothing more, nothing less.
If fea_img[i] was a normal table, {k,{}} would always return nil, since table indices are resolved based on their identity ({k,{}} is always a new table). Based on your code, I have to conclude that the elements of fea_img (i.e. what extract_img returns) are not normal tables.
In Lua, you can override the indexing operation using a metatable. If you index a value that has a metatable with __index, it will be used if there is no matching record in the table:
local t = {}
setmetatable(t, {
__index = function(t, k)
return k
end
})
print(t[{}])
This table has a metatable associated with it, which is used in the indexing operation. In this case __index returns the key, but whatever library you are using might provide more complex behaviour.
This is specific to the library you are using, not something related to the Lua syntax.

Resources