Created
November 8, 2018 09:55
-
-
Save Vftdan/0e582d07d82489846e2134e1a1d1fbb4 to your computer and use it in GitHub Desktop.
Turing machine interpreter. Usage: `python turingm.py program.tsv inputs.txt`
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
class ParsingException(BaseException): | |
pass | |
class TuringException(BaseException): | |
pass | |
header = None | |
data = dict() | |
dataptr = 0 | |
states = dict() | |
stateptr = None | |
stateinit = None | |
stepnum = 0 | |
MAX_STEPNUM = 3000 | |
DEBUG = False | |
def log(*args, **kwargs): | |
if DEBUG: | |
print(*args, **kwargs) | |
def norm_line(ln): | |
if ln[0] == '\ufeff': | |
ln = ln[1:] | |
while ln[-1] in '\r\n': | |
ln = ln[:-1] | |
if len(ln) == 0: | |
return ln | |
return ln | |
def parse_prog_line(ln): | |
global stateinit | |
ln = norm_line(ln).split('\t') | |
p = dict() | |
for i in range(len(ln)): | |
col = header[i] | |
if len(col) == 1 and ln[i] != 'halt': | |
p[col] = ln[i].split(' ') | |
else: | |
p[col] = ln[i] | |
name = p['Q\\A'] | |
if name in states: | |
raise ParsingException('State "{0}" already exists!'.format(name)) | |
states[name] = p | |
if stateinit == None: | |
stateinit = name | |
def run_step(): | |
global dataptr, stateptr, stepnum | |
stepnum += 1 | |
if stepnum > MAX_STEPNUM: | |
raise TuringException('Step limit!') | |
value = data.get(dataptr, '_') | |
log('<', stateptr, ',', value, '>') | |
if value == ' ': | |
value = '_' | |
if value not in header: | |
raise TuringException('Invalid input!') | |
st = states.get(stateptr, None) | |
if st == None: | |
raise TuringException('State "{0}" is not defined!'.format(stateptr)) | |
act = st.get(value) | |
if act != 'halt' and len(act) != 3: | |
raise TuringException('State "{0}" cannot proceed value "{1}"!'.format(stateptr, value)) | |
if len(act) == 3: | |
if act[0] == value and act[1] == 'n' and act[2] == stateptr: | |
act = 'halt' | |
if act == 'halt': | |
return False | |
outp, direct, target = act | |
if len(outp) != 1 or outp not in header: | |
raise ParsingException('Value "{0}" is not valid!'.format(outp)) | |
data[dataptr] = outp | |
if direct == 'l': | |
dataptr -= 1 | |
elif direct == 'r': | |
dataptr += 1 | |
elif direct != 'n': | |
raise ParsingException('Direction "{0}" is not valid!'.format(direct)) | |
stateptr = target | |
return True | |
def print_data(): | |
k = list(sorted(data.keys())) | |
try: | |
while data[k[0]] == '_': | |
k = k[1:] | |
while data[k[-1]] == '_': | |
k = k[:-1] | |
except IndexError: | |
print('_\n^') | |
return | |
for i in range(k[0], k[-1] + 1): | |
print(data.get(i, '_'), end='') | |
sp = dataptr - k[0] | |
if sp < 0: | |
sp = 0 | |
print('\n' + ' ' * sp + '^') | |
def run(inp): | |
global dataptr, stateptr, stepnum | |
for il in inp: | |
ilt = tuple(il) | |
data.clear() | |
dataptr = 0 | |
stateptr = stateinit | |
stepnum = 0 | |
for i in range(len(ilt)): | |
data[i] = ilt[i] | |
try: | |
while run_step(): | |
pass | |
print('Input:', il, 'Output:', sep='\n') | |
print_data() | |
except BaseException as e: | |
print('Error! Current data:') | |
print_data() | |
print('State = "{0}"'.format(stateptr)) | |
raise e | |
prog = map(norm_line, open(sys.argv[1], 'r').readlines()) | |
inp = map(norm_line, open(sys.argv[2], 'r').readlines()) | |
header = tuple(next(prog).split('\t')) | |
tuple(map(parse_prog_line, prog)) | |
run(inp) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment