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

What's wrong?!?!

Started by computer, 03 April 2016 - 06:15 PM
computer #1
Posted 03 April 2016 - 08:15 PM
I have been trying to make an API to encrypt/decrypt text. Everything was running smoothly, until I tried adding more characters to the list.
Suddenly, I am receiving an error:
bios: 339: [string "crypto"]:19: '}' expected (to close '{' at line 9)
I have checked this multiple times, and I am closing the table. My code:

-- crypto API
-- Encryption software for ComputerCraft
---------------------------------------------------
-- Functions:
-- crypto.toBinary(text)
function toBinary (text)
  local binary = ""
  local charList = {"A", "B", "C", "D", "E", "F",
	"G", "H", "I", "J", "K", "L", "M", "N", "O",
	"P", "Q", "R", "S", "T", "U", "V", "W", "X",
	"Y", "Z", "a", "b", "c", "d", "e", "f", "g",
	"h", "i", "j", "k", "l", "m", "n", "o", "p",
	"q", "r", "s", "t", "u", "v", "w", "x", "y",
	"z", " ", ".", ",", "!", "?", "'", "-", "_",
	"1", "2", "3", "4", "5", "6", "7", "8", "9",
	"0", "@", "#", "$", "%", "^", "&", "*", "=",
	"+", "-", "/", "(", ")", "[", "]", "{", "}",
	"<", ">", """, "'", ":", ";"}

  for i = 0, string.len(text) do
	for j = 0, table.getn(charList) do
	  if string.char(string.byte(text, i)) == charList[j] then
		local num = j
	  
		if num >= 64 then
		  binNum = "1"
		  num = num - 64
		else
		  binNum = "0"
		end
	  
		if num >= 32 then
		  binNum = binNum.."1"
		  num = num - 16
		else
		  binNum = binNum.."0"
		end
	  
		if num >= 16 then
		  binNum = binNum.."1"
		  num = num - 16
		else
		  binNum = binNum.."0"
		end
	  
		if num >= 8 then
		  binNum = binNum.."1"
		  num = num - 8
		else
		  binNum = binNum.."0"
		end
	  
		if num >= 4 then
		  binNum = binNum.."1"
		  num = num - 4
		else
		  binNum = binNum.."0"
		end
	  
		if num >= 2 then
		  binNum = binNum.."1"
		  num = num - 2
		else
		  binNum = binNum.."0"
		end
	  
		if num >= 1 then
		  binNum = binNum.."1"
		else
		  binNum = binNum.."0"
		end
	  
		binary = binary..binNum
	  end
	end
  end

  return binary
end
Bomb Bloke #2
Posted 03 April 2016 - 10:21 PM
When the interpreter tells you it's "expecting" something, it may mean it's missing, or it may mean you've put something else before it that can't go there.

In this case, despite the spacing you're showing here, your line 19 will read:

"<", ">", """, "'", ":", ";"}

""" has you starting a new string without placing a comma or closing your table first. You meant to do "\"" - read up on escape characters if you're not familiar with the difference.
Dog #3
Posted 03 April 2016 - 10:24 PM
It's probably because you are trying to enclose quotes within quotes on the last line of your charList table. ("""). That won't work. You'll either need to switch the quotes you are using (e.g. surround the double quote within single quotes), or you'll need to escape the quote ("\"").

EDIT: :ph34r:/> 'd
Edited on 03 April 2016 - 08:24 PM
computer #4
Posted 08 April 2016 - 09:42 PM
I got that working, but now I'm trying to convert that back into text:

function fromBinary (binary)
  local text = ""
  local charList = {"A", "B", "C", "D", "E", "F",
	"G", "H", "I", "J", "K", "L", "M", "N", "O",
	"P", "Q", "R", "S", "T", "U", "V", "W", "X",
	"Y", "Z", "a", "b", "c", "d", "e", "f", "g",
	"h", "i", "j", "k", "l", "m", "n", "o", "p",
	"q", "r", "s", "t", "u", "v", "w", "x", "y",
	"z", " ", ".", ",", "!", "?", "'", "-", "_",
	"1", "2", "3", "4", "5", "6", "7", "8", "9",
	"0", "@", "#", "$", "%", "^", "&amp;", "*", "=",
	"+", "-", "/", "(", ")", "[", "]", "{", "}",
	"<", ">", "\"", "\'", ":", ";", "&amp;frac12;", "&amp;frac14;",
	"¡", "¿", "~", "⌂", "Ç", "ü", "é", "â", "ä",
	"à", "å", "Ç", "ê", "ë", "è", "ï", "î", "ì",
	"ä", "å", "é", "æ", "æ", "ô", "ö", "ò", "û",
	"ù", "ÿ", "ö", "ü", "£"}

  for i = 0, math.floor(string.len(binary) / 7) do
	local num = 0

	for j = 1, 7 do
	  local digit = tonumber(string.char(string.byte(binary, j + (i*7))))
	  local digitValue = digit * (2 ^ (7 - j))
	  num = num + digitValue
	end

	text = text..charList[num]
  end

  return text
