Skip to content

Instantly share code, notes, and snippets.

@tirinox
Last active April 2, 2020 16:24
Show Gist options
  • Save tirinox/421742968f4dbf036fe40a9aa62b15cd to your computer and use it in GitHub Desktop.
Save tirinox/421742968f4dbf036fe40a9aa62b15cd to your computer and use it in GitHub Desktop.
Python brainfuck interpreter
import sys
def run(code, mem_size=256):
# 1. этап составим карту скобок
# ключ и значения - индекс байта в коде
# по любой скобке находим ей пару
bracket_map = {}
stack = []
for pos, symbol in enumerate(code):
# открыли скобку - положим на вершину стэка ее позицию
if symbol == '[':
stack.append(pos)
elif symbol == ']':
# вершина стэка - как раз наша парная скобка - была последней
# удалим ее с вершины
last_open_pos = stack.pop()
# создадим парные записи
bracket_map[pos] = last_open_pos
bracket_map[last_open_pos] = pos
# после прохода по коду - остались незакрытые скобки - это ошибка!
if stack:
raise ValueError("unclosed [")
# 2 этап: выполняем код
memory = [0] * mem_size # оперативная память программы
data_ptr = 0 # индекс текущей ячейки памяти (memory)
instr_ptr = 0 # индекс текущей инструкции кода (code)
# выполняем - пока не вылетели за пределы кода
while instr_ptr < len(code):
command = code[instr_ptr] # текущая команда
if command == '+':
memory[data_ptr] += 1
elif command == '-':
memory[data_ptr] -= 1
elif command == '>':
data_ptr = (data_ptr + 1) % mem_size # циклическая память
elif command == '<':
data_ptr = (data_ptr - 1) % mem_size # циклическая память
elif command == '.':
# печатаем символ с кодом = значения текущей ячейки
print(chr(memory[data_ptr]), end='')
elif command == ',':
# ввод - берем код первого символа или 0, если ввод пустой
inp = input()
memory[data_ptr] = ord(inp[0]) if inp else 0
elif command == '[':
# начало цикла - проверка текущей ячейки
if not memory[data_ptr]: # == 0
# значит надо перейти на ячейку за соответствующей ей закрывающей скобкой
instr_ptr = bracket_map[instr_ptr]
elif command == ']':
# проверяем тоже условие, если выполняется, то скачем на начало цилкаа
if memory[data_ptr]: # не ноль
# перемещаем на конец цикла
instr_ptr = bracket_map[instr_ptr]
instr_ptr += 1 # сдвигаеи к следующей команде
if __name__ == '__main__':
if len(sys.argv) == 2:
# один аргумент - имя файла, который надо выполнить
with open(sys.argv[1], 'r') as f:
code = f.read()
run(code)
else:
print(f'usage: python {sys.argv[0]} file.bf')
import sys
import os
PRELUDE = """// brainfuck generated:
#include <stdio.h>
int main() {
const int mem_size = 30000;
char mem[mem_size];
memset(mem, 0, mem_size);
int cur = 0;
"""
ENDING = """
return 0;
}
"""
def compile_to_c(bf_code):
instr_ptr = 0 # индекс текущей инструкции кода (code)
indent = 4
c_code = PRELUDE
# выполняем - пока не вылетели за пределы кода
for command in bf_code:
content = ''
if command == '+':
content = 'mem[cur]++;'
elif command == '-':
content = 'mem[cur]--;'
elif command == '>':
content = 'cur++;'
elif command == '<':
content = 'cur--;'
elif command == '.':
content = 'putchar(mem[cur]);'
elif command == ',':
content = 'mem[cur] = getchar();'
elif command == '[':
content = 'while(mem[cur]) {'
elif command == ']':
content = '}'
instr_ptr += 1 # сдвигаеи к следующей команде
if content:
if command == ']':
indent -= 4
c_code += ' ' * indent + content + '\n'
if command == '[':
indent += 4
c_code += ENDING
return c_code
if __name__ == '__main__':
if len(sys.argv) == 2:
# один аргумент - имя файла, который надо выполнить
name = sys.argv[1]
with open(name, 'r') as f:
code = f.read()
c_code = compile_to_c(code)
c_file = f'{name}.c'
with open(c_file, 'w') as f:
f.write(c_code)
os.system(f'cc {c_file} -o {name}.out')
else:
print(f'usage: python {sys.argv[0]} file.bf')
++++++++++[>+++++++>++++++++++>+++>+<<<<-]>++
.>+.+++++++..+++.>++.<<+++++++++++++++.>.+++.
------.--------.>+.>.
>>+>,[
<[
[>>+<<-]>[<<+<[->>+[<]]>>>[>]<<-]<<<
]>>[<<+>>-]<[>+<-]>[>>]<,
]<<<[<+<]>[>.>]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment