github.com/gmemcc/yaegi@v0.12.1-0.20221128122509-aa99124c5d16/interp/dot.go (about)

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