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

Interpreter [I have made it but I get an error]

Started by CCTech, 09 May 2018 - 05:46 PM
CCTech #1
Posted 09 May 2018 - 07:46 PM
Hello dear Community,

I know making an Interpreter is hard but I tried it using fs.read etc.

So everything works fine but when I run the interpreter it works as it should. There is a text and it asks for my input, When I enter the file I made in that "language" i get this error:

boios.lua:14: [string "test"]:3 '=' excepted

Here is the "interpreter":


term.setBackgroundColor(colors.black)
term.setTextColor(colors.white)
term.clear()
term.setCursorPos(1,1)

print("-- SDK Interpreter --")
print(" ")
print("Please type in the file/directory and file")
print(" ")
print(" ")
write(">> ")

local input = read()

fs.makeDir("/temp")
fs.copy(input, "/temp/main.sdk")

local write = fs.open("/temp/main.sdk","w")
local read = fs.open(input, "r")
local Code = read.readLine()

if Code == "sdk.File" then
  write.writeLine('os.loadAPI("/sdk")')
end

if Code == "func" then
  write.writeLine(code, "function()")
end

if Code == "}" then
  write.writeLine("end")
end

if Code == "loop {" then
  write.writeLine("while true do")
end

if Code == "?" then
  write.writeLine("if", code)
end

if Code == "sdk.off" then
  io.close(input)
  io.close("/temp/main.sdk")
  fs.delete("/temp")
end

shell.run(input)

and here is the Test file:



sdk.file

i = false

? I = false then
  print("I is false")
  os.pullEvent("key")
  i = true
}

? i == true then
  print("I is true")
  os.pullEvent("key")
}

sdk.end

If it seems like I couldn't make it like that.. I could make an API of this.. like the user types:
sdk.func
Luca_S #2
Posted 09 May 2018 - 07:58 PM
The first thing I notice is that you are only interpreting the first line, try adding a while loops or something similar.
The next thing is that when everything is done you are executing the file which is in input, but that file is just what you already have and not the modified one.
The third thing is that, once your code reaches a line like

? i == false then
It will replace that whole line by just "if", because writeLine(a, b ) only writes a and not a and b to the file.
Even if it would write both it would replace it with:

if ? i == true then
which is still not valid.
In Line 5 of your test file you are also checking for the variable I, which is not the same as the variable i and you are not checking if they are equal, but you are assigning a new value to I.
Edited on 09 May 2018 - 06:56 PM
CCTech #3
Posted 09 May 2018 - 09:22 PM
Thanks for the fast answer..

I see… I first tried changing the normal file but then I gave up and copied it.. so I forgot to change
shell.run(input)
to
shell.run("/temp/main.sdk")

So now I changed the code.. but now when I type in the Interpreter the file name I don't get any error. My computer freezes and I can't do anything. I can't reboot, terminate or shutdown.. after sometime it gets black and.. I can't do anything either..


Code:

running = true

term.setBackgroundColor(colors.black)
term.setTextColor(colors.white)
term.clear()
term.setCursorPos(1,1)

print("-- SDK Interpreter --")
print(" ")
print("Please type in the file/directory and file")
print(" ")
print(" ")
write(">> ")

local input = read()

Modify = function()
  fs.makeDir("/temp")
  fs.copy(input, "/temp/main.sdk")
end

if fs.exists("/temp/main.sdk") then
  fs.delete("/temp")
  Modify()
else
  Modify()
end

local write = fs.open("/temp/main.sdk","w")
local read = fs.open(input, "r")
local Code = read.readAll()
local String = io.input()

while running do
if Code == "sdk.File" then
  write.writeLine('os.loadAPI("/sdk")')
end

if Code == String and Code == "func {" then
  write.writeLine(String, "= function()")
end

if Code == "}" then
  write.writeLine("end")
end

if Code == "loop {" then
  write.writeLine("while true do")
end

if Code == "?" and Code == String and Code == "{" then
  write.writeLine("if", String, "then")
end

if Code == "sdk.off" then
  write.writeLine('io.close("/temp/main.sdk")')
  write.writeLine('fs.delete("/temp")')
  running = false
  shell.run("/temp/main.sdk")
end
end
Edited on 09 May 2018 - 07:23 PM
CCTech #4
Posted 09 May 2018 - 09:35 PM
Small update.. I changed the code to:


