Perplexing issue with simple coroutine in Lua - lua

I'm learning Lua and trying to create a simple coroutine. In Lua 5.1, the code below gives the error: "attempt to yield across metamethod/C-call boundary." I've read about that limitation and I can't see how it applies to my code. I tried it in Lua 5.2 and got "attempt to yield from outside a coroutine," which is equally confusing to me. I'm sure the answer will be embarrassingly obvious!
output = {}
done = false
function mainLoop()
while not done do
if co == nil then
co = coroutine.create(subLoop())
elseif coroutine.status(co) == "suspended" then
print(output[k])
coroutine.resume(co)
elseif coroutine.status(co) == "dead" then
done = true
end
end
end
function subLoop()
for k=1, 20 do
table.insert(output, "This is line " .. k .. " of the test output")
coroutine.yield()
end
end
mainLoop()

You are calling subLoop
if co == nil then
co = coroutine.create(subLoop())
instead of passing it to coroutine.create
if co == nil then
co = coroutine.create(subLoop)
This results in you attempting to yield from the main state / (not-really-)coroutine, which gives errors with varying descriptions across versions.

Related

Lua table.foreach missing

I used https://www.lua.org/cgi-bin/demo quite a lot to quickly hack together some scripts and test if they actually work. But today I stumpled across something that is very strange.
Testing this code
local t = {}
local threshold = 3
local counter = 0
t["1"] = true
t["2"] = true
t["3"] = false
t["4"] = true
table.foreach(t, print)
table.foreach(
t,
function(k,v)
if v then
counter = counter + 1
end
end
)
print(counter)
gave me this error message
input:9: attempt to call a nil value (field 'foreach')
so I tried running
for k,v in pairs(table) do
print(k,v)
end
resulting in this output
concat function: 0x42be90
remove function: 0x42bca0
sort function: 0x42ba50
move function: 0x42baf0
insert function: 0x42bda0
unpack function: 0x42b2a0
pack function: 0x42b3a0
Since it's about a year ago I last used this site to test my code I can't really tell when there was a change. Or if there was a change. I am very sure last time I used that site I used the table.foreach function. But now it does not work.
I also checked the changelog in case there was a change I missed but https://www.lua.org/versions.html does not show any change past June 2020 with version 5.4
May someone tell me where I messed up or what I am missing?
The error 'attempt to call a nil value (field 'foreach')' is indicating that the table.foreach() function is not defined. This error is likely caused by the fact that the table.foreach() function was removed in Lua 5.3.
In Lua 5.3, the equivalent method for iterating over a table is the pairs() function.
To fix this error you can replace the table.foreach with for k, v in pairs(t) do and replace the second table.foreach with for k, v in pairs(t) do
for k, v in pairs(t) do
print(k,v)
end
counter = 0
for k, v in pairs(t) do
if v then
counter = counter + 1
end
end
print(counter)

Cycling through different file location options

I'm essentially trying to create a function which tests the first location I give, in the form:
myComputer.referenceLookup("/address/x/text")
and return the string in that location if it is not NULL or "None" or "" (empty).
If not, I want it to test the next possible location:
myComputer.referenceLookup("/address/1/x/text")
Otherwise, I would like it to return an empty string ("").
I've tried looking in the Lua Manual to no avail as well as testing different forms in repl.it, but unfortunately, I can't replicate a similar example as I usually do when testing.
function firstLine(x)
if myComputer.referenceLookup("/Address/ .. (x) .. /text") != NULL or "None" or "" then
return myComputer.referenceLookup("/Address/ .. (x) .. /text")
elseif myComputer.referenceLookup("/Address/1/ .. (x) .. /text") != NULL or "None" or "" then
return myComputer.referenceLookup("/Address/1/ .. (x) .. /text")
else
return ""
end
end
myComputer.out.firstHouseNumber = firstLine(housenumber)
It's worth noting that the usual way I would reference the fact is as follows:
myComputer.out.firstHouseNumber= myComputer.referenceLookup("/Address/housenumber/text")
or
myComputer.out.firstHouseNumber= myComputer.referenceLookup("/Address/1/housenumber/text")
The platform I'm using doesn't throw errors, it just will return blank instead of running the lua script so I am unable to debug (hence usually using repl.it).
I know this makes it a bit of an abstract question, but if anyone knows how I can do what I am describing, it would be very much appreciated.
Assumptions
Looking at your answer, I will assume that
myComputer.referenceLookup is defined somewhere else and works as intended (and not part of this question)
NULL is also defined somewhere else and represents some sort of nil-value
Answer
The line
if myComputer.referenceLookup("/Address/ .. (x) .. /text") != NULL or "None" or "" then
doesn't work, because the or operator doesn't work that way.
How Lua interprets it is
if (myComputer.referenceLookup("/Address/ .. (x) .. /text") != NULL) or "None" or ""
and since "None" is a String value and thus considered truthy, the if condition will always evaluate to true, so it will always return the first location. Also, there is no != operator in Lua; it's ~= instead.
As for a solution, you essentially need three comparisons like this:
if myComputer.referenceLookup("/Address/" .. x .. "/text") ~= NULL
and myComputer.referenceLookup("/Address/" .. x .. "/text") ~= "None"
and myComputer.referenceLookup("/Address/" .. x .. "/text") ~= "" then
Obviously calling the function three times is a bad idea, both because of performance and because it may have side effects, so it's better to save it into a variable first like so:
local result = myComputer.referenceLookup("/Address/" .. (x) .. "/text")
if result ~= NULL and result ~= "None" and result ~= "" then
return result
end
Extra
If you want to make your program easier to extend, you can also use string.format to build the locations from templates. Say you have a table containing all your locations like this:
local locations = {
"/Address/%s/text";
"/Address/1/%s/text";
}
Then you can iterate through the entries using ipairs and build each location using string.format:
for index, template in ipairs(locations) do
local result = myComputer.referenceLookup(template:format(x))
if result ~= NULL and result ~= "None" and result ~= "" then
return result
end
end
Note that you can write string.format(template, x) as template:format(x) as long as template is a string. (further reading)

