How to implement a basic Lua function in Conky? - function

I am trying to add a function to my Conky which prints the length of a string for debug purposes. The code, inside a file called test.lua, is pretty trivial:
function test(word)
return string.len(word)
end
...and I load it like this. In my conky.config section I have:
lua_load = '/home/xvlaze/test.lua',
lua_draw_hook_pre = 'test'
...in the conky.text section I have:
${lua test "fooo"}
...where test is the name of the function and fooo the string to test.
The expected result should be a printed 4 in Conky, but instead of that I get:
conky: llua_do_call: function conky_test execution failed: /home/xvlaze/test.lua:2: attempt to index a nil value (local 'string')
conky: llua_getstring: function conky_test didn't return a string, result discarded
I have browsed through the documentation, but I can't find anything. Does anybody know where the failure is?

Several guidances on how to implement functions in Conky:
First of all: YOU MUST USE conky_ BEFORE YOUR FUNCTION'S NAME.
Otherwise, you will get the following error when running your Conky:
attempt to call a nil value
Secondly: YOU MUST ALWAYS RETURN A VALUE.
I don't mind repeating it - it is crucial. Otherwise, you will get:
function foobar didn't return a string, result discarded
function_result
...in your terminal, and your Conky will be left empty of values related to your extra code. Nothing will be printed regarding your function.
Last but not least: YOU MUST ALWAYS CALL YOUR FUNCTION LIKE:
lua_load = '/path/to/function.lua',
-- Whatever content...
${lua function_name function_parameter1 function_parameterN} -- In case you use more than one parameter.
In summary, a dummy function template could be:
MAIN FILE (conky.conf):
conky.config = {
-- Whatever content... Lua styled comments.
lua_load = '/path/to/function.lua',
}
conky.text = [[
# Whatever content... In this section comments are started with '#'!
${lua function_name parameter}
]]
FUNCTION FILE:
function conky_function_name(parameter)
-- Whatever content... Remember this is Lua, not conky.text syntax. Always use '--' comments!
return whatever -- No return, no party. A function MUST always return something!
end

Related

Retrieving A Function From A WebhookScript Global Variable

In WebhookScript, I can store a function in a variable with:
sub = function(a, b) {
return a - b
}
I'd like to store a function in a Global Variable so that I can use it in multiple Custom Actions. But if I've saved the above function as $sub$ then
sub2 = var('$sub$')
subX = sub(1,2)
causes an error:
Trying to invoke a non-function 'string' # line...
And
function subX(a,b){
var('$sub$')
}
when sub only contains return a - b, doesn't work either.
Obviously I need to convert the string to a function but I'm not sure whether that's possible.
I know this is a bit of an obscure language but if anyone knows how this can be done in similar languages like JavaScript and PHP, I'm happy to test out any guesses...
The solution here is to remove the function section and just enter the script, which inherits the execution scope so if my global variable $script$ is:
return 'hello ' + a
Then I can execute the function with:
a = 'world'
value = exec(var('$script$'))
echo(value)
(credit to Webhook.Site's support team for explaining this)

How to remove duplicate results from a cts:uris when distinct values does not work

I have the following code that returns an array of array of results I try to use distinct-values to remove duplicates and it does nothing I have tried also removing using looping functions by comparing values with no success.
I have tried converting to "xs anyAtomicType" and using distinct values
I have tried putting in json array and extracting the sub-array
I have tried tokenizing, xdmp quote, string-before/after and many others
declare function local:verify-user-uri($dir as xs:string)
{
for $each in cts:uris($dir, ())
let $uIds := (for $d in $each
where contains($d, "/profile.xml")
return $d)
return $uIds
};
I get back duplicated result in form of:
/users/123-343-/profile.xml
/users/122-222-/profile.xml
/users/123-343-/profile.xml
/users/122-222-/profile.xml
/users/123-343-/profile.xml
/users/122-222-/profile.xml
I am expecting:
/users/123-343-/profile.xml
/users/122-222-/profile.xml
Is it possible that you have simply invoked this function 3 times and didn't realize it?
You have declared $dir to be a single xs:string. If your $dir happened to be a sequence of strings of the same directory, or if you otherwise invoked the function 3 times with the directory variable.
It can easily happen with function mapping enabled (default behavior). https://docs.marklogic.com/guide/xquery/enhanced#id_55459
There are a couple of things that you can do as a diagnostic:
1.) Remove the explicit type on the $dir parameter in the function:
declare function local:verify-user-uri($dir)
{
for $each in cts:uris($dir, ())
let $uIds := (for $d in $each
where contains($d, "/profile.xml")
return $d)
return $uIds
};
do you get an error executing cts:uris() that looks like this:
[1.0-ml] XDMP-ARGTYPE: )err:XPT0004) cts:uris(("/users/", "/users/", "/users/"), ()) -- arg1 is not of type xs:string?
2.) try disabling function mapping by adding the following to the prolog:
declare option xdmp:mapping "false";
and see if you then get an invalid coercion error like:
[1.0-ml] XDMP-AS (err:XPTY0004) $dir as xs:string -- Invalid coersion ("/users/", "/users/", "/users/") as xs:string
3.) You could also add something to the end of the sequence of values returned from the function to indicate how many times it has executed:
declare function local:verify-user-uri($dir as xs:string)
{
for $each in cts:uris($dir, ())
let $uIds := (for $d in $each
where contains($d, "/profile.xml")
return $d)
return $uIds, "#"
};
And see how many times you see "#" in the result. If more than one, you are invoking the function multiple times.
Next to the good suggestions from Mads, I notice a couple of other things about your code:
It doesn't make sense to iterate over $each as it contains one uri only. Keep in mind that a FLWOR statement ends with a return, which tells what should be the result per item
Beware that the first arg to cts:uris only marks a start, not an end. If you feed in /aaa/, you also get back /bbb/ etc, though not vice versa.
To be honest, I think you are looking for cts:uri-match() instead, which would reduce your function to a one-liner:
declare function local:verify-user-uri($dir as xs:string) {
cts:uri-match($dir || "*/profile.xml")
};
HTH!
PS: I do recommend always disabling function mapping as Mads recommends. It can prevent a lot of confusion.

