简单的解释器错误地解析文件

时间:2017-04-19 13:38:43

标签: lua interpreter

昨天我为我的新编程语言编写了翻译,并且我已经把它放在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和Assembly。

如何运行上面的代码:./ 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

我找不到任何错误,但一定有错误。 如果您想查看仓库is "fixed" in vbguest v0.15.0,但它不包含 除许可证,自述文件,构建和测试脚本之外的任何内容 我已经提出一些意见来理解这个破碎的代码 并试图更容易修复它。

有人能指出我在哪里犯了错误吗?

1 个答案:

答案 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