term.setBackgroundColor(colors.black)
term.setTextColor(colors.white)
term.clear()
term.setCursorPos(1,1)

print("-- SDK Interpreter --")
print(" ")
print("Please type in the file/directory and file")
print(" ")
print(" ")
write(">> ")

local input = read()

Modify = function()
  fs.makeDir("/temp")
  fs.open("/temp/main.sdk")
end

if fs.exists("/temp/main.sdk") then
  fs.delete("/temp")
  Modify()
else
  Modify()
end

local write = fs.open("/temp/main.sdk","w")
local read = fs.open(input, "r")
local Code = read.readAll()
local String = io.input() 

  if Code == "sdk.File" then
    write.writeLine('os.loadAPI("/sdk")')
  end

  if Code == String and Code == "func {" then
    write.writeLine(String, "= function()")
  end

  if Code == "}" then
    write.writeLine("end")
  end

  if Code == "loop {" then
    write.writeLine("while true do")
  end

  if Code == String and Code == "{" then
    write.writeLine("if", String, "then")
  end

  if Code == "sdk.off" then
    write.writeLine('io.close("/temp/main.sdk")')
    write.writeLine('fs.delete("/temp")')
    write.writeLine("shell.exit()")
    shell.run("/temp/main.sdk")
  end

But now when I enter the file at the sdk I get a random red number.. even if I type a file name which doesn't exists
Bomb Bloke #5
Posted 10 May 2018 - 08:55 AM
Your Modify() function makes an incorrect fs.open() call - you probably don't want to be attempting to open a file there at all.

Off the top of my head, I'm fairly certain ComputerCraft doesn't implement io.input(), and I'm not entirely certain what you'd expect it to return in this instance even if it did. I guess what you're wanting to do is read through the specified file line by line - a "for" loop incorporating io.lines() is one of the easier ways to do this:

for line in io.lines(input) do
  if line == "sdk.File" then
    --# etc .........
  end
end
Edited on 10 May 2018 - 06:55 AM
CCTech #6
Posted 10 May 2018 - 11:20 AM
Your Modify() function makes an incorrect fs.open() call - you probably don't want to be attempting to open a file there at all.

Off the top of my head, I'm fairly certain ComputerCraft doesn't implement io.input(), and I'm not entirely certain what you'd expect it to return in this instance even if it did. I guess what you're wanting to do is read through the specified file line by line - a "for" loop incorporating io.lines() is one of the easier ways to do this:

for line in io.lines(input) do
  if line == "sdk.File" then
	--# etc .........
  end
end

I don't really understand.. I tried making it how I understood:


for line in io.lines(input) do
  if line == "clearScreen" then
	write.writeLine("term.clear()")
  end
end

Now when I enter at the interpreter the file name it just exists the interpreter.. and when I open the file (I had some trouble with the new and modify file) there is nothing inside
Edited on 10 May 2018 - 09:21 AM
CCTech #7
Posted 10 May 2018 - 11:31 AM
Another.. New.. I've got it to work to clear the screen :lol:/>

Here is the modify function (I will rename it):


for line in îo.lines(input) do
  if line == "clearScreen" do
    term.clear()
    term.setCursorPos(1,1)
  end
end

Now I'm working on changing the cursorPos and changing Backgound color.. and need help with the statements.. (I know how I can replace the if (i think) but executing the if and the rest of the line……)
Windows10User #8
Posted 10 May 2018 - 01:26 PM
I feel like making my own interpreter now!
CCTech #9
Posted 11 May 2018 - 10:26 AM
..One.. more question ^_^/> how can I make it if the user types print("Hello, World!") that the script I run with the interpreter prints something..?
SquidDev #10
Posted 11 May 2018 - 10:35 AM
Well, following on from your above implementation:

if line == 'print("Hello, World!")' then
  print("Hello, World!")
end

