static ControlFlowGraph ReduceBreak(ControlFlowGraph graph, CfgLoop loop, LoopPart part) { // Func<string> vis = () => graph.ToVis().AddPointer("part", part.Index).ToString(); // For VS Code debug visualisation // Break = needs to exit the loop to be a break if (!part.Break) { return(graph); } // Outside entry = non-structured code, so give up if (part.OutsideEntry) { return(graph); } var children = graph.Children(part.Index); if (children.Length != 2) { throw new ControlFlowGraphException($"Break at {part.Index} has unexpected child count ({children.Length})", graph); } bool isfirstChildInLoop = loop.Body.Any(x => x.Index == children[0]) || children[0] == loop.Header.Index; var exitTarget = isfirstChildInLoop ? children[1] : children[0]; // Add LoopSuccessor edge if this is the last link to the MainExit. var remainingLoopChildren = loop.Body .Where(x => x.Index != part.Index) .Aggregate( (IEnumerable <int>)graph.Children(loop.Header.Index), (current, x) => current.Union(graph.Children(x.Index))); if (loop.MainExit.HasValue && !remainingLoopChildren.Contains(loop.MainExit.Value)) { graph = graph.AddEdge(loop.Header.Index, loop.MainExit.Value, CfgEdge.LoopSuccessor); } if (exitTarget != loop.MainExit) { var targetChildren = graph.Children(exitTarget); if (targetChildren.Length != 1 || targetChildren[0] != loop.MainExit) { return(graph); } var condition = graph.Nodes[part.Index]; if (graph.GetEdgeLabel(part.Index, exitTarget) == CfgEdge.False) { condition = Emit.Negation(condition); } var ifNode = Emit.If(condition, Emit.Seq(graph.Nodes[exitTarget], Emit.Break())); return(graph .RemoveNode(exitTarget) .ReplaceNode(part.Index, ifNode)); } return(ReplaceLoopBranch(graph, part.Index, exitTarget, Emit.Break())); }
[Fact] public void BreakTest() => TestRoundTrip("while (a) { if (b) { break }, c }", Emit.While(Emit.Name("a"), Emit.Seq( Emit.If(Emit.Name("b"), Emit.Break()), S(Emit.Name("c")))), ScriptParser.TopLevel);