This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
EpicMCEngineer12's profile picture

[Lua][Error]Program will only return table values various amounts of times, and then return nil

Started by EpicMCEngineer12, 23 January 2013 - 04:18 AM
EpicMCEngineer12 #1
Posted 23 January 2013 - 05:18 AM

Menu1 = {
text_1 = {
  dim = {
   x1 = 10;
   x2 = 37;
   y = 2;
   };
  txt = "Welcome to the test menu!";
  txtcol = colours.orange;
  };
button_1 = {
  dim = {
   x1 = 20;
   x2 = 28;
   y = 4;
   };
  txt = " Continue";
  txtcol = colours.red;
  bckcol = colours.blue;
  func = {
   type = "Redirect";
   arg1 = Menu2;
  };
};
}
Menu2 = {
button_1 = {
  dim = {
   x1 = 20;
   x2 = 35;
   y = 6;
  };
  txt = " Return";
  txtcol = colours.green;
  bckcol = colours.yellow;
  func = {
   type = "Redirect";
   arg1 = Menu1;
  };
};
}
currentMenu = Menu1
function draw(menu)
term.clear()
term.setCursorPos(1,1)
for n,v in pairs(menu) do -- This is where it errors: bad argument #1 to pairs (table expected, got nil)
   local pos = v.dim
   term.setCursorPos(pos.x1,pos.y)
   term.setTextColor(v.txtcol)
   if string.sub(n,1,6) == "button" then
	term.setBackgroundColor(v.bckcol)
   end
   write(v.txt)
   local tx,ty = term.getCursorPos()
   if tx < pos.x2 then
	write(string.rep(" ",pos.x2 - tx))
   end
   term.setTextColor(colours.white)
   term.setBackgroundColor(colours.black)
end
term.setCursorPos(1,12)
end
while true do
draw(currentMenu)
sleep(0.1)
e,b,x,y = os.pullEvent("mouse_click")
for n,v in pairs(currentMenu) do
  if string.sub(n,1,6) == "button" then
   local pos = v.dim
   if x > pos.x1 and x < pos.x2 then
	if y == pos.y then
	 local action = v.func
	 if action.type == "Redirect" then
	  currentMenu = action.arg1
	  break
	 end
	end
   end
  end
end
end
This is a basic menu navigation program. The menu information is stored in tables. In the current state of the program, the button in Menu1 draws Menu2, and vice versa. You can click these buttons various amounts of times until you get an error. I commented the error above. It is very random when the program fails. I have tried many solutions and none have seemed to work. It looks to me like a CC bug because of it being so random. For some reason the variable currentMenu becomes nil after clicking a button sometimes. I would appriciate help with fixing this code.
Eric #2
Posted 23 January 2013 - 06:15 AM

> print(Menu1.button_1.func.arg1 == nil)
true
ChunLing #3
Posted 23 January 2013 - 08:26 AM
You eventually end up setting currentMenu = action.arg1, which is evidently not a table (at least not always). What is it?
EpicMCEngineer12 #4
Posted 23 January 2013 - 08:28 AM
You eventually end up setting currentMenu = action.arg1, which is evidently not a table (at least not always). What is it?
It is not a changing value. In Menu1 the value is Menu2. In Menu2 the value is Menu1. Therefore, it Directs to the other table.
EpicMCEngineer12 #5
Posted 23 January 2013 - 08:31 AM

> print(Menu1.button_1.func.arg1 == nil)
true
That does not explain why it works a random number of times. I have no idea why it is nil when it is always set to table Menu1 or Menu2
ChunLing #6
Posted 23 January 2013 - 08:34 AM
No it doesn't.
EpicMCEngineer12 #7
Posted 23 January 2013 - 08:36 AM
No it doesn't.
Look closer. local action = v.func you will find func in the tables
ChunLing #8
Posted 23 January 2013 - 08:40 AM
Are you getting a random number of switches back and forth between the two tables, or are you getting a random number of mouse clicks before an error?

Anyway, try removing that type = field. It creeps me out, and I think it may be why arg1 ends up as nil. Just check if arg1 is a table before you assign it to currentMenu.
Eric #9
Posted 23 January 2013 - 08:56 AM

> print(Menu1.button_1.func.arg1 == nil)
true
That does not explain why it works a random number of times. I have no idea why it is nil when it is always set to table Menu1 or Menu2

I can guarantee the code will never work when you click the button on menu 1.

As a simple example:


> A = {submenu = B}
> B = {submenu = A}
> print(A.submenu == B)/>
false
> print(B.submenu == A)
true
> print(A.submenu == nil)
true

This code does not do what you might expect it to, because when you define A, B does not yet exist, so is nil.
EpicMCEngineer12 #10
Posted 23 January 2013 - 08:57 AM
Are you getting a random number of switches back and forth between the two tables, or are you getting a random number of mouse clicks before an error?

Anyway, try removing that type = field. It creeps me out, and I think it may be why arg1 ends up as nil. Just check if arg1 is a table before you assign it to currentMenu.
A random number of mouse clicks before an error. If I do something like pcall() loop, it will never work. Once arg1 is nil, it will never become a table again. I do not understand this. The type is for future use when I want buttons with different functionality. It would be nice to actually make func call the draw and controlling. But it ends up as nil due to it being assing before the draw() function. And the draw function cannot be assigned after the table either. Because it uses the table.
EpicMCEngineer12 #11
Posted 23 January 2013 - 09:01 AM

> print(Menu1.button_1.func.arg1 == nil)
true
That does not explain why it works a random number of times. I have no idea why it is nil when it is always set to table Menu1 or Menu2

I can guarantee the code will never work when you click the button on menu 1.

As a simple example:


> A = {submenu = B}
> B = {submenu = A}
> print(A.submenu == B)/>/>
false
> print(B.submenu == A)
true
> print(A.submenu == nil)
true

This code does not do what you might expect it to, because when you define A, B does not yet exist, so is nil.
It seems like on startup the first time, the button press fails immediatly. But the Next times you need a bit more clicks. And next run needs even more clicks.
Eric #12
Posted 23 January 2013 - 09:08 AM
It seems like on startup the first time, the button press fails immediatly. But the Next times you need a bit more clicks. And next run needs even more clicks.
Yep, that's exactly what I'd expect. Change your variables to locals, and it will fail the first time every time (after an initial reboot).

Did you read my example at all? It explains exactly why this is happening
ChunLing #13
Posted 23 January 2013 - 04:37 PM
It should be possible to declare the tables locally and then define them afterwards, and have it work. But type is a built in identifier in Lua, it can be overwritten, but I'm far from sure what happens to everything after it in the subtable definition when you do that.

I love me some tables, but this structure seems excessive, even leaving aside my discomfort with using the type identifier that way.
EpicMCEngineer12 #14
Posted 23 January 2013 - 06:57 PM
Alright then. Out of your information the solution might be to put Menu1 and Menu2 under a table. And rename them ["Menu1"] and ["Menu2"]. I will rename the type thing if it is something built in. And on the arg1 I Guess I will set "Menu2" and "Menu1". That should work? I have to do it this way because it is the only way I think tables can be converted to string when you do not use the textutils serialize(), and unserialize()
ChunLing #15
Posted 23 January 2013 - 10:30 PM

I'm not sure that defining them in the same table will work. However, defining them as one table with two values for everything and then using the index of the bottom subtable for selecting between the two tables certainly would work, but may not be worth the effort.

As long as you can find a way to declare/define the tables so that type(Menu1.button_1.func.arg1) == "table", rather than nil, things should start to work.