github.com/gotranspile/cxgo@v0.3.7/flow_viz.go (about) 1 package cxgo 2 3 import ( 4 "bytes" 5 "go/format" 6 "go/token" 7 "strconv" 8 "strings" 9 10 "github.com/emicklei/dot" 11 ) 12 13 func (cf *ControlFlow) dumpDot() string { 14 g := dot.NewGraph() 15 s := g.Node("n1").Label("begin") 16 start := cf.dumpBlockDot(g, cf.Start, make(map[Block]dot.Node)) 17 g.Edge(s, start) 18 return g.String() 19 } 20 21 func printExpr(e Expr) string { 22 buf := bytes.NewBuffer(nil) 23 fs := token.NewFileSet() 24 if err := format.Node(buf, fs, e.AsExpr()); err != nil { 25 panic(err) 26 } 27 return buf.String() 28 } 29 30 func dotAddNode(g *dot.Graph, id string, b Block) dot.Node { 31 var ( 32 label string 33 shape = "circle" 34 ) 35 switch b := b.(type) { 36 case nil: 37 panic("must not be nil") 38 case *CodeBlock: 39 label = printStmts(b.Stmts) 40 shape = "box" 41 case *CondBlock: 42 label = "if " + printExpr(b.Expr) 43 shape = "hexagon" 44 case *ReturnBlock: 45 label = printStmts([]CStmt{b.CReturnStmt}) 46 shape = "box" 47 case *SwitchBlock: 48 label = "switch " + printExpr(b.Expr) 49 shape = "trapezium" 50 default: 51 panic(b) 52 } 53 label = strings.ReplaceAll(label, "\t", " ") 54 return g.Node(id).Label(label).Attr("shape", shape) 55 } 56 57 func (cf *ControlFlow) dumpBlockDot(g *dot.Graph, b Block, seen map[Block]dot.Node) dot.Node { 58 if n, ok := seen[b]; ok { 59 return n 60 } 61 id := "n" + strconv.Itoa(len(seen)+2) // n1 is "begin" node 62 n := dotAddNode(g, id, b) 63 seen[b] = n 64 for _, p := range b.PrevBlocks() { 65 pn := cf.dumpBlockDot(g, p, seen) 66 g.Edge(n, pn).Attr("color", "#99999955") 67 } 68 switch b := b.(type) { 69 case *CondBlock: 70 g.Edge(n, cf.dumpBlockDot(g, b.Then, seen)).Attr("color", "#00aa00") 71 g.Edge(n, cf.dumpBlockDot(g, b.Else, seen)).Attr("color", "#aa0000") 72 case *CodeBlock: 73 if b.Next != nil { 74 g.Edge(n, cf.dumpBlockDot(g, b.Next, seen)) 75 } 76 case *SwitchBlock: 77 for i, c := range b.Blocks { 78 e := g.Edge(n, cf.dumpBlockDot(g, c, seen)) 79 if v := b.Cases[i]; v == nil { 80 e.Attr("color", "#aa0000") 81 } else { 82 e.Label(printExpr(v)) 83 } 84 } 85 case *ReturnBlock: 86 default: 87 panic(b) 88 } 89 return n 90 } 91 92 func (cf *ControlFlow) dumpDomDot() string { 93 g := dot.NewGraph() 94 s := g.Node("n1").Label("begin") 95 nodes := make(map[Block]dot.Node) 96 cf.eachBlock(func(b Block) { 97 id := "n" + strconv.Itoa(len(nodes)+2) // n1 is "begin" node 98 n := dotAddNode(g, id, b) 99 nodes[b] = n 100 }) 101 g.Edge(s, nodes[cf.Start]) 102 cf.eachBlock(func(b Block) { 103 d := cf.IDom(b) 104 if d == nil { 105 return 106 } 107 g.Edge(nodes[d], nodes[b]) 108 }) 109 return g.String() 110 } 111 112 func printStmts(stmts []CStmt) string { 113 var out []GoStmt 114 for _, s := range stmts { 115 out = append(out, s.AsStmt()...) 116 } 117 out = fixLabels(out) 118 119 buf := bytes.NewBuffer(nil) 120 fs := token.NewFileSet() 121 for i, s := range out { 122 if i > 0 { 123 buf.WriteByte('\n') 124 } 125 if err := format.Node(buf, fs, s); err != nil { 126 panic(err) 127 } 128 } 129 return buf.String() 130 }