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  }