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

augustas656's read() replacement questions topic

Started by augustas656, 09 May 2014 - 09:49 PM
augustas656 #1
Posted 09 May 2014 - 11:49 PM
Regards
Augustas
awsmazinggenius #2
Posted 10 May 2014 - 12:05 AM
Look in the APIs and bios.lua
Yevano #3
Posted 10 May 2014 - 12:45 AM
Specifically, those files can be found inside your computercraft mod folder under assets\computercraft\lua\.
theoriginalbit #4
Posted 10 May 2014 - 02:07 AM
because I'm feeling in quite a charitable mood today, here you go, write, print and read as of ComputerCraft 1.6x (iirc)
augustas656 #5
Posted 11 May 2014 - 01:18 PM
So:

functiont test()
  function a()
  print("a")
  end
end
test.a()
or something?
Or you can't?
theoriginalbit #6
Posted 11 May 2014 - 01:29 PM
no, well yes, well yes and no…

if you program correctly, then no. however using the example function declaration you have there, yes, but you don't invoke it like you think you do…

Correct programming convention (this will not allow invocation of function)

local function foo() --# note the local
  local function bar() --# note the local
    print("bar")
  end
  bar() --# you can call it here
end

bar() --# but not here

Incorrect programming convention (will allow invocation of function)

local function foo()
  function bar() --# note the lack of local, just like in your example
    print("bar")
  end
  print("foo")
  bar() --# you can call it here
end

bar() --# you can also call it here
but do you notice how we invoke it? we invoke it just like normal, meaning we may as well define it like so

local function foo()
  print("foo")
end

local function bar()
  print("bar")
end

bar()
and doing it like this reduces clutter in the global scope (which is why we should always use 'local' unless we want our functions to be accessed from the global scope)

if you give more of a description as to why you want to do this we can give you further assistance as to a method of doing what you want, or potentially steer you down a better path.
Tjakka5 #7
Posted 11 May 2014 - 01:52 PM
You could also do it like this:


local function test(func)
  if func == "a" then a()
  elseif func == "b" then b()
  elseif func == "c" then c() end
end
local function a()
  print("a")
end
local function b()
  print("b")
end
local function c()
  print("c")
end
test("a")
test("c")
test("b")


I dont think this would be useful in anyway, but yeah, its somewhat possible.
Edited on 11 May 2014 - 12:03 PM
viluon #8
Posted 11 May 2014 - 01:54 PM
You could also do it like this:


local function test(func)
  if func == "a" then a()
  elseif func == "b" then b()
  elseif func == "c" then c()
end
local function a()
  print("a")
end
local function b()
  print("b")
end
local function c()
  print("c")
