@eagleoutice/flowr
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
95 lines • 5.11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.cfgToMermaid = cfgToMermaid;
exports.cfgToMermaidUrl = cfgToMermaidUrl;
const mermaid_1 = require("./mermaid");
const type_1 = require("../../r-bridge/lang-4.x/ast/model/type");
const control_flow_graph_1 = require("../../control-flow/control-flow-graph");
const reconstruct_1 = require("../../reconstruct/reconstruct");
const auto_select_defaults_1 = require("../../reconstruct/auto-select/auto-select-defaults");
function getLexeme(n) {
return n ? n.info.fullLexeme ?? n.lexeme ?? '' : undefined;
}
function cfgOfNode(vert, normalizedVertex, id, content, output) {
if (normalizedVertex && content !== undefined) {
const start = vert.type === control_flow_graph_1.CfgVertexType.Expression ? '([' : '[';
const end = vert.type === control_flow_graph_1.CfgVertexType.Expression ? '])' : ']';
const name = `"\`${(0, mermaid_1.escapeMarkdown)(normalizedVertex.type)} (${id})${content ? '\n' + (0, mermaid_1.escapeMarkdown)(JSON.stringify(content)) : ''}${vert.callTargets ? '\n calls:' + (0, mermaid_1.escapeMarkdown)(JSON.stringify([...vert.callTargets])) : ''}\`"`;
output += ` n${id}${start}${name}${end}\n`;
}
else {
output += String(id).endsWith('-exit') ? ` n${id}((${id}))\n` : ` n${id}[[${id}]]\n`;
}
return output;
}
const getDirRegex = /flowchart\s+([A-Za-z]+)/;
/**
* Convert the control flow graph to a mermaid string.
* @param cfg - The control flow graph to convert.
* @param normalizedAst - The normalized AST to use for the vertex content.
* @param prefix - The prefix to use for the mermaid string.
* @param simplify - Whether to simplify the control flow graph (especially in the context of basic blocks).
*/
function cfgToMermaid(cfg, normalizedAst, prefix = 'flowchart BT\n', simplify = false) {
let output = prefix;
const dirIs = getDirRegex.exec(prefix)?.at(1) ?? 'LR';
for (const [id, vertex] of cfg.graph.vertices(false)) {
const normalizedVertex = normalizedAst?.idMap.get(id);
const content = getLexeme(normalizedVertex);
if (vertex.name === type_1.RType.ExpressionList && vertex.type === control_flow_graph_1.CfgVertexType.Expression && cfg.graph.hasVertex(id + '-exit')) {
output += ` subgraph ${type_1.RType.ExpressionList} ${normalizedVertex?.info.fullLexeme ?? id}\n`;
output += ` direction ${dirIs}\n`;
}
if (vertex.type === control_flow_graph_1.CfgVertexType.Block) {
if (simplify) {
const ids = vertex.elems?.map(e => e.id) ?? [];
const reconstruct = (0, reconstruct_1.reconstructToCode)(normalizedAst, new Set(ids), auto_select_defaults_1.doNotAutoSelect).code;
const name = `"\`Basic Block (${id})\n${(0, mermaid_1.escapeMarkdown)(reconstruct)}\`"`;
output += ` n${id}[[${name}]]\n`;
}
else {
output += ` subgraph n${vertex.id} [Block ${normalizedVertex?.info.fullLexeme ?? id}]\n`;
output += ` direction ${dirIs}\n`;
let last = undefined;
for (const element of vertex.elems ?? []) {
const childNormalizedVertex = normalizedAst?.idMap.get(element.id);
const childContent = getLexeme(childNormalizedVertex);
output = cfgOfNode(vertex, childNormalizedVertex, element.id, childContent, output);
// just to keep the order
if (last) {
output += ` ${last} -.-> n${element.id}\n`;
}
last = `n${element.id}`;
}
output += ' end\n';
}
}
else {
output = cfgOfNode(vertex, normalizedVertex, id, content, output);
}
if (vertex.name === type_1.RType.ExpressionList && vertex.type === control_flow_graph_1.CfgVertexType.EndMarker) {
output += ' end\n';
}
}
for (const [from, targets] of cfg.graph.edges()) {
for (const [to, edge] of targets) {
const edgeType = edge.label === 1 /* CfgEdgeType.Cd */ ? '-->' : '-.->';
const edgeSuffix = edge.label === 1 /* CfgEdgeType.Cd */ ? ` (${edge.when})` : '';
output += ` n${from} ${edgeType}|"${(0, mermaid_1.escapeMarkdown)((0, control_flow_graph_1.edgeTypeToString)(edge.label))}${edgeSuffix}"| n${to}\n`;
}
}
for (const entryPoint of cfg.entryPoints) {
output += ` style n${entryPoint} stroke:cyan,stroke-width:6.5px;`;
}
for (const exitPoint of cfg.exitPoints) {
output += ` style n${exitPoint} stroke:green,stroke-width:6.5px;`;
}
return output;
}
/**
* Use mermaid to visualize the normalized AST.
*/
function cfgToMermaidUrl(cfg, normalizedAst, prefix = 'flowchart BT\n') {
return (0, mermaid_1.mermaidCodeToUrl)(cfgToMermaid(cfg, normalizedAst, prefix));
}
//# sourceMappingURL=cfg.js.map