Lua: How to Properly Construct Nested Functions

I'm trying to create a function with local functions within it. The main function would receive an output from an outside source, and the functions within it would be required to translate that input and return results for later use. My problem is that the way I am currently attempting this, when I try to put in my first local function inside the main function, I continue to get nil. Here is an example:
function stats(input)
height, weight, name, age, gender, relate = string.match(input, "(%d*)ft,(%d*)lbs,(%w*),(%d*),(%u*),(%u)")
if name then
function nameInit(relate)
relateTable = {["F"] = "Friend", ["R"] = "Relative"}
for k,v in pairs (relateTable) do
if relate == k then
relship = v
return relship
end
end
end
end
person = name.." is "..age.." years old, weighs "..weight.." and blah blah blah....
return person
end
print (stats("5.8ft, 160lbs, Mike Scott, 19, M, F"))
Obviously, this subject isn't practical but what I'm trying to do is along the same lines in terms of end response. I'm currently getting lua: filename: attempt to concatenate global 'relship' (a nil value)? I can get the response I want without the nested function. But when I try to elaborate more on the response I would like to receive, and place that function inside the global function, I begin to get these response(s). This seems to be my problem anytime I attempt to use functions within other functions. I can make two separate global functions and print results from either one. But the minute I try to use one within another, I screw myself up. Anyone who can take some time to help a beginner better understand what he is doing wrong would be great! Thanks all.
Based on your statement "the functions within it would be required to translate that input and return results for later use", I'm not sure that nested functions is what you want. You say that when you have two global functions your code works:
function func1(args)
...
end
function func2(args)
...
end
but when you nest (for example) func1 inside func2, it no longer works. Lua does allow you to define nested functions, but I can only think of two reasons to use them:
to return a function that encapsulates a task, usually with some of the wrapper function's args and/or locals as upvalues.
to encapsulate some logic in a function to be called from within the wrapper function, with no need for any other functions to call it.
For example of case 1:
function func2(a, b, c)
function func1()
do something with a, b, c eventhough they are not args of func1
return result
end
return func1
end
someFunc = func2(1,2,3)
....
result = someFunc() -- calls func1 created inside func2, using 1,2,3
For example of case 2:
function func2(a, b, c)
function func1()
do something with a, b, c eventhough they are not args of func1
return result
end
result = func1()
...
end
func2(1,2,3)
You could also add a nested function to a table object (class) passed as argument, but I see this as a variation on case 1.

Calling the function name

