1
0
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:
Sven Van de Velde 2023-12-03 21:42:42 +01:00
parent f0dc36b205
commit 7b856a0f74
6 changed files with 285 additions and 12 deletions

View File

@ -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());

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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.
*/

View File

@ -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);
}
};
}
}

View 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();
}
}