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  }