在解释器中实现 Brainfuck 循环
我想用我新创建的编程语言构建一个 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
这就是重点——这完全取决于您的语言。对于基于堆栈的编程语言(或任何可以使用堆栈的语言)的情况,@rjh 给出了一个很好的解决方案。其他语言将使用不同的解决方案,例如递归(即隐式使用堆栈)。
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).
从我的想法来看,其中可能存在一些错误,但类似这样的事情应该有效:
使用 Brainf*ck 源代码调用解释。指针 p 指向当前内存位置。发现 [ 时进行递归调用。当遇到 ] 时,从此递归调用返回。
From the top of my head, might be some errors in it, but something like this should work:
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 ].
我更喜欢使用跳转表(以及将原始 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!
当到达
[
时,测试数据指针。如果为 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.
这是我的解释器的“优化”版本,它预编译循环跳转。
它对代码进行空运行,跟踪括号(在堆栈中)并在并行跳转数组中标记转到地址,稍后在执行过程中会查阅该地址。
我比较了长时间运行的 BF 程序(计算 Pi 的 N 位)的执行速度,与向前扫描源以退出
[
并向后扫描以循环的无辜实现相比,这将速度提高了 2 倍在]
上。Here is my "optimizing" version of interpreter, that pre-compiles the loop jumps.
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]
.