Boxed up control flow structures and preparation for edge labeling
This commit is contained in:
parent
137d01938c
commit
d7abcb7b55
3 changed files with 148 additions and 28 deletions
|
@ -1,22 +1,49 @@
|
||||||
package grafos;
|
package grafos;
|
||||||
|
|
||||||
import com.github.javaparser.ast.Node;
|
import com.github.javaparser.ast.Node;
|
||||||
import com.github.javaparser.ast.stmt.EmptyStmt;
|
import com.github.javaparser.ast.expr.BooleanLiteralExpr;
|
||||||
|
import com.github.javaparser.ast.stmt.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.Collection;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Stack;
|
||||||
|
|
||||||
public class CFG {
|
public class CFG {
|
||||||
private final List<Node> nodes = new ArrayList<>();
|
private final List<Node> nodes = new LinkedList<>();
|
||||||
private final List<Map.Entry<Node, Node>> edges = new LinkedList<>();
|
private final LinkedList<Block> blocks = new LinkedList<>();
|
||||||
|
private Stack<Block> blockStack = new Stack<>();
|
||||||
|
private final List<Edge> edges = new LinkedList<>();
|
||||||
private Node beginNode, endNode;
|
private Node beginNode, endNode;
|
||||||
|
private String nextLabel;
|
||||||
|
|
||||||
public void addNode(Node stmt) {
|
public void addNode(Node stmt) {
|
||||||
nodes.add(stmt);
|
nodes.add(stmt);
|
||||||
|
if (!blockStack.isEmpty())
|
||||||
|
blockStack.peek().add(stmt);
|
||||||
System.out.println("NODO: " + node2str(stmt));
|
System.out.println("NODO: " + node2str(stmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void beginBlock(Node node) {
|
||||||
|
Block b = new Block(node, this);
|
||||||
|
if (blockStack.isEmpty())
|
||||||
|
blocks.addFirst(b);
|
||||||
|
else
|
||||||
|
blockStack.peek().addSubBlock(b);
|
||||||
|
blockStack.push(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endBlock() {
|
||||||
|
blockStack.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNextLabel(boolean b) {
|
||||||
|
nextLabel = String.valueOf(b);
|
||||||
|
}
|
||||||
|
|
||||||
public void connect(Node begin, Node end) {
|
public void connect(Node begin, Node end) {
|
||||||
edges.add(new HashMap.SimpleEntry<>(begin, end));
|
edges.add(new Edge(this, begin, end, nextLabel));
|
||||||
|
nextLabel = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(Collection<Node> begin, Node end) {
|
public void connect(Collection<Node> begin, Node end) {
|
||||||
|
@ -38,18 +65,18 @@ public class CFG {
|
||||||
return endNode;
|
return endNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> toStringList() {
|
public List<String> toStringList(GraphViz gv) {
|
||||||
List<String> res = new LinkedList<String>();
|
List<String> res = new LinkedList<>();
|
||||||
for (Map.Entry<Node, Node> e : edges) {
|
res.add(gv.start_graph());
|
||||||
res.add(edge2str(e.getKey(), e.getValue()));
|
for (Block b : blocks)
|
||||||
|
res.addAll(b.toStringList(gv));
|
||||||
|
for (Edge e : edges) {
|
||||||
|
res.add(e.toString());
|
||||||
}
|
}
|
||||||
|
res.add(gv.end_graph());
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String edge2str(Node a, Node b) {
|
|
||||||
return node2str(a) + "->" + node2str(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String node2str(Node s) {
|
private String node2str(Node s) {
|
||||||
if (s == beginNode)
|
if (s == beginNode)
|
||||||
return "Start";
|
return "Start";
|
||||||
|
@ -67,4 +94,92 @@ public class CFG {
|
||||||
return "\"(" + (index + 1) + ", " + s.getRange().get().begin.line + ") "
|
return "\"(" + (index + 1) + ", " + s.getRange().get().begin.line + ") "
|
||||||
+ s.toString().replace("\"", "\\\"") + "\"";
|
+ s.toString().replace("\"", "\\\"") + "\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class Block extends LinkedList<Node> {
|
||||||
|
private static int clusterId = 0;
|
||||||
|
|
||||||
|
private final Node container;
|
||||||
|
private final CFG cfg;
|
||||||
|
private final List<Block> inners = new LinkedList<>();
|
||||||
|
|
||||||
|
Block(Node container, CFG cfg) {
|
||||||
|
this.container = container;
|
||||||
|
this.cfg = cfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addSubBlock(Block block) {
|
||||||
|
inners.add(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> toStringList(GraphViz gv) {
|
||||||
|
List<String> res = new LinkedList<>();
|
||||||
|
res.add(gv.start_subgraph(clusterId++));
|
||||||
|
res.add("label = \"" + getTitle() + "\"");
|
||||||
|
for (Block b : inners) {
|
||||||
|
res.addAll(b.toStringList(gv));
|
||||||
|
}
|
||||||
|
for (Node n : this) {
|
||||||
|
res.add(cfg.node2str(n));
|
||||||
|
}
|
||||||
|
res.add(gv.end_subgraph());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getTitle() {
|
||||||
|
String template;
|
||||||
|
Object[] filler;
|
||||||
|
if (container instanceof IfStmt) {
|
||||||
|
template = "if (%s)";
|
||||||
|
filler = new Object[]{((IfStmt) container).getCondition()};
|
||||||
|
} else if (container instanceof WhileStmt) {
|
||||||
|
template = "while (%s)";
|
||||||
|
filler = new Object[]{((WhileStmt) container).getCondition()};
|
||||||
|
} else if (container instanceof DoStmt) {
|
||||||
|
template = "do-while (%s)";
|
||||||
|
filler = new Object[]{((DoStmt) container).getCondition()};
|
||||||
|
} else if (container instanceof ForStmt) {
|
||||||
|
template = "for (%s; %s; %s)";
|
||||||
|
filler = new Object[]{
|
||||||
|
((ForStmt) container).getInitialization(),
|
||||||
|
((ForStmt) container).getCompare().orElse(new BooleanLiteralExpr(true)),
|
||||||
|
((ForStmt) container).getUpdate()};
|
||||||
|
} else if (container instanceof ForeachStmt) {
|
||||||
|
template = "for (%s : %s)";
|
||||||
|
filler = new Object[]{
|
||||||
|
((ForeachStmt) container).getVariable(),
|
||||||
|
((ForeachStmt) container).getIterable()};
|
||||||
|
} else if (container instanceof BlockStmt) {
|
||||||
|
template = "";
|
||||||
|
filler = new Object[0];
|
||||||
|
} else if (container instanceof SwitchStmt) {
|
||||||
|
template = "switch (%s)";
|
||||||
|
filler = new Object[]{((SwitchStmt) container).getSelector()};
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Unimplemented!!!");
|
||||||
|
// TODO: remove
|
||||||
|
}
|
||||||
|
return String.format(template, filler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Edge {
|
||||||
|
private final CFG cfg;
|
||||||
|
private final Node origin, destination;
|
||||||
|
private final String label;
|
||||||
|
|
||||||
|
Edge(CFG cfg, Node origin, Node destination, String label) {
|
||||||
|
this.cfg = cfg;
|
||||||
|
this.origin = origin;
|
||||||
|
this.destination = destination;
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
String s = cfg.node2str(origin) + " -> " + cfg.node2str(destination);
|
||||||
|
if (label != null) {
|
||||||
|
s += " [ label = \"" + label + "\" ]";
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,20 +47,12 @@ public class Transformador {
|
||||||
visitador.visit(cu, graph);
|
visitador.visit(cu, graph);
|
||||||
|
|
||||||
// Imprimimos el CFG del program
|
// Imprimimos el CFG del program
|
||||||
StringBuilder builder = new StringBuilder();
|
GraphViz gv = new GraphViz();
|
||||||
for (String s : graph.toStringList()) {
|
String dotInfo = String.join("\n", graph.toStringList(gv));
|
||||||
builder.append(s);
|
|
||||||
builder.append(";");
|
|
||||||
System.out.println("ARCO: " + s);
|
|
||||||
}
|
|
||||||
String dotInfo = builder.toString();
|
|
||||||
|
|
||||||
// Generamos un PDF con el CFG del programa
|
// Generamos un PDF con el CFG del programa
|
||||||
System.out.print("\nGenerando PDF...");
|
System.out.print("\nGenerando PDF...");
|
||||||
GraphViz gv = new GraphViz();
|
|
||||||
gv.addln(gv.start_graph());
|
|
||||||
gv.add(dotInfo);
|
gv.add(dotInfo);
|
||||||
gv.addln(gv.end_graph());
|
|
||||||
String type = "pdf"; // String type = "gif";
|
String type = "pdf"; // String type = "gif";
|
||||||
// gv.increaseDpi();
|
// gv.increaseDpi();
|
||||||
gv.decreaseDpi();
|
gv.decreaseDpi();
|
||||||
|
|
|
@ -52,6 +52,7 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(IfStmt n, CFG graph) {
|
public void visit(IfStmt n, CFG graph) {
|
||||||
|
graph.beginBlock(n);
|
||||||
Node ifStart = n.getCondition();
|
Node ifStart = n.getCondition();
|
||||||
graph.addNode(ifStart);
|
graph.addNode(ifStart);
|
||||||
graph.connect(prevNode, ifStart);
|
graph.connect(prevNode, ifStart);
|
||||||
|
@ -75,10 +76,12 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
|
||||||
newPrev.add(ifStart);
|
newPrev.add(ifStart);
|
||||||
}
|
}
|
||||||
prevNode = newPrev;
|
prevNode = newPrev;
|
||||||
|
graph.endBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WhileStmt n, CFG graph) {
|
public void visit(WhileStmt n, CFG graph) {
|
||||||
|
graph.beginBlock(n);
|
||||||
Node whileStart = n.getCondition();
|
Node whileStart = n.getCondition();
|
||||||
graph.addNode(whileStart);
|
graph.addNode(whileStart);
|
||||||
graph.connect(prevNode, whileStart);
|
graph.connect(prevNode, whileStart);
|
||||||
|
@ -89,20 +92,25 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
|
||||||
n.getBody().accept(this, graph);
|
n.getBody().accept(this, graph);
|
||||||
graph.connect(prevNode, whileStart);
|
graph.connect(prevNode, whileStart);
|
||||||
prevNode = Collections.singletonList(whileStart);
|
prevNode = Collections.singletonList(whileStart);
|
||||||
|
graph.endBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(DoStmt n, CFG graph) {
|
public void visit(DoStmt n, CFG graph) {
|
||||||
|
graph.beginBlock(n);
|
||||||
|
Node condition = n.getCondition();
|
||||||
prevNode = new LinkedList<>(prevNode);
|
prevNode = new LinkedList<>(prevNode);
|
||||||
prevNode.add(n.getCondition());
|
prevNode.add(condition);
|
||||||
n.getBody().accept(this, graph);
|
n.getBody().accept(this, graph);
|
||||||
graph.addNode(n.getCondition());
|
graph.addNode(condition);
|
||||||
graph.connect(prevNode, n.getCondition());
|
graph.connect(prevNode, condition);
|
||||||
prevNode = Collections.singletonList(n.getCondition());
|
prevNode = Collections.singletonList(condition);
|
||||||
|
graph.endBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ForStmt n, CFG graph) {
|
public void visit(ForStmt n, CFG graph) {
|
||||||
|
graph.beginBlock(n);
|
||||||
// Initialization expressions
|
// Initialization expressions
|
||||||
if (n.getInitialization() != null) {
|
if (n.getInitialization() != null) {
|
||||||
for (Expression e : n.getInitialization()) {
|
for (Expression e : n.getInitialization()) {
|
||||||
|
@ -139,10 +147,12 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
|
||||||
prevNode = Collections.singletonList(n.getCompare().get());
|
prevNode = Collections.singletonList(n.getCompare().get());
|
||||||
else // There is no comparison, can't exit the loop TODO implement break and continue as way to exit
|
else // There is no comparison, can't exit the loop TODO implement break and continue as way to exit
|
||||||
prevNode = Collections.emptyList();
|
prevNode = Collections.emptyList();
|
||||||
|
graph.endBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(ForeachStmt n, CFG graph) {
|
public void visit(ForeachStmt n, CFG graph) {
|
||||||
|
graph.beginBlock(n);
|
||||||
ForeachStmt copy = new ForeachStmt(n.getTokenRange().orElse(null), n.getVariable(), n.getIterable(), new EmptyStmt());
|
ForeachStmt copy = new ForeachStmt(n.getTokenRange().orElse(null), n.getVariable(), n.getIterable(), new EmptyStmt());
|
||||||
graph.addNode(copy);
|
graph.addNode(copy);
|
||||||
graph.connect(prevNode, copy);
|
graph.connect(prevNode, copy);
|
||||||
|
@ -150,6 +160,7 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
|
||||||
n.getBody().accept(this, graph);
|
n.getBody().accept(this, graph);
|
||||||
graph.connect(prevNode, copy);
|
graph.connect(prevNode, copy);
|
||||||
prevNode = Collections.singletonList(copy);
|
prevNode = Collections.singletonList(copy);
|
||||||
|
graph.endBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -165,8 +176,9 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(SwitchStmt n, CFG graph) {
|
public void visit(SwitchStmt n, CFG graph) {
|
||||||
|
graph.beginBlock(n);
|
||||||
// Link previous statement to the switch's selector
|
// Link previous statement to the switch's selector
|
||||||
Node selectorNode = new SwitchStmt(n.getTokenRange().orElse(null), n.getSelector(), new NodeList<>());
|
Node selectorNode = n.getSelector();
|
||||||
graph.addNode(selectorNode);
|
graph.addNode(selectorNode);
|
||||||
graph.connect(prevNode, selectorNode);
|
graph.connect(prevNode, selectorNode);
|
||||||
// Analyze switch's cases
|
// Analyze switch's cases
|
||||||
|
@ -191,6 +203,7 @@ public class Visitador extends VoidVisitorAdapter<CFG> {
|
||||||
prevNode.addAll(brokenStmts); // 1
|
prevNode.addAll(brokenStmts); // 1
|
||||||
if (n.getEntries().get(n.getEntries().size() - 1).getLabel().isPresent())
|
if (n.getEntries().get(n.getEntries().size() - 1).getLabel().isPresent())
|
||||||
prevNode.add(selectorNode); // 2
|
prevNode.add(selectorNode); // 2
|
||||||
|
graph.endBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in a new issue