""" yvtm.py - yaml virtual turing machine Usage: $ python yvtm.py busybeaver3.yvtm <0> : one 1 <0> : two 1 0 <0> : three 1 <0> 1 : three <1> 1 1 : three <0> 1 1 1 : one 1 <1> 1 1 : two 1 1 <1> 1 : two 1 1 1 <1> : two 1 1 1 1 <0> : two 1 1 1 1 0 <0> : three 1 1 1 1 <0> 1 : three 1 1 1 <1> 1 1 : three 1 1 <1> 1 1 1 : one 1 1 1 <1> 1 1 : HALT ** HALT ** (No matching rule) $ python yvtm.py -help yvtm.py : YAML Virtual Turing Machine in python Usage: python yvtm.py [-quiet] [-help] [-tape=*] [-steps=*] filename.yvtm $ python yvtm.py -quiet busybeaver3.yvtm 1 1 1 <1> 1 1 : HALT $ python yvtm.py -tape=foo busybeaver3.yvtm o o : one ** HALT ** (No matching rule) See the comments in ./busybeaver3.yvtm for the YAML machine specification. History 2019-10-08 updated to python3.7 2016-10-03 first working version 2016-10-10 added -tape option Jim Mahoney | MIT License """ import sys, yaml def tape_state_string(tape, position, state): """ Return a string representation of current turing machine status """ line = " " for i in range(len(tape)): if i == position: line += "<{}>".format(tape[i]) else: line += " {} ".format(tape[i]) line += " : {}".format(state) return line def run(turing, verbose=True, max_steps=10000): """ Simulate a Turing machine, printing each step if verbose is True. """ offset = {'right': 1, 'left': -1, 'stay': 0} # Initialize the machine. state = turing['start'] # e.g. 'start' tape = list(turing['tape']) # e.g. ['0', '0', '1'] position = 0 # position of read head on tape # Convert the machine table into a more convenient dictionary form. rules = {} for rule in turing['machine']: try: assert len(rule) == 5 except AssertionError: print("Oops - the rule {} doesn't have 5 parts.".format(rule)) return rule = map(str, rule) (this_state, read_symbol, next_state, write_symbol , move) = rule if not this_state in rules: rules[this_state] = {} rules[this_state][read_symbol] = {'next': next_state, 'write': write_symbol, 'move': move } # Run the machine. steps = 0 while True: steps += 1 if verbose: print(tape_state_string(tape, position, state)) if state in turing['accept']: if verbose: print(" ** Accept **") break if state in turing['reject']: if verbose: print(" ** Reject **") break try: update = {} symbol = tape[position] for what in ('next', 'write', 'move'): update[what] = rules[state][symbol][what] except KeyError: if verbose: print(" ** HALT ** (No matching rule)") break try: state = update['next'] tape[position] = update['write'] position += offset[update['move']] except KeyError: if verbose: print(" ** HALT ** (Movement {} not recognized)".format( update['move'])) break while position < 0: # moved off left edge of tape tape = [ turing['blank'] ] + tape position += 1 while position >= len(tape): # moved off right edge of tape tape = tape + [ turing['blank'] ] if steps >= max_steps: print(" ** STOPPED ** (maximum steps {} reached)".format(steps)) break if not verbose: print(tape_state_string(tape, position, state)) def get_options(): """ Return (file, verbose, tape) from command line invocation where tape=None if it isn't set there. Or print the help message and exit if -help or no filename. """ help = " yvtm.py : YAML Virtual Turing Machine in python \n" + \ " Usage: python yvtm.py [-quiet] [-help] [tape=...] filename.yvtm " verbose = True # default values tape = None # steps = 10000 # for arg in sys.argv: if arg[:2] == '-h' or arg[:3] == '--h': # i.e. -help print(help) sys.exit() if arg[:2] == '-q' or arg[:3] == '--q': # i.e. -quiet verbose = False if arg[:2] == '-t' or arg[:3] == '--t': # i.e. -tape=00110 tapestring = arg.split('=') if len(tapestring) == 2: tape = tapestring[-1] if arg[:2] == '-s' or arg[:3] == '--s': # i.e. -steps=100000 stepsstring = arg.split('=') if len(stepsstring) == 2: steps = int(stepsstring[-1]) if len(sys.argv) < 2: print(help) sys.exit() return (sys.argv[-1], verbose, tape, steps) def main(): (filename, verbose, tape, steps) = get_options() turing = yaml.load(open(filename)) if tape != None: turing['tape'] = tape run(turing, verbose=verbose, max_steps=steps) if __name__ == '__main__': import doctest doctest.testmod() main()