sp-04/src/ch/usi/inf/sp/cfg/ControlFlowGraphRenderer.java

64 lines
2.3 KiB
Java

package ch.usi.inf.sp.cfg;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class ControlFlowGraphRenderer {
private static final String INDENTATION = " ";
private final StringBuilder code = new StringBuilder();
private int indentationLevel = 0;
private static String nodeIdentifier(final BasicBlock bb, final String graphId) {
// as the basic block ID is negative for the entry and exit node, and negative numbers contain
// a dash symbol, we need to quote the identifier to make a syntactically correct DOT file
return String.format("\"%sbb%d\"", graphId, bb.getId());
}
private static String nodeStyle(final BasicBlock bb, final String label) {
if (bb.getInEdges().isEmpty()) {
return "[shape=circle,label=\"e\",xlabel=\"" + label + "\"]";
} else if (bb.getOutEdges().isEmpty()) {
return "[shape=circle,label=\"x\"]";
} else {
return "[label=\"" + bb.getId() + "|{" +
StreamSupport.stream(bb.getInstructions().spliterator(), false)
.collect(Collectors.joining("|"))
+ "}\"]";
}
}
public static String renderControlFlowGraph(final String label, final ControlFlowGraph cfg) {
return new ControlFlowGraphRenderer().render(label, cfg);
}
private void line(String line) {
code.append(INDENTATION.repeat(indentationLevel)).append(line).append('\n');
}
private String render(final String desiredLabel, final ControlFlowGraph graph) {
final String label = desiredLabel.replaceAll("\\W+", "");
line("digraph " + label + " {");
code.append('\n');
indentationLevel++;
line("node [shape=record]");
for (final BasicBlock bb : graph.getNodes()) {
line(nodeIdentifier(bb, label) + " " + nodeStyle(bb, label));
}
code.append('\n');
for (var e : graph.getEdges()) {
final String l = e.getLabel();
final String suffix = l == null || l.isBlank() ? "" : (" [label=\"" + e.getLabel() + "\"]");
line(nodeIdentifier(e.getFrom(), label) + " -> " + nodeIdentifier(e.getTo(), label) + suffix);
}
indentationLevel--;
line("}");
return code.toString();
}
}