lua - get the list of parameter names of a function, from outside the function - lua

I'm generating some (non-html) documentation for a Lua library that I developed. I will generate the documentation by hand, but I'd appreciate some kind of automation if possible (i.e. generating skeletons for each function so I can fill them in)
I'd like to know if there's a way for lua to know the names of the parameters that a function takes, from outside it.
For example, is there a way to do this in Lua?
function foo(x,y)
... -- any code here
end
print( something ... foo ... something)
-- expected output: "x", "y"
Thanks a lot.

ok,here is the core code:
function getArgs(fun)
local args = {}
local hook = debug.gethook()
local argHook = function( ... )
local info = debug.getinfo(3)
if 'pcall' ~= info.name then return end
for i = 1, math.huge do
local name, value = debug.getlocal(2, i)
if '(*temporary)' == name then
debug.sethook(hook)
error('')
return
end
table.insert(args,name)
end
end
debug.sethook(argHook, "c")
pcall(fun)
return args
end
and you can use like this:
print(getArgs(fun))

Try my bytecode inspector library. In Lua 5.2 you'll be able to use debug.getlocal.

Take a look at debug.getinfo, but you probably need a parser for this task. I don't know of any way to fetch the parameters of a function from within Lua without actually running the function and inspecting its environment table (see debug.debug and debug.getlocal).

function GetArgs(func)
local args = {}
for i = 1, debug.getinfo(func).nparams, 1 do
table.insert(args, debug.getlocal(func, i));
end
return args;
end
function a(bc, de, fg)
end
for k, v in pairs(GetArgs(a)) do
print(k, v)
end
will print
1 bc
2 de
3 fg
Basically we use debug.getinfo to retrieve the nparams attribute (which gives us the information of how many parameters the function takes) and debug.getlocal to access the name of the parameters.
Tested and working with Lua 5.4

Take a look at the luadoc utility. It is sort of like Doxygen, but for Lua. It is intended to allow the documentation to be written in-line with the source code, but it could certainly be used to produce a template of the documentation structure to be fleshed out separately. Of course, the template mechanism will leave you with a maintenance issue down the road...

Related

Importing a bunch of names in Lua

In my script I follow the practice of "importing" functions from another module. So I have code similar to the following at the start of my script:
local some_function = assert(require("utils").some_function)
local another_func = assert(require("utils").another_func)
local yet_another = assert(require("utils").yet_another)
local and_another = assert(require("utils").and_another)
But this chunk of code is not quite readable.
(The assert() is there to guard against spelling mistakes in the function names.)
I know that I can easily write my own function, say require_names(), and turn the above code into:
local some_function, another_func, yet_another, and_another
= require_names("utils", { "some_function", "another_func", "yet_another", "and_another" })
This looks much better. Still, it's not optimal: there's redundancy in this code: the names of the functions repeat twice.
Is there a way for me to write my require_names() so that it doesn't have the redundancy problem?
Or, do you you have any ideas for solving the readability problem in some other way?
(I need solutions that run on both Lua 5.1 and 5.2)
If you want only a subset of what is in utils module, yet only create locals, then you can't. Other answers give you the whole utils module, in that case I don't see why not just use require 'yourmodule'. If you can relinquish locals, then:
function require_names(modName, objNames)
for i,v in ipairs(objNames) do
_G[v] = assert(require(modName)[v])
end
end
works in both 5.1 :
> print(setn)
nil
> print(insert)
nil
> require_names("table", {"setn", "insert"})
> print(setn)
function: 005F7910
> print(insert)
function: 005F7890
The only non-globals option is to put what you want in a local table, to get only the subset you need:
function require_names(modName, objNames)
local mod = {}
for i,v in ipairs(objNames) do
mod[v] = assert(require(modName)[v])
end
return mod
end
local utils = require_names("utils", { 'a', 'b' })
utils.a = asdfasf
print(utils.b)
However, the only benefit of above compared to local utils=require 'utils' is that it documents what you module is going to use from the required module. But it is a little noisy with all the quotes and braces.
I would do this:
local u = require 'utils'
Then use it like this:
u.some_function(...)
This is very easy to type and very obvious.
If you really need locals, then I would not use a single function called require_names but two: the regular require + extract. Here's extract
local function extract(t, keys)
local values = {}
for i=1, #keys do values[i] = t[keys[i]] end
return unpack(values)
end
Usage:
local utils = require 'utils'
local some_function, another_func, yet_another, and_another =
extract(utils, { "some_function", "another_function", "yet_another", "and_another"})

