2017-04-19 99 views
-1

昨天我爲我的新編程語言編寫了解釋器,並且我已將它放在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,但它不包含 除許可證,自述文件,構建和測試腳本以外的任何內容。 我已經提出了一些意見,使理解這個破碎的代碼 ,並試圖解決它更容易。

任何人都可以指出我在哪裏犯了一個錯誤?

+1

這個在Lua中的習慣方式是定義一個表,其鍵是命令,其值是執行每個命令的函數。這極大地簡化了代碼,避免了ifs鏈。 – lhf

+0

我以前想過但我拒絕它,因爲我之前製作過一個程序,就像你說的那樣,它很難維護(沒有人想修改這麼長的代碼) –

回答

1

我冒昧地徹底修改你的代碼,因爲我看到了太多的問題。 (它甚至沒有像你發佈的那樣編譯。)我想你是編程新手。無論如何,仍然需要更多的工作,但我會把剩下的工作留給你。但是,至少現在它似乎適用於您的示例輸入。

我將您的示例程序保存爲sample.bf並使用命令 brainfuck#.lua sample.bf -gencode | lua得到了Hello World!作爲輸出。據我所知,這是正確的。

--[[ 
    ____   _  __   _  _ _ 
| _ \   (_) /_|   | | _| || |_ 
| |_) |_ __ __ _ _ _ __ | |_ _ _ ___| | _|_ __ _| 
| _ <| '__/ _` | | '_ \| _| | | |/ __| |/ /_| || |_ 
| |_) | | | (_| | | | | | | | |_| | (__| <|_ __ _| 
|____/|_| \__,_|_|_| |_|_| \__,_|\___|_|\_\ |_||_| 
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 = {} 
local pointer = 0 
]] --We will use Lua 'eval'-like function. 
brackets = 0 

-- Parse command with parameters 'params', and parameter number 'paramno'. 
function parse(cmd,params,paramno) 
    if cmd == nil or params == nil or paramno == nil then return end 
    cmd = cmd:upper() 

    local amount 

    if cmd == ";" then 
    elseif cmd == "ADD" then 
    amount = params[1] 
    output = output .. "tape[pointer] = tape[pointer] + " .. amount .. "\n" 
    elseif cmd == "SUB" then 
    amount = params[1] 
    output = output .. "tape[pointer] = tape[pointer] - " .. amount .. "\n" 
    elseif cmd == "MUL" then 
    amount = params[1] 
    output = output .. "tape[pointer] = tape[pointer] * " .. amount .. "\n" 
    elseif cmd == "DIV" then 
    amount = params[1] 
    if amount == 0 then 
     print("[JIT] - Divide-by-zero error.") 
    end 
    output = output .. "tape[pointer] = tape[pointer]/" .. amount .. "\n" 
    elseif cmd == "ADDP" then 
    amount = params[1] 
    output = output .. "pointer = pointer + " .. amount .. "\n" 
    elseif cmd == "SUBP" then 
    amount = params[1] 
    output = output .. "pointer = pointer - " .. amount .. "\n" 
    elseif cmd == "MULP" then 
    amount = params[1] 
    output = output .. "pointer = pointer * " .. amount .. "\n" 
    elseif cmd == "DIVP" then 
    amount = params[1] 
    if amount == 0 then 
     print("[JIT] - Divide-by-zero error.") 
    end 
    output = output .. "pointer = pointer/" .. amount .. "\n" 
    elseif cmd == "LOCATE" then 
    local pos = params[1] 
    output = output .. "pointer = " .. pos .. "\n" 
    elseif cmd == "SETV" then 
    local val = params[1] 
    output = output .. "tape[pointer] = " .. val .. "\n" 
    elseif cmd == "STRING" then 
    local s = params[1] 
    for i = 1, #s do 
     local c = s: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 on tape, so please have this in mind). 
    output = output .. "pointer = pointer + 1\n" 
    elseif cmd == "PUTCH" then --Simply, no arguments 
    output = output .. "io.write(string.char(tape[pointer]))\n" 
    elseif cmd == "PUTD" then --Simply, no arguments                       ^
    output = output .. "io.write(tape[pointer])\n" --Just print integer (as integer, not character, for character see |) 
    elseif cmd == "PUTS" then 
    output = output .. [[ 
lastpntr=0 
while true do 
    if tape[pointer] == 0 then break end 
    io.write(string.char(tape[pointer])) 
    pointer = pointer + 1 
end 
pointer=lastpntr 
]] --I believe it's too complicated but it works. 
    elseif cmd == "GETCH" then 
    output = output .. "tape[pointer] = io.read()\n" --HACK: Any raw input is not possible in multiplatform way. 
    elseif cmd == "UNTILZERO" then 
    output = output .. "while tape[pointer]\n" 
    elseif cmd == "{" then 
    output = output .. "do\n" 
    brackets = brackets + 1 
    elseif cmd == "}" then 
    output = output .. "end\n" 
    brackets = brackets - 1 
    elseif cmd == "IUNTIL" then 
    local type = params[1] 
    local value = params[2] 
    output = output .. "while tape[pointer] " .. type .. value .. "\n" 
    brackets = brackets - 1 
    elseif cmd == "TUNTIL" then 
    local type = params[1] 
    local value = params[2] 
    output = output .. "while tape[pointer] " .. type .. "tape[" .. value .. "]" .. "\n" 
    brackets = brackets - 1 
    elseif 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 
    elseif 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 
    elseif 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) 
    outResults = outResults or {} 
    local theStart = 1 
    local theSplitStart, theSplitEnd = self:find(inSplitPattern, theStart) 
    while theSplitStart do 
    table.insert(outResults, self:sub(theStart, theSplitStart-1)) 
    theStart = theSplitEnd + 1 
    theSplitStart, theSplitEnd = self:find(inSplitPattern, theStart) 
    end 
    table.insert(outResults, self:sub(theStart)) 
    return outResults 
