我的 Python 自修改 Brainf*** 解释器有一个错误
My Python interpreter for Self-modifying Brainf*** has a bug
我为一种名为 Self-modifying Brainf*** (SMBF) 的语言编写了这个 Python 解释器。今天我发现了一个错误,如果程序在磁带上的初始单元格或之后动态创建代码,它将不会被执行。我写这个解释器是为了尽可能接近链接页面上的 Ruby 解释器。请注意,此错误也可能存在于原始 Ruby 解释器中。不知道,没用过
SMBF 与普通 BF 的不同之处在于,源代码位于指针开始所在单元格左侧的磁带上。所以程序 <.
将打印源的最后一个字符(一个句点)。这行得通。
请注意,我删减了一些代码,因此它仍然 运行 可用,但在此 post.
中需要更少的 space
口译员:
from __future__ import print_function
import os, sys
class Tape(bytearray):
def __init__(self):
self.data = bytearray(b'[=11=]' * 1000)
self.center = len(self.data) // 2
def __len__(self):
return len(self.data)
def __getitem__(self, index):
try:
return self.data[index + self.center]
except:
return 0
def __setitem__(self, index, val):
i = index + self.center
if i < 0 or i >= len(self.data):
# resize the data array to be large enough
new_size = len(self.data)
while True:
new_size *= 2
test_index = index + (new_size // 2)
if test_index >= 0 and test_index < new_size:
# array is big enough now
break
# generate the new array
new_data = bytearray(b'[=11=]' * new_size)
new_center = new_size // 2
# copy old data into new array
for j in range(0, len(self.data)):
new_data[j - self.center + new_center] = self.data[j]
self.data = new_data
self.center = new_center
self.data[index + self.center] = val & 0xff
class Interpreter():
def __init__(self, data):
self.tape = Tape()
# copy the data into the tape
for i in range(0, len(data)):
self.tape[i - len(data)] = data[i]
# program start point
self.entrypoint = -len(data)
def call(self):
pc = self.entrypoint
ptr = 0
# same as -len(self.tape) // 2 <= pc + self.tape.center < len(self.tape) // 2
while -len(self.tape) <= pc < 0: # used to be "while pc < 0:"
c = chr(self.tape[pc])
if c == '>':
ptr += 1
elif c == '<':
ptr -= 1
elif c == '+':
self.tape[ptr] += 1
elif c == '-':
self.tape[ptr] -= 1
elif c == '.':
print(chr(self.tape[ptr]), end="")
elif c == ',':
sys.stdin.read(1)
elif c == '[':
if self.tape[ptr] == 0:
# advance to end of loop
loop_level = 1
while loop_level > 0:
pc += 1
if chr(self.tape[pc]) == '[': loop_level += 1
elif chr(self.tape[pc]) == ']': loop_level -= 1
elif c == ']':
# rewind to the start of the loop
loop_level = 1
while loop_level > 0:
pc -= 1
if chr(self.tape[pc]) == '[': loop_level -= 1
elif chr(self.tape[pc]) == ']': loop_level += 1
pc -= 1
pc += 1
# DEBUG
#print(pc, self.tape.data.find(b'.'))
def main():
# Working "Hello, World!" program.
#data = bytearray(b'<[.<]>>>>>>>>+\x00!dlroW ,olleH')
# Should print a period, but doesn't.
data = bytearray(b'>++++++++++++++++++++++++++++++++++++++++++++++')
intr = Interpreter(data)
intr.call()
#print(intr.tape.data.decode('ascii').strip('[=11=]'))
if __name__ == "__main__":
main()
问题:
这一行是我设置程序的方式(所以我可以在 Ideone.com 上 运行):
data = bytearray(b'++++++++++++++++++++++++++++++++++++++++++++++')
程序将添加到单元格直到它是 46,这是 ASCII .
的十进制值,它应该打印当前单元格(一个句点)。但由于某种原因,程序计数器 pc
永远不会到达该单元格。我希望程序 运行 它找到的所有代码,直到它到达磁带的末尾,但我很难让程序计数器考虑到磁带的中心,并确保它仍然如果在 __setitem__
.
中调整磁带大小则正确
相关行是(我正在尝试的):
while -len(self.tape) <= pc < 0:
原来是这样的:
while pc < 0:
所以我认为 while
行要么需要调整,要么我需要将其更改为 while True:
并在获取 chr(self.tape[pc])
时使用 try/except
确定我是否已经读完磁带。
有人知道哪里出了问题或如何解决吗?
感谢 Sp3000 找到了解决方案。
Tape.__init__
中的self.end = 0
、Tape.__setitem__
中的self.end = max(self.end, index+1)
并将Interpreter.call
中的while
替换为while pc < self.tape.end:
。
我为一种名为 Self-modifying Brainf*** (SMBF) 的语言编写了这个 Python 解释器。今天我发现了一个错误,如果程序在磁带上的初始单元格或之后动态创建代码,它将不会被执行。我写这个解释器是为了尽可能接近链接页面上的 Ruby 解释器。请注意,此错误也可能存在于原始 Ruby 解释器中。不知道,没用过
SMBF 与普通 BF 的不同之处在于,源代码位于指针开始所在单元格左侧的磁带上。所以程序 <.
将打印源的最后一个字符(一个句点)。这行得通。
请注意,我删减了一些代码,因此它仍然 运行 可用,但在此 post.
中需要更少的 space口译员:
from __future__ import print_function
import os, sys
class Tape(bytearray):
def __init__(self):
self.data = bytearray(b'[=11=]' * 1000)
self.center = len(self.data) // 2
def __len__(self):
return len(self.data)
def __getitem__(self, index):
try:
return self.data[index + self.center]
except:
return 0
def __setitem__(self, index, val):
i = index + self.center
if i < 0 or i >= len(self.data):
# resize the data array to be large enough
new_size = len(self.data)
while True:
new_size *= 2
test_index = index + (new_size // 2)
if test_index >= 0 and test_index < new_size:
# array is big enough now
break
# generate the new array
new_data = bytearray(b'[=11=]' * new_size)
new_center = new_size // 2
# copy old data into new array
for j in range(0, len(self.data)):
new_data[j - self.center + new_center] = self.data[j]
self.data = new_data
self.center = new_center
self.data[index + self.center] = val & 0xff
class Interpreter():
def __init__(self, data):
self.tape = Tape()
# copy the data into the tape
for i in range(0, len(data)):
self.tape[i - len(data)] = data[i]
# program start point
self.entrypoint = -len(data)
def call(self):
pc = self.entrypoint
ptr = 0
# same as -len(self.tape) // 2 <= pc + self.tape.center < len(self.tape) // 2
while -len(self.tape) <= pc < 0: # used to be "while pc < 0:"
c = chr(self.tape[pc])
if c == '>':
ptr += 1
elif c == '<':
ptr -= 1
elif c == '+':
self.tape[ptr] += 1
elif c == '-':
self.tape[ptr] -= 1
elif c == '.':
print(chr(self.tape[ptr]), end="")
elif c == ',':
sys.stdin.read(1)
elif c == '[':
if self.tape[ptr] == 0:
# advance to end of loop
loop_level = 1
while loop_level > 0:
pc += 1
if chr(self.tape[pc]) == '[': loop_level += 1
elif chr(self.tape[pc]) == ']': loop_level -= 1
elif c == ']':
# rewind to the start of the loop
loop_level = 1
while loop_level > 0:
pc -= 1
if chr(self.tape[pc]) == '[': loop_level -= 1
elif chr(self.tape[pc]) == ']': loop_level += 1
pc -= 1
pc += 1
# DEBUG
#print(pc, self.tape.data.find(b'.'))
def main():
# Working "Hello, World!" program.
#data = bytearray(b'<[.<]>>>>>>>>+\x00!dlroW ,olleH')
# Should print a period, but doesn't.
data = bytearray(b'>++++++++++++++++++++++++++++++++++++++++++++++')
intr = Interpreter(data)
intr.call()
#print(intr.tape.data.decode('ascii').strip('[=11=]'))
if __name__ == "__main__":
main()
问题:
这一行是我设置程序的方式(所以我可以在 Ideone.com 上 运行):
data = bytearray(b'++++++++++++++++++++++++++++++++++++++++++++++')
程序将添加到单元格直到它是 46,这是 ASCII .
的十进制值,它应该打印当前单元格(一个句点)。但由于某种原因,程序计数器 pc
永远不会到达该单元格。我希望程序 运行 它找到的所有代码,直到它到达磁带的末尾,但我很难让程序计数器考虑到磁带的中心,并确保它仍然如果在 __setitem__
.
相关行是(我正在尝试的):
while -len(self.tape) <= pc < 0:
原来是这样的:
while pc < 0:
所以我认为 while
行要么需要调整,要么我需要将其更改为 while True:
并在获取 chr(self.tape[pc])
时使用 try/except
确定我是否已经读完磁带。
有人知道哪里出了问题或如何解决吗?
感谢 Sp3000 找到了解决方案。
Tape.__init__
中的self.end = 0
、Tape.__setitem__
中的self.end = max(self.end, index+1)
并将Interpreter.call
中的while
替换为while pc < self.tape.end:
。