#!/usr/bin/env python3 # Adapted from Eli Bendersky's simple BF interpreter. import sys # for accessing command-line args import time # for timing execution in verbose mode MEMORY_SIZE = 30000 # ----------------------------------------------------- # This functions parses a program string, # filtering out all non-command characters. # ----------------------------------------------------- def parse_bf(src_str): program = '' for c in src_str: if c in '><+-.,[]': program += c return program # ----------------------------------------------------- # The interpreter # ----------------------------------------------------- def interpret(program, is_verbose): memory = [0] * MEMORY_SIZE pc = 0 data_ptr = 0 while pc < len(program): instruction = program[pc] if instruction == '>': data_ptr += 1 elif instruction == '<': data_ptr -= 1 elif instruction == '+': memory[data_ptr] += 1 elif instruction == '-': memory[data_ptr] -= 1 elif instruction == '.': print(chr(memory[data_ptr]), end='') elif instruction == ',': memory[data_ptr] = ord(sys.stdin.read(1)) elif instruction == '[': if memory[data_ptr] == 0: bracket_nesting = 1; saved_pc = pc; while bracket_nesting > 0 and pc < len(program) - 1: pc += 1 if program[pc] == ']': bracket_nesting -= 1 elif program[pc] == '[': bracket_nesting += 1 if bracket_nesting != 0: raise ValueError('unmatched [ at pc = ', saved_pc) elif instruction == ']': if memory[data_ptr] != 0: bracket_nesting = 1; saved_pc = pc; while bracket_nesting > 0 and pc > 0: pc -= 1 if program[pc] == '[': bracket_nesting -= 1 elif program[pc] == ']': bracket_nesting += 1 if bracket_nesting != 0: raise ValueError('unmatched ] at pc = ', saved_pc) else: msg = 'bad character {} at pc = {}'.format(instruction, pc) raise ValueError(msg) pc += 1 # Done running the program. Dump state if verbose. if is_verbose: print_state(pc, data_ptr, memory) # ----------------------------------------------------- # The main function takes the command-line args as an # array. args[1] is the filename and args[2] is an # optional '-v' flag to turn on verbose mode. # ----------------------------------------------------- def main(args): bf_file_path = args[1] if len(args) > 2 and args[2] == '-v': is_verbose = True else: is_verbose = False if is_verbose: start_time = time.time() print('[>] Parsing: {}'.format(bf_file_path)) srcfile = open(bf_file_path, 'r') program = parse_bf(srcfile.read()) if is_verbose: print('[<] Done parsing') print('Parsing took: {}s'.format( time.time() - start_time )) print() print('Length of program: {} chars'.format( len(program) )) print('Program: {}'.format( program )) print() print('[>] Running {}: '.format(args[0])) start_time = time.time() interpret(program, is_verbose) if is_verbose: print('[<] Done running') print('Execution took: {}s'.format( time.time() - start_time )) print() # -------------------------------------------------------- # These functions print state of machine, for verbose mode # -------------------------------------------------------- def print_state(pc, data_ptr, memory): print() print('* pc = ', pc ) print('* data_ptr = ', data_ptr) print_memory(memory) print() def print_memory(memory): print('* Nonzero locations in memory:') pcount = 0 for i in range(len(memory)): if memory[i] > 0: print('[{:5d}] = {:5d} '.format(i, memory[i]), end='') pcount += 1 if pcount > 0 and pcount % 4 == 0: print() # ----------------------------------------------------- # These lines call the main function with one of three # test programs, or with the command-line args # ----------------------------------------------------- if __name__ == "__main__": # is_verbose = '-v' # program_name = 'hello-world.bf' # 'cell-size.bf' 'factor.bf' # main(['simple', program_name, is_verbose]) main(sys.argv)