unpack() or ColourNote() weirdness

Posted by Ked on Sat 27 May 2006 11:05 PM — 14 posts, 45,429 views.

Russia #0
While trying to answer a question about displaying a prompt with colours preserved, in this thread: http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=6589&page=999999, I've stumbled upon Lua's unpack() function, which solves a problem I've encountered a while ago, when trying to do essentially what the thread above asks. The problem was needing multiple ColourTell calls to display a prompt, which can be very bad for performance in some cases. Back then I opted for parsing packets in OnPluginPartialLine, which turned out to be much faster, but now I'd like to try to do it the "clean" way. There's a new problem however. Here's the function:


function echoLastPrompt()
   prompt = table.remove(prompts)
   -- if prompt is nil then we are out of prompts to
   -- display for now
   if prompt == nil then
      return
   end

   local args = {}  -- a table to hold ColourNote arguments

   -- iterate over the styles in the prompt
   for chunk,style in prompt do
      table.insert(args, RGBColourToName(style.textcolour))
      table.insert(args, RGBColourToName(style.backcolour))
      table.insert(args, style.text)
   end

   ColourNote(unpack(args))
end


This looks like it should work, but every time I try to use it, I get an error complaining about a bad last argument to ColourNote (no value instead of string). The really confusing part is that if I add a print(unpack(args)) statement right before ColourNote(unpack(args)) then everything works just fine, except I get an extra note printed on the screen.

I've tried just unpacking the list prior to calling ColourNote, in hope that this is some quirk of unpack() that for some reason requires me to call it twice, so I can usefully employ the return from the second call, but with no luck - it seems to only work when I use print(unpack(args)).

Any ideas?

Australia Forum Administrator #1
Well this works for me with no errors, and displaying as it should:


local args = {}  -- a table to hold ColourNote arguments


   table.insert(args, "blue")
   table.insert(args, "green")
   table.insert(args, "Hello")

   table.insert(args, "white")
   table.insert(args, "red")
   table.insert(args, ", world")

   ColourNote(unpack(args))



Try doing: tprint (args)

Just to check your args are set up as you expect.
Amended on Sat 27 May 2006 11:43 PM by Nick Gammon
Russia #2
Still no luck and no clue. tprint(args) shows that I have the table laid out correctly, and the function works every time I use tprint(args) before ColourNote(unpack(args)). Just as it works when I use print() or print(anything) before the ColourNote. But stops working as soon as I skip the printing and use the ColourNote alone.

Now I am suspecting that ColourNote is somehow to blame. I've changed the function a bit, to display a label before the echoed prompt:


function echoLastPrompt()
   local prompt = table.remove(prompts)
   -- if prompt is nil then we are out of prompts to
   -- display for now
   if prompt == nil then
      return
   end

   local args = {}  -- a table to hold ColourNote arguments

   -- iterate over the styles in the prompt
   for chunk,style in ipairs(prompt) do
      table.insert(args, RGBColourToName(style.textcolour))
      table.insert(args, RGBColourToName(style.backcolour))
      table.insert(args, style.text)
   --tprint(args)
   end

   ColourNote("silver", "", "[Note]", unpack(args))
end


I call the function from the command line after making sure that some prompts have been collected in the global table. The first time I call echoLastPrompt() I get the following error message and nothing on the screen:


Error number: 0
Event:        Run-time error
Description:  [string "Script file"]:41: bad argument #6 to `ColourNote' (string expected, got no value)
stack traceback:
	[C]: in function `ColourNote'
	[string "Script file"]:41: in function `echoLastPrompt'
	[string "Command line"]:1: in main chunk
Called by:    Immediate execution


If I call echoLastPrompt() again right afterwards (without reloading the script or touching anything else), then I get this string printed with all the correct colours:

[Note][Note]4073h, 3380m, 19265e, 15800w exd-


Amended on Sun 28 May 2006 12:29 AM by Ked
Russia #3
Another weirdness... The error message I posted above is displayed for a ColourNote that should print a prompt with two style runs and that "[Note]" prepend, so there should be a total of 9 arguments (3 parts * 3 args each), yet the error reports a 6th argument missing. How is that even possible? If one of the arguments is indeed missing (which it is not but I am willing to play along) then I am passing 8 arguments, all of which are strings. How is it able to figure out that the 6th argument out of 8 is missing? Shouldn't it be reporting argument #9?
Australia Forum Administrator #4
This isn't all making total sense. For one thing, your function echoLastPrompt is changing its environment (by doing table.remove) so I would expect different behaviour if you called it twice in a row.

Second, I would tprint at the end of the loop, to see what we are sending to ColourNote, not in each iteration,

Try this - I have removed the deletion of the last element, and am printing both the prompt table, and the args table.


function echoLastPrompt()
   -- get last prompt
   local prompt = prompts [table.getn (prompts)]
   if prompt == nil then
      return
   end

   print ("Prompt table = ")
   tprint (prompt)

   local args = {}  -- a table to hold ColourNote arguments

   -- iterate over the styles in the prompt
   for chunk,style in ipairs(prompt) do
      table.insert(args, RGBColourToName(style.textcolour))
      table.insert(args, RGBColourToName(style.backcolour))
      table.insert(args, style.text)
   end

   print ("args table = ")
   tprint(args)

   print ("arg count = ", table.getn (args))

   ColourNote("silver", "", "[Note]", unpack(args))