end 

-- I create new function to ensure that variables won't escape local context. 
function main(filename,gencodeswitch) 
    local file = io.open(filename, "r") 
    if file == nil then 
    --Oops, an error occured. Couldn't open file. 
    print("[JIT]: Please pass valid filename.") 
    os.exit() --Bye, see ya later 
    return 
    end 
    --No error found while opening file 

    local space,params,command,paramTable,paramAmount 

    for line in file:lines() do 
    --We need to parse this instruction. 
    --So break it into main command and it's params. 
    space = (line..' '):find(" ") --Find first space occurence (to divide command from it's arguments). 
    params = line:sub(space+1) -- To get params just split string. 
    command = line:sub(1, space-1) -- To get command without trailing space. 
    --Actually, this space will get removed. 

    paramTable = params:split(',') 

    -- HACK: Looks like ineffective solution, but who cares? 
    --for i = 1, #paramTable do paramAmount = paramAmount + 1 end 
    paramAmount = #paramTable 
    print ('--' .. line) 
    parse(command,paramTable,paramAmount) 
    end 
    file:close() 
    --Done parsing. Generate code 
    if brackets ~= 0 then print("[JIT] Unbalanced brackets.") os.exit() end 
    if gencodeswitch == "-gencode" then print(output) end 
    --loadstring(output)() 
end 

if arg[1] == nil then 
    --User didn't pass any arguments. 
    print [[ 
Brainfuck# v 1.0 
Usage: 
.\lua start.lua <input> [-print] 
Where: 
.\lua - lua executable 
start.lua - main module name 
<input> - input filename (non-optional!) 
[-print] - Optional, print source before execution.]] 
    os.exit() 
else 
    --User passed an argument 
    main(arg[1],arg[2]) 
end 
+0

所以我正在學習Lua實際上和C/C++編程我有4年的經驗,但無論如何你的解決方案似乎沒問題。還要記住,字符串集中'..'需要彼此分開 –