Use a simple IR that should also produce better code
This commit is contained in:
parent
62f93875c1
commit
e510661d9c
176
sf2xed.py
176
sf2xed.py
|
@ -2,10 +2,10 @@
|
|||
import enum
|
||||
from collections import namedtuple
|
||||
|
||||
class writes(enum.Enum):
|
||||
zero, one, same = range(3)
|
||||
class actions(enum.Enum):
|
||||
change, left, right, nothing = range(4)
|
||||
|
||||
State = namedtuple('State', ['write', 'move', 'transitions'])
|
||||
State = namedtuple('State', ['action', 'transitions', 'reachable'])
|
||||
|
||||
class SFParseError(Exception): pass
|
||||
|
||||
|
@ -41,138 +41,108 @@ def parse_smallfuck(s):
|
|||
parsed, index = parse(s)
|
||||
return parsed
|
||||
|
||||
def chunk(parsed):
|
||||
def append_change_move():
|
||||
nonlocal change, move, chunked
|
||||
while change or move != 0:
|
||||
chunk = '*' if change else ''
|
||||
change = False
|
||||
|
||||
if move < 0:
|
||||
chunk += '<'
|
||||
move += 1
|
||||
elif move > 0:
|
||||
chunk += '>'
|
||||
move -= 1
|
||||
|
||||
if chunk != '':
|
||||
chunked.append(chunk)
|
||||
|
||||
chunked = []
|
||||
change = False
|
||||
move = 0
|
||||
for c in parsed:
|
||||
if type(c) == list:
|
||||
append_change_move()
|
||||
chunked.append(chunk(c))
|
||||
|
||||
elif c == '*':
|
||||
if move == 0:
|
||||
change = not change
|
||||
else:
|
||||
append_change_move()
|
||||
change = True
|
||||
|
||||
elif c == '<':
|
||||
move -= 1
|
||||
|
||||
elif c == '>':
|
||||
move += 1
|
||||
|
||||
else:
|
||||
assert not "Unreachable"
|
||||
|
||||
append_change_move()
|
||||
|
||||
return chunked
|
||||
|
||||
def turingify(chunked):
|
||||
def turingify(parsed):
|
||||
states = []
|
||||
def add_state(write, move, transitions):
|
||||
def add_state(action, transitions):
|
||||
nonlocal states
|
||||
assert write in writes
|
||||
assert -1 <= move <= 1
|
||||
assert action in actions
|
||||
assert len(transitions) == 2
|
||||
|
||||
index = len(states)
|
||||
states.append(State(write, move, transitions))
|
||||
states.append(State(action, transitions, [False, False]))
|
||||
|
||||
return index
|
||||
|
||||
def worker(chunked, end = None, in_loop = False):
|
||||
def worker(parsed, end = None):
|
||||
nonlocal states
|
||||
|
||||
ifunset = ifset = end
|
||||
while len(chunked) > 0:
|
||||
c = chunked[-1]
|
||||
chunked = chunked[:-1]
|
||||
while len(parsed) > 0:
|
||||
c = parsed[-1]
|
||||
parsed = parsed[:-1]
|
||||
|
||||
if type(c) == list:
|
||||
loop_test = add_state(writes.same, 0, [ifunset, None])
|
||||
loop_body = worker(c, loop_test, True)
|
||||
loop_test = add_state(actions.nothing, [end, None])
|
||||
loop_body = worker(c, loop_test)
|
||||
# We want the loop to repeat if the cell is set
|
||||
states[loop_test].transitions[1] = loop_body
|
||||
ifunset = ifset = loop_test
|
||||
end = loop_test
|
||||
|
||||
elif c == '*':
|
||||
end = add_state(actions.change, [end, end])
|
||||
|
||||
elif c == '<':
|
||||
end = add_state(actions.left, [end, end])
|
||||
|
||||
elif c == '>':
|
||||
end = add_state(actions.right, [end, end])
|
||||
|
||||
else:
|
||||
change = '*' in c
|
||||
move = (1 if '>' in c else 0) - (1 if '<' in c else 0)
|
||||
assert not "Unreachable"
|
||||
|
||||
assert change or move != 0
|
||||
return end
|
||||
|
||||
if change:
|
||||
# Create a pair of states
|
||||
new_ifunset = add_state(writes.one, move, [ifunset, ifset])
|
||||
new_ifset = add_state(writes.zero, move, [ifunset, ifset])
|
||||
|
||||
ifunset, ifset = new_ifunset, new_ifset
|
||||
|
||||
else:
|
||||
# Create only one state
|
||||
ifunset = ifset = add_state(writes.same, move, [ifunset, ifset])
|
||||
|
||||
if in_loop:
|
||||
# At the start of the loop body, the cell will always be set
|
||||
return ifset
|
||||
else:
|
||||
# At the start of the program, the cell will always be unset
|
||||
return ifunset
|
||||
|
||||
return worker(chunked), states
|
||||
return worker(parsed), states
|
||||
|
||||
def prettyprint_states(states):
|
||||
for i in range(len(states)):
|
||||
state = states[i]
|
||||
write = {writes.zero: '0 ', writes.one: '1 ', writes.same: ''}[state.write]
|
||||
move = {-1: '< ', 0: '', 1: '> '}[state.move]
|
||||
print('%i: %s%s%s' % (i, write, move, state.transitions))
|
||||
action = {actions.change: '* ', actions.left: '< ', actions.right: '> ', actions.nothing: ''}[state.action]
|
||||
if state.reachable == [True, True]:
|
||||
reachable = '(*)'
|
||||
elif state.reachable == [True, False]:
|
||||
reachable = '(0)'
|
||||
elif state.reachable == [False, True]:
|
||||
reachable = '(1)'
|
||||
elif state.reachable == [False, False]:
|
||||
reachable = '(!)'
|
||||
else:
|
||||
reachable = ''
|
||||
print('%4i%s: %s%s' % (i, reachable, action, state.transitions))
|
||||
|
||||
def tree_shake(start, states):
|
||||
# Since we always end up with start at index 0, no need to create shaken_start
|
||||
shaken = [states[start]]
|
||||
translations = {start: 0, None: None}
|
||||
def reachability(start, states):
|
||||
def copy_state(index):
|
||||
state = states[index]
|
||||
action = state.action
|
||||
transitions = state.transitions[:]
|
||||
reachable = [False if i is None else i for i in state.reachable]
|
||||
return State(action, transitions, reachable)
|
||||
|
||||
shaken_sweep = 0
|
||||
while shaken_sweep < len(shaken):
|
||||
for i in range(len(shaken[shaken_sweep].transitions)):
|
||||
index_states = shaken[shaken_sweep].transitions[i]
|
||||
# Since we always end up with start at index 0, no need to create processed_start
|
||||
processed = [copy_state(start)]
|
||||
processed[0].reachable[0] = True
|
||||
processed[0].reachable[1] = True
|
||||
translations = {start: 0}
|
||||
|
||||
processed_sweep = 0
|
||||
while processed_sweep < len(processed):
|
||||
for i in range(len(processed[processed_sweep].transitions)):
|
||||
index_states = processed[processed_sweep].transitions[i]
|
||||
|
||||
# Nothing to be done if we point to halt state
|
||||
if index_states is None:
|
||||
continue
|
||||
|
||||
# Copy the state to processed if it isn't already there
|
||||
if index_states not in translations:
|
||||
index_shaken = len(shaken)
|
||||
shaken.append(states[index_states])
|
||||
translations[index_states] = index_shaken
|
||||
index_processed = len(processed)
|
||||
processed.append(copy_state(index_states))
|
||||
translations[index_states] = index_processed
|
||||
|
||||
shaken[shaken_sweep].transitions[i] = translations[index_states]
|
||||
# Fix transition to the state
|
||||
processed[processed_sweep].transitions[i] = translations[index_states]
|
||||
|
||||
shaken_sweep += 1
|
||||
# Mark how we got to the state
|
||||
processed[translations[index_states]].reachable[i] = True
|
||||
|
||||
return shaken
|
||||
processed_sweep += 1
|
||||
|
||||
return processed
|
||||
|
||||
def main():
|
||||
program = input('program: ')
|
||||
start_nonshaken, states_nonshaken = turingify(chunk(parse_smallfuck(program)))
|
||||
shaken = tree_shake(start_nonshaken, states_nonshaken)
|
||||
prettyprint_states(shaken)
|
||||
start_noreachability, states_noreachability = turingify(parse_smallfuck(program))
|
||||
processed = reachability(start_noreachability, states_noreachability)
|
||||
prettyprint_states(processed)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
Loading…
Reference in New Issue