summaryrefslogtreecommitdiff
path: root/parser/dot.go
blob: f486cd5e6e854592b37cacb7abdd559bbea5e791 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package parser

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os/exec"
	"strings"
)

func (*Parser) Adot(nodes []*Node, c string) {
	if c == "" {
		return
	}
	n := &Node{Child: nodes}
	n.Dot(c, "")
}

func (n *Node) Dot(c, s string) {
	dw, cmd := dotWriter(c)
	n.astDot(dw, s)
	if cmd == nil {
		return
	}
	if err := cmd.Wait(); err != nil {
		log.Fatal(err)
	}
}

func (n *Node) Sdot(s string) string {
	var buf bytes.Buffer
	n.astDot(&buf, s)
	return buf.String()
}

// TODO: rewrite it using Walk2
func (n *Node) astDot(out io.Writer, label string) {
	fmt.Fprintf(out, "digraph ast { ")
	if label != "" {
		fmt.Fprintf(out, "labelloc=\"t\"; label=\"%s\";", label)
	}
	anc := map[*Node]*Node{}
	index := map[*Node]int{}
	count := 0
	n.Walk(func(nod *Node) bool {
		index[nod] = count
		count++

		for _, c := range nod.Child {
			anc[c] = nod
		}
		name := strings.ReplaceAll(nod.Name(), `"`, `\"`)
		fmt.Fprintf(out, "%d [label=\"%s\"]; ", index[nod], name)
		if anc[nod] != nil {
			fmt.Fprintf(out, "%d -> %d; ", index[anc[nod]], index[nod])
		}
		return true
	}, nil)
	fmt.Fprintf(out, "}")
	if c, ok := out.(io.Closer); ok {
		c.Close()
	}
}

type nopCloser struct {
	io.Writer
}

func (nopCloser) Close() error { return nil }

func dotWriter(dotCmd string) (io.WriteCloser, *exec.Cmd) {
	if dotCmd == "" {
		return nopCloser{io.Discard}, nil
	}
	fields := strings.Fields(dotCmd)
	cmd := exec.Command(fields[0], fields[1:]...)
	dotin, err := cmd.StdinPipe()
	if err != nil {
		log.Fatal(err)
	}
	if err = cmd.Start(); err != nil {
		log.Fatal(err)
	}
	return dotin, cmd
}