Ideally though, you'd be tokenising the input, so the above gets converted into print, (, "Hello, World!", ). Then you can convert it into some AST and codegen from that. It might be worth checking out Urn's lexer (or Howl's, which is substantially more complex but parses Lua).
CCTech #11
Posted 11 May 2018 - 10:46 AM
Well, following on from your above implementation:

if line == 'print("Hello, World!")' then
  print("Hello, World!")
end

I didn't ment like that.

If the user types


print("SOmething")

that the if function in the interpreter scans what it prints…. Idk like:


if(line == "print(" string ")" then
  print(string)
end

But I don't know how to make it.. (Above is just an example so you understand what I mean..)
Bomb Bloke #12
Posted 11 May 2018 - 12:13 PM
Frankly, if you had the experience needed to understand the answer, then you wouldn't need to ask that question in the first place.

But to try to give you a picture: this is what SquidDev is talking about when he refers to tokenisation. In a nutshell, you need to break that line down into parts so that you can identify that it starts with a request to display something, and that it then moves on to establish what that "something" is.

Consider the items in the line print("Hello, World!") - first it provides a name ("print"). Then it throws in some brackets, an indication that the preceding name is that of a variable containing a function pointer, and that function is to be called. Within those brackets, quotes then appear, which are used to define a single string of text, "Hello, World!", which is the argument to be passed to the called function.

You need to code your own way of breaking the line down into those elements. The way I would personally do this is to switch from inspecting your file line by line to instead going character by character - start by adding characters to a string until you have a complete word (terminated by a space, or a bracket, or an equals sign, or any of the other characters that signify a break), and then use the following symbol to determine what to do with it. Following the above example of printing something, the first word is ended with a bracket, indicating you should be preparing to call a function - so within your interpreter, define a function for the purpose of interpreting function calls. Pass that the name of the function you read in, along with the remainder of the text, and have it keep parsing character by character until it's gathered all the information contained within the brackets.

You'll soon want to define additional functions for the purpose of translating other data types. A string-identifier would be next, in order to recognise "Hello, World!". Soon you'll want to be able to recognise numbers, and then perhaps booleans, too.

The basic structure of your script would start to look a bit like this very-rough-and-loose bit of psuedo-code:

Spoiler
local data = <everythingThat'sInYourInputFile>

local currentWord = ""

for i = 1, #data do
	currentChar = data:sub(i, i)
	
	if currentChar is a normal alphabetical character then
		add it to the end of currentWord
		currentWord = currentWord .. currentChar
	
	elseif currentChar is a ( symbol then
		the word we assembled represents a variable pointing to a function to be called.
		Start gathering arguments from that word...
		
		arguments = gatherFunctionArgs(remainingData)
		
		output code to call function with arguments - this might look something like _G[currentWord](unpack(arguments))		
		
	elseif currentChar is an = symbol then
		the word we assembled represents a variable we want to assign something to.
		Start evaluating the statement defining what is to be assigned...
		
		evaluateStatement(remainingData)

		output code to assign values to variable

	elseif currentChar is a space then
		spaces would be sort-of ignored - they signify that a word has ended,
		but they don't tell us what happens next. More characters must be read in.
	
	end
end

function gatherFunctionArgs(data)
	--# Run a loop much like the last, gathering a list of arguments!
	
	local arguments = {}  --# A table to start putting arguments in.
	
	local currentWord = ""	
	
	for i = 1, #data do
		currentChar = data:sub(i, i)

		if currentChar is a normal alphabetical character then
			probably going to be a variable name, might be a boolean or something - add it to the end of currentWord
			currentWord = currentWord .. currentChar

		elseif currentChar is a " symbol then
			must be a string of text
			use anther function to go get that:
			
			arguments[#arguments + 1] = gatherString(remainingData)
			
		elseif currentChar is a number then
			more numeric symbols might follow
			
			arguments[#arguments + 1] = gatherNumber(remainingData)

		elseif currentChar is a ( then
			oh hey there's a function call in our function arguments!
			so now we start recursing:
			
			newArguments = gatherFunctionArgs(remainingData)
					
			arguments[#arguments + 1] = code to call this new function with its own list of arguments	

		end
	end
	
	return arguments
end

function gatherString(data)
	--# Run a loop much like the others, gathering a string!
	
	local currentWord = ""	
	
	for i = 1, #data do
		currentChar = data:sub(i, i)

		if currentChar is " then
			return currentWord

		else
			currentWord = currentWord + currentChar
		end
	end
	
	return arguments
end

function gatherNumber(data)
	--# and so on and so forth
	.
	.
	.

As you start to add support for more actions, data types, and so on, this'll start to get a lot longer. Take another look at Howl - this is the sort of thing that's doing.

So, um… yeah.

What I suggest is that you switch projects and start work on a text adventure game - these sorts of "simple interpreters" would help you step up to the more complex source code interpreters.