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_nested_loops(self): # ++[->++[->++[->++[->++[->++<]<]<]<]<]>>>>>.[-]. self.run_ir([ir.ADD(2), ir.OPEN(), ir.SUB(1), ir.RIGHT(1), ir.ADD(2), ir.OPEN(), ir.SUB(1), ir.RIGHT(1), ir.ADD(2), ir.OPEN(), ir.SUB(1), ir.RIGHT(1), ir.ADD(2), ir.OPEN(), ir.SUB(1), ir.RIGHT(1), ir.ADD(2), ir.OPEN(), ir.SUB(1), ir.RIGHT(1), ir.ADD(2), ir.LEFT(1), ir.CLOSE(), ir.LEFT(1), ir.CLOSE(), ir.LEFT(1), ir.CLOSE(), ir.LEFT(1), ir.CLOSE(), ir.LEFT(1), ir.CLOSE(), ir.RIGHT(5), ir.OUTPUT(), ir.SET(0), ir.OUTPUT()], [], [64, 0]) # ++>+[[[[->]<]>]<].<. self.run_ir([ir.ADD(2), ir.RIGHT(1), ir.ADD(1), ir.OPEN(), ir.OPEN(), ir.OPEN(), ir.OPEN(), ir.SUB(1), ir.RIGHT(1), ir.CLOSE(), ir.LEFT(1), ir.CLOSE(), ir.RIGHT(1), ir.CLOSE(), ir.LEFT(1), ir.CLOSE(), ir.OUTPUT(), ir.LEFT(1), ir.OUTPUT()], [], [0, 2])
def test_clear_loops(self): self._run_and_check_ir( "+[-]+[+]", [ir.ADD(1), ir.SET(1), ir.OPEN(), ir.ADD(1), ir.CLOSE()], maxdepth=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_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_deep_nested_loops(self): # +[{many}-(>+.<]){many} self.run_ir([ir.ADD(1)] + ([ir.OPEN()] * self.MAX_NESTED_LOOPS) + [ir.SUB(1)] + ([ir.RIGHT(1), ir.ADD(1), ir.OUTPUT(), ir.LEFT(1), ir.CLOSE()] * self.MAX_NESTED_LOOPS), [], [i % 256 for i in range(1, self.MAX_NESTED_LOOPS + 1)], steps=50000000)
def ops(d): ops = [] # first use only LEFT(1) and RIGHT(1) to set up memory ops += ([ir.RIGHT(1)] * (d) + [ir.SET(12)] + [ir.RIGHT(1)] * (d) + [ir.SET(32)] + [ir.RIGHT(1)] * (d) + [ir.SET(17)] + [ir.LEFT(1)] * (d * 2)) # state: 0(d) *12 0(d - 1) 32 0(d - 1) 17 0 # run a [.>>>>] construct to move up the pointer. this # should output 12, 32 and 17. ops += ([ir.OPEN(), ir.OUTPUT(), ir.RIGHT(d), ir.CLOSE()]) # state: 0(d) 12 0(d - 1) 32 0(d - 1) 17 0(d - 1) *0 # same thing in other direction. should output 17,32 ops += [ir.LEFT(d)] ops += ([ir.OPEN(), ir.OUTPUT(), ir.LEFT(d), ir.CLOSE()]) return ops
def test_loop_elimination(self): self._run_and_check_ir("[.]", []) self._run_and_check_ir( "[+++]+[][+++].[-][+++]", [ir.ADD(1), ir.OPEN(), ir.CLOSE(), ir.OUTPUT(), ir.SET(0)], maxdepth=2) self._run_and_check_ir("[-][+++]", [])
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_nested_loops(self): 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), ir.OPEN(), ir.ADD(1), ir.CLOSE() ], maxdepth=4)
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_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)