def test_loop_elimination(self): # Obviously dead loops are skipped entirely # Loop open as first op => dead self._run_and_check_ir("[+,-.]", []) self._run_and_check_ir("[+[[[,]-]-]-.]", []) self._run_and_check_ir("[+[[[,]-]-]-.][-]", []) self._run_and_check_ir("[+[[[,]-]-]-.][+]", []) self._run_and_check_ir("[+[[[,]-]-]-.][>>>]", []) self._run_and_check_ir("[+[[[,]-]-]-.][>>>][.]", []) # Loop open after loop close => dead self._run_and_check_ir( "+[>][.]", [ir.ADD(1), ir.OPEN(), ir.RIGHT(1), ir.CLOSE()], maxdepth=2) # Loop open after SET(0) => dead self._run_and_check_ir(",[-][>>]", [ir.INPUT(), ir.SET(0)]) self._run_and_check_ir(",[-][>>][..]", [ir.INPUT(), ir.SET(0)]) self._run_and_check_ir( ",[-][>>].", [ir.INPUT(), ir.SET(0), ir.OUTPUT()]) # But open after SET(x) where x > 0 => alive self._run_and_check_ir( ",[-]+[>>]", [ir.INPUT(), ir.SET(1), ir.OPEN(), ir.RIGHT(2), ir.CLOSE()], maxdepth=2)
def test_copy_loop_multiple_separate_offsets(self): # 2 offsets self._run_and_check_ir( ',[->+>+<<]', [ir.INPUT(), ir.RMUL(1, 1), ir.RMUL(2, 1), ir.SET(0)]) self._run_and_check_ir( ',[->->-<<]', [ir.INPUT(), ir.RMUL(1, 255), ir.RMUL(2, 255), ir.SET(0)]) self._run_and_check_ir( ',[<+>>-<-]', [ir.INPUT(), ir.LMUL(1, 1), ir.RMUL(1, 255), ir.SET(0)]) # many but less than 127 offsets self._run_and_check_ir(',[-' + '<+' * 126 + '>' * 126 + ']', [ir.INPUT()] + [ir.LMUL(i, 1) for i in range(1, 127)] + [ir.SET(0)]) self._run_and_check_ir(',[<<<+>>>>>-<<<<<<<+>>>>>-<+>]', [ ir.INPUT(), ir.LMUL(3, 1), ir.RMUL(2, 255), ir.LMUL(5, 1), ir.LMUL(1, 1), ir.SET(0) ])
def test_target_386_linux(self): self._run_and_check_target(17, _386_LINUX, "@386_linux ") self._run_and_check_target(17, _386_LINUX, "@386_linux,,", ir=[ir.INPUT(), ir.INPUT()]) self._run_and_check_target(17, _386_LINUX, "@386_linux->", ir=[ir.SUB(1), ir.RIGHT(1)]) self._run_and_check_target(17, _386_LINUX, "@386_linux[foobar]") self._run_and_check_target(17, _386_LINUX, "@386_linux foobar")
def test_contraction_set_arithmetic(self): # Arithmetic ops after a SET() are collapsed into the SET() self._run_and_check_ir(',[-]--', [ir.INPUT(), ir.SET(254)]) self._run_and_check_ir(',[-]-', [ir.INPUT(), ir.SET(255)]) self._run_and_check_ir(',[-]', [ir.INPUT(), ir.SET(0)]) self._run_and_check_ir(',[-]+', [ir.INPUT(), ir.SET(1)]) self._run_and_check_ir(',[-]++', [ir.INPUT(), ir.SET(2)]) self._run_and_check_ir(',[+]--', [ir.INPUT(), ir.SET(254)]) self._run_and_check_ir(',[+]-', [ir.INPUT(), ir.SET(255)]) self._run_and_check_ir(',[+]', [ir.INPUT(), ir.SET(0)]) self._run_and_check_ir(',[+]+', [ir.INPUT(), ir.SET(1)]) self._run_and_check_ir(',[+]++', [ir.INPUT(), ir.SET(2)])
def test_nested_loops(self): # Loops can be nested # Loop depth is calculated self._run_and_check_ir("+[-[+<].]>[[[]+]+]+", [ ir.ADD(1), ir.OPEN(), ir.SUB(1), ir.OPEN(), ir.ADD(1), ir.LEFT(1), ir.CLOSE(), ir.OUTPUT(), ir.CLOSE(), ir.RIGHT(1), ir.OPEN(), ir.OPEN(), ir.OPEN(), ir.CLOSE(), ir.ADD(1), ir.CLOSE(), ir.ADD(1), ir.CLOSE(), ir.ADD(1) ], maxdepth=4) self._run_and_check_ir("+[[[[[[[[.]]]]],[[[]]]]]]", [ir.ADD(1)] + [ir.OPEN()] * 8 + [ir.OUTPUT()] + [ir.CLOSE()] * 5 + [ir.INPUT()] + [ir.OPEN()] * 3 + [ir.CLOSE()] * 6, maxdepth=9)
def test_clear_loops(self): # The [-] and [+] constructs become SET(0) # These are not included when calculating loop depth self._run_and_check_ir( ",[-].[+]", [ir.INPUT(), ir.SET(0), ir.OUTPUT(), ir.SET(0)], maxdepth=1)
def test_single_instruction(self): # Single brainfuck instructions can be valid programs self._run_and_check_ir(">", [ir.RIGHT(1)]) self._run_and_check_ir("<", [ir.LEFT(1)]) self._run_and_check_ir(",", [ir.INPUT()]) self._run_and_check_ir(".", [ir.OUTPUT()]) self._run_and_check_ir("+", [ir.ADD(1)]) self._run_and_check_ir("-", [ir.SUB(1)])
def test_single_instruction_with_comment(self): # Single instructions with comments can be valid programs self._run_and_check_ir("a>", [ir.RIGHT(1)]) self._run_and_check_ir("<a", [ir.LEFT(1)]) self._run_and_check_ir("a.a", [ir.OUTPUT()]) self._run_and_check_ir("aa,", [ir.INPUT()]) self._run_and_check_ir("+aa", [ir.ADD(1)]) self._run_and_check_ir("aa-aa", [ir.SUB(1)])
def test_basic_operations(self): # ,[->>++++++++<<]>>.[-].[-]+ self.run_ir([ir.INPUT(), ir.OPEN(), ir.SUB(1), ir.RIGHT(2), ir.ADD(8), ir.LEFT(2), ir.CLOSE(), ir.RIGHT(2), ir.OUTPUT(), ir.SET(0), ir.OUTPUT()], [8], [64, 0])
def test_cancellation_of_basic_instructions(self): # Two pairs of the basic brainfuck instructions are mutually # cancelling # Pairs of cancelling instructions are reduced self._run_and_check_ir("+-", []) self._run_and_check_ir("+-", []) self._run_and_check_ir("><", []) self._run_and_check_ir("<>", []) # Longer sequences of cancelling instructions are reduced self._run_and_check_ir("-+<>+-><", []) self._run_and_check_ir(".<<<<>>>>++++----.", [ir.OUTPUT(), ir.OUTPUT()]) # The cancelling instructions do not have to be immediately adjacent self._run_and_check_ir("+++-->><-+<-, <>-++-+-<><>.", [ir.INPUT(), ir.OUTPUT()]) self._run_and_check_ir(",>>+++>+--+<----+<<++>><<--", [ir.INPUT()])
def test_copy_loop_multiple_repeated_offsets(self): # repeated offsets result in multiple LMUL/RMUL self._run_and_check_ir(',[->+>++<+++>----<<]', [ ir.INPUT(), ir.RMUL(1, 1), ir.RMUL(2, 2), ir.RMUL(1, 3), ir.RMUL(2, 252), ir.SET(0) ])
def test_basic_operations(self): self._run_and_check_ir(">+.<-[,]", [ ir.RIGHT(1), ir.ADD(1), ir.OUTPUT(), ir.LEFT(1), ir.SUB(1), ir.OPEN(), ir.INPUT(), ir.CLOSE() ], maxdepth=2)
def test_all_instructions(self): # All instructions can live in the same program self._run_and_check_ir(">+.<-[,]", [ ir.RIGHT(1), ir.ADD(1), ir.OUTPUT(), ir.LEFT(1), ir.SUB(1), ir.OPEN(), ir.INPUT(), ir.CLOSE() ], maxdepth=2) self._run_and_check_ir("+,[->.]<", [ ir.ADD(1), ir.INPUT(), ir.OPEN(), ir.SUB(1), ir.RIGHT(1), ir.OUTPUT(), ir.CLOSE(), ir.LEFT(1) ], maxdepth=2)
def test_copy_loop_wrap_around(self): self._run_and_check_ir( ',[->' + '+' * 255 + '<]', [ir.INPUT(), ir.RMUL(1, 255), ir.SET(0)]) self._run_and_check_ir(',[->' + '+' * 256 + '<]', [ir.INPUT(), ir.SET(0)]) self._run_and_check_ir( ',[->' + '+' * 257 + '<]', [ir.INPUT(), ir.RMUL(1, 1), ir.SET(0)]) self._run_and_check_ir( ',[-<<' + '-' * 255 + '>>]', [ir.INPUT(), ir.LMUL(2, 1), ir.SET(0)]) self._run_and_check_ir(',[-<<' + '-' * 256 + '>>]', [ir.INPUT(), ir.SET(0)]) self._run_and_check_ir( ',[-<<' + '-' * 257 + '>>]', [ir.INPUT(), ir.LMUL(2, 255), ir.SET(0)])
def test_copy_loop_simple(self): # single cell copy loops self._run_and_check_ir( ',[->+<]', [ir.INPUT(), ir.RMUL(1, 1), ir.SET(0)]) self._run_and_check_ir( ',[->-<]', [ir.INPUT(), ir.RMUL(1, 255), ir.SET(0)]) self._run_and_check_ir( ',[-<+>]', [ir.INPUT(), ir.LMUL(1, 1), ir.SET(0)]) self._run_and_check_ir( ',[-<->]', [ir.INPUT(), ir.LMUL(1, 255), ir.SET(0)]) self._run_and_check_ir( ',[<->-]', [ir.INPUT(), ir.LMUL(1, 255), ir.SET(0)]) self._run_and_check_ir( ',[>+<-]', [ir.INPUT(), ir.RMUL(1, 1), ir.SET(0)])
def test_copy_loop_not_optimizeable(self): # loop must end where it started self._run_and_check_ir(',[->+]', [ ir.INPUT(), ir.OPEN(), ir.SUB(1), ir.RIGHT(1), ir.ADD(1), ir.CLOSE() ], maxdepth=2) self._run_and_check_ir(',[->+<<]', [ ir.INPUT(), ir.OPEN(), ir.SUB(1), ir.RIGHT(1), ir.ADD(1), ir.LEFT(2), ir.CLOSE() ], maxdepth=2) # must subtract 1 from cell 0 exactly once self._run_and_check_ir(',[->+<-]', [ ir.INPUT(), ir.OPEN(), ir.SUB(1), ir.RIGHT(1), ir.ADD(1), ir.LEFT(1), ir.SUB(1), ir.CLOSE() ], maxdepth=2) self._run_and_check_ir(',[+>+<--]', [ ir.INPUT(), ir.OPEN(), ir.ADD(1), ir.RIGHT(1), ir.ADD(1), ir.LEFT(1), ir.SUB(2), ir.CLOSE() ], maxdepth=2) self._run_and_check_ir(',[->+<+<->-]', [ ir.INPUT(), ir.OPEN(), ir.SUB(1), ir.RIGHT(1), ir.ADD(1), ir.LEFT(1), ir.ADD(1), ir.LEFT(1), ir.SUB(1), ir.RIGHT(1), ir.SUB(1), ir.CLOSE() ], maxdepth=2) # must be fewer than 127 < and 127 > self._run_and_check_ir(',[-' + '>' * 127 + '+' + '<' * 127 + ']', [ ir.INPUT(), ir.OPEN(), ir.SUB(1), ir.RIGHT(127), ir.ADD(1), ir.LEFT(127), ir.CLOSE() ], maxdepth=2) self._run_and_check_ir(',[-' + '<' * 127 + '+' + '>' * 127 + ']', [ ir.INPUT(), ir.OPEN(), ir.SUB(1), ir.LEFT(127), ir.ADD(1), ir.RIGHT(127), ir.CLOSE() ], maxdepth=2)
def test_set(self): # simple cases self._run_and_check_ir(',[-]-', [ir.INPUT(), ir.SET(255)]) self._run_and_check_ir(',[-]', [ir.INPUT(), ir.SET(0)]) self._run_and_check_ir(',[-]+', [ir.INPUT(), ir.SET(1)]) self._run_and_check_ir(',[-]++', [ir.INPUT(), ir.SET(2)]) self._run_and_check_ir(',[-]+++', [ir.INPUT(), ir.SET(3)]) self._run_and_check_ir(',[-]-', [ir.INPUT(), ir.SET(255)]) self._run_and_check_ir(',[-]--', [ir.INPUT(), ir.SET(254)]) self._run_and_check_ir(',[-]---', [ir.INPUT(), ir.SET(253)]) # overflow self._run_and_check_ir(',[-]' + '+' * 255, [ir.INPUT(), ir.SET(255)]) self._run_and_check_ir(',[-]' + '+' * 256, [ir.INPUT(), ir.SET(0)]) self._run_and_check_ir(',[-]' + '+' * 257, [ir.INPUT(), ir.SET(1)]) self._run_and_check_ir(',[-]' + '-' * 255, [ir.INPUT(), ir.SET(1)]) self._run_and_check_ir(',[-]' + '-' * 256, [ir.INPUT(), ir.SET(0)]) self._run_and_check_ir(',[-]' + '-' * 257, [ir.INPUT(), ir.SET(255)])
def test_eof_behaviour(self): # do no-change on EOF # ++++++++++,. self.run_ir([ir.ADD(10), ir.INPUT(), ir.OUTPUT()], [], [10])
def test_cancellation(self): self._run_and_check_ir("+++-->><-+<-, <>-++-+-<><>.", [ir.INPUT(), ir.OUTPUT()]) self._run_and_check_ir("-+<>+-><", []) self._run_and_check_ir(".<<<<>>>>++++----.", [ir.OUTPUT(), ir.OUTPUT()])