在解释器中实现 Brainfuck 循环

发布于 2024-08-27 17:52:30 字数 534 浏览 6 评论 0原文

我想用我新创建的编程语言构建一个 Brainfuck(该死的名字)解释器,以证明它的图灵完备性。

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

  • How do I Implement the BF Loops in myterpreter?

伪代码是什么样的?当解释器到达循环开始 ([) 或循环结束 (]) 时,我应该做什么?

检查循环是否应该继续或停止不是问题(当前单元==0),而是:

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

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

我见过用各种语言实现的非常小的 BF 解释器,我想知道他们是如何设法让循环工作的,但无法弄清楚。

I want to build a Brainfuck (Damn that name) interpreter in my freshly created programming language to prove it's turing-completeness.

Now, everything is clear so far (<>+-,.) - except one thing: The loops ([]).
I assume that you know the (extremely hard) BF syntax from here on:

  • How do I implement the BF loops in my interpreter?

How could the pseudocode look like? What should I do when the interpreter reaches a loop beginning ([) or a loop end (])?

Checking if the loop should continue or stop is not the problem (current cell==0), but:

  • When and where do I have to check?
  • How to know where the loop beginning is located?
  • How to handle nested loops?

As loops can be nested I suppose that I can't just use a variable containing the starting position of the current loop.

I've seen very small BF interpreters implemented in various languages, I wonder how they managed to get the loops working but can't figure it out.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

护你周全 2024-09-03 17:52:31

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

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

How do I implement the BF loops in my interpreter?

That’s the point – it entirely depends on your language. For the case of a stack-based programming language (or any language which can use a stack), @rjh has given a good solution. Other languages would use different solutions, such as recursion (i.e. implicit use of a stack).

一城柳絮吹成雪 2024-09-03 17:52:31

从我的想法来看,其中可能存在一些错误,但类似这样的事情应该有效:

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 指向当前内存位置。发现 [ 时进行递归调用。当遇到 ] 时,从此递归调用返回。

From the top of my head, might be some errors in it, but something like this should work:

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;
}

Call interpret with the brainf*ck source code. The pointer p is to the current memory position. Do a recursive call when spotting a [. Return from this recursive call when encountering a ].

在你怀里撒娇 2024-09-03 17:52:31

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

I prefer to use a jump table (along with conversion of raw BF to 'bytecode'). Optimizing BF interpreters are clearly the way to go!

A君 2024-09-03 17:52:30

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

如果为 false,您可以扫描下一个匹配 ] 字符,计算您看到的 [ 数量,并确保将它们标记为您会看到每个 ]

如果这是真的,您需要跟踪它的位置,以便稍后跳回它。我建议使用堆栈。将当前程序位置压入堆栈,然后当到达 ] 时,测试数据指针。如果为真,则转到堆栈中最顶层的程序位置。如果为 false,则将该位置从堆栈中弹出并继续。

当您嵌套到内部循环中时,堆栈将清楚地记录每个循环的上下文。

请参阅堆栈 (wikipedia)。这类似于汇编程序处理函数调用的方式。

When you reach [, you test the data pointer.

If it's false, you can scan for the next matched ] character, counting up how many [ you see and making sure you mark them off as you see each ].

If it's true, you need to keep track of its position so you can jump back to it later. I suggest using a stack. Push the current program position onto the stack, then when you reach ], test the data pointer. If it's true, go to the topmost program position on the stack. If it's false, pop the position off the stack and continue.

As you nest into inner loops, the stack will cleanly record the context of each loop.

See stack (wikipedia). This is analogous to how assembly programs deal with function calls.

羁绊已千年 2024-09-03 17:52:30

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

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

它对代码进行空运行,跟踪括号(在堆栈中)并在并行跳转数组中标记转到地址,稍后在执行过程中会查阅该地址。

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

Here is my "optimizing" version of interpreter, that pre-compiles the loop jumps.

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

It does a dry run of the code, keeping track of the brackets (in a stack) and marks the goto addresses in parallel jump array which is later consulted during execution.

I compared the execution speed on long-running BF program (calculate N digits of Pi) and this increased the speed 2x compared to an innocent implementation in which source is scanned forward to exit [ and scanned backwards to loop on ].

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文