func (gen *generator) addStmt(dst encoding.Builder, stmt ast.Stmt) {
	switch stmt := stmt.(type) {
	case *ast.NodeStmt:
		n, ok := gen.node(dst, stmt.Node.ID).(encoding.AttributeSetter)
		if !ok {
			return
		}
		for _, attr := range stmt.Attrs {
			a := encoding.Attribute{
				Key:   attr.Key,
				Value: attr.Val,
			}
			if err := n.SetAttribute(a); err != nil {
				panic(fmt.Errorf("unable to unmarshal node DOT attribute (%s=%s)", a.Key, a.Value))
			}
		}
	case *ast.EdgeStmt:
		gen.addEdgeStmt(dst, stmt)
	case *ast.AttrStmt:
		var n encoding.AttributeSetter
		var dst string
		switch stmt.Kind {
		case ast.GraphKind:
			if gen.graphAttr == nil {
				return
			}
			n = gen.graphAttr
			dst = "graph"
		case ast.NodeKind:
			if gen.nodeAttr == nil {
				return
			}
			n = gen.nodeAttr
			dst = "node"
		case ast.EdgeKind:
			if gen.edgeAttr == nil {
				return
			}
			n = gen.edgeAttr
			dst = "edge"
		default:
			panic("unreachable")
		}
		for _, attr := range stmt.Attrs {
			a := encoding.Attribute{
				Key:   attr.Key,
				Value: attr.Val,
			}
			if err := n.SetAttribute(a); err != nil {
				panic(fmt.Errorf("unable to unmarshal global %s DOT attribute (%s=%s)", dst, a.Key, a.Value))
			}
		}
	case *ast.Attr:
	case *ast.Subgraph:
		for _, stmt := range stmt.Stmts {
			gen.addStmt(dst, stmt)
		}
	default:
		panic(fmt.Sprintf("unknown statement type %T", stmt))
	}
}
addStmt adds the given statement to the graph.
package dot
import (
	"fmt"
	"gonum.org/v1/gonum/graph"
	"gonum.org/v1/gonum/graph/encoding"
	"gonum.org/v1/gonum/graph/formats/dot"
	"gonum.org/v1/gonum/graph/formats/dot/ast"
	"gonum.org/v1/gonum/graph/internal/set"
)
type AttributeSetters interface {
	DOTAttributeSetters() (graph, node, edge encoding.AttributeSetter)
}
type DOTIDSetter interface {
	SetDOTID(id string)
}
func Unmarshal(data []byte, dst encoding.Builder) error {
	file, err := dot.ParseBytes(data)
	if err != nil {
		return err
	}
	if len(file.Graphs) != 1 {
		return fmt.Errorf("invalid number of graphs; expected 1, got %d", len(file.Graphs))
	}
	return copyGraph(dst, file.Graphs[0])
}
func copyGraph(dst encoding.Builder, src *ast.Graph) (err error) {
	defer func() {
		switch e := recover().(type) {
		case nil:
		case error:
			err = e
		default:
			panic(e)
		}
	}()
	gen := &generator{
		directed: src.Directed,
		ids:      make(map[string]graph.Node),
	}
	if dst, ok := dst.(DOTIDSetter); ok {
		dst.SetDOTID(src.ID)
	}
	if a, ok := dst.(AttributeSetters); ok {
		gen.graphAttr, gen.nodeAttr, gen.edgeAttr = a.DOTAttributeSetters()
	}
	for _, stmt := range src.Stmts {
		gen.addStmt(dst, stmt)
	}
	return err
}
type generator struct {
	directed bool
	ids map[string]graph.Node
	subNodes []graph.Node
	subStart []int
	graphAttr, nodeAttr, edgeAttr encoding.AttributeSetter
}
func (gen *generator) node(dst encoding.Builder, id string) graph.Node {
	if n, ok := gen.ids[id]; ok {
		return n
	}
	n := dst.NewNode()
	dst.AddNode(n)
	if n, ok := n.(DOTIDSetter); ok {
		n.SetDOTID(id)
	}
	gen.ids[id] = n
	if gen.isInSubgraph() {
		gen.appendSubgraphNode(n)
	}
	return n
}