From 09977f8fede8aa1353180aa5c6a146843b09cab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juhani=20Krekel=C3=A4?= Date: Thu, 12 Jul 2018 17:34:24 +0300 Subject: [PATCH] Produce xed --- README | 12 ++++- sf2xed.py | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 148 insertions(+), 2 deletions(-) diff --git a/README b/README index f863fd8..8e5a17f 100644 --- a/README +++ b/README @@ -1 +1,11 @@ -Project to translate smallfuck (with infinite tape) to xed +Project to translate smallfuck (with unbounded to the right tape) to xed. + +The command created by bf2xed.py produces a replacement command that advances +the program execution by one step each time it is executed. + +To produce a tape usable by the step command, input your tape in the form: + +0 0 ^0 1 0 + +Which says there is a tape with 5 cells, second from right being 1 and all +others being 0, and the tape head is in the middle of this tape diff --git a/sf2xed.py b/sf2xed.py index d15f190..51f7e53 100644 --- a/sf2xed.py +++ b/sf2xed.py @@ -138,11 +138,147 @@ def reachability(start, states): return processed +def string_id(state, value, processed): + def intlog(num, base): + # This is floor(log_{base} num) + assert num > 0 + result = 0 + while num > base - 1: + num = num // base + result += 1 + return result + + assert value == 0 or value == 1 + assert state is None or 0 <= state < len(processed) + + # One for each (cell value, state) combination and one for the cell values without a state + max_num = 2 * len(processed) + 2 - 1 + # Base 62 because we have 0-9A-Za-y, +1 because the intlog is one less than the digits needed + width = intlog(max_num, 61) + 1 + + if state is not None: + num = 2 * (state + 1) + value + else: + num = value + + digits = [] + + while num > 0: + digit = num % 61 + num = num // 61 + + if digit < 10: + digits.append(chr(ord('0') + digit)) + elif digit < 36: + digits.append(chr(ord('A') + digit - 10)) + else: + digits.append(chr(ord('a') + digit - 36)) + + string = ''.join(reversed(digits)) + + # Pad to width + string = '0' * (width - len(string)) + string + + return string + +def create_replacements(processed): + replacements = [] + + for index in range(len(processed)): + state = processed[index] + + if state.action == actions.nothing: + for value in [0, 1]: + if state.reachable[value]: + start = string_id(index, value, processed) + end = string_id(state.transitions[value], value, processed) + replacements.append((start, end)) + + elif state.action == actions.change: + for original_value in [0, 1]: + if state.reachable[original_value]: + changed_value = original_value ^ 1 + start = string_id(index, original_value, processed) + end = string_id(state.transitions[changed_value], changed_value, processed) + replacements.append((start, end)) + + elif state.action == actions.left: + for this_value in [0, 1]: + if state.reachable[this_value]: + for left_value in [0, 1]: + this_start = string_id(index, this_value, processed) + left_start = string_id(None, left_value, processed) + start = '%s %s' % (left_start, this_start) + + this_end = string_id(None, this_value, processed) + left_end = string_id(state.transitions[left_value], left_value, processed) + end = '%s %s' % (left_end, this_end) + + replacements.append((start, end)) + + elif state.action == actions.right: + for this_value in [0, 1]: + if state.reachable[this_value]: + for right_value in [0, 1, None]: + this_start = string_id(index, this_value, processed) + if right_value is not None: + right_start = string_id(None, right_value, processed) + else: + right_start = 'z' + start = '%s %s' % (this_start, right_start) + + this_end = string_id(None, this_value, processed) + if right_value is not None: + right_end = string_id(state.transitions[right_value], right_value, processed) + else: + right_end = string_id(state.transitions[0], 0, processed) + ' z' + end = '%s %s' % (this_end, right_end) + + replacements.append((start, end)) + + else: + assert not "Unreachable" + + return replacements + +def xedify(replacements): + replaced = '|'.join(start for start, end in replacements) + replacement = '|'.join(end for start, end in replacements) + + return 'x/%s/%s/' % (replaced, replacement) + +def process_tape(tape, processed): + assert all(i in ['0', '1', '^0', '^1'] for i in tape) + processed_tape = [] + for i in tape: + if i == '0': + processed_tape.append(string_id(None, 0, processed)) + elif i == '1': + processed_tape.append(string_id(None, 1, processed)) + elif i == '^0': + processed_tape.append(string_id(0, 0, processed)) + elif i == '^1': + processed_tape.append(string_id(0, 1, processed)) + else: + assert not "Unreachable" + + return ' '.join(processed_tape) + ' z' + def main(): program = input('program: ') + start_noreachability, states_noreachability = turingify(parse_smallfuck(program)) processed = reachability(start_noreachability, states_noreachability) - prettyprint_states(processed) + + replacements = create_replacements(processed) + print(xedify(replacements)) + + print('\nSend empty to exit. ^0/^1 for initial tape head position') + while True: + tape = input('tape: ') + if tape == '': break + tape = tape.split(' ') + print(process_tape(tape, processed)) if __name__ == '__main__': main()