Modify Lua Chunk Environment: Lua 5.2

It is my understanding that in Lua 5.2 that environments are stored in upvalues named _ENV. This has made it really confusing for me to modify the environment of a chunk before running it, but after loading it.
I would like to load a file with some functions and use the chunk to inject those functions into various environments. Example:
chunk = loadfile( "file" )
-- Inject chunk's definitions
chunk._ENV = someTable -- imaginary syntax
chunk( )
chunk._ENV = someOtherTable
chunk( )
Is this possible from within Lua? The only examples I can find of modifying this upvalue are with the C api (another example from C api), but I am trying to do this from within Lua. Is this possible?
Edit: I'm unsure of accepting answers using the debug library. The docs state that the functions may be slow. I'm doing this for efficiency so that entire chunks don't have to be parsed from strings (or a file, even worse) just to inject variable definitions into various environments.
Edit: Looks like this is impossible: Recreating setfenv() in Lua 5.2
Edit: I suppose the best way for me to do this is to bind a C function that can modify the environment. Though this is a much more annoying way of going about it.
Edit: I believe a more natural way to do this would be to load all chunks into separate environments. These can be "inherited" by any other environment by setting a metatable that refers to a global copy of a chunk. This does not require any upvalue modification post-load, but still allows for multiple environments with those function definitions.
The simplest way to allow a chunk to be run in different environments is to make this explicit and have it receive an environment. Adding this line at the top of the chunk achieves this:
_ENV=...
Now you can call chunk(env1) and later chunk(env2) at your pleasure.
There, no debug magic with upvalues.
Although it will be clear if your chunk contains that line, you can add it at load time, by writing a suitable reader function that first sends that line and then the contents of the file.
I do not understand why you want to avoid using the debug library, while you are happy to use a C function (neither is possible in a sandbox.)
It can be done using debug.upvaluejoin:
function newEnvForChunk(chunk, index)
local newEnv = {}
local function source() return newEnv end
debug.upvaluejoin(chunk, 1, source, 1)
if index then setmetatable(newEnv, {__index=index}) end
return newEnv
end
Now load any chunk like this:
local myChunk = load "print(x)"
It will initially inherit the enclosing _ENV. Now give it a new one:
local newEnv = newEnvForChunk(myChunk, _ENV)
and insert a value for 'x':
newEnv.x = 99
Now when you run the chunk, it should see the value for x:
myChunk()
=> 99
If you don't want to modify your chunk (per LHF's great answer) here are two alternatives:
Set up a blank environment, then dynamically change its environment to yours
function compile(code)
local meta = {}
local env = setmetatable({},meta)
return {meta=meta, f=load('return '..code, nil, nil, env)}
end
function eval(block, scope)
block.meta.__index=scope
return block.f()
end
local block = compile('a + b * c')
print(eval(block, {a=1, b=2, c=3})) --> 7
print(eval(block, {a=2, b=3, c=4})) --> 14
Set up a blank environment, and re-set its values with your own each time
function compile(code)
local env = {}
return {env=env, f=load('return '..code, nil, nil, env)}
end
function eval(block, scope)
for k,_ in pairs(block.env) do block.env[k]=nil end
for k,v in pairs(scope) do block.env[k]=v end
return block.f()
end
local block = compile('a + b * c')
print(eval(block, {a=1, b=2, c=3})) --> 7
print(eval(block, {a=2, b=3, c=4})) --> 14
Note that if micro-optimizations matter, the first option is about 2✕ as slow as the _ENV=... answer, while the second options is about 8–9✕ as slow.

Get name of argument of function in lua