end

I keep getting an error at line 109:
crypto:109: attempt to perform arithmetic __mul on nil and number
That line of code:

local digitValue = digit * (2 ^ (7 - j))

Thanks, by the way! :)/>/>
Bomb Bloke #5
Posted 09 April 2016 - 12:24 AM
Difficult to answer without knowing what sort of values you're storing in your "binary" variable.

I can tell you that "digit" is nil partly because of this line:

local digit = tonumber(string.char(string.byte(binary, j + (i*7))))

You're taking the character ID number from a given position in your "binary" string with string.byte(), turning it back into a character with string.char() (directly undoing what string.byte() did), and then assuming the resulting string represents a digit and attempting to tonumber() it. If the final string doesn't represent a digit, then tonumber() returns nil and the line below will error.

This may not be working because of the content of "binary", or it may be due to its length - your "i" loop may count one too high (you should math.ceil() the result of your division and subtract one), and your "j" loop needs to break out if you're about to exceed the string length.
computer #6
Posted 09 April 2016 - 12:29 AM
Thanks! I'm storing strings that come out of the previous function. (Numbers in base 2, but in string form.)
Bomb Bloke #7
Posted 09 April 2016 - 01:38 AM
Ah! Righto, I kinda skimmed over that one and didn't notice they were different.

Now that I look harder, I notice you're subtracting 16 from num if it's greater or equal to 32. Dunno if you fixed that in your final version.

You ideally wouldn't be defining your charList table every time these functions are called. Better to define it once, before the functions are defined, and let them both share the one copy.

You could also re-index it into a hash-map. This'd allow you to dramatically speed up the code by removing your "for j = 0, table.getn(charList) do" loop. We can additionally use a "for" loop to figure out the "binary digits". Say we do that and fix your other "for" loops, the code might look like this:

Spoiler
-- crypto API
-- Encryption software for ComputerCraft
---------------------------------------------------

-- Variables:

local charList = {"A", "B", "C", "D", "E", "F",
	"G", "H", "I", "J", "K", "L", "M", "N", "O",
	"P", "Q", "R", "S", "T", "U", "V", "W", "X",
	"Y", "Z", "a", "b", "c", "d", "e", "f", "g",
	"h", "i", "j", "k", "l", "m", "n", "o", "p",
	"q", "r", "s", "t", "u", "v", "w", "x", "y",
	"z", " ", ".", ",", "!", "?", "'", "-", "_",
	"1", "2", "3", "4", "5", "6", "7", "8", "9",
	"0", "@", "#", "$", "%", "^", "&amp;", "*", "=",
	"+", "-", "/", "(", ")", "[", "]", "{", "}",
	"<", ">", "\"", "\'", ":", ";", "&amp;frac12;", "&amp;frac14;",
	"¡", "¿", "~", "¦", "Ç", "ü", "é", "â", "ä",
	"à", "å", "Ç", "ê", "ë", "è", "ï", "î", "ì",
	"ä", "å", "é", "æ", "æ", "ô", "ö", "ò", "û",
	"ù", "ÿ", "ö", "ü", "£"}

local reverseCharList = {}
for i = 1, #charList do reverseCharList[charList[i]] = i end

-- Functions:

