def test_mul(self): self.run_ir([ir.ADD(4), ir.RMUL(2, 4), ir.RIGHT(2), ir.OUTPUT()], [], [16]) self.run_ir([ir.ADD(4), ir.RIGHT(2), ir.ADD(7), ir.LMUL(2, 254), ir.LEFT(2), ir.OUTPUT()], [], [246]) self.run_ir([ir.RIGHT(127), ir.SUB(2), ir.LMUL(127, 1), ir.LEFT(127), ir.OUTPUT()], [], [254]) self.run_ir([ir.RIGHT(127), ir.SUB(2), ir.LMUL(127, 12), ir.LEFT(127), ir.OUTPUT()], [], [232]) self.run_ir([ir.RIGHT(127), ir.SUB(2), ir.LMUL(127, 255), ir.LEFT(127), ir.OUTPUT()], [], [2]) self.run_ir([ir.SET(3), ir.RMUL(127, 1), ir.RIGHT(127), ir.OUTPUT()], [], [3]) self.run_ir([ir.SET(3), ir.RMUL(127, 12), ir.RIGHT(127), ir.OUTPUT()], [], [36]) self.run_ir([ir.SET(3), ir.RMUL(127, 255), ir.RIGHT(127), ir.OUTPUT()], [], [253]) self.run_ir([ir.ADD(12), ir.RMUL(1, 12), ir.RIGHT(1), ir.OUTPUT()], [], [144]) self.run_ir([ir.RIGHT(1), ir.ADD(12), ir.LMUL(1, 12), ir.LEFT(1), ir.OUTPUT()], [], [144])
def test_contraction_overflow_movement(self): # Argument overflow on movement operations only add more # operations self._run_and_check_ir(['>'] * 255, [ir.RIGHT(255)]) self._run_and_check_ir(['>'] * 256, [ir.RIGHT(255), ir.RIGHT(1)]) self._run_and_check_ir(['>'] * 257, [ir.RIGHT(255), ir.RIGHT(2)]) self._run_and_check_ir(['<'] * 255, [ir.LEFT(255)]) self._run_and_check_ir(['<'] * 256, [ir.LEFT(255), ir.LEFT(1)]) self._run_and_check_ir(['<'] * 257, [ir.LEFT(255), ir.LEFT(2)])
def test_contraction(self): self._run_and_check_ir("+++", [ir.ADD(3)]) self._run_and_check_ir(['+'] * 255, [ir.ADD(255)]) self._run_and_check_ir(['+'] * 256, []) self._run_and_check_ir("---", [ir.SUB(3)]) self._run_and_check_ir(['-'] * 255, [ir.SUB(255)]) self._run_and_check_ir(['-'] * 256, []) self._run_and_check_ir("<<<", [ir.LEFT(3)]) self._run_and_check_ir(['<'] * 128, [ir.LEFT(127), ir.LEFT(1)]) self._run_and_check_ir(">>>", [ir.RIGHT(3)]) self._run_and_check_ir(['>'] * 128, [ir.RIGHT(127), ir.RIGHT(1)])
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_contraction_simple_cases(self): # Sequences of multiple brainfuck instructions can be # contracted into single bytecode operations self._run_and_check_ir("++", [ir.ADD(2)]) self._run_and_check_ir("+++", [ir.ADD(3)]) self._run_and_check_ir("+" * 128, [ir.ADD(128)]) self._run_and_check_ir("--", [ir.SUB(2)]) self._run_and_check_ir("---", [ir.SUB(3)]) self._run_and_check_ir("-" * 128, [ir.SUB(128)]) self._run_and_check_ir("<<", [ir.LEFT(2)]) self._run_and_check_ir("<<<", [ir.LEFT(3)]) self._run_and_check_ir("<" * 128, [ir.LEFT(128)]) self._run_and_check_ir(">>", [ir.RIGHT(2)]) self._run_and_check_ir(">>>", [ir.RIGHT(3)]) self._run_and_check_ir(">" * 128, [ir.RIGHT(128)])
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_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_left_right(self): # the 386_linux backend outputs different machine instructions # for LEFT and RIGHT depending on the size of the # argument. this tests the edge cases. ops = [] # set up memory to 0,1,2,...,255,0,0,0,... for i in range(256): ops.append(ir.SET(i)) ops.append(ir.RIGHT(1)) # jump all the "edgy" distances left and right and output edges = [1, 2, 3, 4, 126, 127, 128, 129, 130, 131, 253, 254, 255] for i in edges: ops.append(ir.LEFT(i)) ops.append(ir.OUTPUT()) ops.append(ir.RIGHT(i)) ops.append(ir.OUTPUT()) # expected output is 0 alternated with all the edges expected = [] for i in edges: expected.append(256 - i) expected.append(0) self.run_ir(ops, [], expected)
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_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 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 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_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_cancellation_partial(self): # Cancellation can be partial, leaving part of the cancelling # instructions behind. # The basic brainfuck instructions self._run_and_check_ir("++++--", [ir.ADD(2)]) self._run_and_check_ir("--++++", [ir.ADD(2)]) self._run_and_check_ir("++----", [ir.SUB(2)]) self._run_and_check_ir("----++", [ir.SUB(2)]) self._run_and_check_ir(">>>><<", [ir.RIGHT(2)]) self._run_and_check_ir("<<>>>>", [ir.RIGHT(2)]) self._run_and_check_ir(">><<<<", [ir.LEFT(2)]) self._run_and_check_ir("<<<<>>", [ir.LEFT(2)]) # Cases with argument overflow, i.e. >255 of the same # instructions in a row, are also cancelled correctly. self._run_and_check_ir(['>'] * 258 + ['<', '<'], [ir.RIGHT(255), ir.RIGHT(1)]) self._run_and_check_ir(['>'] * 258 + ['<', '<', '<'], [ir.RIGHT(255)]) self._run_and_check_ir(['>'] * 258 + ['<', '<', '<', '<'], [ir.RIGHT(254)]) self._run_and_check_ir(['<'] * 258 + ['>', '>'], [ir.LEFT(255), ir.LEFT(1)]) self._run_and_check_ir(['<'] * 258 + ['>', '>', '>'], [ir.LEFT(255)]) self._run_and_check_ir(['<'] * 258 + ['>', '>', '>', '>'], [ir.LEFT(254)])
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_contraction_and_cancellation(self): # basic cases self._run_and_check_ir("++++--", [ir.ADD(2)]) self._run_and_check_ir("--++++", [ir.ADD(2)]) self._run_and_check_ir("++----", [ir.SUB(2)]) self._run_and_check_ir("----++", [ir.SUB(2)]) self._run_and_check_ir(">>>><<", [ir.RIGHT(2)]) self._run_and_check_ir("<<>>>>", [ir.RIGHT(2)]) self._run_and_check_ir(">><<<<", [ir.LEFT(2)]) self._run_and_check_ir("<<<<>>", [ir.LEFT(2)]) # cases triggering argument overflow self._run_and_check_ir(['>'] * 130 + ['<', '<'], [ir.RIGHT(127), ir.RIGHT(1)]) self._run_and_check_ir(['>'] * 130 + ['<', '<', '<'], [ir.RIGHT(127)]) self._run_and_check_ir(['>'] * 130 + ['<', '<', '<', '<'], [ir.RIGHT(126)]) self._run_and_check_ir(['<'] * 130 + ['>', '>'], [ir.LEFT(127), ir.LEFT(1)]) self._run_and_check_ir(['<'] * 130 + ['>', '>', '>'], [ir.LEFT(127)]) self._run_and_check_ir(['<'] * 130 + ['>', '>', '>', '>'], [ir.LEFT(126)])
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)