When call a lua function like
PrintMe(MyVariableName)
I would like to be able to actually print "MyVariableName" and not it's value(well, for demo purposes).
Obviously I could just pass the string but that requires extra quotes and I also would like to print it's value.
e.g.,
MyVariable = 4
PrintVariable(MyVariable)
Would print "MyVariable is 4" or whatever
I do not want to have to duplicate the name and variable like
PrintVariable(MyVariable, "MyVariable")
as this is unnecessary duplication.
Can lua handle it?
What I'm doing now is passing the variable name in quotes and using loadstring to get the value but I would like to just pass the variable directly without the extra unnecessary quotes(which I thought debug.getlocal did but it ends up returning the value instead of the name).
Here is mock example
function printme1(var, val)
print(var.." = "..val)
end
function printme2(v)
local r
loadstring("r = "..v)() -- equivalent to r = a but must be used since v is a string representing a and not the object a
print(v.." = "..tostring(r))
end
function printme3(v)
-- unknown
end
a = 3
printme1("a", a)
printme2("a")
printme3(a)
In this case all 3 should print the same thing. printme3 obviously is the most convenient.
You can't say PrintVariable(MyVariable), because Lua gives you no way of determining which variable (if any; a constant could have been used) was used to pass an argument to your function. However, you can say PrintVariable('MyVariable') then used the debug API to look for a local variable in the caller's scope which has that name:
function PrintVariable(name)
-- default to showing the global with that name, if any
local value = _G[name]
-- see if we can find a local in the caller's scope with that name
for i=1,math.huge do
local localname, localvalue = debug.getlocal(2,i,1)
if not localname then
break -- no more locals to check
elseif localname == name then
value = localvalue
end
end
if value then
print(string.format("%s = %s", name, tostring(value)))
else
print(string.format("No variable named '%s' found.", name))
end
end
Now you can say:
PrintVariable('MyVariable')
While in this case will print "MyVariable = 4".
Not, if you really want to do this without the quotes, you could check the caller's locals for variables that have a supplied value, but that's occasionally going to give you the wrong variable name if there is more than one variable in the caller's scope with a given value. With that said, here's how you'd do that:
function PrintVariable(value)
local name
-- see if we can find a local in the caller's scope with the given value
for i=1,math.huge do
local localname, localvalue = debug.getlocal(2,i,1)
if not localname then
break
elseif localvalue == value then
name = localname
end
end
-- if we couldn't find a local, check globals
if not name then
for globalname, globalvalue in pairs(_G) do
if globalvalue == value then
name = globalname
end
end
end
if name then
print(string.format("%s = %s", name, tostring(value)))
else
print(string.format("No variable found for the value '%s'.", tostring(value)))
end
end
Now you can say PrintVariable(MyVariable), but if there happened to be another variable in the caller's scope with the value 4, and it occurred before MyVariable, it's that variable name that will be printed.
you can do stuff like this with the debug library... something like this does what you seem to be looking for:
function a_func(arg1, asdf)
-- if this function doesn't use an argument... it shows up as (*temporary) in
-- calls to debug.getlocal() because they aren't used...
if arg1 == "10" then end
if asdf == 99 then end
-- does stuff with arg1 and asdf?
end
-- just a function to dump variables in a user-readable format
function myUnpack(tbl)
if type(tbl) ~= "table" then
return ""
end
local ret = ""
for k,v in pairs(tbl) do
if tostring(v) ~= "" then
ret = ret.. tostring(k).. "=".. tostring(v).. ", "
end
end
return string.gsub(ret, ", $", "")
end
function hook()
-- passing 2 to to debug.getinfo means 'give me info on the function that spawned
-- this call to this function'. level 1 is the C function that called the hook.
local info = debug.getinfo(2)
if info ~= nil and info.what == "Lua" then
local i, variables = 1, {""}
-- now run through all the local variables at this level of the lua stack
while true do
local name, value = debug.getlocal(2, i)
if name == nil then
break
end
-- this just skips unused variables
if name ~= "(*temporary)" then
variables[tostring(name)] = value
end
i = i + 1
end
-- this is what dumps info about a function thats been called
print((info.name or "unknown").. "(".. myUnpack(variables).. ")")
end
end
-- tell the debug library to call lua function 'hook 'every time a function call
-- is made...
debug.sethook(hook, "c")
-- call a function to try it out...
a_func("some string", 2012)
this results in the output:
a_func(asdf=2012, arg1=some string)
you can do fancier stuff to pretty this up, but this basically covers how to do what you're asking.
I have bad news, my friend. You can access function parameter names as they appear at the top of the function, but the data to access exactly what they were named in the calling function does not exist. See the following:
function PrintVariable(VariableToPrint)
--we can use debug.getinfo() to determine the name 'VariableToPrint'
--we cannot determine the name 'MyVariable' without some really convoluted stuff (see comment by VBRonPaulFan on his own answer)
print(VariableToPrint);
end
MyVariable = 4
PrintVariable(MyVariable)
To illustrate this, imagine if we had done:
x = 4
MyVariable = x
MyOtherVariable = x
x = nil
PrintVariable(MyVariable)
Now if you were Lua, what name would you attach in the metadata to the variable that ends up getting passed to the function? Yes, you could walk up the stack with debug.getint() looking for the variable that was passed in, but you may find several references.
Also consider:
PrintVariable("StringLiteral")
What would you call that variable? It has a value but no name.
You could just use this form:
local parms = { "MyVariable" }
local function PrintVariable(vars)
print(parms[1]..": "..vars[1])
end
local MyVariable = "bar"
PrintVariable{MyVariable}
Which gives:
MyVariable: bar
It isn't generic, but it is simple. You avoid the debug library and loadstring by doing it this way. If your editor is any good, you could write a macro to do it.
Another possible solution is add this facility your self.
The Lua C API and source is pretty simple and extendable.
I/we don't know the context of your project/work but if you ARE making/embedding your own Lua build you could extend the debug library with something to do this.
Lua passes it's values by reference, but unknown offhand if these contain a string name in them and if so if easily accessible.
In your example the value declaration is the same as:
_G["MyVariable"] = 4
Since it's global. If it were declared local then like others stated here you can enumerate those via debug.getlocal(). But again in the C context of the actual reference context it might not matter.
Implement a debug.getargumentinfo(...) that extends the argument table with name key, value pairs.
This is quite an old topic and I apologize for bringing it back to life.
In my experience with lua, the closest I know to what the OP asked for is something like this:
PrintVariable = {}
setmetatable(PrintVariable, {__index = function (self, k, v) return string.format('%s = %s', k, _G[k]) end})
VAR = 0
VAR2 = "Hello World"
print(PrintVariable.VAR, PrintVariable.VAR2)
-- Result: VAR = 0 VAR2 = Hello World
I do not give more explanation, because the code is quite readable, however:
What happens here is simple, you only set a metatable to the PrintVariable variable and add the __index metamethod that is called when the table is forced to search for a value in its index, thanks to this functionality you can achieve what you see in the example.
Reference: https://www.lua.org/manual/5.1/manual.html
I hope that future and new visitors will find this helpful.

