def _unwarp_breaks(start, blocks, next_block): blocks_set = set([start] + blocks) ends = _gather_possible_ends(next_block) breaks = set() patched = [] for i, block in enumerate(blocks): warp = block.warp if not isinstance(warp, nodes.UnconditionalWarp): patched.append(block) continue target = _get_target(warp) if target in blocks_set: patched.append(block) continue assert target in ends, "GOTO statements are not supported" if block.warpins_count != 0: new_block = _create_next_block(block) new_block.warpins_count = block.warpins_count _set_flow_to(block, new_block) patched.append(block) patched.append(new_block) block = new_block else: patched.append(block) block.contents.append(nodes.Break()) if i + 1 == len(blocks): _set_end(block) else: _set_flow_to(block, blocks[i + 1]) breaks.add(block) blocks[:] = patched if len(breaks) == 0: return breaks_stack = [] warpsout = [] pending_break = None for i, block in enumerate(reversed(blocks)): if block in breaks: pending_break = None if block.warpins_count == 0: breaks_stack.append((BREAK_ONE_USE, block)) else: breaks_stack.append((BREAK_INFINITE, block)) continue warp = block.warp if not isinstance(warp, nodes.ConditionalWarp): if _is_flow(warp): pending_break = None continue target = _get_target(warp) if target in blocks_set: continue assert target in ends, "GOTO statements are not supported" if pending_break is None: assert len(breaks_stack) > 0 top_break = breaks_stack[-1] _set_target(warp, top_break[1]) if top_break[0] == BREAK_ONE_USE: pending_break = breaks_stack.pop() warpsout = [] else: warpsout.append(block) else: _set_target(warp, pending_break[1]) warpsout.append(block) if len(block.contents) > 0: pending_break = None while len(breaks_stack) > 0 and breaks_stack[-1][0] == BREAK_INFINITE: breaks_stack.pop() # And pray for the best... while len(warpsout) > 0 and len(breaks_stack) > 0: _set_target(warpsout.pop().warp, breaks_stack.pop()[1])
def _unwarp_loop(start, end, body): if len(body) > 0: last = body[-1] else: last = start if isinstance(start.warp, nodes.IteratorWarp): assert isinstance(last.warp, nodes.UnconditionalWarp) assert last.warp.target == start loop = nodes.IteratorFor() loop.statements.contents = body loop.identifiers = start.warp.variables loop.expressions = start.warp.controls loop._addr = body[0].first_address _set_flow_to(start, body[0]) elif isinstance(start.warp, nodes.NumericLoopWarp): assert isinstance(last.warp, nodes.UnconditionalWarp) assert last.warp.target == start loop = nodes.NumericFor() loop.statements.contents = body loop.variable = start.warp.index loop.expressions = start.warp.controls loop._addr = body[0].first_address _set_flow_to(start, body[0]) # While (including "while true" and "repeat until false" which will be # while true) elif isinstance(last.warp, nodes.UnconditionalWarp): assert last.warp.target == start # while true if _is_flow(start.warp): loop = nodes.While() loop.expression = nodes.Primitive() loop.expression.type = nodes.Primitive.T_TRUE loop.statements.contents = body else: # There shouldn't be many problems similar to ifs, as # we are processing loops in the order from innermost # to outermost for i, block in enumerate(body): assert len(block.contents) == 0 if _is_flow(block.warp): break assert i < len(body) expression = [start] + body[:i] body = body[i:] # Sometimes expression may decide to jump to the # outer loop start instead _fix_expression(expression, start, end) true = body[0] false = end expression = _compile_expression(expression, None, true, false) # If something jumps to the start (instead of the end) # - that's a nested if loop = nodes.While() loop.expression = expression loop.statements.contents = body _fix_nested_ifs(body, start) _set_flow_to(start, body[0]) # Repeat until else: assert isinstance(last.warp, nodes.ConditionalWarp) assert last.warp.false_target == start i = len(body) - 1 while i >= 0: block = body[i] warp = block.warp if _is_flow(warp): i += 1 break if len(block.contents) != 0: break i -= 1 expression = body[i:] body = body[:i + 1] assert len(expression) > 0 first = expression[0] if _is_jump(first.warp): # Don't append to the body - it already has it expression.pop(0) body[-1].contents.append(nodes.Break()) false = body[0] # Don't use end as it could be broken by a previous # repeat until pass true = expression[-1].warp.true_target loop = nodes.RepeatUntil() loop.expression = _compile_expression(expression, None, true, false) start_copy = copy.copy(start) start.contents = [] if len(body) > 1: _set_flow_to(start_copy, body[1]) else: _set_end(start_copy) _set_flow_to(start, start_copy) body[0] = start_copy loop.statements.contents = body return loop