end


Quote:

How is it able to figure out that the 6th argument out of 8 is missing?


See this test:



ColourNote ("white", "red", "hi there", 
            "blue", "green", nil, 
            "white", "black", "everyone")


Output

[string "Immediate"]:17: bad argument #6 to `ColourNote' (string expected, got nil)
stack traceback:
	[C]: in function `ColourNote'
	[string "Immediate"]:17: in main chunk



In this example, I have 9 arguments, but it is reporting an error on argument 6, that is possible for it to do.

Your error was "got no value", not "got nil", so this example is probably not what is happening to you. However without the printout that I have inserted (print ("arg count = ", table.getn (args))) you can't really be sure you have 9 arguments there.

Amended on Sun 28 May 2006 05:18 AM by Nick Gammon
Australia Forum Administrator #5
What version of MUSHclient are you using?
Russia #6
The latest one - 3.74
Russia #7
The tprint() ended up inside the loop because I wanted to try out all possible points where I could put it - it was originally outside.

And as for the nil argument example, that's perfectly reasonable: it expects a string in the 6th argument and gets a nil there, so it complains about argument #6 being wrong. But in my case it gets strings in all arguments except (presumably) 9th, yet complains about the 6th, which should be perfectly fine. So there has to be something fishy going on inside ColourNote, as I doubt that unpack() can magically insert a hole inside an argument list of another function. This could happen if, for example, some outter function (also called ColourNote), upon receiving 9 arguments for ColourNote were to call the real ColourNote with the first three, then itself with the last 6 arguments -> ColourNote with the first 3 out of 6 -> itself with the last 3 -> ColourNote with 3. Then at the second step there it would indeed report the 6th argument missing. That's about the only idea I have.

Finally, you are probably on to something with the table.remove() call - removing it and passing the prompt to be displayed as an argument to the function solved the problem completely. It's still pretty intriguing.
Australia Forum Administrator #8
Here is an interesting clue:


local args = {}  -- a table to hold ColourNote arguments


   table.insert(args, "blue")    -- 1
   table.insert(args, "green")   -- 2
   table.insert(args, "Hello")   -- 3
   table.insert(args, "white")   -- 4
 
   ColourNote (unpack(args))

Output
[string "Immediate"]:9: bad argument #6 to `ColourNote' (string expected, got no value)
stack traceback:
	[C]: in function `ColourNote'
	[string "Immediate"]:9: in main chunk



In this example there is no argument 5 either, however it is objecting to argument 6.
Australia Forum Administrator #9
Try as I might with various combinations I can't reproduce your error message.

Your loop:


-- iterate over the styles in the prompt
   for chunk,style in ipairs(prompt) do
      table.insert(args, RGBColourToName(style.textcolour))
      table.insert(args, RGBColourToName(style.backcolour))
      table.insert(args, style.text)
   end


... should always be putting values in groups of 3 into args, so I don't see how the last one is discarded.

Quote:

This could happen if, for example, some outter function (also called ColourNote) ...


Are you in fact calling the real ColourNote or some wrapper you wrote?
Russia #10
Doesn't tell me anything :) But check this out (in the command window and one after the other, not all at once):


ColourNote(unpack({"white", "", "hello", "red", ""}))
ColourNote(unpack({"white", "", "hello", "red", ""}))
print()


Output:


hellohello
Australia Forum Administrator #11
I can't reproduce that either. I get an error message. Are you sure you haven't written your own ColourNote function? Try this:


/tprint (debug.getinfo (ColourNote))

Output

"source"="=[C]"
"what"="C"
"func"=function: 010ECFC8
"short_src"="[C]"
"currentline"=-1
"namewhat"=""
"linedefined"=-1
"nups"=0


Your address will probably be different, but you should see a C function. However if you have your own wrapper, you will see something like this:


"source"="Script file"
"what"="Lua"
"func"=function: 01154A30
"name"="ColourNote"
"nups"=0
"currentline"=-1
"namewhat"="global"
"linedefined"=657
"short_src"="[string "Script file"]"
Russia #12
Nope, I am using it directly, and I don't have a wrapper lost somewhere - I am doing all this on a clean world file with no plugins and my debug.getinfo shows the same thing as yours, except for the address.

Quote:
I can't reproduce that either. I get an error message.


You get two - one for each ColourNote. You just need to dismiss them by pressing the Close button and do the next ColourNote, then print().
Amended on Sun 28 May 2006 06:31 AM by Ked
Australia Forum Administrator #13
So you are referring to the fact that they are on the same line?

I would expect that. All that ColourNote does in Lua is do repeated calls to ColourTell, except that the last one is a call to ColourNote.

So, if it crashes halfway through, you have done some ColourTells without the final ColourNote.

And, as you can see, you won't save any particular time doing it the way you are proposing, to manually doing your own ColourTells in a loop.