How to set name for function which is in the table

For example, I have a table
table.insert( t, 1, function()
print ("rock");
end );
Is there any way to get function name from this table. I know that I can store name like a key, but what if I want to keep numeric index and also I want to know function name?
Is there any way to do it?
Thanks, on advance.
Say you have this code:
t = {}
x = 5
table.insert(t, 1, x)
t would then be {[1] = 5}. "5" is just a number - it has no name, and isn't associated with the variable "x"; it's a value.
In Lua, functions are treated exactly the same way, as values:
t = {}
x = function() print("test! :D") end
table.insert(t, 1, x)
The value of x is not associated with x in any way, shape, or form. If you want to manually name a function, you can do it by wrapping the function in a table, for example:
t = {}
x = function() print("test! :D") end
table.insert(t, 1, {
name = "MyFunctionName",
func = x
})
That is how you would do it!
...unless..
..you break the rules!
When Lua was developed, the developers realised that the anonymous nature of functions would make productive error messages difficult to produce, if not impossible.
The best thing you'd see would be:
stdin: some error!
stdin: in function 'unknown'
stdin: in function 'unknown'
So, they made it so that when Lua code was parsed, it would record some debug information, to make life easier. To access this information from Lua itself, the debug library is provided.
Be very careful with functions in this library.
You should exert care when using this library. The functions provided here should be used exclusively for debugging and similar tasks, such as profiling. Please resist the temptation to use them as a usual programming tool: they can be very slow. Moreover, several of these functions violate some assumptions about Lua code (e.g., that variables local to a function cannot be accessed from outside or that userdata metatables cannot be changed by Lua code) and therefore can compromise otherwise secure code.
To achieve your desired effect, you must use the debug.getinfo function; an example:
x = function()
print("test!")
print(debug.getinfo(1, "n").name)
end
x() -- prints "test!" followed by "x"
Unfortunately, the form of debug.getinfo that operates directly on a function doesn't fill the name argument (debug.getinfo(x, "n").name == nil) and the version above requires you to run the function.
It seems hopeless!
...unless..
..you really break the rules.
The debug.sethook function allows you to interrupt running Lua code at certain events, and even change things while it's all happening. This, combined with coroutines, allows you to do some interestingly hacky stuff.
Here is an implementation of debug.getfuncname:
function debug.getfuncname(f)
--[[If name found, returns
name source line
If name not found, returns
nil source line
If error, returns
nil nil error
]]
if type(f) == "function" then
local info = debug.getinfo(f, "S")
if not info or not info.what then
return nil, nil, "Invalid function"
elseif info.what == "C" then
-- cannot be called on C functions, as they would execute!
return nil, nil, "C function"
end
--[[Deep magic, look away!]]
local co = coroutine.create(f)
local name, source, linedefined
debug.sethook(co, function(event, line)
local info = debug.getinfo(2, "Sn")
name = info.namewhat ~= "" and info.name or nil
source, linedefined = info.short_src, info.linedefined
coroutine.yield() -- prevent function from executing code
end, "c")
coroutine.resume(co)
return name, source, linedefined
end
return nil, nil, "Not a function"
end
Example usage:
function test()
print("If this prints, stuff went really wrong!")
end
print("Name = ", debug.getfuncname(test))
This function isn't very reliable - it works sometimes, and doesn't others. The debug library is very touchy, so it's to be expected.
Note that you should never use this for actual release code! Only for debugging!
The most extreme case that is still acceptable is logging errors on piece of released software, to help the developer fix issues. No vital code should depend on functions from the debug library.
Good luck!
The function hasn't got any name. If you want you can assign it to a named variable:
theFunction = t[1]
-- Call it:
theFunction()
If what you want is storing a named function to the table, define it beforehand and use its name to store it:
theFunction = function()
print ("rock");
end
table.insert(t, 1, theFunction)
If this is not what you meant, give more details; for example how you would like to access the function. You're question is a bit misty.
The thing is table.insert considers the table as a sequence, only with numeric keys.
If you want to be able to call the function as t.fun() you'll have to use the table as an associative array and hence use a string as key. (BTW any type except nil or NaN are allowed as key)
t={}
t['MyFun']=function print'foo' end
t.myFun() -- uses syntactic sugar for string keys that are valid identifiers.
You might also notice that functions are passed by reference. So all functions are actually anonymous, and are just stored as a value to a certain key or variable.
You can store the names in a separate table.
functions = {}
functionNames = {}
function addFunction(f, name)
table.insert(functions, f)
functionNames[name] = f
end
To get the function, you can use the index. Once you have the function, you can get its name from function_names:
f = functions[3]
name = functionNames[f]
Good luck!