end
test('a")
test("c")
test("b")


I dont think this would be useful in anyway, but yeah, its somewhat possible.
How is this related to declaring a function in another function?
Edit:test('a") should be test("a") and it wouldn't work anyway, missing end of the if statement.
Edited on 11 May 2014 - 11:56 AM
augustas656 #9
Posted 11 May 2014 - 02:01 PM
I've found a slightly more helpful way using object-oriented programming, creating a table with values and functions, and I use the functions to change the values, I need this because I'm creating an API which creates a customised reader. Then you use this reader onto my read function to execute a customised read.

Thanks Anyway
Tjakka5 #10
Posted 11 May 2014 - 02:05 PM
You could also do it like this:


local function test(func)
  if func == "a" then a()
  elseif func == "b" then b()
  elseif func == "c" then c()
end
local function a()
  print("a")
end
local function b()
  print("b")
end
local function c()
  print("c")
end
test('a")
test("c")
test("b")


I dont think this would be useful in anyway, but yeah, its somewhat possible.
How is this related to declaring a function in another function?
Edit:test('a") should be test("a") and it wouldn't work anyway, missing end of the if statement.

I'm sorry, it has been such a long time since I have programmed ComputerCraft stuff.
Also, the test('a") might work, but I'm not sure.
theoriginalbit #11
Posted 11 May 2014 - 02:09 PM
Also, the test('a") might work, but I'm not sure.
no it won't. also you will get an error 'cause you've got the order of functions around the wrong way, when declaring local functions you should declare them ABOVE where you invoke them.
MKlegoman357 #12
Posted 11 May 2014 - 02:19 PM
From your posted code I assume you want a function to be called through indexing another function. Using metatables, specifically __call metamethod, you can make a table callable like a function:


local class = {
  test = "Hello";

  foo = function ()
    print("foo")
  end;
}

local classMT = { --// metatable of 'class' table
  __call = function (self, text) --// Define the '__call' metamethod
    print(self.test, text)
  end
}

setmetatable(class, classMT) --// Set 'classMT' to be 'class' metatable

class.foo() -->> foo

class() -->> Hello

class(" World!") -->> Hello World!
Edited on 11 May 2014 - 12:20 PM
augustas656 #13
Posted 11 May 2014 - 02:51 PM
I've created a function called multicompare(args) that throws an error if the amount of args is an uneven number or is below two, if not it will then run an for loop that compares all values on both sides. So like checks if a, b, c, d are equal to e, f, g, h. It can work with 2, 4, 6, 8, 10 and any amount of arguements. Comparing the first on the left with the first on the right side of the even amount of values.

I'm thinking was this wasteful, is there a way to compare multiple values like this easier? Because I've created a custom read function that allows you to customise how you want the read to end, the normal read() function will break if you press ENTER and return the finished string value. I've made it so you can select your own event, and parameters. I want to check if the event and all the parameters and equal to the event and all the parameters of a pullevent faster than a super-long if statement like this: if event == c.event and p1 == c.p1 and p2 == c.p2 and p3 == c.p3 then do something.

Regards
Augustas
CometWolf #14
Posted 11 May 2014 - 03:37 PM
the amount of args is an uneven number or is below two
Is this not the same criteria? 1 is an uneven number…

There's not really any simpler way i can think of of the top of my head, personally i think it would be easier to manage if it compared arg 1 to 2 and 3 to 4 etc. The way i handle custom events in my read function, is it's own function. Basically you pass a function as a parameter, this function is called whenever an event is received, and passed the the event and all it's parameters. If the function returns true, the read ends. This makes it far more modular, than just if event and param.
augustas656 #15
Posted 11 May 2014 - 04:13 PM
The code is part of my API and since I don't want to give away too much of my API, I'm only giving the most relevant code, simple functions such as setPos are obviously setCursorPos, and findSize is getSize and findPos is getCursorPos etc etc.

To start off you have to create a reader, an object that has default values and you can change them by using the reader. functions. To begin with blink is setting if there's going to be a cursor blink or not, charlimit is how many characters you can write, the limit. Readlimit is how many characters are going to be displayed (not implemented yet), char is if the char is going to be hidden and if so then with what string replacing each letter. stop is the event with parameters that has to occur for the reader to terminate, as in finish reading the user's input, by default the read() function stops with pressing key ENTER (28).

For the stop function you don't have to include all the parameters required for the event, because let's say you have a "mouse_click" event and you don't care which button is going to be pressed, left mouse button, middle mouse button or right mouse button. So you can leave it out, which means it's going to be nil. The comparetables function that I use in the read accepts a paremeter called len, for length, which means how many places of both tables do I compare? So if there is going to be no button, there's going to be less parameters event, p1, p2, and no p3 because p3 defines button. The length for that being 3 because it's only event, p1 and p2.

For the char function, if you input false as the char, it will replace it with a pair of double quotes, meaning nothing will be typed at all. So you could do no blink and false which would literally mean you just input chars without seeing a blink or any sign of text. Like the most secret input.

I'm yet to implement the readlimit, although you can set it in the reader, but it won't affect the actual read function, currently I'm getting a malfunction where the cursor blink is always at the front, eventhough if you click right or left arrow keys and type it works, it's only the blink that always stays at the end and if you type extra text it will more forward, always being at the very end.

Code:
Spoiler

function compareTables(tbl1, tbl2, len)
local equal = true
  for i = 1,len do
    if tbl1[i] ~= tbl2[i] then
    equal = false
    break
    end
  end
end
local function uRead(reader)
term.setCursorBlink(reader.vBlink)
local str = ""
local x, y = findPos()
local pos = 1
local w, h = findSize()
  if reader.vChar[1] then
    function drawstr()
	  for i = 1,#str do
	  term.write(reader.vChar[2])
	  end
    end
  else
    function drawstr()
    term.write(str)
    end
  end
  local function redraw()
  setPos(x, y)
  term.write(string.rep(" ", w - x + 1))
  setPos(x, y) drawstr()
  end
  while true do
  local e, p1, p2, p3, p4 = os.pullEvent()
  local einfo = {e, p1, p2, p3, p4}
  local rinfo = reader.vStop
    if compareTables(einfo, rinfo, #rinfo) then
    write("\n")
    break
    elseif e == "key" then
	  if p1 == 205 then
	    if pos <= str:len() then
	    pos = pos + 1
	    end
	  redraw()
	  elseif p1 == 203 then
	    if pos > 1 then
	    pos = pos - 1
	    end
	  redraw()
	  elseif p1 == 14 then
	    if pos > 1 then
	    pos = pos - 1
	    str = str:sub(1, pos - 1)..str:sub(pos + 1)
	    redraw()
	    end
	  end
    elseif e == "char" then
    str = str:sub(1, pos - 1)..p1..str:sub(pos)
    pos = pos + 1
    redraw()
    end
  redraw()
  end
return str
end
   
	   
function newReader()
local reader = {
["vBlink"] = true,
["vCharlimit"] = 0,
["vReadlimit"] = 0,
["vChar"] = {false, nil},
["vStop"] = {"key", 28},
}
  function reader.blink(bool)
  reader.vBlink = bool end
  function reader.charlimit(limit)
  reader.vCharlimit = limit end
  function reader.readlimit(limit)
  reader.vReadlimit = limit end
  function reader.char(char)
    if char == nil then
    reader.vChar = {false, nil}
    else
	  if char == false then
	  char = ""
	  else
	  char = tostring(char)
	  end
    reader.vChar = {true, char}
    end
  end
  function reader.stop(...)
  reader.vStop = {...} end
  function reader.run()
  uRead(reader) end
return reader
end

Regards
Augustas
augustas656 #16
Posted 11 May 2014 - 04:24 PM
Basically if there's only 1 arugmenet or even amount, that means you are comparing like 1, 2, 3 and 4, 5 you can't do that, it's like an unfinished if statement if 1 == 4 and 2 == 5 and 3 == then. And you can't have 1 because you are comparing one with itself that's unecessary. So yeah. You understand what I maen?
Inksaver #17
Posted 11 May 2014 - 04:40 PM
I was trying to something similar, but ended up with a full class / object creation
This is to keep a list of coordinates:


function createCoordObject()
	--[[
	0 = go south (z increases)
	1 = go west  (x decreases)
	2 = go north (z decreases
	3 = go east  (x increases)

	compass[0] = "south"
	compass[1] = "west"
	compass[2] = "north"
	compass[3] = "east"]]--
	
	clsCoord = {} -- the table representing the class, which will double as the metatable for any instances
	clsCoord.__index = clsCoord -- failed table lookups on the instances should fallback to the class table, to get methods

	function clsCoord.new(coordName) --equivalent to VB class initialise
		local self = setmetatable({}, clsCoord)
		self.name = coordName
		self.x = 0
		self.y = 0
		self.z = 0
		self.facing = 0
		self.compass = "south"
		
		return self
	end
	function clsCoord.getX(self)
		return self.x
	end
	function clsCoord.setX(self, newVal) -- property let in VB
		self.x = newVal
	end
	function clsCoord.getY(self) -- property get in VB
		return self.y
	end
	function clsCoord.setY(self, newVal)
		self.y = newVal
	end
	function clsCoord.getZ(self)
		return self.z
	end
	function clsCoord.setZ(self, newVal)
		self.z = newVal
	end
	function clsCoord.getFacing(self)
		return self.facing
	end
	function clsCoord.setFacing(self, newVal)
		local direction = {}
		direction[0] = "south"
		direction[1] = "west"
		direction[2] = "north"
		direction[3] = "east"
		
		self.facing = newVal
		if self.facing < 0 then
			self.facing = 3
		elseif self.facing > 3 then
			self.facing = 0
		end
		-- change self.compass to match
		clsCoord.setCompass(self, direction[newVal])
	end
	function clsCoord.getCompass(self)
		return self.compass
	end
	function clsCoord.setCompass(self, newVal)
		local direction = {}
		direction["south"] = 0
		direction["west"] = 1
		direction["north"] = 2
		direction["east"] = 3
		
		self.compass = newVal
		-- set self.facing to match
		clsCoord.setFacing(self, direction[newVal])
	end
	function clsCoord.goUp(blocks)
		blocks = blocks or 1
		self.y = self.y + blocks
	end
	function clsCoord.goDown(blocks)
		blocks = blocks or 1
		self.y = self.y - blocks
	end
	--[[uses:
		location:getX()			  get current turtle x coordinate
		location:getY()			  get current turtle y coordinate
		location:getZ()			  get current turtle z coordinate
		location:setX(xCoord)	   set current turtle x coordinate eg location:setX(-235)
		location:setY(yCoord)		  set current turtle y coordinate eg location:setY(66)
		location:setZ(zCoord)		 set current turtle z coordinate eg location:setZ(125)
		location:getFacing()		returns a number 0 - 3 representing direction of player
		location:setFacing(facing)	 sets direction eg location:setFacing(1) (West)
		location:getCompass()		  returns direction as text eg "north"
		location:setCompass("X")	 sets direction using text eg location:setCompass("south")
		location:goUp(X)			increases Y coord by X
		location:goDown(X)			decreases Y coord by X
	]]--
	location = clsCoord.new("currentLocation")
	coordHome = clsCoord.new("homeLocation")
	mineEntrance = clsCoord.new("mineEntrance")
end

Three objects are created within the function, but as it has global scope, the objects can be created anywhere.
It has the advantage of changing other properties automatically, for example if you change the direction using 0,1,2,3 the text equivalent is automatically changed as well.


interestingLocation = clsCoord.new("Rocky Outcrop")
interestingLocation:setX(-243)
interestingLocation:setY(64)
interestingLocation:setZ(112)
interestingLocation:setFacing(3)
print("There is a "..interestingLocation:getName().." facing "..interestingLocation:getFacing().." ("..interestingLocation:getCompass()..")")
print("Its coordinates are x = "..interestingLocation:getX()..", y = "..interestingLocation:getY()..", z = "..interestingLocation:getZ())
> There is a Rocky Outcrop facing 3 (east)
> Its coordinates are x = -243, y = 64, z = 112
the output of interestingLocation:getCompass() is already set using the function within a function

clsCoord.setCompass(self, direction[newVal])
By the way this will not work if the shorthand version

clsCoord:setCompass(direction[newVal])
is used (colon instead of dot)
CometWolf #18
Posted 11 May 2014 - 05:00 PM
I already understood what you meant, i'd suggest you read more closely.
CometWolf #19
Posted 11 May 2014 - 05:06 PM
You appear to have forgotten that term.write moves the cursor when it writes.

  local function redraw()
  setPos(x, y)
  term.write(string.rep(" ", w - x + 1))
  setPos(x, y) drawstr()
  --move the cursor back to where it's supposed to be here
  end
augustas656 #20
Posted 11 May 2014 - 05:14 PM
Updated Version:
Spoiler

function compareTables(tbl1, tbl2, len)
local equal = true
  for i = 1,len do
	if tbl1[i] ~= tbl2[i] then
	equal = false
	break
	end
  end
return equal
end
local function uRead(reader)
term.setCursorBlink(reader.vBlink)
[color=#ff0000]local str = ""
local x, y = term.getCursorPos()
local pos = 1
local w, h = term.getSize()
  if reader.vChar[1] then
	function drawstr()
	  for i = 1,#str do
	  term.write(reader.vChar[2])
	  end
	end
  else
	function drawstr()
	term.write(str)
	end
  end
  local function redraw()
  setPos(x, y)
  term.write(string.rep(" ", w - x + 1))
  setPos(x, y)
  drawstr()
  setPos(x + pos - 1, y)
  end[/color]
  while true do
  local e, p1, p2, p3, p4 = os.pullEvent()
  local einfo = {e, p1, p2, p3, p4}
  local rinfo = reader.vStop
	if compareTables(einfo, rinfo, #rinfo) then
	write("\n")
	break
	elseif e == "key" then
	  if p1 == 205 then
		if pos <= str:len() then
		pos = pos + 1
		end
	  redraw()
	  elseif p1 == 203 then
		if pos > 1 then
		pos = pos - 1
		end
	  redraw()
	  elseif p1 == 14 then
		if pos > 1 then
		pos = pos - 1
		str = str:sub(1, pos - 1)..str:sub(pos + 1)
		redraw()
		end
	  end
   [color=#ff0000] elseif e == "char" then
	str = str:sub(1, pos - 1)..p1..str:sub(pos)
	pos = pos + 1
	redraw()
	end[/color]
[color=#ff0000]  redraw()[/color]
  end
return str
end
  
	  
function newReader()
local reader = {
["vBlink"] = true,
["vCharlimit"] = 0,
["vReadlimit"] = 0,
["vChar"] = {false, nil},
["vStop"] = {"key", 28},
}
  function reader.blink(bool)
  reader.vBlink = bool end
  function reader.charlimit(limit)
  reader.vCharlimit = limit end
  function reader.readlimit(limit)
  reader.vReadlimit = limit end
  function reader.char(char)
	if char == nil then
	reader.vChar = {false, nil}
	else
	  if char == false then
	  char = ""
	  else
	  char = tostring(char)
	  end
	reader.vChar = {true, char}
	end
  end
  function reader.stop(...)
  reader.vStop = {...} end
  function reader.run()
  uRead(reader) end
return reader
end

Unlike the read() function, this for some reason doesn't move the entire string when there are characters out of bounds, so if I keep typing characters after the string has reached the end of the screen, it will go beyond the border and I will not see. How can I fix this!? So frustrating can't find the error, looked through my code and it's literally identical apart from the additions to the bios.lua one.

Edit: I highlited red where I think this problem occurs.
Edited on 11 May 2014 - 03:48 PM
augustas656 #21
Posted 11 May 2014 - 05:16 PM
Oh yeah I used that because just incase if there's 0 arguements, just as an error check.
Edit: I just solved my issue, I'm using two tables, and checking up to a defined length.
Edited on 11 May 2014 - 03:17 PM
augustas656 #22
Posted 11 May 2014 - 06:06 PM
bios.lua in github, doesn't have the custom char parameter, you know when you can make your password hidden when inputing text by typing read("*") putting a string in the paremeters to replace each and every character. Yeah the bios.lua file doesn't have it, I'm asking this because I made a custom read function that has a problem, I've tried copying the github's bios.lua read function into my API but it still didn't work on that one aspect, it's that when I type the text can go outside of screen borders, keeps typing eventhough I can't see it. Whereas the normal read() works! So I'm confused, what's the problem here. Since they are different and that is because there isn't a parameter in the bios.lua file for a custom string replacing each character, I wanted to check maybe the script is different so it may fix my problem!

Regards
Augustas
Lignum #23
Posted 11 May 2014 - 06:11 PM
Are you referring to this page? On line 226 it's _sReplaceChar.
augustas656 #24
Posted 11 May 2014 - 06:18 PM
Thanks!
Lignum #25
Posted 11 May 2014 - 06:18 PM
Thanks!
You're welcome.
KingofGamesYami #26
Posted 11 May 2014 - 06:59 PM
Updated Version:
Spoiler

function compareTables(tbl1, tbl2, len)
local equal = true
  for i = 1,len do
	if tbl1[i] ~= tbl2[i] then
	equal = false
	break
	end
  end
return equal
end
local function uRead(reader)
term.setCursorBlink(reader.vBlink)
[color=#ff0000]local str = ""
local x, y = term.getCursorPos()
local pos = 1
local w, h = term.getSize()
  if reader.vChar[1] then
	function drawstr()
	  for i = 1,#str do
	  term.write(reader.vChar[2])
	  end
	end
  else
	function drawstr()
	term.write(str)
	end
  end
  local function redraw()
  setPos(x, y)
  term.write(string.rep(" ", w - x + 1))
  setPos(x, y)
  drawstr()
  setPos(x + pos - 1, y)
  end[/color]
  while true do
  local e, p1, p2, p3, p4 = os.pullEvent()
  local einfo = {e, p1, p2, p3, p4}
  local rinfo = reader.vStop
	if compareTables(einfo, rinfo, #rinfo) then
	write("\n")
	break
	elseif e == "key" then
	  if p1 == 205 then
		if pos <= str:len() then
		pos = pos + 1
		end
	  redraw()
	  elseif p1 == 203 then
		if pos > 1 then
		pos = pos - 1
		end
	  redraw()
	  elseif p1 == 14 then
		if pos > 1 then
		pos = pos - 1
		str = str:sub(1, pos - 1)..str:sub(pos + 1)
		redraw()
		end
	  end
   [color=#ff0000] elseif e == "char" then
	str = str:sub(1, pos - 1)..p1..str:sub(pos)
	pos = pos + 1
	redraw()
	end[/color]
[color=#ff0000]  redraw()[/color]
  end
return str
end
  
	  
function newReader()
local reader = {
["vBlink"] = true,
["vCharlimit"] = 0,
["vReadlimit"] = 0,
["vChar"] = {false, nil},
["vStop"] = {"key", 28},
}
  function reader.blink(bool)
  reader.vBlink = bool end
  function reader.charlimit(limit)
  reader.vCharlimit = limit end
  function reader.readlimit(limit)
  reader.vReadlimit = limit end
  function reader.char(char)
	if char == nil then
	reader.vChar = {false, nil}
	else
	  if char == false then
	  char = ""
	  else
	  char = tostring(char)
	  end
	reader.vChar = {true, char}
	end
  end
  function reader.stop(...)
  reader.vStop = {...} end
  function reader.run()
  uRead(reader) end
return reader
end

Unlike the read() function, this for some reason doesn't move the entire string when there are characters out of bounds, so if I keep typing characters after the string has reached the end of the screen, it will go beyond the border and I will not see. How can I fix this!? So frustrating can't find the error, looked through my code and it's literally identical apart from the additions to the bios.lua one.

Edit: I highlited red where I think this problem occurs.
Without reading your code, I would say just do this

local tx, ty = term.getSize()
local cx, cy = term.getCursorPos()
if tx > cx then
 term.setCursorPos(1, cy + 1)
end
augustas656 #27
Posted 11 May 2014 - 08:16 PM
I'm trying to make a multi-line supported read function, and I'm thinking to stop reading you press enter, to create a new line you hold shift and press enter, possible?

Regards
Augustas
Lignum #28
Posted 11 May 2014 - 08:20 PM
Maybe the ctrlkeys API will help?
Lyqyd #29
Posted 11 May 2014 - 09:26 PM
Threads merged. Stop creating new topics for different questions about the same piece of code.
augustas656 #30
Posted 13 May 2014 - 05:57 PM
I get an error: bios:114: bad arguement: number expected, got nil
Code: http://pastebin.com/wzgFB8Wx

Help please
CometWolf #31
Posted 13 May 2014 - 06:18 PM
My Bios (1.58) line 114 is just an end statement, so we obviously don't have the same one. Open up your own and have a look, it should point you in the right direction.
augustas656 #32
Posted 17 May 2014 - 05:43 PM
I have version 1.6 Beta pr2, it may have changed, no? Where do I find the bios file?
EDIT: Fixed it, in string.sub, I had an incorrect arguement.
Edited on 17 May 2014 - 03:48 PM