// This file is dual licensed under CC0 and The Gonum License. // // Copyright ©2017 The Gonum Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // // Copyright ©2017 Robin Eklind. // This file is made available under a Creative Commons CC0 1.0 // Universal Public Domain Dedication. package ast import ( "bytes" "fmt" ) // === [ File ] ================================================================ // A File represents a DOT file. // // Examples. // // digraph G { // A -> B // } // graph H { // C - D // } type File struct { // Graphs. Graphs []*Graph } // String returns the string representation of the file. func (f *File) String() string { buf := new(bytes.Buffer) for i, graph := range f.Graphs { if i != 0 { buf.WriteString("\n") } buf.WriteString(graph.String()) } return buf.String() } // === [ Graphs ] ============================================================== // A Graph represents a directed or an undirected graph. // // Examples. // // digraph G { // A -> {B C} // B -> C // } type Graph struct { // Strict graph; multi-edges forbidden. Strict bool // Directed graph. Directed bool // Graph ID; or empty if anonymous. ID string // Graph statements. Stmts []Stmt } // String returns the string representation of the graph. func (g *Graph) String() string { buf := new(bytes.Buffer) if g.Strict { buf.WriteString("strict ") } if g.Directed { buf.WriteString("digraph ") } else { buf.WriteString("graph ") } if len(g.ID) > 0 { fmt.Fprintf(buf, "%s ", g.ID) } buf.WriteString("{\n") for _, stmt := range g.Stmts { fmt.Fprintf(buf, "\t%s\n", stmt) } buf.WriteString("}") return buf.String() } // === [ Statements ] ========================================================== // A Stmt represents a statement, and has one of the following underlying types. // // *NodeStmt // *EdgeStmt // *AttrStmt // *Attr // *Subgraph type Stmt interface { fmt.Stringer // isStmt ensures that only statements can be assigned to the Stmt interface. isStmt() } // --- [ Node statement ] ------------------------------------------------------ // A NodeStmt represents a node statement. // // Examples. // // A [color=blue] type NodeStmt struct { // Node. Node *Node // Node attributes. Attrs []*Attr } // String returns the string representation of the node statement. func (e *NodeStmt) String() string { buf := new(bytes.Buffer) buf.WriteString(e.Node.String()) if len(e.Attrs) > 0 { buf.WriteString(" [") for i, attr := range e.Attrs { if i != 0 { buf.WriteString(" ") } buf.WriteString(attr.String()) } buf.WriteString("]") } return buf.String() } // --- [ Edge statement ] ------------------------------------------------------ // An EdgeStmt represents an edge statement. // // Examples. // // A -> B // A -> {B C} // A -> B -> C type EdgeStmt struct { // Source vertex. From Vertex // Outgoing edge. To *Edge // Edge attributes. Attrs []*Attr } // String returns the string representation of the edge statement. func (e *EdgeStmt) String() string { buf := new(bytes.Buffer) fmt.Fprintf(buf, "%s %s", e.From, e.To) if len(e.Attrs) > 0 { buf.WriteString(" [") for i, attr := range e.Attrs { if i != 0 { buf.WriteString(" ") } buf.WriteString(attr.String()) } buf.WriteString("]") } return buf.String() } // An Edge represents an edge between two vertices. type Edge struct { // Directed edge. Directed bool // Destination vertex. Vertex Vertex // Outgoing edge; or nil if none. To *Edge } // String returns the string representation of the edge. func (e *Edge) String() string { op := "--" if e.Directed { op = "->" } if e.To != nil { return fmt.Sprintf("%s %s %s", op, e.Vertex, e.To) } return fmt.Sprintf("%s %s", op, e.Vertex) } // --- [ Attribute statement ] ------------------------------------------------- // An AttrStmt represents an attribute statement. // // Examples. // // graph [rankdir=LR] // node [color=blue fillcolor=red] // edge [minlen=1] type AttrStmt struct { // Graph component kind to which the attributes are assigned. Kind Kind // Attributes. Attrs []*Attr } // String returns the string representation of the attribute statement. func (a *AttrStmt) String() string { buf := new(bytes.Buffer) fmt.Fprintf(buf, "%s [", a.Kind) for i, attr := range a.Attrs { if i != 0 { buf.WriteString(" ") } buf.WriteString(attr.String()) } buf.WriteString("]") return buf.String() } // Kind specifies the set of graph components to which attribute statements may // be assigned. type Kind uint // Graph component kinds. const ( GraphKind Kind = iota // graph NodeKind // node EdgeKind // edge ) // String returns the string representation of the graph component kind. func (k Kind) String() string { switch k { case GraphKind: return "graph" case NodeKind: return "node" case EdgeKind: return "edge" } panic(fmt.Sprintf("invalid graph component kind (%d)", k)) } // --- [ Attribute ] ----------------------------------------------------------- // An Attr represents an attribute. // // Examples. // // rank=same type Attr struct { // Attribute key. Key string // Attribute value. Val string } // String returns the string representation of the attribute. func (a *Attr) String() string { return fmt.Sprintf("%s=%s", a.Key, a.Val) } // --- [ Subgraph ] ------------------------------------------------------------ // A Subgraph represents a subgraph vertex. // // Examples. // // subgraph S {A B C} type Subgraph struct { // Subgraph ID; or empty if none. ID string // Subgraph statements. Stmts []Stmt } // String returns the string representation of the subgraph. func (s *Subgraph) String() string { buf := new(bytes.Buffer) if len(s.ID) > 0 { fmt.Fprintf(buf, "subgraph %s ", s.ID) } buf.WriteString("{") for i, stmt := range s.Stmts { if i != 0 { buf.WriteString(" ") } buf.WriteString(stmt.String()) } buf.WriteString("}") return buf.String() } // isStmt ensures that only statements can be assigned to the Stmt interface. func (*NodeStmt) isStmt() {} func (*EdgeStmt) isStmt() {} func (*AttrStmt) isStmt() {} func (*Attr) isStmt() {} func (*Subgraph) isStmt() {} // === [ Vertices ] ============================================================ // A Vertex represents a vertex, and has one of the following underlying types. // // *Node // *Subgraph type Vertex interface { fmt.Stringer // isVertex ensures that only vertices can be assigned to the Vertex // interface. isVertex() } // --- [ Node identifier ] ----------------------------------------------------- // A Node represents a node vertex. // // Examples. // // A // A:nw type Node struct { // Node ID. ID string // Node port; or nil if none. Port *Port } // String returns the string representation of the node. func (n *Node) String() string { if n.Port != nil { return fmt.Sprintf("%s%s", n.ID, n.Port) } return n.ID } // A Port specifies where on a node an edge should be aimed. type Port struct { // Port ID; or empty if none. ID string // Compass point. CompassPoint CompassPoint } // String returns the string representation of the port. func (p *Port) String() string { buf := new(bytes.Buffer) if len(p.ID) > 0 { fmt.Fprintf(buf, ":%s", p.ID) } if p.CompassPoint != CompassPointNone { fmt.Fprintf(buf, ":%s", p.CompassPoint) } return buf.String() } // CompassPoint specifies the set of compass points. type CompassPoint uint // Compass points. const ( CompassPointNone CompassPoint = iota // CompassPointNorth // n CompassPointNorthEast // ne CompassPointEast // e CompassPointSouthEast // se CompassPointSouth // s CompassPointSouthWest // sw CompassPointWest // w CompassPointNorthWest // nw CompassPointCenter // c CompassPointDefault // _ ) // String returns the string representation of the compass point. func (c CompassPoint) String() string { switch c { case CompassPointNone: return "" case CompassPointNorth: return "n" case CompassPointNorthEast: return "ne" case CompassPointEast: return "e" case CompassPointSouthEast: return "se" case CompassPointSouth: return "s" case CompassPointSouthWest: return "sw" case CompassPointWest: return "w" case CompassPointNorthWest: return "nw" case CompassPointCenter: return "c" case CompassPointDefault: return "_" } panic(fmt.Sprintf("invalid compass point (%d)", uint(c))) } // isVertex ensures that only vertices can be assigned to the Vertex interface. func (*Node) isVertex() {} func (*Subgraph) isVertex() {}