@@ -10,6 +10,12 @@ import {Log} from "./util";
1010/**
1111 * A generic precedence graph.
1212 */
13+
14+ export interface HierarchyGraphLevel < T > {
15+ name : string ,
16+ nodes : T [ ] ,
17+ childrenLevels : Array < HierarchyGraphLevel < T > > ,
18+ }
1319export class PrecedenceGraph < T > {
1420 /**
1521 * A map from nodes to the set of their upstream neighbors.
@@ -189,10 +195,10 @@ export class PrecedenceGraph<T> {
189195 * @param edgesWithIssue An array containing arrays with [origin, effect].
190196 * Denotes edges in the graph that causes issues to the execution, will be visualized as `--x` in mermaid.
191197 */
192- toMermaidString ( edgesWithIssue ?: Array < [ T , T ] > ) : string {
198+ toMermaidString ( edgesWithIssue ?: Array < [ T , T ] > , hierarchy ?: HierarchyGraphLevel < T > ) : string {
193199 if ( edgesWithIssue == null ) edgesWithIssue = [ ] ;
194- let result = "graph" ;
195- const nodeToNumber = new Map < T , number > ( ) ;
200+ let result = "graph\n " ;
201+ const nodeToSymbolString = new Map < T , string > ( ) ;
196202 const getNodeString = ( node : T , def : string ) : string => {
197203 if ( node == null || node ?. toString === Object . prototype . toString ) {
198204 console . error (
@@ -205,27 +211,45 @@ export class PrecedenceGraph<T> {
205211 return node . toString ( ) ;
206212 } ;
207213
208- // Build a block here since we only need `counter` temporarily here
214+ if ( hierarchy != null ) {
215+ let counter = 0 ;
216+ const recurse = ( h : HierarchyGraphLevel < T > , level : number ) : void => {
217+ const indent = " " . repeat ( level ) ;
218+ result += level === 0 ? "" : `${ indent } subgraph "${ h . name } "\n` ;
219+ for ( const v of h . nodes ) {
220+ result += `${ indent } ${ counter } ["${ getNodeString ( v , String ( counter ) ) } "]\n`
221+ nodeToSymbolString . set ( v , `${ counter ++ } ` ) ;
222+ }
223+ for ( const c of h . childrenLevels ) {
224+ recurse ( c , level + 1 ) ;
225+ }
226+ result += level === 0 ? "" : `${ indent } end\n` ;
227+ }
209228
229+ recurse ( hierarchy , 0 ) ;
230+ }
231+
232+ // Build a block here since we only need `counter` temporarily here
210233 // We use numbers instead of names of reactors directly as node names
211234 // in mermaid.js because mermaid has strict restrictions regarding
212235 // what could be used as names of the node.
213236 {
214237 let counter = 0 ;
215238 for ( const v of this . getNodes ( ) ) {
216- result += `\n${ counter } ["${ getNodeString ( v , String ( counter ) ) } "]` ;
217- nodeToNumber . set ( v , counter ++ ) ;
239+ if ( nodeToSymbolString . has ( v ) ) { continue ; }
240+ result += `\nmissing${ counter } ["${ getNodeString ( v , String ( counter ) ) } "]` ;
241+ nodeToSymbolString . set ( v , `missing${ counter ++ } ` ) ;
218242 }
219243 }
220244 // This is the effect
221245 for ( const s of this . getNodes ( ) ) {
222246 // This is the origin
223247 for ( const t of this . getUpstreamNeighbors ( s ) ) {
224- result += `\n${ nodeToNumber . get ( t ) } ` ;
248+ result += `\n${ nodeToSymbolString . get ( t ) } ` ;
225249 result += edgesWithIssue . some ( ( v ) => v [ 0 ] === t && v [ 1 ] === s )
226250 ? " --x "
227251 : " --> " ;
228- result += `${ nodeToNumber . get ( s ) } ` ;
252+ result += `${ nodeToSymbolString . get ( s ) } ` ;
229253 }
230254 }
231255 return result ;
0 commit comments