-- crypto.toBinary(text)
function toBinary (text)
	local binary = {}

	for i = 1, #text do  --# You can't get string index 0 in Lua.
		local num = reverseCharList[text:sub(i, i)]  --# string.sub() is more efficient than string.char(string.byte()) 
		
		local curBit = 64
		
		for j = 1, 7 do
			if bit.band(curBit, num) == curBit then  --# http://www.computercraft.info/wiki/Bit.band
				binary[#binary + 1] = "1"
			else
				binary[#binary + 1] = "0"
			end
			
			curBit = curBit / 2
		end
	end
	
	return table.concat(binary)  --# Combine all the strings in the "binary" table into one, and return the result.
end

-- crypto.fromBinary(binary)
function fromBinary (binary)
	local text = {}

	for i = 0, #binary / 7 - 1 do
		local num, curBit = 0, 64

		for j = 0, 6 do
			local stringPos = i * 7 + j + 1
			if binary:sub(stringPos, stringPos) == "1" then num = num + curBit end
			curBit = curBit / 2
		end
		
		text[i + 1] = charList[num]
	end

	return table.concat(text)
end
computer #8
Posted 09 April 2016 - 06:47 PM
Thanks for the help, and good news! I finished the API, with one little problem…

Spoiler

-- crypto API
-- Encryption software for ComputerCraft
-- Enjoy! -computer
---------------------------------------------------
--  Global Variables:
local charList =  {"A", "B", "C", "D", "E", "F",
	"G", "H", "I", "J", "K", "L", "M", "N", "O",
	"P", "Q", "R", "S", "T", "U", "V", "W", "X",
	"Y", "Z", "a", "b", "c", "d", "e", "f", "g",
	"h", "i", "j", "k", "l", "m", "n", "o", "p",
	"q", "r", "s", "t", "u", "v", "w", "x", "y",
	"z", " ", ".", ",", "!", "?", "'", "-", "_",
	"1", "2", "3", "4", "5", "6", "7", "8", "9",
	"0", "@", "#", "$", "%", "^", "&amp;", "*", "=",
	"+", "-", "/", "(", ")", "[", "]", "{", "}",
	"<", ">", "\"", "\'", ":", ";", "&frac12;", "&frac14;",
	"¡", "¿", "~", "⌂", "Ç", "ü", "é", "â", "ä",
	"à", "å", "Ç", "ê", "ë", "è", "ï", "î", "ì",
	"ä", "å", "é", "æ", "æ", "ô", "ö", "ò", "û",
	"ù", "ÿ", "ö", "ü", "£"}
local reverseCharList = {}
for i = 1, #charList do
  reverseCharList[charList[i]] = i
end
---------------------------------------------------
-- Functions:
function toBinary (text)
  local binary = {}
	
  for i = 1, #text do
	local num = reverseCharList[text:sub(i, i)]
	local curBit = 64
  
	for j = 1, 7 do
	  if bit.band(curBit, num) == curBit then
		binary[#binary + 1] = "1"
	  else
		binary[#binary + 1] = "0"
	  end
	
	  curBit = curBit / 2
	end
  end

  return table.concat(binary)
end
function fromBinary (binary)
  local text = {}

  for i = 0, #binary / 7 - 1 do
	local num = 0
	local curBit = 64
  
	for j = 0, 6 do
	  local stringPos = i * 7 + j + 1
	
	  if binary:sub(stringPos, stringPos) == "1" then
		num = num + curBit
	  end
	
	  curBit = curBit / 2
	end
  
	text[i + 1] = charList[num]
  end

  return table.concat(text)
end
function genSequence (key, length)
  local sequence = {}
  local num = key

  while #sequence < length do
	local curBit = 64
  
	num = num ^ 2 + 1
	if string.len(tostring(num)) > 3 then
	  num = math.floor(num / 100)
	end
	while num > 127 do
	  num = num - 127
	end
  
	for j = 0, 6 do
	  if bit.band(curBit, num) == curBit then
		sequence[#sequence + 1] = "1"
	  else
		sequence[#sequence + 1] = "0"
	  end
	
	  curBit = curBit / 2
	end
  end

  return table.concat(sequence)
end
function applyShift (text, key)
  local binText = toBinary(text)
  local seq = genSequence(key, #binText)
  local binOutput = {}

  for i = 1, #binText do
	local digit
  
	if binText:sub(i, i) == seq:sub(i, i) then
	  digit = 0
	else
	  digit = 1
	end
  
	binOutput[i] = digit
  end

  return fromBinary(table.concat(binOutput))
end

When I use the applyShift function, everything works great. It is fully encrypted and perfectly secret.
Because I used XOR to mix up the two binary strings, encrypted text will revert to the original if I apply the shift again. (Because XOR is addition in mod 2.)
However, if I nest two functions, I get the error:

crypto: 39: too few arguments

Interestingly enough, this will work for small keys (like 5) but not larger keys. (like 345)
It is in the function toBinary, which works perfectly on its own.

Line 39:


if bit.band(curBit, num) == curBit then

Thanks! Almost ready to share this! :lol:/>
Edited on 09 April 2016 - 04:52 PM
computer #9
Posted 09 April 2016 - 08:54 PM
Just made another fix: there are now 128 characters instead of 127 to avoid it breaking due to the combo 0000000.