mirror of
https://gitlab.com/camelot/kickc.git
synced 2024-06-03 07:29:37 +00:00
- Added MermaidGraph class for mail flow charts generation.
This commit is contained in:
parent
f0dc36b205
commit
7b856a0f74
|
@ -10,7 +10,7 @@ import dk.camelot64.kickc.model.values.StringEncoding;
|
|||
import dk.camelot64.kickc.parser.CParser;
|
||||
import dk.camelot64.kickc.parser.KickCParser;
|
||||
import dk.camelot64.kickc.passes.*;
|
||||
import dk.camelot64.kickc.passes.reports.ReportCFG;
|
||||
import dk.camelot64.kickc.passes.reports.*;
|
||||
import dk.camelot64.kickc.preprocessor.CPreprocessor;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
import org.antlr.v4.runtime.TokenSource;
|
||||
|
@ -671,7 +671,7 @@ public class Compiler {
|
|||
if(getLog().isReportsCFGMermaid())
|
||||
new ReportCFG(program, "pass3-cfg-final", "md");
|
||||
if(getLog().isReportsCGMermaid()) {
|
||||
new ReportCFG(program, "pass3-cg-final", "md");
|
||||
new ReportCG(program, "pass3-cg-final", "md");
|
||||
}
|
||||
getLog().append(program.prettyControlFlowGraph());
|
||||
|
||||
|
|
|
@ -0,0 +1,123 @@
|
|||
package dk.camelot64.kickc.passes.reports;
|
||||
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Create a flow chart for mermaid report generation.
|
||||
* Flowcharts contain nodes or sub graphs and flows, and can either be located as a single entity in root, or embedded in other sub graphs.
|
||||
*/
|
||||
public class MermaidGraph extends Mermaid {
|
||||
|
||||
public enum Direction {
|
||||
TOP_DOWN ("TD"),
|
||||
BOTTOM_UP ("BU"),
|
||||
LEFT_RIGHT("LR"),
|
||||
RIGHT_LEFT("RL");
|
||||
|
||||
private final String direction;
|
||||
|
||||
Direction(String direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.direction;
|
||||
}
|
||||
}
|
||||
|
||||
protected Direction direction;
|
||||
protected HashMap<String, MermaidNode> nodes;
|
||||
protected HashMap<String, MermaidFlow> flows;
|
||||
protected HashMap<String, MermaidGraph> subGraphs;
|
||||
|
||||
public MermaidGraph(Direction direction) {
|
||||
super(toID(""), "");
|
||||
this.direction = direction;
|
||||
this.nodes = new HashMap<String, MermaidNode>();
|
||||
this.flows = new HashMap<String, MermaidFlow>();
|
||||
this.subGraphs = new HashMap<String, MermaidGraph>();
|
||||
}
|
||||
|
||||
public String getText(Program program) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
// graph with direction
|
||||
text.append("graph ").append(this.direction.toString()).append("\n");
|
||||
|
||||
// sub graphs
|
||||
for(MermaidGraph subGraph : getSubGraphs().values()) {
|
||||
text.append(subGraph.getText(program));
|
||||
}
|
||||
|
||||
// nodes of the graph
|
||||
for(MermaidNode node : nodes.values()) {
|
||||
text.append(node.getText(program));
|
||||
}
|
||||
|
||||
// flows to and from nodes
|
||||
for(MermaidFlow flow : flows.values()) {
|
||||
text.append(flow.getText(program));
|
||||
}
|
||||
|
||||
return text.toString();
|
||||
}
|
||||
|
||||
public HashMap<String, MermaidNode> getNodes() {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
public void setNodes(HashMap<String, MermaidNode> nodes) {
|
||||
this.nodes = nodes;
|
||||
}
|
||||
|
||||
public MermaidNode addNode(MermaidNode node) {
|
||||
this.nodes.putIfAbsent(node.id, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public MermaidNode addNode(String nodeName) {
|
||||
MermaidNode node = new MermaidNode(nodeName);
|
||||
this.nodes.putIfAbsent(node.id, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
public HashMap<String, MermaidFlow> getFlows() {
|
||||
return flows;
|
||||
}
|
||||
|
||||
public void setFlows(HashMap<String, MermaidFlow> flows) {
|
||||
this.flows = flows;
|
||||
}
|
||||
|
||||
public void addFlow(MermaidFlow flow) {
|
||||
this.flows.put(flow.getFrom().getId() + "::" + flow.getTo().getId(), flow);
|
||||
}
|
||||
|
||||
public void addFlow(MermaidNode from, MermaidNode to, String title, MermaidFlow.Type type, MermaidFlow.Direction direction) {
|
||||
MermaidFlow flow = new MermaidFlow(from, to, title, type, direction);
|
||||
this.flows.put(from.getId() + "::" + to.getId(), flow);
|
||||
}
|
||||
|
||||
public MermaidFlow getFlow(MermaidNode from, MermaidNode to) {
|
||||
return this.flows.get(from.getId() + "::" + to.getId());
|
||||
}
|
||||
|
||||
public void removeFlow(MermaidFlow flow) {
|
||||
this.flows.remove(flow.getFrom().getId() + "::" + flow.getTo().getId());
|
||||
}
|
||||
|
||||
public HashMap<String, MermaidGraph> getSubGraphs() {
|
||||
return subGraphs;
|
||||
}
|
||||
|
||||
public void setSubGraphs(HashMap<String, MermaidGraph> subGraphs) {
|
||||
this.subGraphs = subGraphs;
|
||||
}
|
||||
|
||||
public MermaidGraph addProcedureSubGraph(MermaidGraph subGraph) {
|
||||
this.subGraphs.putIfAbsent(subGraph.id, subGraph);
|
||||
return subGraph;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import dk.camelot64.kickc.model.statements.StatementCall;
|
|||
import dk.camelot64.kickc.model.statements.StatementConditionalJump;
|
||||
|
||||
import javax.swing.text.AbstractDocument;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -24,16 +25,28 @@ public class MermaidNode extends Mermaid {
|
|||
super(toID(title), title);
|
||||
this.type = Type.SEQUENCE;
|
||||
this.statements = new LinkedList<Statement>();
|
||||
this.flows = new HashSet<MermaidFlow>();
|
||||
}
|
||||
|
||||
public MermaidNode(String id, String title) {
|
||||
super(id, title);
|
||||
this.type = Type.SEQUENCE;
|
||||
this.statements = new LinkedList<Statement>();
|
||||
this.flows = new HashSet<MermaidFlow>();
|
||||
}
|
||||
|
||||
public enum Type { SEQUENCE, CALL, BRANCH }
|
||||
|
||||
private Type type;
|
||||
|
||||
protected HashSet<MermaidFlow> flows;
|
||||
|
||||
private List<Statement> statements;
|
||||
|
||||
public List<Statement> getStatements() {
|
||||
return statements;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
@ -42,12 +55,6 @@ public class MermaidNode extends Mermaid {
|
|||
this.type = type;
|
||||
}
|
||||
|
||||
private Type type;
|
||||
|
||||
public List<Statement> getStatements() {
|
||||
return statements;
|
||||
}
|
||||
|
||||
public void setStatements(List<Statement> statements) {
|
||||
this.statements = statements;
|
||||
if(!statements.isEmpty()) {
|
||||
|
@ -73,8 +80,6 @@ public class MermaidNode extends Mermaid {
|
|||
return stmtString.toString();
|
||||
}
|
||||
|
||||
private List<Statement> statements;
|
||||
|
||||
public String getText(Program program) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
|
@ -112,4 +117,21 @@ public class MermaidNode extends Mermaid {
|
|||
|
||||
return text.toString();
|
||||
}
|
||||
|
||||
public HashSet<MermaidFlow> getFlows() {
|
||||
return flows;
|
||||
}
|
||||
|
||||
public void setFlows(HashSet<MermaidFlow> flows) {
|
||||
this.flows = flows;
|
||||
}
|
||||
|
||||
public void addFlow(MermaidFlow flow) {
|
||||
this.flows.add(flow);
|
||||
}
|
||||
|
||||
public void addFlow(MermaidNode from, MermaidNode to, String title, MermaidFlow.Type type, MermaidFlow.Direction direction) {
|
||||
MermaidFlow flow = new MermaidFlow(from, to, title, type, direction);
|
||||
this.flows.add(flow);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import java.util.HashMap;
|
|||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* Create sub graphcs for mermaid report generation.
|
||||
* Create sub graphics for mermaid report generation.
|
||||
* Sub graphs contain nodes, and can either be located as a single entity in root, or embedded in other sub graphs.
|
||||
* A sub graph models dependencies to other sub graphs.
|
||||
*/
|
||||
|
|
|
@ -13,6 +13,8 @@ import java.nio.file.Path;
|
|||
public abstract class Report {
|
||||
|
||||
final protected Program program;
|
||||
protected String pass;
|
||||
protected String extension;
|
||||
|
||||
abstract String configureReport();
|
||||
|
||||
|
@ -20,6 +22,11 @@ public abstract class Report {
|
|||
|
||||
Report(Program program, String pass, String extension) {
|
||||
this.program = program;
|
||||
this.pass = pass;
|
||||
this.extension = extension;
|
||||
};
|
||||
|
||||
public void generateReport() {
|
||||
Path reportPath = program.getOutputFileManager().getMermaidOutputFile(pass, extension);
|
||||
System.out.println("Generating report " + reportPath);
|
||||
try {
|
||||
|
@ -32,5 +39,5 @@ public abstract class Report {
|
|||
} catch(Exception e) {
|
||||
throw new InternalError("Error generating report " + reportPath, e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
121
src/main/java/dk/camelot64/kickc/passes/reports/ReportCG.java
Normal file
121
src/main/java/dk/camelot64/kickc/passes/reports/ReportCG.java
Normal file
|
@ -0,0 +1,121 @@
|
|||
package dk.camelot64.kickc.passes.reports;
|
||||
|
||||
import dk.camelot64.kickc.model.Graph;
|
||||
import dk.camelot64.kickc.model.ProcedureCompilation;
|
||||
import dk.camelot64.kickc.model.Program;
|
||||
import dk.camelot64.kickc.model.statements.Statement;
|
||||
import dk.camelot64.kickc.model.statements.StatementCall;
|
||||
import dk.camelot64.kickc.model.symbols.Procedure;
|
||||
import dk.camelot64.kickc.model.values.LabelRef;
|
||||
import dk.camelot64.kickc.model.values.ProcedureRef;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Create a report of the Call Graph (CFG) of the procedures of the whole Program.
|
||||
* The report is generated in markdown with embedded mermaid graphics.
|
||||
*/
|
||||
public class ReportCG extends Report {
|
||||
|
||||
public MermaidGraph mermaidGraph;
|
||||
|
||||
public ReportCG(Program program, String pass, String extension) {
|
||||
super(program, pass, extension);
|
||||
this.mermaidGraph = new MermaidGraph(MermaidGraph.Direction.TOP_DOWN);
|
||||
generateReport();
|
||||
}
|
||||
|
||||
public ReportCG(Program program, String pass, String extension, MermaidGraph mermaidGraph) {
|
||||
super(program, pass, extension);
|
||||
this.mermaidGraph = mermaidGraph;
|
||||
generateReport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String configureReport() {
|
||||
StringBuilder report = new StringBuilder();
|
||||
report.append("---\n");
|
||||
report.append("config:\n");
|
||||
report.append(" theme: dark\n");
|
||||
report.append(" flowchart:\n");
|
||||
report.append(" wrappingWidth: 800\n");
|
||||
report.append(" nodeSpacing : 40\n");
|
||||
report.append(" rankSpacing : 40\n");
|
||||
report.append(" titleTopMargin : 40\n");
|
||||
report.append(" padding : 10\n");
|
||||
report.append(" htmlLabels: false\n");
|
||||
|
||||
|
||||
|
||||
report.append("---\n");
|
||||
return report.toString();
|
||||
}
|
||||
|
||||
|
||||
public void mermaidCGBlock(Graph.Block block, MermaidNode node) {
|
||||
|
||||
if(!block.getStatements().isEmpty()) {
|
||||
for (Statement statement : block.getStatements()) {
|
||||
if(statement instanceof StatementCall) {
|
||||
String procedureName = ((StatementCall) statement).getProcedureName();
|
||||
Procedure procedure = program.getScope().getProcedure(new ProcedureRef(procedureName));
|
||||
ProcedureCompilation procedureCompilation = program.getProcedureCompilation(new ProcedureRef(procedureName));
|
||||
Graph procedureGraph = procedureCompilation.getGraph();
|
||||
MermaidNode procedureNode = mermaidCGProcedure(procedure, procedureGraph);
|
||||
MermaidFlow flow = mermaidGraph.getFlow(node, procedureNode);
|
||||
String flowTitle = "1";
|
||||
if(flow != null) {
|
||||
flowTitle = flow.getTitle();
|
||||
Integer count = Integer.parseInt(flowTitle);
|
||||
count++;
|
||||
flowTitle = count.toString();
|
||||
mermaidGraph.removeFlow(flow);
|
||||
}
|
||||
mermaidGraph.addFlow(node, procedureNode, flowTitle, MermaidFlow.Type.FULL, MermaidFlow.Direction.BI);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private MermaidNode mermaidCGProcedure(Procedure procedure, Graph graph) {
|
||||
String procedureName = procedure.getLocalName();
|
||||
String procedureTitle = procedure.toString(program);
|
||||
|
||||
MermaidNode node = new MermaidNode(procedureName, procedureTitle);
|
||||
for(Graph.Block block : graph.getAllBlocks()) {
|
||||
mermaidCGBlock(block, node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
private String mermaidCGRoot(Procedure procedure, Graph graph) {
|
||||
String procedureName = procedure.getLocalName();
|
||||
String procedureTitle = procedure.toString(program);
|
||||
|
||||
MermaidNode node = new MermaidNode(procedureName, procedureTitle);
|
||||
mermaidGraph.addNode(node);
|
||||
for(Graph.Block block : graph.getAllBlocks()) {
|
||||
mermaidCGBlock(block, node);
|
||||
}
|
||||
return mermaidGraph.getText(program);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateReport(Program program) {
|
||||
StringBuilder report = new StringBuilder();
|
||||
ProcedureCompilation procedureCompilation = program.getProcedureCompilation(new ProcedureRef("main"));
|
||||
if(procedureCompilation != null) {
|
||||
Procedure procedure = program.getScope().getProcedure(procedureCompilation.getProcedureRef());
|
||||
Graph graph = procedureCompilation.getGraph();
|
||||
if(graph != null) {
|
||||
report.append("Procedure " + procedure.getFullName() + "\n");
|
||||
report.append("```mermaid\n");
|
||||
report.append(configureReport());
|
||||
report.append(mermaidCGRoot(procedure, graph));
|
||||
report.append("```\n\n");
|
||||
}
|
||||
}
|
||||
return report.toString();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user