当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解释器。真的很丑

你可能感兴趣的:(当Python大刀砍向Brainfuck(2011-04-09))