First commit
This commit is contained in:
commit
62f93875c1
|
@ -0,0 +1,2 @@
|
||||||
|
__pycache__
|
||||||
|
*.swp
|
|
@ -0,0 +1 @@
|
||||||
|
Project to translate smallfuck (with infinite tape) to xed
|
|
@ -0,0 +1,24 @@
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to [http://unlicense.org]
|
|
@ -0,0 +1,178 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import enum
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
class writes(enum.Enum):
|
||||||
|
zero, one, same = range(3)
|
||||||
|
|
||||||
|
State = namedtuple('State', ['write', 'move', 'transitions'])
|
||||||
|
|
||||||
|
class SFParseError(Exception): pass
|
||||||
|
|
||||||
|
def parse_smallfuck(s):
|
||||||
|
def parse(s, index = 0, in_loop = False):
|
||||||
|
parsed = []
|
||||||
|
while True:
|
||||||
|
assert index <= len(s)
|
||||||
|
if index == len(s):
|
||||||
|
if in_loop:
|
||||||
|
raise SFParseError('Unexpected end of text (expected "]")')
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
c = s[index]
|
||||||
|
index += 1
|
||||||
|
|
||||||
|
if c == ']':
|
||||||
|
if in_loop:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise SFParseError('Unexpected "]"')
|
||||||
|
|
||||||
|
elif c == '[':
|
||||||
|
loop, index = parse(s, index, True)
|
||||||
|
parsed.append(loop)
|
||||||
|
|
||||||
|
elif c in ['<', '>', '*']:
|
||||||
|
parsed.append(c)
|
||||||
|
|
||||||
|
return parsed, index
|
||||||
|
|
||||||
|
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):
|
||||||
|
states = []
|
||||||
|
def add_state(write, move, transitions):
|
||||||
|
nonlocal states
|
||||||
|
assert write in writes
|
||||||
|
assert -1 <= move <= 1
|
||||||
|
assert len(transitions) == 2
|
||||||
|
|
||||||
|
index = len(states)
|
||||||
|
states.append(State(write, move, transitions))
|
||||||
|
|
||||||
|
return index
|
||||||
|
|
||||||
|
def worker(chunked, end = None, in_loop = False):
|
||||||
|
nonlocal states
|
||||||
|
|
||||||
|
ifunset = ifset = end
|
||||||
|
while len(chunked) > 0:
|
||||||
|
c = chunked[-1]
|
||||||
|
chunked = chunked[:-1]
|
||||||
|
|
||||||
|
if type(c) == list:
|
||||||
|
loop_test = add_state(writes.same, 0, [ifunset, None])
|
||||||
|
loop_body = worker(c, loop_test, True)
|
||||||
|
# We want the loop to repeat if the cell is set
|
||||||
|
states[loop_test].transitions[1] = loop_body
|
||||||
|
ifunset = ifset = loop_test
|
||||||
|
|
||||||
|
else:
|
||||||
|
change = '*' in c
|
||||||
|
move = (1 if '>' in c else 0) - (1 if '<' in c else 0)
|
||||||
|
|
||||||
|
assert change or move != 0
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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))
|
||||||
|
|
||||||
|
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}
|
||||||
|
|
||||||
|
shaken_sweep = 0
|
||||||
|
while shaken_sweep < len(shaken):
|
||||||
|
for i in range(len(shaken[shaken_sweep].transitions)):
|
||||||
|
index_states = shaken[shaken_sweep].transitions[i]
|
||||||
|
|
||||||
|
if index_states not in translations:
|
||||||
|
index_shaken = len(shaken)
|
||||||
|
shaken.append(states[index_states])
|
||||||
|
translations[index_states] = index_shaken
|
||||||
|
|
||||||
|
shaken[shaken_sweep].transitions[i] = translations[index_states]
|
||||||
|
|
||||||
|
shaken_sweep += 1
|
||||||
|
|
||||||
|
return shaken
|
||||||
|
|
||||||
|
def main():
|
||||||
|
program = input('program: ')
|
||||||
|
start_nonshaken, states_nonshaken = turingify(chunk(parse_smallfuck(program)))
|
||||||
|
shaken = tree_shake(start_nonshaken, states_nonshaken)
|
||||||
|
prettyprint_states(shaken)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in New Issue