What's with PCALL or is Wowwiki wrong?

This is a WoW (World of Warcraft) lua script question. Not many of these get asked here but I have no where to turn and Stackoverflow is the programmer oasis for answers.
Question:
Wowwiki states that the 2nd, 3rd, 4th arguments are your calling functions 1st, 2nd, 3rd arguments. I don't find this to be true. I find that the 3rd, 4th, 5th arguments end up being the 1st, 2nd, 3rd arguments.
Link: http://www.wowwiki.com/API_pcall
Function:
function myTest(arg1)
return arg1 .. 10;
end
Problem:
local retOK, ret1 = pcall(myTest,"string value");
when I try the sample I get an error of "trying to perform concatenate on local 'arg1' (a nil value)". If I change the code to:
local retOK, ret1 = pcall(myTest,"string value", "bob");
then I get the output of "bob10". Where does the 2nd argument go and what is it for?
More Testing:
function BobsToolbox:RunTest()
local test1, value1 = pcall(BobsToolbox.Test1, "string value");
SharpDeck:Print("Test1: " .. tostring(test1) .. " Value: " .. tostring(value1));
end
function BobsToolbox:Test1(arg1)
return arg1 .. "10";
end
Results: attempt to concatenate local 'arg1' (a nil value)
function BobsToolbox:RunTest()
local test1, value1 = pcall(Test1, "string value");
SharpDeck:Print("Test1: " .. tostring(test1) .. " Value: " .. tostring(value1));
end
function Test1(arg1)
return arg1 .. "10";
end
Results: string value10
I am new to lua and I can't understand why these are different.
New Question:
The following code works but why?
function BobsToolbox:RunTest()
local test1, value1 = pcall(BobsToolbox.Test1, "string value");
SharpDeck:Print("Test1: " .. tostring(test1) .. " Value: " .. tostring(value1));
end
function BobsToolbox.Test1(arg1)
return arg1 .. "10";
end
What's the difference between the following: ("." vs ":")
function BobsToolbox.Test1(arg1)
function BobsToolbox:Test1(arg1)
Lua Documentation:
http://www.lua.org/pil/16.html
This use of a self parameter is a central point in any object-oriented language. Most OO languages have this mechanism partially hidden from the programmer, so that she does not have to declare this parameter (although she still can use the word self or this inside a method). Lua can also hide this parameter, using the colon operator. We can rewrite the previous method definition as
function Account:withdraw (v)
self.balance = self.balance - v
end
and the method call as
a:withdraw(100.00)
The effect of the colon is to add an extra hidden parameter in a method definition and to add an extra argument in a method call. The colon is only a syntactic facility, although a convenient one; there is nothing really new here. We can define a function with the dot syntax and call it with the colon syntax, or vice-versa, as long as we handle the extra parameter correctly:
Account = { balance=0,
withdraw = function (self, v)
self.balance = self.balance - v
end
}
function Account:deposit (v)
self.balance = self.balance + v
end
Account.deposit(Account, 200.00)
Account:withdraw(100.00)
Possible Conclusion:
With this in mind I assume that when calling a ":" function using "pcall" you must supply the "self" argument.
Related: There are nice live code editors for WoW. I used to use LuaSlinger, but turns out that's no longer developed and the developer recommends Hack instead.
However, what you might be encountering here is that the colon method-call syntax is just syntax sugar, ditto for method definitions, IIRC. Basically, if you do foo:bar("quux!"), where foo is an object, you are in reality just doing foo.bar(foo, "quux!").
Hope that helps!
Well, I don't think WoWWiki is wrong. Here is the code I am using:
function myTest(arg1) return arg1 .. 10; end
local retOK, ret1 = pcall(myTest,"string value");
DEFAULT_CHAT_FRAME:AddMessage(ret1);
local retOK, ret1 = pcall(myTest,"string value", "bob");
DEFAULT_CHAT_FRAME:AddMessage(ret1);
Here is the output I get in my General chat box:
string value10
string value10
How are you trying your sample code? I just pasted my code into an existing mod lua file and made sure that mod was enabled in the addons window before selecting my character and logging in. I made a few changes to the source lua file and typed:
/console reloadui
To try the new changes and have the results output to my screen. I don't have much advice to offer you, because I haven't done much work with WoW addons. Have you tried this code in a blank addon to make sure nothing else is interfering? Have you actually tried the code in game? If you can provide any more information or want me to try anything else, let me know!
Update: Decided to try a few more tests. Here are the tests (with the same function):
local retOK, ret1 = pcall(myTest,"");
DEFAULT_CHAT_FRAME:AddMessage(ret1);
local retOK, ret1 = pcall(myTest, nil, "bob");
DEFAULT_CHAT_FRAME:AddMessage(ret1);
And the results:
10
attempt to concatenate local 'arg1' (a nil value)
It's interesting that the error I see when arg1 is nil is slightly different than the error you see. I'd be interested in knowing how you are testing your code. Or maybe you didn't copy the error down verbatim? I guess you could also try clearing out your WTF folder and disabling the rest of your addons to test this function. If it makes a difference, then you can enable them one a time until you find the problem.

Resources