昨天我爲我的新編程語言編寫了解釋器,並且我已將它放在Github上。在此之前,我做了一些簡單的測試,它似乎工作,但現在當我想寫一些例子 - 它根本不工作!我真的很驚訝,我是如何得到這個輸出的。對於這樣簡單的程序:簡單的解釋器錯誤地解析文件
string Hello World!
locate 0
puts
它工作得很好,顯示輸出'Hello World!'。更復雜的程序是這樣的:
ADD 49
PUTCH
SUB 1
PUTCH
MUL 2
ADD 1
PUTCH
SUB 1
DIV 2
PUTCH
LOCATE 2
PUTD
LOCATE 0
SETV 10
PUTCH
SETV 0
LOCATE 0
STRING Hello World
LOCATE 0
PUTS
SETV 10
UNTILZERO
{
SUB 1
}
IFCC 10 == 10 {
LOCATE 0
SETV 0
STRING Ok!
LOCATE 0
PUTS
}
要理解這一點,你只需要Lua中,C和彙編的一些知識。
如何運行上面的代碼:./lua start.lua的test.txt -gencode 輸出:
}UTSTE 0k! 10 {rld
local tape = {}
local pointer = 0
}UTSTE 0k! 10 {rldpe[pointer] + 49
(我不明白這個invaild輸出的任何部分)
所以其實我的代碼看起來是這樣的:
--[[
____ _ __ _ _ _
| _ \ (_) /_| | | _| || |_
| |_) |_ __ __ _ _ _ __ | |_ _ _ ___| | _|_ __ _|
| _ <| '__/ _` | | '_ \| _| | | |/ __| |/ /_| || |_
| |_) | | | (_| | | | | | | | |_| | (__| <|_ __ _|
|____/|_| \__,_|_|_| |_|_| \__,_|\___|_|\_\ |_||_|
Brainfuck# v 1.0. Copyright (C) by Krzysztof Szewczyk.
For more information check CONTRIB.MD and LICENSE.
Code is licensed under GPLv3.
--]]
output = "local tape = {}\nlocal pointer = 0\n" --We will use Lua 'eval'-like function.
brackets = 0
-- Parse command with parameters 'params', and parameter number
-- 'paramno'.
function parse(command,params,paramno)
if command == nil or params == nil or paramno == nil then return end
local cmd = string.upper(command)
if cmd == ";" then end
if cmd == "ADD" then
local amount = params[1]
output = output .. "tape[pointer] = tape[pointer] + " .. amount .. "\n"
end
if cmd == "SUB" then
local amount = params[1]
output = output .. "tape[pointer] = tape[pointer] - " .. amount .. "\n"
end
if cmd == "MUL" then
local amount = params[1]
output = output .. "tape[pointer] = tape[pointer] * " .. amount .. "\n"
end
if cmd == "DIV" then
local amount = params[1]
if amount == 0 then
print("[JIT] - Divide-by-zero error.")
end
output = output .. "tape[pointer] = tape[pointer]/" .. amount .. "\n"
end
if cmd == "ADDP" then
local amount = params[1]
output = output .. "pointer = pointer + " .. amount .. "\n"
end
if cmd == "SUBP" then
local amount = params[1]
output = output .. "pointer = pointer - " .. amount .. "\n"
end
if cmd == "MULP" then
local amount = params[1]
output = output .. "pointer = pointer * " .. amount .. "\n"
end
if cmd == "DIVP" then
local amount = params[1]
if amount == 0 then
print("[JIT] - Divide-by-zero error.")
end
output = output .. "pointer = pointer/" .. amount .. "\n"
end
if cmd == "LOCATE" then
local pos = params[1]
output = output .. "pointer = " .. pos .. "\n"
end
if cmd == "SETV" then
local val = params[1]
output = output .. "tape[pointer] = " .. val .. "\n"
end
if cmd == "STRING" then
local str = params[1]
for i = 1, #str do
local c = str:sub(i,i)
output = output .. "tape[pointer] = string.byte(\"" .. c .. "\")\n"
output = output .. "pointer = pointer + 1\n"
end
output = output .. "tape[pointer] = 0\n" --Remember to add null terminator (this can overwrite some of
--your crap stored in tape, so please have this in mind).
output = output .. "pointer = pointer + 1\n"
end
if cmd == "PUTCH" then
--Simply, no arguments
output = output .. "io.write(string.char(tape[pointer]))\n"
end
if cmd == "PUTD" then
--Simply, no arguments ^
output = output .. "io.write(tape[pointer])\n" --Just print integer (as integer, not character, for character see |)
end
if cmd == "PUTS" then
output = output .. "lastpntr=0\nwhile true do\nif tape[pointer] == 0 then break end\nio.write(string.char(tape[pointer]))\npointer = pointer + 1\nend\npointer=lastpntr\n" --I belive it's too complicated
--But it works.
end
if cmd == "GETCH" then
output = output .. "tape[pointer] = io.read()\n" --HACK: Any raw input is not possible in multiplatform way.
end
if cmd == "UNTILZERO" then
output = output .. "while tape[pointer]\n"
end
if cmd == "{" then
output = output .. "do\n"
brackets = brackets + 1;
end
if cmd == "}" then
output = output .. "end\n"
brackets = brackets - 1;
end
if cmd == "IUNTIL" then
local type = params[1]
local value = params[2]
output = output .. "while tape[pointer] " .. type .. value .. "\n"
brackets = brackets - 1;
end
if cmd == "TUNTIL" then
local type = params[1]
local value = params[2]
output = output .. "while tape[pointer] " .. type .. "tape[" .. value .. "]" .. "\n"
brackets = brackets - 1;
end
if cmd == "IFCC" then
local val1 = params[1];
local comp = params[2];
local val2 = params[3];
if params[4] == "{" then
output = output .. "if " .. val1 .. comp .. val2 .. " then\n"
else
print("[JIT]: IFCC needs starting bracket at 4th argument. Please pass it and don't forget to close it.")
end
end
if cmd == "IFCT" then
local val1 = params[1];
local comp = params[2];
local val2 = params[3];
if params[4] == "{" then
output = output .. "if " .. val1 .. comp .. "tape[" .. val2 .. "] then\n"
else
print("[JIT]: IFCC needs starting bracket at 4th argument. Please pass it and don't forget to close it.")
end
end
if cmd == "IFTT" then
local val1 = params[1];
local comp = params[2];
local val2 = params[3];
if params[4] == "{" then
output = output .. "if tape[" .. val1 .. "]" .. comp .. "tape[" .. val2 .. "] then\n"
else
print("[JIT]: IFCC needs starting bracket at 4th argument. Please pass it and don't forget to close it.")
end
end
end
-- Function to split strings. Any questions?
function string:split(inSplitPattern, outResults)
if not outResults then
outResults = { }
end
local theStart = 1
local theSplitStart, theSplitEnd = string.find(self, inSplitPattern, theStart)
while theSplitStart do
table.insert(outResults, string.sub(self, theStart, theSplitStart-1))
theStart = theSplitEnd + 1
theSplitStart, theSplitEnd = string.find(self, inSplitPattern, theStart)
end
table.insert(outResults, string.sub(self, theStart))
return outResults
end
-- I create new function to ensure that variables
-- won't escape local context.
function main(filename,gencodeswitch)
local input = io.open(filename, "r")
if input then
--No error found while opening file
while true do
--First, read line.
local line = input:read()
--Now, let's check is it nil.
--If so, we can break out of this loop.
if line == nil then break end
--Else, we need to parse this instruction.
--So break it into main command and it's params.
local space = string.find(line, " ") --Find first space occurence (to divide
--command from it's arguments).
local params = string.sub(line, space+1) -- To get params just split string.
local command = string.sub(line, 0, space-1) -- To get command without trailing space.
--Actually, this space will get removed.
local paramTable = params:split(",")
local paramAmount = 0
-- HACK: Looks like ineffective solution, but who cares?
for i = 1, #paramTable do
paramAmount = paramAmount + 1;
end
print ("line:" .. line)
parse(command,paramTable,paramAmount)
end
--Done parsing. Generate code
if brackets ~= 0 then print("[JIT] Unbalanced brackets.") os.exit() end
if gencodeswitch == "-gencode" then print(output) end
--loadstring(output)()
else
--Oops, an error occured. Couldn't open file.
print("[JIT]: Please pass vaild filename.")
os.exit() --Bye, see ya later
end
end
if arg[1] == nil then
--User didn't pass any arguments.
print("Brainfuck# v 1.0")
print("Ussage:")
print(".\lua start.lua <input> [-print]")
print("Where:")
print(".\lua - lua executable")
print("start.lua - main module name");
print("<input> - input filename (non-optional!)")
print("[-print] - Optional, print source before execution.")
os.exit();
else
--User passed an argument
main(arg[1],arg[2])
end
我無法找到任何錯誤,但必須有一個mistak即 如果要查看回購,here it is,但它不包含 除許可證,自述文件,構建和測試腳本以外的任何內容。 我已經提出了一些意見,使理解這個破碎的代碼 ,並試圖解決它更容易。
任何人都可以指出我在哪裏犯了一個錯誤?
這個在Lua中的習慣方式是定義一個表,其鍵是命令,其值是執行每個命令的函數。這極大地簡化了代碼,避免了ifs鏈。 – lhf
我以前想過但我拒絕它,因爲我之前製作過一個程序,就像你說的那樣,它很難維護(沒有人想修改這麼長的代碼) –