208 lines
4 KiB
Python
208 lines
4 KiB
Python
"""interpreter of brainfuck written in python"""
|
|
import sys
|
|
import os
|
|
|
|
from termcolor import cprint
|
|
|
|
os.system('color')
|
|
|
|
class BFError(RuntimeError):
|
|
pass
|
|
|
|
def pair_bracket(prog):
|
|
result = []
|
|
brackets = []
|
|
for index, instr in enumerate(prog):
|
|
if instr == '[':
|
|
brackets.append(index)
|
|
if instr == ']':
|
|
open_brackets = brackets.pop()
|
|
result.append((open_brackets,index))
|
|
return dict(result)
|
|
|
|
|
|
def interpret(prog):
|
|
ar = [0]
|
|
ptr = 0
|
|
index = 0
|
|
blocks = []
|
|
bracket_info = pair_bracket(prog)
|
|
while index < len(prog):
|
|
instr = prog[index]
|
|
|
|
if instr == '+':
|
|
ar[ptr] += 1
|
|
elif instr == '-':
|
|
ar[ptr] -= 1
|
|
elif instr == '>':
|
|
ptr += 1
|
|
if ptr == len(ar):
|
|
ar.append(0)
|
|
elif instr == '<':
|
|
ptr -= 1
|
|
if ptr < 0:
|
|
raise ValueError("You can't go to a cell before 0")
|
|
elif instr == '.':
|
|
print(chr(ar[ptr]),end='')
|
|
elif instr == ',':
|
|
val = input()
|
|
if len(val) > 1:
|
|
raise ValueError('please put character one by one')
|
|
if val:
|
|
ar[ptr] = ord(val)
|
|
else:
|
|
ar[ptr] = 0
|
|
elif instr == '[':
|
|
if ar[ptr]:
|
|
blocks.append(index)
|
|
else:
|
|
index = bracket_info[index]
|
|
elif instr == ']':
|
|
if ar[ptr]:
|
|
index = blocks.pop()
|
|
continue
|
|
else:
|
|
blocks.pop()
|
|
elif instr == '#':
|
|
try:
|
|
if prog[index+1] == '(':
|
|
index = index+2
|
|
while prog[index] != ')':
|
|
print(prog[index],end='',flush=True)
|
|
index += 1
|
|
print(' ',end='')
|
|
except IndexError:
|
|
pass
|
|
print(ar, ptr, ar[ptr])
|
|
|
|
index += 1
|
|
|
|
return ar
|
|
|
|
|
|
|
|
|
|
def repl():
|
|
"""
|
|
use brainfuck in an interactive mode
|
|
|
|
special commands :
|
|
p: where is the pointer
|
|
v: val of the cell (the number, not the ascii val)
|
|
a: show the entire array
|
|
|
|
"""
|
|
ar = [0]
|
|
ptr = 0
|
|
blocks = []
|
|
while 1:
|
|
try:
|
|
not_ok = True
|
|
while not_ok:
|
|
try:
|
|
prog = input('> ')
|
|
not_ok = False
|
|
except KeyboardInterrupt:
|
|
print()
|
|
except EOFError:
|
|
break
|
|
|
|
if prog == 'p':
|
|
print(ptr)
|
|
elif prog == 'v':
|
|
print(ar[ptr])
|
|
elif prog == 'a':
|
|
print(ar)
|
|
elif prog == 'help':
|
|
repl_help()
|
|
elif prog == 'q':
|
|
break
|
|
elif prog == 'r':
|
|
repl()
|
|
return
|
|
else:
|
|
index = 0
|
|
bracket_info = pair_bracket(prog)
|
|
try:
|
|
while index < len(prog):
|
|
instr = prog[index]
|
|
|
|
if instr == '+':
|
|
ar[ptr] += 1
|
|
elif instr == '-':
|
|
ar[ptr] -= 1
|
|
elif instr == '>':
|
|
ptr += 1
|
|
if ptr == len(ar):
|
|
ar.append(0)
|
|
elif instr == '<':
|
|
ptr -= 1
|
|
if ptr < 0:
|
|
ptr = 0
|
|
raise BFError(f"You can't go to a cell before 0 (code before : {prog[:index]}, code after : {prog[index:]})")
|
|
elif instr == '.':
|
|
print(chr(ar[ptr]),end='',flush=True)
|
|
elif instr == ',':
|
|
val = input()
|
|
if len(val) > 1:
|
|
raise ValueError('please put character one by one')
|
|
if val:
|
|
ar[ptr] = ord(val)
|
|
else:
|
|
ar[ptr] = 0
|
|
elif instr == '[':
|
|
if ar[ptr]:
|
|
blocks.append(index)
|
|
else:
|
|
index = bracket_info[index]
|
|
elif instr == ']':
|
|
if ar[ptr]:
|
|
index = blocks.pop()
|
|
continue
|
|
else:
|
|
blocks.pop()
|
|
elif instr == '#':
|
|
try:
|
|
if prog[index+1] == '(':
|
|
index = index+2
|
|
while prog[index] != ')':
|
|
print(prog[index],end='',flush=True)
|
|
index += 1
|
|
print(' ',end='')
|
|
except IndexError:
|
|
pass
|
|
print(ar, ptr, ar[ptr])
|
|
|
|
index += 1
|
|
except KeyboardInterrupt:
|
|
pass
|
|
except BFError as e:
|
|
cprint(e,'red')
|
|
|
|
if '.' in prog:
|
|
print()
|
|
|
|
|
|
def repl_help():
|
|
print("""\
|
|
In this repl, you can enter any valid brainfuck expression.
|
|
There is also some special command :
|
|
'a' will show you the state of the memory
|
|
'p' will show you where is the pointer
|
|
'v' will show you the value of the cell
|
|
'r' will reset the array""")
|
|
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) == 2:
|
|
interpret(open(sys.argv[1]).read())
|
|
elif len(sys.argv) == 3:
|
|
if sys.argv[1] == '-i': #oui c'est de lam erde comme manière de faire
|
|
interpret(sys.argv[2])
|
|
else:
|
|
repl()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|