diff --git a/src/core/print/dataflow-printer.ts b/src/core/print/dataflow-printer.ts index 9378b8279d..d817671616 100644 --- a/src/core/print/dataflow-printer.ts +++ b/src/core/print/dataflow-printer.ts @@ -2,6 +2,8 @@ import { jsonReplacer } from '../../util/json' import { DataflowInformation } from '../../dataflow/internal/info' import { QuadSerializationConfiguration } from '../../util/quads' import { df2quads } from '../../dataflow/graph/quads' +import { graphToMermaid, graphToMermaidUrl } from '../../util/mermaid' +import { DataflowMap } from '../../dataflow' function mayObjectJson(d: unknown): string { @@ -43,13 +45,18 @@ function objectJson(df: object): string { } /** Should work with larger things as well */ -// eslint-disable-next-line @typescript-eslint/require-await -export async function dataflowGraphToJson(df: DataflowInformation): Promise { +export function dataflowGraphToJson(df: DataflowInformation): string { return objectJson(df) } +export function dataflowGraphToMermaid(df: DataflowInformation, idMap: DataflowMap): string { + return graphToMermaid(df.graph, idMap) +} + +export function dataflowGraphToMermaidUrl(df: DataflowInformation, idMap: DataflowMap): string { + return graphToMermaidUrl(df.graph, idMap) +} -// eslint-disable-next-line @typescript-eslint/require-await -export async function dataflowGraphToQuads(df: DataflowInformation, config: QuadSerializationConfiguration): Promise { +export function dataflowGraphToQuads(df: DataflowInformation, config: QuadSerializationConfiguration): string { return df2quads(df.graph, config) } diff --git a/src/core/print/normalize-printer.ts b/src/core/print/normalize-printer.ts index 4b7d81f92d..7f658afeca 100644 --- a/src/core/print/normalize-printer.ts +++ b/src/core/print/normalize-printer.ts @@ -1,10 +1,10 @@ import { NormalizedAst } from '../../r-bridge' import { jsonReplacer } from '../../util/json' import { QuadSerializationConfiguration, serialize2quads } from '../../util/quads' +import { normalizedAstToMermaid, normalizedAstToMermaidUrl } from '../../util/mermaid' /** Should work with larger things as well */ -// eslint-disable-next-line @typescript-eslint/require-await -export async function normalizedAstToJson(ast: NormalizedAst): Promise { +export function normalizedAstToJson(ast: NormalizedAst): string { // we never serialize the idmap, as it just duplicates the ast, additionally we now miss the full-lexeme to further safe memory return JSON.stringify({ ...ast.ast, idMap: undefined }, (k, v) => { if(k === 'fullLexeme') { @@ -15,7 +15,14 @@ export async function normalizedAstToJson(ast: NormalizedAst): Promise { }) } -// eslint-disable-next-line @typescript-eslint/require-await -export async function normalizedAstToQuads(ast: NormalizedAst, config: QuadSerializationConfiguration): Promise { +export function normalizedAstToQuads(ast: NormalizedAst, config: QuadSerializationConfiguration): string { return serialize2quads(ast.ast, config) } + +export function printNormalizedAstToMermaid(ast: NormalizedAst): string { + return normalizedAstToMermaid(ast.ast) +} + +export function printNormalizedAstToMermaidUrl(ast: NormalizedAst): string { + return normalizedAstToMermaidUrl(ast.ast) +} diff --git a/src/core/print/print.ts b/src/core/print/print.ts index d284839c5f..86923338ef 100644 --- a/src/core/print/print.ts +++ b/src/core/print/print.ts @@ -51,4 +51,4 @@ export function internalPrinter(input: Input): Input { */ export type IStepPrinter = Format extends StepOutputFormat.Internal ? (input: Awaited>) => Awaited> : - (input: Awaited>, ...additional: AdditionalInput) => Promise + (input: Awaited>, ...additional: AdditionalInput) => Promise | string diff --git a/src/core/steps.ts b/src/core/steps.ts index 160bda4856..0fbb1e88e3 100644 --- a/src/core/steps.ts +++ b/src/core/steps.ts @@ -21,9 +21,19 @@ import { import { produceDataFlowGraph } from '../dataflow' import { reconstructToCode, staticSlicing } from '../slicing' import { internalPrinter, IStepPrinter, StepOutputFormat } from './print/print' -import { normalizedAstToJson, normalizedAstToQuads } from './print/normalize-printer' +import { + normalizedAstToJson, + normalizedAstToQuads, + printNormalizedAstToMermaid, + printNormalizedAstToMermaidUrl +} from './print/normalize-printer' import { guard } from '../util/assert' -import { dataflowGraphToJson, dataflowGraphToQuads } from './print/dataflow-printer' +import { + dataflowGraphToJson, + dataflowGraphToMermaid, + dataflowGraphToMermaidUrl, + dataflowGraphToQuads +} from './print/dataflow-printer' import { parseToQuads } from './print/parse-printer' /** @@ -65,8 +75,7 @@ export const STEPS_PER_FILE = { required: 'once-per-file', printer: { [StepOutputFormat.Internal]: internalPrinter, - // eslint-disable-next-line @typescript-eslint/require-await -- async printer wrapper, string is already json - [StepOutputFormat.Json]: async text => text, + [StepOutputFormat.Json]: text => text, [StepOutputFormat.RdfQuads]: parseToQuads } } satisfies IStep, @@ -75,9 +84,11 @@ export const STEPS_PER_FILE = { processor: normalize, required: 'once-per-file', printer: { - [StepOutputFormat.Internal]: internalPrinter, - [StepOutputFormat.Json]: normalizedAstToJson, - [StepOutputFormat.RdfQuads]: normalizedAstToQuads + [StepOutputFormat.Internal]: internalPrinter, + [StepOutputFormat.Json]: normalizedAstToJson, + [StepOutputFormat.RdfQuads]: normalizedAstToQuads, + [StepOutputFormat.Mermaid]: printNormalizedAstToMermaid, + [StepOutputFormat.MermaidUrl]: printNormalizedAstToMermaidUrl } } satisfies IStep, 'dataflow': { @@ -85,9 +96,11 @@ export const STEPS_PER_FILE = { processor: produceDataFlowGraph, required: 'once-per-file', printer: { - [StepOutputFormat.Internal]: internalPrinter, - [StepOutputFormat.Json]: dataflowGraphToJson, - [StepOutputFormat.RdfQuads]: dataflowGraphToQuads + [StepOutputFormat.Internal]: internalPrinter, + [StepOutputFormat.Json]: dataflowGraphToJson, + [StepOutputFormat.RdfQuads]: dataflowGraphToQuads, + [StepOutputFormat.Mermaid]: dataflowGraphToMermaid, + [StepOutputFormat.MermaidUrl]: dataflowGraphToMermaidUrl } } satisfies IStep } as const diff --git a/src/dataflow/graph/graph.ts b/src/dataflow/graph/graph.ts index c8a9bbe6f4..806e5eebfb 100644 --- a/src/dataflow/graph/graph.ts +++ b/src/dataflow/graph/graph.ts @@ -1,5 +1,5 @@ import { guard } from '../../util/assert' -import { NodeId, RNodeWithParent } from '../../r-bridge' +import { NodeId, NoInfo, RNodeWithParent } from '../../r-bridge' import { cloneEnvironments, IdentifierDefinition, @@ -22,7 +22,7 @@ import { setEquals } from '../../util/set' import { dataflowLogger } from '../index' /** Used to get an entry point for every id, after that it allows reference-chasing of the graph */ -export type DataflowMap = BiMap> +export type DataflowMap = BiMap> diff --git a/src/util/mermaid/dfg.ts b/src/util/mermaid/dfg.ts index c33668dbd4..aa48d3ceba 100644 --- a/src/util/mermaid/dfg.ts +++ b/src/util/mermaid/dfg.ts @@ -1,4 +1,4 @@ -import { NodeId, NoInfo } from '../../r-bridge' +import { NodeId } from '../../r-bridge' import { SourceRange } from '../range' import { BuiltIn, @@ -42,7 +42,7 @@ function scopeToMermaid(scope: DataflowScopeName, when: DataflowGraphEdgeAttribu return `, *${scope.replace('<', '#lt;')}${whenText}*` } -function createArtificialExitPoints(exitPoints: NodeId[], mermaid: MermaidGraph, dataflowIdMap: DataflowMap, idPrefix: string) { +function createArtificialExitPoints(exitPoints: NodeId[], mermaid: MermaidGraph, dataflowIdMap: DataflowMap, idPrefix: string) { for(const exitPoint of exitPoints) { if(!mermaid.rootGraph.hasNode(exitPoint, true)) { const node = dataflowIdMap.get(exitPoint) @@ -53,7 +53,7 @@ function createArtificialExitPoints(exitPoints: NodeId[], mermaid: MermaidGraph, } } -function subflowToMermaid(nodeId: NodeId, exitPoints: NodeId[], subflow: DataflowFunctionFlowInformation | undefined, dataflowIdMap: DataflowMap | undefined, mermaid: MermaidGraph, idPrefix = ''): void { +function subflowToMermaid(nodeId: NodeId, exitPoints: NodeId[], subflow: DataflowFunctionFlowInformation | undefined, dataflowIdMap: DataflowMap | undefined, mermaid: MermaidGraph, idPrefix = ''): void { if(subflow === undefined) { return } @@ -123,7 +123,7 @@ function mermaidNodeBrackets(def: boolean, fCall: boolean) { return { open, close } } -function nodeToMermaid(graph: DataflowGraph, info: DataflowGraphVertexInfo, mermaid: MermaidGraph, id: NodeId, idPrefix: string, dataflowIdMap: DataflowMap | undefined, mark: Set | undefined): void { +function nodeToMermaid(graph: DataflowGraph, info: DataflowGraphVertexInfo, mermaid: MermaidGraph, id: NodeId, idPrefix: string, dataflowIdMap: DataflowMap | undefined, mark: Set | undefined): void { const def = info.tag === 'variable-definition' || info.tag === 'function-definition' const fCall = info.tag === 'function-call' const defText = def ? scopeToMermaid(info.scope, info.when) : '' @@ -159,7 +159,7 @@ function nodeToMermaid(graph: DataflowGraph, info: DataflowGraphVertexInfo, merm // make the passing of root ids more performant again -function graphToMermaidGraph(rootIds: ReadonlySet, graph: DataflowGraph, dataflowIdMap: DataflowMap | undefined, prefix: string | null = 'flowchart TD', idPrefix = '', includeEnvironments = true, mark?: Set, rootGraph?: DataflowGraph): MermaidGraph { +function graphToMermaidGraph(rootIds: ReadonlySet, graph: DataflowGraph, dataflowIdMap: DataflowMap | undefined, prefix: string | null = 'flowchart TD', idPrefix = '', includeEnvironments = true, mark?: Set, rootGraph?: DataflowGraph): MermaidGraph { const mermaid: MermaidGraph = { nodeLines: prefix === null ? [] : [prefix], edgeLines: [], presentEdges: new Set(), hasBuiltIn: false, mark, rootGraph: rootGraph ?? graph, includeEnvironments } for(const [id, info] of graph.vertices(true)) { @@ -173,7 +173,7 @@ function graphToMermaidGraph(rootIds: ReadonlySet, graph: DataflowGraph, return mermaid } -export function graphToMermaid(graph: DataflowGraph, dataflowIdMap: DataflowMap | undefined, prefix: string | null = 'flowchart TD', idPrefix = '', includeEnvironments?: boolean, mark?: Set, rootGraph?: DataflowGraph): string { +export function graphToMermaid(graph: DataflowGraph, dataflowIdMap: DataflowMap | undefined, prefix: string | null = 'flowchart TD', idPrefix = '', includeEnvironments?: boolean, mark?: Set, rootGraph?: DataflowGraph): string { const mermaid = graphToMermaidGraph(graph.rootIds(), graph, dataflowIdMap, prefix, idPrefix, includeEnvironments, mark, rootGraph) return `${mermaid.nodeLines.join('\n')}\n${mermaid.edgeLines.join('\n')}` } @@ -186,7 +186,7 @@ export function graphToMermaid(graph: DataflowGraph, dataflowIdMap: DataflowMap< * @param includeEnvironments - Whether to include the environments in the mermaid graph code * @param mark - Special nodes to mark (e.g. those included in the slice) */ -export function graphToMermaidUrl(graph: DataflowGraph, dataflowIdMap: DataflowMap, includeEnvironments?: boolean, mark?: Set): string { +export function graphToMermaidUrl(graph: DataflowGraph, dataflowIdMap: DataflowMap, includeEnvironments?: boolean, mark?: Set): string { return mermaidCodeToUrl(graphToMermaid(graph, dataflowIdMap, undefined, undefined, includeEnvironments, mark)) } @@ -196,7 +196,7 @@ export interface LabeledDiffGraph { } /** uses same id map but ensures, it is different from the rhs so that mermaid can work with that */ -export function diffGraphsToMermaid(left: LabeledDiffGraph, right: LabeledDiffGraph, dataflowIdMap: DataflowMap | undefined, prefix: string): string { +export function diffGraphsToMermaid(left: LabeledDiffGraph, right: LabeledDiffGraph, dataflowIdMap: DataflowMap | undefined, prefix: string): string { // we add the prefix ourselves const leftGraph = graphToMermaid(left.graph, dataflowIdMap, '', `l-${left.label}`) const rightGraph = graphToMermaid(right.graph, dataflowIdMap, '', `r-${right.label}`) @@ -204,6 +204,6 @@ export function diffGraphsToMermaid(left: LabeledDiffGraph, right: LabeledDiffGr return `${prefix}flowchart TD\nsubgraph "${left.label}"\n${leftGraph}\nend\nsubgraph "${right.label}"\n${rightGraph}\nend` } -export function diffGraphsToMermaidUrl(left: LabeledDiffGraph, right: LabeledDiffGraph, dataflowIdMap: DataflowMap | undefined, prefix: string): string { +export function diffGraphsToMermaidUrl(left: LabeledDiffGraph, right: LabeledDiffGraph, dataflowIdMap: DataflowMap | undefined, prefix: string): string { return mermaidCodeToUrl(diffGraphsToMermaid(left, right, dataflowIdMap, prefix)) }