LUA: trying to remove a value from a table when it is found in another table

I'm trying to delete a key and value from a table when it is found in another table. I've been using this so far but although it recognizes the duplicate, it always removes the last item in the table...
function get_key_for_value( t, value )
for k,v in pairs(t) do
if v==value then return k
end
return nil
end
end
for k,v in pairs (Iranian_Protected_Groups) do
v[6] = 0
if Springfield_3_Target_Name == v[2] then
v[6] = v[6] + 1
if v[6] > 0 then
local Key_To_Remove = get_key_for_value (Iranian_Protected_Groups, v)
MESSAGE:New( "Shared target is "..v[2], 40):ToBlue()
table.remove (Iranian_Protected_Groups, Key_To_Remove)
end
end
end
any help would be appreciated!
First, you should format your code using standard indentation to make it easier to parse as a human reading the code:
function get_key_for_value(t, value)
for k, v in pairs(t) do
if v == value then
return k
end
return nil
end
end
Look carefully at the for loop. You will never get past the first iteration, because every iteration returns.
Your function is fixed if you move your return nil statement outside the loop. (Though for most purposes, is redundant, because generally no value is equivalent to returning nil).
Before, Key_To_Remove was nil. When passing nil as the index to remove in table.remove, Lua removes the last element. This is convenient when treating a list like a stack, but hid a bug for you in this case.

Lua Scripting for G13 Syntax Error on While Loop

I'm trying to put together an autofishing script for Terraria that will do more than just click at scheduled intervals. At this point, it's giving me a syntax error at the line that says while fishing do.
I've tried separating the while and the do to different lines, putting the fishing into parentheses, putting something else between the line before and the while loop in case it's the line before actually causing the problem. The only thing that any of any of those accomplished was when I put the do on the next line. When I did that it complained about the line with just do.
I'm pretty new to Lua scripting, but it looks like the exact same sort of while loop as I've seen in the documentation.
fishing = false
function goFish()
PressAndReleaseKey("d")
Sleep(5)
PressAndReleaseKey("d")
PressAndReleaseKey("1")
local x = GetRunningTime()
while fishing do
if(GetRunningTime() % 180000) == 0) then PressAndReleaseKey("b") end
PressAndReleaseMouseButton(1)
Sleep(4500)
if(GetRunningTime()-x > 6000000) then
x = getBait()
end
end
end
The error is at this line:
if(GetRunningTime() % 180000) == 0) then PressAndReleaseKey("b") end
which should be
if(GetRunningTime() % 180000) == 0 then PressAndReleaseKey("b") end
or
if((GetRunningTime() % 180000) == 0) then PressAndReleaseKey("b") end
In Lua IF conditions do not need to be wrapped in parentheses.

Call stack has exceeded maximum of depth of 100 ,Lua

function findWord(s,i)
-- find first word in given text
local j = i+1
while not _isWhite(s:byte(j)) and j < #s do -- getting error here
j = j + 1
end
return s:sub(i,j), j
end
function splitText(s,maxLen)
-- split text into chunks of maxLen length
rs ={}
local function _g(s,i,c,rs)
-- recursively split text
local function _f(s,i,c)
-- recursively find words and add each word to a chunk
local w,i = findWord(s,i)
if i == #s then return c..w end
if #(c..w) <= maxLen then
c = c..w
s = s:sub(i+1,#s,true)
return _f(s,1,c)
else
return c
end
end
rs[#rs+1] = _f(s,1,'')
i = i+#rs[#rs]
if i < #s then
local s = s:sub(i,#s,true)
return _g(s,1,'',rs)
else
return rs
end
end
return _g(s,1,'',rs)
end
I have above function to split a string, It has been working earlier but this time it started giving error "call stack has exceeded maximum of depth of 100, verify a function is not calling itself by accident."
Any idea why I might be getting this error, this behaviour seems random since I am quite sure about rest of the script and same split function has been working fine as well.
EDIT:
Yes, isWhiteSpace was provided to me and has the following code, I am not supposed to change it since it worked earlier . Here is isWhite function:
function _isWhite(byte)
return byte == 32 or byte == 9
end
So both _g and _f call themselves, and _g calls _f. So clearly the recursion-stop conditions you have are too weak. In _g I see
if i < #s then
local s = ...
return _g(s,1,'',rs)
else
return rs
end
which will stop when i >= #s. If this never happens, you will get infinite recursion. It is hard to say by looking at the code how i varies, but based on this line:
i = i+#rs[#rs]
it appears to by some value, but can't say if there is guarantee that stop condition will ever be reached. With _f it is worse: the stop recursion conditions are
if i == #s then return c..w end
and
#(c..w) > maxLen
Again very hard to say if this is strong enough: what if i is greater than #s, does the rest of the function work? Although findWord() returns i<#s for non empty s, not sure what will happen if s empty.
Best way to find out is to put some print statements that give you a trace of _g and _f and the parameters received, this will tell you clearly what stop conditions are being missed.

Resources