在解释器中实现Brainfuck循环

时间:2010-04-06 20:43:44

标签: interpreter brainfuck

我想用我刚刚创建的编程语言构建一个Brainfuck(该死的名字)解释器来证明它是图灵完整性。

现在,到目前为止一切都很清楚(<>+-,.) - 除了一件事:循环([])。 我假设你从这里知道(极其困难的)BF语法:

  • 如何在我的解释器中实现BF循环?

伪代码怎么样?当解释器到达循环开始([)或循环结束(])时,我该怎么办?

检查循环是否应该继续或停止不是问题(current cell==0),而是:

  • 我必须在何时何地检查?
  • 如何知道循环开始的位置?
  • 如何处理嵌套循环?

由于循环可以嵌套,我想我不能只使用包含当前循环起始位置的变量。

我已经看到用各种语言实现的非常小的BF解释器,我想知道他们是如何设法让循环工作但却无法弄明白。

5 个答案:

答案 0 :(得分:8)

当您到达[时,您将测试数据指针。

如果错误,您可以扫描下一个匹配的 ]字符,计算您看到的[],并确保将其标记为关闭]

如果确实如此,您需要跟踪其位置,以便稍后可以跳回到它。我建议使用堆栈。将当前程序位置推入堆栈,然后当到达{{1}}时,测试数据指针。如果是,请转到堆栈上最顶层的程序位置。如果它是假的,则将该位置从堆栈中弹出并继续。

当您嵌入内部循环时,堆栈将干净地记录每个循环的上下文。

stack (wikipedia)。这类似于汇编程序处理函数调用的方式。

答案 1 :(得分:5)

这是我的“优化”解释器版本,预先编译循环跳转。

def interpret2(code):
    data = [0] * 5000   # data memory
    cp = 0              # code pointer
    dp = 0              # data pointer
    # pre-compile a jump table
    stack = []
    jump = [None] * len(code)
    for i,o in enumerate(code):
        if o=='[':
            stack.append(i)
        elif o==']':
            jump[i] = stack.pop()
            jump[jump[i]] = i
    # execute
    while cp < len(code):
        cmd = code[cp]
        if   cmd == '>': dp += 1
        elif cmd == '<': dp -= 1
        elif cmd == '+': data[dp] += 1 
        elif cmd == '-': data[dp] -= 1 
        elif cmd == '.': stdout.write(chr(data[dp]))
        elif cmd == ',': data[dp] = ord(stdin.read(1))
        elif cmd == '[' and not data[dp]: # skip loop if ==0
            cp = jump[cp]
        elif cmd == ']' and data[dp]:     # loop back if !=0
            cp = jump[cp]
        cp += 1

执行代码的干运行,跟踪括号(在堆栈中)并在并行jump数组中标记goto地址,稍后在执行期间查询该数据。

我比较了长时间运行的BF程序的执行速度(计算Pi的N位数),这比无辜的实现提高了2倍,其中源被向前扫描到退出[并向后扫描到循环在]

答案 2 :(得分:1)

  

如何在我的解释器中实现BF循环?

这就是重点 - 这完全取决于你的语言。对于基于堆栈的编程语言(或任何可以使用堆栈的语言)的情况,@ rjh给出了一个很好的解决方案。其他语言将使用不同的解决方案,例如递归(即隐式使用堆栈)。

答案 3 :(得分:1)

从我的头脑中,可能是一些错误,但这样的事情应该有效:

char* interpret(char* instructions){
  char* c = instructions;
  while (*c) {
    if (*c == ".") putchar(*p);
    else if (*c == ",") *p = getchar();
    else if (*c == '+') (*p)++;
    else if (*c == '-') (*p)--;
    else if (*c == '<') p--;
    else if (*c == '>') p++;
    else if (*c == '[') c = interpret(c+1);
    else if (*c == ']') { if (*p) c = instructions else return c; }
    c++;
  }
  return 0;
}

使用brainf * ck源代码调用解释。指针p指向当前存储器位置。在发现[。]时进行递归调用。遇到一个]时从这个递归调用返回。

答案 4 :(得分:0)

我更喜欢使用跳转表(以及将原始BF转换为'字节码')。优化BF解释器显然是要走的路!