當Python大刀砍向Brainfuck(2011-04-09)

以前學編譯的都喜歡用Lisp練手,現在貌似改Brainfuck(參見:維基、Wiki)了。

Brainfuck更簡單(就八個命令),而且還是Turing完備的。Brainfuck簡直就是對Turing機的直接模擬:一條初始為零的帶子、一個指針。不過,Turing機的紙帶似乎具有代碼和數據雙重功能,而Brainfuck更像是把紙帶當容器)。

Brainfuck的語法如下:

指令 動作
> 右移一格
< 左移一格
+ 指針指向的字節的值加一
- 指針指向的字節的值減一
. 輸出指針指向的單元內容(ASCII碼)
, 輸入內容到指針指向的單元(ASCII碼)
[ 向後跳轉到對應的]
] 向前跳轉到對應的[指令的次一指令處,如果指針指向的字節非零

前六條都還好理解,最后兩條就不那么直觀了(Turing機里是沒有直接對應的)。

def mainloop(program, bracket_map):  
    pc = 0  
    tape = Tape()  
    while pc < len(program):  
        code = program[pc]  
        if code == ">":  
            tape.advance()  
        elif code == "<":  
            tape.devance()  
        elif code == "+":  
            tape.inc()  
        elif code == "-":  
            tape.dec()  
        elif code == ".":  
            # print  
            os.write(1, chr(tape.get()))  
        elif code == ",":  
            # read from stdin  
            tape.set(ord(os.read(0, 1)[0]))  
        elif code == "[" and tape.get() == 0:  
            # Skip forward to the matching ]  
            pc = bracket_map[pc]  
        elif code == "]" and tape.get() != 0:  
            # Skip back to the matching [  
            pc = bracket_map[pc]  
        pc += 1

pc就是指針(program counter)。

紙帶:

 class Tape(object):  
    def __init__(self):  
        self.thetape = [0]  
        self.position = 0  
    def get(self):  
        return self.thetape[self.position]  
    def set(self, val):  
        self.thetape[self.position] = val  
    def inc(self):  
        self.thetape[self.position] += 1  
    def dec(self):  
        self.thetape[self.position] -= 1  
    def advance(self):  
        self.position += 1  
        if len(self.thetape) <= self.position:  
            self.thetape.append(0)  
    def devance(self):  
        self.position -= 1

這就體現出高階語言的好處了:不用考慮分配內存,近乎無限的紙帶。

def parse(program):  
    parsed = []  
    bracket_map = {}  
    leftstack = []  
    pc = 0  
    for char in program:  
        if char in ('[', ']', '<', '>', '+', '-', ',', '.'):  
            parsed.append(char)  
            if char == '[':  
                leftstack.append(pc)  
            elif char == ']':  
                left = leftstack.pop()  
                right = pc  
                bracket_map[left] = right  
                bracket_map[right] = left  
            pc += 1  

    return "".join(parsed), bracket_map  
def run(fp):  
    program_contents = ""  
    while True:  
        read = os.read(fp, 4096)  
        if len(read) == 0:  
            break  
        program_contents += read  
    os.close(fp)  
    program, bm = parse(program_contents)  
    mainloop(program, bm)  

run(os.open("/Users/Pope/Desktop/test.bf", os.O_RDONLY, 0777))  

test.bf 的內容:

++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.------.--------.>+.>.  

Wiki 上有解釋:

+++++ +++++             initialize counter (cell #0) to 10  
[                       use loop to set the **next** four cells to 70/100/30/10  
    > +++++ ++              add  7 to cell #1  
    > +++++ +++++           add 10 to cell #2   
    > +++                   add  3 to cell #3  
    > +                     add  1 to cell #4  
    <<<< -                  decrement counter (cell #0)  
]                     
> ++ .                  print 'H'  
> + .                   print 'e'  
+++++ ++ .              print 'l'  
.                       print 'l'  
+++ .                   print 'o'  
> ++ .                  print ' '  
<< +++++ +++++ +++++ .  print 'W'  
> .                     print 'o'  
+++ .                   print 'r'  
----- - .               print 'l'  
----- --- .             print 'd'  
> + .                   print '!'  
> .                     print '/n'  

P.S. 本文代碼源自一篇介紹如何用PyPy寫解釋器(已被墻)的文章,不過前半部分與PyPy沒什么關系。

P.P.S 還好一般不譯語言的名字,不然Brainfuck是該譯成“腦殘”好呢,還是“腦袋被驢踢了”。

P.P.P.S 本著“Python能干的事情Ruby也能做”的理念,我用Ruby改寫了一遍:

def mainloop(program, bracket_map)
  pc = 0
  tape = Tape.new
  while pc < program.length
    code = program[pc]
    case code
    when ">"
      tape.advance()
    when "<"
      tape.devance()
    when "+"
      tape.inc()
    when "-"
      tape.dec()
    when "."
      print(tape.get().chr)
    when ","
      tape.set(gets())
    when "["
      pc = bracket_map[pc] if tape.get == 0
    when "]"
      pc = bracket_map[pc] if tape.get != 0
    else
      # do nothing
    end

    pc += 1
  end
end

class Tape
  def initialize
    @the_tape = [0]
    @position = 0
  end

  def get; @the_tape[@position]; end
  def set(val); @the_tape[@position] = val; end
  def inc; @the_tape[@position] += 1; end
  def dec; @the_tape[@position] -= 1; end
  def advance
    @position += 1
    @the_tape.push(0) if @position >= @the_tape.length
  end
  def devance; @position -= 1; end

end

def parse(program)
  parsed = []
  bracket_map = {}
  leftstack = []
  pc = 0
  program.each_char do |char|
    if ['[', ']', '<', '>', '+', '-', ',', '.'].include? char
      parsed.push(char)
      if char == '['
        leftstack.push(pc)
      elsif char == ']'
        left = leftstack.pop
        right = pc
        bracket_map[left] = right
        bracket_map[right] = left
      end
      pc += 1
    end
  end
  [parsed, bracket_map]
end

def run(fp)
  program_contents = IO.read(fp)
  program, bm = parse(program_contents)
  mainloop(program, bm)
end

run("/Users/Pope/Desktop/test.bf")

P.P.P.P.S 有人寫過一個很丑但很短的Ruby版Brainfuck解釋器。真的很丑??

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容