package de.gaalop.dot;
import de.gaalop.cfg.*;
import de.gaalop.dfg.Expression;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
/**
* This class exports a control flow graph to the DOT language used by GraphViz.
*/
public class CfgVisitor implements ControlFlowVisitor {
private StringBuilder result = new StringBuilder();
private Map<Object, String> idMap = new HashMap<Object, String>();
private Stack<LoopNode> nestedLoops = new Stack<LoopNode>();
private int highestId = 0;
private static final String EDGE_COLOR_FORWARD = "darkolivegreen1";
private static final String EDGE_COLOR_BACKWARDS = "darkorange1";
public String getResult() {
return result.toString();
}
private String getId(Object obj) {
if (!idMap.containsKey(obj)) {
highestId++;
idMap.put(obj, "node" + highestId);
}
return idMap.get(obj);
}
private void addForwardEdge(Object n1, Object n2) {
addEdge(n1, n2, EDGE_COLOR_FORWARD);
}
private void addBackwardsEdge(Object n1, Object n2) {
addEdge(n1, n2, EDGE_COLOR_BACKWARDS);
}
private void addEdge(Object n1, Object n2, String color) {
if (n2 instanceof BlockEndNode) {
return;
}
result.append('\t');
result.append(getId(n1));
result.append(" -> ");
result.append(getId(n2));
result.append(" [color=\"").append(color).append("\"]");
result.append(";\n");
}
private void addNode(Node node, String label) {
result.append("\t");
result.append(getId(node));
result.append(" [label=\"");
result.append(label);
result.append("\"];\n");
addPredecessorEdges(node);
}
private void addPredecessorEdges(Node node) {
// Add edges back to predecessors
for (Node predecessor : node.getPredecessors()) {
addBackwardsEdge(node, predecessor);
}
}
@Override
public void visit(StartNode startNode) {
result.append("digraph {\n");
addNode(startNode, "Start");
addForwardEdge(startNode, startNode.getSuccessor());
startNode.getSuccessor().accept(this);
}
@Override
public void visit(AssignmentNode assignmentNode) {
addNode(assignmentNode, "Assignment:\\n" + assignmentNode.getVariable());
addForwardEdge(assignmentNode, assignmentNode.getSuccessor());
String subnodePrefix = getId(assignmentNode) + "_";
String stmtList = getCode(assignmentNode.getValue(), subnodePrefix);
result.append(stmtList);
result.append("\t");
result.append(subnodePrefix);
result.append("1 -> ");
result.append(getId(assignmentNode));
result.append(";\n");
assignmentNode.getSuccessor().accept(this);
}
@Override
public void visit(ExpressionStatement node) {
addNode(node, "Expression:\\n" + node.getExpression());
node.getSuccessor().accept(this);
}
@Override
public void visit(StoreResultNode node) {
addNode(node, "Output:\\n" + node.getValue());
addForwardEdge(node, node.getSuccessor());
node.getSuccessor().accept(this);
}
@Override
public void visit(IfThenElseNode node) {
String label = "if\\n" + node.getCondition().toString();
addNode(node, label);
addForwardEdge(node, node.getPositive());
node.getPositive().accept(this);
if (!(node.getNegative() instanceof BlockEndNode)) {
addForwardEdge(node, node.getNegative());
node.getNegative().accept(this);
}
node.getSuccessor().accept(this);
}
@Override
public void visit(LoopNode node) {
addNode(node, "loop");
addForwardEdge(node, node.getBody());
nestedLoops.push(node);
node.getBody().accept(this);
nestedLoops.pop();
node.getSuccessor().accept(this);
}
@Override
public void visit(BreakNode node) {
addNode(node, "break");
addForwardEdge(node, nestedLoops.peek().getSuccessor());
node.getSuccessor().accept(this);
}
@Override
public void visit(BlockEndNode node) {
SequentialNode base = node.getBase();
if (base instanceof LoopNode) {
for (Node p : node.getPredecessors()) {
if (idMap.containsKey(p)) {
addForwardEdge(p, base);
}
}
} else {
addForwardEdge(base, base.getSuccessor());
}
}
@Override
public void visit(EndNode endNode) {
addNode(endNode, "End");
result.append("}\n");
}
private String getCode(Expression expression, String prefix) {
DfgVisitor visitor = new DfgVisitor();
visitor.setIdPrefix(prefix);
expression.accept(visitor);
return visitor.toString();
}
@Override
public void visit(Macro node) {
throw new IllegalArgumentException("Macros should have been inlined.");
}
@Override
public void visit(ColorNode node) {
node.getSuccessor().accept(this);
}
}