UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

95 lines 5.11 kB
"use strict"; 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