As a beginner in Lua, I am sorry if the answer on this is easy.
I was trying to call a function within a code, yet after 2 hours of searching I couldn't find the wanted results. (Maybe I use the wrong search query's?)
Example code
function Test123 ()
SayTest = True
if SayTest = True then
-- This Is where I want to call the function name Test123,
-- yet I can't seem to succeed in this since it is just
-- starting a new function
SystemNotice ( role, function)
end
end
This should be the result:
function Test123 ()
SayTest = True
if SayTest = True then
SystemNotice ( role, 'Test123')
end
end
If anyone can help me out, I would be thankful. If I am still being unclear, just tell me and I will try to describe it better. My excuses for my limited English.
In Lua functions are actually values. That means they do not really have a name, you can only assign them to a variable or table field, but since the value itself has no concept of its name, you can't retrieve it.
That said, with the debug library, you can do this:
function getfname()
return debug.traceback("", 2):match("in function '(.-)'");
end
function bar()
print(getfname())
end
bar(); -- prints bar
foo = bar;
foo() -- prints foo
knerf = {rab = bar};
knerf.rab() -- prints rab
Note that this only works with the default Lua error handler or one that returns the same or very similar output, however you can obviously modify the pattern to suit what you need.
Read this: I would not advise this solution for performance-intensive tasks. Both string matching and the traceback are not really suited for this. Also, obviously the debug library must be enabled so you can actually use the traceback function.
Your function declaration lacks anend.
function Test123 ()
end
Read the functions chapter from the Lua manual. Just for the record, your if will also need an end.
This is a construct that just goes against the Lua philosophy that functions are first class citizens:
a function is just another value, and as such it has no name.
A variable can be assigned a value, thus binding a function to a name.
But that name can change. or a function can have multiple names. Which one to pick?
A better solution would be creating an anonymous function with an upvalue (a closure) instead:
function genTest(name)
return function()
SayTest = true
if SayTest == true then
print ( 'role', name)
end
end
end
Test123 = genTest('Test123')
Test123()
foobar = Test123
foobar()
This creates a function with a bound local variable name (see PiL 6.1 ).

Lua - Execute a Function Stored in a Table

I was able to store functions into a table. But now I have no idea of how to invoke them. The final table will have about 100 calls, so if possible, I'd like to invoke them as if in a foreach loop. Thanks!
Here is how the table was defined:
game_level_hints = game_level_hints or {}
game_level_hints.levels = {}
game_level_hints.levels["level0"] = function()
return
{
[on_scene("scene0")] =
{
talk("hint0"),
talk("hint1"),
talk("hint2")
},
[on_scene("scene1")] =
{
talk("hint0"),
talk("hint1"),
talk("hint2")
}
}
end
Aaand the function definitions:
function on_scene(sceneId)
-- some code
return sceneId
end
function talk(areaId)
-- some code
return areaId
end
EDIT:
I modified the functions so they'll have a little more context. Basically, they return strings now. And what I was hoping to happen is that at then end of invoking the functions, I'll have a table (ideally the levels table) containing all these strings.
Short answer: to call a function (reference) stored in an array, you just add (parameters), as you'd normally do:
local function func(a,b,c) return a,b,c end
local a = {myfunc = func}
print(a.myfunc(3,4,5)) -- prints 3,4,5
In fact, you can simplify this to
local a = {myfunc = function(a,b,c) return a,b,c end}
print(a.myfunc(3,4,5)) -- prints 3,4,5
Long answer: You don't describe what your expected results are, but what you wrote is likely not to do what you expect it to do. Take this fragment:
game_level_hints.levels["level0"] = function()
return
{
[on_scene("scene0")] =
{
talk("hint0"),
}
}
end
[This paragraph no longer applies after the question has been updated] You reference on_scene and talk functions, but you don't "store" those functions in the table (since you explicitly referenced them in your question, I presume the question is about these functions). You actually call these functions and store the values they return (they both return nil), so when this fragment is executed, you get "table index is nil" error as you are trying to store nil using nil as the index.
If you want to call the function you stored in game_level_hints.levels["level0"], you just do game_level_hints.levels["level0"]()
Using what you guys answered and commented, I was able to come up with the following code as a solution:
asd = game_level_hints.levels["level0"]()
Now, asd contains the area strings I need. Although ideally, I intended to be able to access the data like:
asd[1][1]
accessing it like:
asd["scene0"][1]
to retrieve the area data would suffice. I'll just have to work around the keys.
Thanks, guys.
It's not really clear what you're trying to do. Inside your anonymous function, you're returning a table that uses on_scene's return value as keys. But your on_scene doesn't return anything. Same thing for talk.
I'm going to assume that you wanted on_scene and talk to get called when invoking each levels in your game_level_hints table.
If so, this is how you can do it:
local maxlevel = 99
for i = 0, maxlevel do
game_level_hints.levels["level" .. i] = function()
on_scene("scene" .. i)
talk("hint" .. i)
end
end
-- ...
for levelname, levelfunc in pairs(game_level_hints.levels) do
levelfunc()
end