github.com/switchupcb/yaegi@v0.10.2/interp/dot.go (about) 1 package interp 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "log" 8 "os/exec" 9 "path/filepath" 10 "strings" 11 ) 12 13 // astDot displays an AST in graphviz dot(1) format using dotty(1) co-process. 14 func (n *node) astDot(out io.Writer, name string) { 15 fmt.Fprintf(out, "digraph ast {\n") 16 fmt.Fprintf(out, "labelloc=\"t\"\n") 17 fmt.Fprintf(out, "label=\"%s\"\n", name) 18 n.Walk(func(n *node) bool { 19 var label string 20 switch n.kind { 21 case basicLit, identExpr: 22 label = strings.ReplaceAll(n.ident, "\"", "\\\"") 23 default: 24 if n.action != aNop { 25 label = n.action.String() 26 } else { 27 label = n.kind.String() 28 } 29 } 30 fmt.Fprintf(out, "%d [label=\"%d: %s\"]\n", n.index, n.index, label) 31 if n.anc != nil { 32 fmt.Fprintf(out, "%d -> %d\n", n.anc.index, n.index) 33 } 34 return true 35 }, nil) 36 fmt.Fprintf(out, "}\n") 37 } 38 39 // cfgDot displays a CFG in graphviz dot(1) format using dotty(1) co-process. 40 func (n *node) cfgDot(out io.Writer) { 41 fmt.Fprintf(out, "digraph cfg {\n") 42 n.Walk(nil, func(n *node) { 43 if n.kind == basicLit || n.tnext == nil { 44 return 45 } 46 var label string 47 if n.action == aNop { 48 label = "nop: end_" + n.kind.String() 49 } else { 50 label = n.action.String() 51 } 52 fmt.Fprintf(out, "%d [label=\"%d: %v %d\"]\n", n.index, n.index, label, n.findex) 53 if n.fnext != nil { 54 fmt.Fprintf(out, "%d -> %d [color=green]\n", n.index, n.tnext.index) 55 fmt.Fprintf(out, "%d -> %d [color=red]\n", n.index, n.fnext.index) 56 } else if n.tnext != nil { 57 fmt.Fprintf(out, "%d -> %d\n", n.index, n.tnext.index) 58 } 59 }) 60 fmt.Fprintf(out, "}\n") 61 } 62 63 type nopCloser struct { 64 io.Writer 65 } 66 67 func (nopCloser) Close() error { return nil } 68 69 // dotWriter returns an output stream to a dot(1) co-process where to write data in .dot format. 70 func dotWriter(dotCmd string) io.WriteCloser { 71 if dotCmd == "" { 72 return nopCloser{ioutil.Discard} 73 } 74 fields := strings.Fields(dotCmd) 75 cmd := exec.Command(fields[0], fields[1:]...) 76 dotin, err := cmd.StdinPipe() 77 if err != nil { 78 log.Fatal(err) 79 } 80 if err = cmd.Start(); err != nil { 81 log.Fatal(err) 82 } 83 return dotin 84 } 85 86 func defaultDotCmd(filePath, prefix string) string { 87 dir, fileName := filepath.Split(filePath) 88 ext := filepath.Ext(fileName) 89 if ext == "" { 90 fileName += ".dot" 91 } else { 92 fileName = strings.Replace(fileName, ext, ".dot", 1) 93 } 94 return "dot -Tdot -o" + dir + prefix + fileName 95 }