Skip to content

Instantly share code, notes, and snippets.

@davidhcefx
Last active May 25, 2024 21:56
Show Gist options
  • Save davidhcefx/5b93f9d722c167356f916706726ac5e1 to your computer and use it in GitHub Desktop.
Save davidhcefx/5b93f9d722c167356f916706726ac5e1 to your computer and use it in GitHub Desktop.
Brainfuck REPL interpreter

Brainfuck REPL interpreter

Example

$ ./brainfuck_repl.py
Brainfuck REPL intepreter in Python!
> ++

Tape dump: [0, 0, 2, 0, 0]
> ,[>+<--]>.
b
1
Tape dump: [0, 0, 0, 49, 0]
>

With DEBUG_STEP enabled, it can also display step-by-step execution:

$ ./brainfuck_repl.py --debug-step
Brainfuck REPL intepreter in Python!
> [this will add input char by one],+.
#1
Tape: [0, 0, -> 0, 0, 0]
Code: [this will add input char by one],+.
      ^
#2
Tape: [0, 0, -> 0, 0, 0]
Code: [this will add input char by one],+.
                                       ^
a
#3
Tape: [0, 0, -> 97, 0, 0]
Code: [this will add input char by one],+.
                                        ^
#4
Tape: [0, 0, -> 98, 0, 0]
Code: [this will add input char by one],+.
                                         ^
b
Tape dump: [0, 0, 98, 0, 0]
>
#! /usr/bin/env python3
# Simple Brainfuck REPL interpreter. Written by davidhcefx, 2024.5.25
from typing import List
import io
import readline
from select import select
import sys
DEBUG_STEP = False
MAX_INSTRUCTIONS = 2 ** 16
last_stdin_char = '\n'
def interpret(code: str, stdin: io.TextIOWrapper, stdout: io.TextIOWrapper) -> List[int]:
"""
@brief Interpret code and I/O to stdin and stdout.
@return The resulting data array (tape) after execution
@throw SyntaxError when encountered invalid instructions or unmatching brackets
@throw RuntimeError when number of instructions exceeded MAX_INSTRUCTIONS
"""
global last_stdin_char
ins_cnt = 0 # number of executed instructions
cod_idx = 0 # curent code index
dat_idx = 2 # current read/write index
dat_len = 5 # length of data array
data = [0] * dat_len
left_brackets = [] # stack of cod_idx of unmatched left brackets
while cod_idx < len(code) and ins_cnt < MAX_INSTRUCTIONS:
c = code[cod_idx]
ins_cnt += 1
if DEBUG_STEP:
print('#{}'.format(ins_cnt))
datastr = list(map(str, data))
datastr[dat_idx] = '-> ' + datastr[dat_idx]
print('Tape: [{}]'.format(', '.join(datastr)))
print('Code: {}'.format(code))
print(' {}^'.format(' ' * cod_idx))
if c in '><':
dat_idx += 1 if c == '>' else -1
# grows data array twice as large
if dat_idx < 0:
data = ([0] * dat_len) + data
dat_idx += dat_len
dat_len += dat_len
elif dat_idx >= dat_len:
data = data + ([0] * dat_len)
dat_len += dat_len
elif c in '+-':
data[dat_idx] += 1 if c == '+' else -1
elif c == '.':
stdout.write(chr(data[dat_idx]))
elif c == ',':
last_stdin_char = stdin.read(1)
data[dat_idx] = ord(last_stdin_char)
elif c == '[':
if data[dat_idx]:
left_brackets.append(cod_idx)
else:
# goto end of matching right bracket (NOTE: this is suboptimal)
old_cod_idx = cod_idx
bk = 1
cod_idx += 1
while cod_idx < len(code) and bk > 0:
if code[cod_idx] == '[':
bk += 1
elif code[cod_idx] == ']':
bk -= 1
cod_idx += 1
if bk > 0:
raise SyntaxError('Unmatched opening bracket at index {}'.format(old_cod_idx))
continue
elif c == ']':
if len(left_brackets) == 0:
raise SyntaxError('Unmatched closing bracket at index {}'.format(cod_idx))
if data[dat_idx]:
# goto matching left bracket
cod_idx = left_brackets.pop()
continue
else:
left_brackets.pop()
else:
raise SyntaxError('Bad instruction "{}". Brainf**k only accepts "><+-.,[]"'.format(c))
cod_idx += 1
if ins_cnt >= MAX_INSTRUCTIONS:
raise RuntimeError('Exceeding maximum number of instructions! ({})'.format(MAX_INSTRUCTIONS))
return data
if __name__ == '__main__':
for arg in sys.argv[1:]:
if arg == '--debug-step':
DEBUG_STEP = True
print('Brainfuck REPL intepreter in Python!')
while True:
try:
line = input('> ').strip()
if line == '': # no input
continue
except EOFError:
break
try:
tape = interpret(line, sys.stdin, sys.stdout)
print('\nTape dump: {}'.format(tape))
# consume trailing newlines from input if any
if last_stdin_char != '\n':
sys.stdin.readline()
last_stdin_char = '\n'
except (SyntaxError, RuntimeError) as e:
print('{}: {}'.format(type(e).__name__, e))
print('Bye!')
# Sample program:
# ,+. input a output b
# ,[>+<--]>. input b output 1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment