mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-06-02 04:50:49 +00:00
Conver to regular Go vendor + dep tool
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
||||
script:
|
||||
- go build ./...
|
||||
- go test ./...
|
||||
- gofmt -l -s -w .
|
||||
- git diff --exit-code
|
||||
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.7.1
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
# This is the official list of GoGraphviz authors for copyright purposes.
|
||||
# This file is distinct from the CONTRIBUTORS file, which
|
||||
# lists people. For example, employees are listed in CONTRIBUTORS,
|
||||
# but not in AUTHORS, because the employer holds the copyright.
|
||||
|
||||
# Names should be added to this file as one of
|
||||
# Organization's name
|
||||
# Individual's name <submission email address>
|
||||
# Individual's name <submission email address> <email2> <emailN>
|
||||
|
||||
# Please keep the list sorted.
|
||||
|
||||
Vastech SA (PTY) LTD
|
||||
Xavier Chassin <xavier.chassin@live.fr>
|
||||
+1
@@ -0,0 +1 @@
|
||||
Walter Schulze <awalterschulze@gmail.com>
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
Copyright 2013 GoGraphviz Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Portions of gocc's source code has been derived from Go, and are covered by the
|
||||
following license:
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
Parses the Graphviz DOT language and creates an interface, in golang, with which to easily create new and manipulate existing graphs which can be written back to the DOT format.
|
||||
|
||||
This parser has been created using [gocc](http://code.google.com/p/gocc).
|
||||
|
||||
### Example (Parse and Edit) ###
|
||||
|
||||
```
|
||||
graphAst, _ := parser.ParseString(`digraph G {}`)
|
||||
graph := NewGraph()
|
||||
Analyse(graphAst, graph)
|
||||
graph.AddNode("G", "a", nil)
|
||||
graph.AddNode("G", "b", nil)
|
||||
graph.AddEdge("a", "b", true, nil)
|
||||
output := graph.String()
|
||||
```
|
||||
|
||||
### Documentation ###
|
||||
|
||||
The [godoc](https://godoc.org/github.com/awalterschulze/gographviz) includes some more examples.
|
||||
|
||||
### Installation ###
|
||||
go get github.com/awalterschulze/gographviz
|
||||
|
||||
### Tests ###
|
||||
|
||||
[](https://travis-ci.org/awalterschulze/gographviz)
|
||||
|
||||
### Users ###
|
||||
|
||||
[aptly](https://github.com/smira/aptly) - Debian repository management tool
|
||||
|
||||
### Mentions ###
|
||||
|
||||
[Using Golang and GraphViz to Visualize Complex Grails Applications](http://ilikeorangutans.github.io/2014/05/03/using-golang-and-graphviz-to-visualize-complex-grails-applications/)
|
||||
+168
@@ -0,0 +1,168 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package gographviz
|
||||
|
||||
import (
|
||||
"github.com/awalterschulze/gographviz/ast"
|
||||
)
|
||||
|
||||
//Creates a Graph structure by analysing an Abstract Syntax Tree representing a parsed graph.
|
||||
func NewAnalysedGraph(graph *ast.Graph) Interface {
|
||||
g := NewGraph()
|
||||
Analyse(graph, g)
|
||||
return g
|
||||
}
|
||||
|
||||
//Analyses an Abstract Syntax Tree representing a parsed graph into a newly created graph structure Interface.
|
||||
func Analyse(graph *ast.Graph, g Interface) {
|
||||
graph.Walk(&graphVisitor{g})
|
||||
}
|
||||
|
||||
type nilVisitor struct {
|
||||
}
|
||||
|
||||
func (this *nilVisitor) Visit(v ast.Elem) ast.Visitor {
|
||||
return this
|
||||
}
|
||||
|
||||
type graphVisitor struct {
|
||||
g Interface
|
||||
}
|
||||
|
||||
func (this *graphVisitor) Visit(v ast.Elem) ast.Visitor {
|
||||
graph, ok := v.(*ast.Graph)
|
||||
if !ok {
|
||||
return this
|
||||
}
|
||||
this.g.SetStrict(graph.Strict)
|
||||
this.g.SetDir(graph.Type == ast.DIGRAPH)
|
||||
graphName := graph.Id.String()
|
||||
this.g.SetName(graphName)
|
||||
return newStmtVisitor(this.g, graphName)
|
||||
}
|
||||
|
||||
func newStmtVisitor(g Interface, graphName string) *stmtVisitor {
|
||||
return &stmtVisitor{g, graphName, make(Attrs), make(Attrs), make(Attrs)}
|
||||
}
|
||||
|
||||
type stmtVisitor struct {
|
||||
g Interface
|
||||
graphName string
|
||||
currentNodeAttrs Attrs
|
||||
currentEdgeAttrs Attrs
|
||||
currentGraphAttrs Attrs
|
||||
}
|
||||
|
||||
func (this *stmtVisitor) Visit(v ast.Elem) ast.Visitor {
|
||||
switch s := v.(type) {
|
||||
case ast.NodeStmt:
|
||||
return this.nodeStmt(s)
|
||||
case ast.EdgeStmt:
|
||||
return this.edgeStmt(s)
|
||||
case ast.NodeAttrs:
|
||||
return this.nodeAttrs(s)
|
||||
case ast.EdgeAttrs:
|
||||
return this.edgeAttrs(s)
|
||||
case ast.GraphAttrs:
|
||||
return this.graphAttrs(s)
|
||||
case *ast.SubGraph:
|
||||
return this.subGraph(s)
|
||||
case *ast.Attr:
|
||||
return this.attr(s)
|
||||
case ast.AttrList:
|
||||
return &nilVisitor{}
|
||||
default:
|
||||
//fmt.Fprintf(os.Stderr, "unknown stmt %T\n", v)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
func ammend(attrs Attrs, add Attrs) Attrs {
|
||||
for key, value := range add {
|
||||
if _, ok := attrs[key]; !ok {
|
||||
attrs[key] = value
|
||||
}
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
func overwrite(attrs Attrs, overwrite Attrs) Attrs {
|
||||
for key, value := range overwrite {
|
||||
attrs[key] = value
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
func (this *stmtVisitor) nodeStmt(stmt ast.NodeStmt) ast.Visitor {
|
||||
attrs := Attrs(stmt.Attrs.GetMap())
|
||||
attrs = ammend(attrs, this.currentNodeAttrs)
|
||||
this.g.AddNode(this.graphName, stmt.NodeId.String(), attrs)
|
||||
return &nilVisitor{}
|
||||
}
|
||||
|
||||
func (this *stmtVisitor) edgeStmt(stmt ast.EdgeStmt) ast.Visitor {
|
||||
attrs := stmt.Attrs.GetMap()
|
||||
attrs = ammend(attrs, this.currentEdgeAttrs)
|
||||
src := stmt.Source.GetId()
|
||||
srcName := src.String()
|
||||
if stmt.Source.IsNode() {
|
||||
this.g.AddNode(this.graphName, srcName, this.currentNodeAttrs.Copy())
|
||||
}
|
||||
srcPort := stmt.Source.GetPort()
|
||||
for i := range stmt.EdgeRHS {
|
||||
directed := bool(stmt.EdgeRHS[i].Op)
|
||||
dst := stmt.EdgeRHS[i].Destination.GetId()
|
||||
dstName := dst.String()
|
||||
if stmt.EdgeRHS[i].Destination.IsNode() {
|
||||
this.g.AddNode(this.graphName, dstName, this.currentNodeAttrs.Copy())
|
||||
}
|
||||
dstPort := stmt.EdgeRHS[i].Destination.GetPort()
|
||||
this.g.AddPortEdge(srcName, srcPort.String(), dstName, dstPort.String(), directed, attrs)
|
||||
src = dst
|
||||
srcPort = dstPort
|
||||
srcName = dstName
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
func (this *stmtVisitor) nodeAttrs(stmt ast.NodeAttrs) ast.Visitor {
|
||||
this.currentNodeAttrs = overwrite(this.currentNodeAttrs, ast.AttrList(stmt).GetMap())
|
||||
return &nilVisitor{}
|
||||
}
|
||||
|
||||
func (this *stmtVisitor) edgeAttrs(stmt ast.EdgeAttrs) ast.Visitor {
|
||||
this.currentEdgeAttrs = overwrite(this.currentEdgeAttrs, ast.AttrList(stmt).GetMap())
|
||||
return &nilVisitor{}
|
||||
}
|
||||
|
||||
func (this *stmtVisitor) graphAttrs(stmt ast.GraphAttrs) ast.Visitor {
|
||||
attrs := ast.AttrList(stmt).GetMap()
|
||||
for key, value := range attrs {
|
||||
this.g.AddAttr(this.graphName, key, value)
|
||||
}
|
||||
this.currentGraphAttrs = overwrite(this.currentGraphAttrs, attrs)
|
||||
return &nilVisitor{}
|
||||
}
|
||||
|
||||
func (this *stmtVisitor) subGraph(stmt *ast.SubGraph) ast.Visitor {
|
||||
subGraphName := stmt.Id.String()
|
||||
this.g.AddSubGraph(this.graphName, subGraphName, this.currentGraphAttrs)
|
||||
return newStmtVisitor(this.g, subGraphName)
|
||||
}
|
||||
|
||||
func (this *stmtVisitor) attr(stmt *ast.Attr) ast.Visitor {
|
||||
this.g.AddAttr(this.graphName, stmt.Field.String(), stmt.Value.String())
|
||||
return this
|
||||
}
|
||||
+318
@@ -0,0 +1,318 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package gographviz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/awalterschulze/gographviz/parser"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func (this *Nodes) String() string {
|
||||
s := "Nodes:"
|
||||
for i := range this.Nodes {
|
||||
s += fmt.Sprintf("Node{%v}", this.Nodes[i])
|
||||
}
|
||||
return s + "\n"
|
||||
}
|
||||
|
||||
func (this *Edges) String() string {
|
||||
s := "Edges:"
|
||||
for i := range this.Edges {
|
||||
s += fmt.Sprintf("Edge{%v}", this.Edges[i])
|
||||
}
|
||||
return s + "\n"
|
||||
}
|
||||
|
||||
func check(t *testing.T, err error) {
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func assert(t *testing.T, msg string, v1 interface{}, v2 interface{}) {
|
||||
if v1 != v2 {
|
||||
t.Fatalf("%v %v != %v", msg, v1, v2)
|
||||
}
|
||||
}
|
||||
|
||||
func anal(t *testing.T, input string) Interface {
|
||||
t.Logf("Input: %v\n", input)
|
||||
g, err := parser.ParseString(input)
|
||||
check(t, err)
|
||||
t.Logf("Parsed: %v\n", g)
|
||||
ag := NewGraph()
|
||||
Analyse(g, ag)
|
||||
t.Logf("Analysed: %v\n", ag)
|
||||
agstr := ag.String()
|
||||
t.Logf("Written: %v\n", agstr)
|
||||
g2, err := parser.ParseString(agstr)
|
||||
check(t, err)
|
||||
t.Logf("Parsed %v\n", g2)
|
||||
ag2 := NewEscape()
|
||||
Analyse(g2, ag2)
|
||||
t.Logf("Analysed %v\n", ag2)
|
||||
ag2str := ag2.String()
|
||||
t.Logf("Written: %v\n", ag2str)
|
||||
assert(t, "analysed", agstr, ag2str)
|
||||
return ag2
|
||||
}
|
||||
|
||||
func analfile(t *testing.T, filename string) Interface {
|
||||
f, err := os.Open(filename)
|
||||
check(t, err)
|
||||
all, err := ioutil.ReadAll(f)
|
||||
check(t, err)
|
||||
return anal(t, string(all))
|
||||
}
|
||||
|
||||
func analtest(t *testing.T, testname string) Interface {
|
||||
return analfile(t, "./testdata/"+testname)
|
||||
}
|
||||
|
||||
func TestHelloWorldString(t *testing.T) {
|
||||
input := `digraph G {Hello->World}`
|
||||
anal(t, input)
|
||||
}
|
||||
|
||||
func TestHelloWorldFile(t *testing.T) {
|
||||
analfile(t, "./testdata/helloworld.gv.txt")
|
||||
}
|
||||
|
||||
func TestAttr(t *testing.T) {
|
||||
anal(t,
|
||||
"digraph finite_state { rankdir = LR }")
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
anal(t,
|
||||
`digraph finite_state { rankdir = "LR" }`)
|
||||
}
|
||||
|
||||
func TestAttrList(t *testing.T) {
|
||||
anal(t, `
|
||||
digraph { node [ shape = doublecircle ] }`)
|
||||
}
|
||||
|
||||
func TestStringLit(t *testing.T) {
|
||||
anal(t, `digraph finite_state_machine {
|
||||
size= "8" ; }`)
|
||||
}
|
||||
|
||||
func TestHashComments(t *testing.T) {
|
||||
anal(t, `## bla \n
|
||||
digraph G {Hello->World}`)
|
||||
}
|
||||
|
||||
func TestIntLit(t *testing.T) {
|
||||
anal(t, `graph G {
|
||||
1 -- 30 [f=1];}`)
|
||||
}
|
||||
|
||||
func TestFloat1(t *testing.T) {
|
||||
anal(t, `digraph { bla = 2.0 }`)
|
||||
}
|
||||
|
||||
func TestFloat2(t *testing.T) {
|
||||
anal(t, `digraph { bla = .1 }`)
|
||||
}
|
||||
|
||||
func TestNegative(t *testing.T) {
|
||||
anal(t, `digraph { -2 -> -1 }`)
|
||||
}
|
||||
|
||||
func TestUnderscore(t *testing.T) {
|
||||
anal(t, `digraph { a_b = 1 }`)
|
||||
}
|
||||
|
||||
func TestNonAscii(t *testing.T) {
|
||||
anal(t, `digraph { label=T�th }`)
|
||||
}
|
||||
|
||||
func TestPorts(t *testing.T) {
|
||||
anal(t, `digraph { "node6":f0 -> "node9":f1 }`)
|
||||
}
|
||||
|
||||
func TestHtml(t *testing.T) {
|
||||
anal(t, `digraph { a = <<table></table>> }`)
|
||||
}
|
||||
|
||||
func TestIdWithKeyword(t *testing.T) {
|
||||
anal(t, `digraph { edgeURL = "a" }`)
|
||||
}
|
||||
|
||||
func TestSubGraph(t *testing.T) {
|
||||
anal(t, `digraph { subgraph { a -> b } }`)
|
||||
}
|
||||
|
||||
func TestImplicitSubGraph(t *testing.T) {
|
||||
anal(t, `digraph { { a -> b } }`)
|
||||
}
|
||||
|
||||
func TestEdges(t *testing.T) {
|
||||
anal(t, `digraph { a0 -> a1 -> a2 -> a3 }`)
|
||||
}
|
||||
|
||||
func TestEasyFsm1(t *testing.T) {
|
||||
anal(t, `digraph finite_state_machine {
|
||||
rankdir=LR;
|
||||
size="8,5";
|
||||
node [shape = circle];
|
||||
LR_0 -> LR_2 [ label = "SS(B)" ];
|
||||
LR_0 -> LR_1 [ label = "SS(S)" ];
|
||||
LR_1 -> LR_3 [ label = "S($end)" ];
|
||||
LR_2 -> LR_6 [ label = "SS(b)" ];
|
||||
LR_2 -> LR_5 [ label = "SS(a)" ];
|
||||
LR_2 -> LR_4 [ label = "S(A)" ];
|
||||
LR_5 -> LR_7 [ label = "S(b)" ];
|
||||
LR_5 -> LR_5 [ label = "S(a)" ];
|
||||
LR_6 -> LR_6 [ label = "S(b)" ];
|
||||
LR_6 -> LR_5 [ label = "S(a)" ];
|
||||
LR_7 -> LR_8 [ label = "S(b)" ];
|
||||
LR_7 -> LR_5 [ label = "S(a)" ];
|
||||
LR_8 -> LR_6 [ label = "S(b)" ];
|
||||
LR_8 -> LR_5 [ label = "S(a)" ];
|
||||
}`)
|
||||
}
|
||||
|
||||
//node [shape = doublecircle]; LR_0 LR_3 LR_4 LR_8; should be applied to the nodes
|
||||
func TestEasyFsm2(t *testing.T) {
|
||||
anal(t, `digraph finite_state_machine {
|
||||
rankdir=LR;
|
||||
size="8,5";
|
||||
node [shape = doublecircle]; LR_0 LR_3 LR_4 LR_8;
|
||||
node [shape = circle];
|
||||
LR_0 -> LR_2 [ label = "SS(B)" ];
|
||||
LR_0 -> LR_1 [ label = "SS(S)" ];
|
||||
LR_1 -> LR_3 [ label = "S($end)" ];
|
||||
LR_2 -> LR_6 [ label = "SS(b)" ];
|
||||
LR_2 -> LR_5 [ label = "SS(a)" ];
|
||||
LR_2 -> LR_4 [ label = "S(A)" ];
|
||||
LR_5 -> LR_7 [ label = "S(b)" ];
|
||||
LR_5 -> LR_5 [ label = "S(a)" ];
|
||||
LR_6 -> LR_6 [ label = "S(b)" ];
|
||||
LR_6 -> LR_5 [ label = "S(a)" ];
|
||||
LR_7 -> LR_8 [ label = "S(b)" ];
|
||||
LR_7 -> LR_5 [ label = "S(a)" ];
|
||||
LR_8 -> LR_6 [ label = "S(b)" ];
|
||||
LR_8 -> LR_5 [ label = "S(a)" ];
|
||||
}`)
|
||||
}
|
||||
|
||||
func TestEmptyAttrList(t *testing.T) {
|
||||
anal(t, `digraph g { edge [ ] }`)
|
||||
}
|
||||
|
||||
func TestHelloWorld(t *testing.T) {
|
||||
analtest(t, "helloworld.gv.txt")
|
||||
}
|
||||
|
||||
func TestCluster(t *testing.T) {
|
||||
analtest(t, "cluster.gv.txt")
|
||||
}
|
||||
|
||||
func TestPsg(t *testing.T) {
|
||||
analtest(t, "psg.gv.txt")
|
||||
}
|
||||
|
||||
func TestTransparency(t *testing.T) {
|
||||
analtest(t, "transparency.gv.txt")
|
||||
}
|
||||
|
||||
func TestCrazy(t *testing.T) {
|
||||
analtest(t, "crazy.gv.txt")
|
||||
}
|
||||
|
||||
func TestKennedyanc(t *testing.T) {
|
||||
analtest(t, "kennedyanc.gv.txt")
|
||||
}
|
||||
|
||||
func TestRoot(t *testing.T) {
|
||||
analtest(t, "root.gv.txt")
|
||||
}
|
||||
|
||||
func TestTwpoi(t *testing.T) {
|
||||
analtest(t, "twopi.gv.txt")
|
||||
}
|
||||
|
||||
func TestDataStruct(t *testing.T) {
|
||||
analtest(t, "datastruct.gv.txt")
|
||||
}
|
||||
|
||||
func TestLionShare(t *testing.T) {
|
||||
analtest(t, "lion_share.gv.txt")
|
||||
}
|
||||
|
||||
func TestSdh(t *testing.T) {
|
||||
analtest(t, "sdh.gv.txt")
|
||||
}
|
||||
|
||||
func TestUnix(t *testing.T) {
|
||||
analtest(t, "unix.gv.txt")
|
||||
}
|
||||
|
||||
func TestEr(t *testing.T) {
|
||||
analtest(t, "er.gv.txt")
|
||||
}
|
||||
|
||||
func TestNerworkMapTwopi(t *testing.T) {
|
||||
analtest(t, "networkmap_twopi.gv.txt")
|
||||
}
|
||||
|
||||
func TestSibling(t *testing.T) {
|
||||
analtest(t, "siblings.gv.txt")
|
||||
}
|
||||
|
||||
func TestWorld(t *testing.T) {
|
||||
analtest(t, "world.gv.txt")
|
||||
}
|
||||
|
||||
func TestFdpclust(t *testing.T) {
|
||||
analtest(t, "fdpclust.gv.txt")
|
||||
}
|
||||
|
||||
func TestPhilo(t *testing.T) {
|
||||
analtest(t, "philo.gv.txt")
|
||||
}
|
||||
|
||||
func TestSoftmaint(t *testing.T) {
|
||||
analtest(t, "softmaint.gv.txt")
|
||||
}
|
||||
|
||||
func TestFsm(t *testing.T) {
|
||||
analtest(t, "fsm.gv.txt")
|
||||
}
|
||||
|
||||
func TestProcess(t *testing.T) {
|
||||
analtest(t, "process.gv.txt")
|
||||
}
|
||||
|
||||
func TestSwitchGv(t *testing.T) {
|
||||
analtest(t, "switch.gv.txt")
|
||||
}
|
||||
|
||||
func TestGd19942007(t *testing.T) {
|
||||
analtest(t, "gd_1994_2007.gv.txt")
|
||||
}
|
||||
|
||||
func TestProfile(t *testing.T) {
|
||||
analtest(t, "profile.gv.txt")
|
||||
}
|
||||
|
||||
func TestTrafficLights(t *testing.T) {
|
||||
analtest(t, "traffic_lights.gv.txt")
|
||||
}
|
||||
+690
@@ -0,0 +1,690 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
//Abstract Syntax Tree representing the DOT grammar
|
||||
package ast
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/awalterschulze/gographviz/token"
|
||||
"math/rand"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
r = rand.New(rand.NewSource(1234))
|
||||
)
|
||||
|
||||
type Visitor interface {
|
||||
Visit(e Elem) Visitor
|
||||
}
|
||||
|
||||
type Elem interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
type Walkable interface {
|
||||
Walk(v Visitor)
|
||||
}
|
||||
|
||||
type Bool bool
|
||||
|
||||
const (
|
||||
FALSE = Bool(false)
|
||||
TRUE = Bool(true)
|
||||
)
|
||||
|
||||
func (this Bool) String() string {
|
||||
switch this {
|
||||
case false:
|
||||
return "false"
|
||||
case true:
|
||||
return "true"
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func (this Bool) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v.Visit(this)
|
||||
}
|
||||
|
||||
type GraphType bool
|
||||
|
||||
const (
|
||||
GRAPH = GraphType(false)
|
||||
DIGRAPH = GraphType(true)
|
||||
)
|
||||
|
||||
func (this GraphType) String() string {
|
||||
switch this {
|
||||
case false:
|
||||
return "graph"
|
||||
case true:
|
||||
return "digraph"
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func (this GraphType) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v.Visit(this)
|
||||
}
|
||||
|
||||
type Graph struct {
|
||||
Type GraphType
|
||||
Strict bool
|
||||
Id Id
|
||||
StmtList StmtList
|
||||
}
|
||||
|
||||
func NewGraph(t, strict, id, l Elem) (*Graph, error) {
|
||||
g := &Graph{Type: t.(GraphType), Strict: bool(strict.(Bool)), Id: Id("")}
|
||||
if id != nil {
|
||||
g.Id = id.(Id)
|
||||
}
|
||||
if l != nil {
|
||||
g.StmtList = l.(StmtList)
|
||||
}
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func (this *Graph) String() string {
|
||||
s := this.Type.String() + " " + this.Id.String() + " {\n"
|
||||
if this.StmtList != nil {
|
||||
s += this.StmtList.String()
|
||||
}
|
||||
s += "\n}\n"
|
||||
return s
|
||||
}
|
||||
|
||||
func (this *Graph) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v = v.Visit(this)
|
||||
this.Type.Walk(v)
|
||||
this.Id.Walk(v)
|
||||
this.StmtList.Walk(v)
|
||||
}
|
||||
|
||||
type StmtList []Stmt
|
||||
|
||||
func NewStmtList(s Elem) (StmtList, error) {
|
||||
ss := make(StmtList, 1)
|
||||
ss[0] = s.(Stmt)
|
||||
return ss, nil
|
||||
}
|
||||
|
||||
func AppendStmtList(ss, s Elem) (StmtList, error) {
|
||||
this := ss.(StmtList)
|
||||
this = append(this, s.(Stmt))
|
||||
return this, nil
|
||||
}
|
||||
|
||||
func (this StmtList) String() string {
|
||||
if len(this) == 0 {
|
||||
return ""
|
||||
}
|
||||
s := ""
|
||||
for i := 0; i < len(this); i++ {
|
||||
ss := this[i].String()
|
||||
if len(ss) > 0 {
|
||||
s += "\t" + ss + ";\n"
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (this StmtList) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v = v.Visit(this)
|
||||
for i := range this {
|
||||
this[i].Walk(v)
|
||||
}
|
||||
}
|
||||
|
||||
type Stmt interface {
|
||||
Elem
|
||||
Walkable
|
||||
isStmt()
|
||||
}
|
||||
|
||||
func (this NodeStmt) isStmt() {}
|
||||
func (this EdgeStmt) isStmt() {}
|
||||
func (this EdgeAttrs) isStmt() {}
|
||||
func (this NodeAttrs) isStmt() {}
|
||||
func (this GraphAttrs) isStmt() {}
|
||||
func (this *SubGraph) isStmt() {}
|
||||
func (this *Attr) isStmt() {}
|
||||
|
||||
type SubGraph struct {
|
||||
Id Id
|
||||
StmtList StmtList
|
||||
}
|
||||
|
||||
func NewSubGraph(id, l Elem) (*SubGraph, error) {
|
||||
g := &SubGraph{Id: Id(fmt.Sprintf("anon%d", r.Int63()))}
|
||||
if id != nil {
|
||||
if len(id.(Id)) > 0 {
|
||||
g.Id = id.(Id)
|
||||
}
|
||||
}
|
||||
if l != nil {
|
||||
g.StmtList = l.(StmtList)
|
||||
}
|
||||
return g, nil
|
||||
}
|
||||
|
||||
func (this *SubGraph) GetId() Id {
|
||||
return this.Id
|
||||
}
|
||||
|
||||
func (this *SubGraph) GetPort() Port {
|
||||
port, err := NewPort(nil, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
func (this *SubGraph) String() string {
|
||||
gName := this.Id.String()
|
||||
if strings.HasPrefix(gName, "anon") {
|
||||
gName = ""
|
||||
}
|
||||
s := "subgraph " + this.Id.String() + " {\n"
|
||||
if this.StmtList != nil {
|
||||
s += this.StmtList.String()
|
||||
}
|
||||
s += "\n}\n"
|
||||
return s
|
||||
}
|
||||
|
||||
func (this *SubGraph) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v = v.Visit(this)
|
||||
this.Id.Walk(v)
|
||||
this.StmtList.Walk(v)
|
||||
}
|
||||
|
||||
type EdgeAttrs AttrList
|
||||
|
||||
func NewEdgeAttrs(a Elem) (EdgeAttrs, error) {
|
||||
return EdgeAttrs(a.(AttrList)), nil
|
||||
}
|
||||
|
||||
func (this EdgeAttrs) String() string {
|
||||
s := AttrList(this).String()
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
return `edge ` + s
|
||||
}
|
||||
|
||||
func (this EdgeAttrs) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v = v.Visit(this)
|
||||
for i := range this {
|
||||
this[i].Walk(v)
|
||||
}
|
||||
}
|
||||
|
||||
type NodeAttrs AttrList
|
||||
|
||||
func NewNodeAttrs(a Elem) (NodeAttrs, error) {
|
||||
return NodeAttrs(a.(AttrList)), nil
|
||||
}
|
||||
|
||||
func (this NodeAttrs) String() string {
|
||||
s := AttrList(this).String()
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
return `node ` + s
|
||||
}
|
||||
|
||||
func (this NodeAttrs) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v = v.Visit(this)
|
||||
for i := range this {
|
||||
this[i].Walk(v)
|
||||
}
|
||||
}
|
||||
|
||||
type GraphAttrs AttrList
|
||||
|
||||
func NewGraphAttrs(a Elem) (GraphAttrs, error) {
|
||||
return GraphAttrs(a.(AttrList)), nil
|
||||
}
|
||||
|
||||
func (this GraphAttrs) String() string {
|
||||
s := AttrList(this).String()
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
return `graph ` + s
|
||||
}
|
||||
|
||||
func (this GraphAttrs) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v = v.Visit(this)
|
||||
for i := range this {
|
||||
this[i].Walk(v)
|
||||
}
|
||||
}
|
||||
|
||||
type AttrList []AList
|
||||
|
||||
func NewAttrList(a Elem) (AttrList, error) {
|
||||
as := make(AttrList, 0)
|
||||
if a != nil {
|
||||
as = append(as, a.(AList))
|
||||
}
|
||||
return as, nil
|
||||
}
|
||||
|
||||
func AppendAttrList(as, a Elem) (AttrList, error) {
|
||||
this := as.(AttrList)
|
||||
if a == nil {
|
||||
return this, nil
|
||||
}
|
||||
this = append(this, a.(AList))
|
||||
return this, nil
|
||||
}
|
||||
|
||||
func (this AttrList) String() string {
|
||||
s := ""
|
||||
for _, alist := range this {
|
||||
ss := alist.String()
|
||||
if len(ss) > 0 {
|
||||
s += "[ " + ss + " ] "
|
||||
}
|
||||
}
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (this AttrList) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v = v.Visit(this)
|
||||
for i := range this {
|
||||
this[i].Walk(v)
|
||||
}
|
||||
}
|
||||
|
||||
func PutMap(attrmap map[string]string) AttrList {
|
||||
attrlist := make(AttrList, 1)
|
||||
attrlist[0] = make(AList, 0)
|
||||
keys := make([]string, 0, len(attrmap))
|
||||
for key := range attrmap {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, name := range keys {
|
||||
value := attrmap[name]
|
||||
attrlist[0] = append(attrlist[0], &Attr{Id(name), Id(value)})
|
||||
}
|
||||
return attrlist
|
||||
}
|
||||
|
||||
func (this AttrList) GetMap() map[string]string {
|
||||
attrs := make(map[string]string)
|
||||
for _, alist := range this {
|
||||
for _, attr := range alist {
|
||||
attrs[attr.Field.String()] = attr.Value.String()
|
||||
}
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
|
||||
type AList []*Attr
|
||||
|
||||
func NewAList(a Elem) (AList, error) {
|
||||
as := make(AList, 1)
|
||||
as[0] = a.(*Attr)
|
||||
return as, nil
|
||||
}
|
||||
|
||||
func AppendAList(as, a Elem) (AList, error) {
|
||||
this := as.(AList)
|
||||
attr := a.(*Attr)
|
||||
this = append(this, attr)
|
||||
return this, nil
|
||||
}
|
||||
|
||||
func (this AList) String() string {
|
||||
if len(this) == 0 {
|
||||
return ""
|
||||
}
|
||||
str := this[0].String()
|
||||
for i := 1; i < len(this); i++ {
|
||||
str += `, ` + this[i].String()
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func (this AList) Walk(v Visitor) {
|
||||
v = v.Visit(this)
|
||||
for i := range this {
|
||||
this[i].Walk(v)
|
||||
}
|
||||
}
|
||||
|
||||
type Attr struct {
|
||||
Field Id
|
||||
Value Id
|
||||
}
|
||||
|
||||
func NewAttr(f, v Elem) (*Attr, error) {
|
||||
a := &Attr{Field: f.(Id)}
|
||||
a.Value = Id("true")
|
||||
if v != nil {
|
||||
ok := false
|
||||
a.Value, ok = v.(Id)
|
||||
if !ok {
|
||||
return nil, errors.New(fmt.Sprintf("value = %v", v))
|
||||
}
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (this *Attr) String() string {
|
||||
return this.Field.String() + `=` + this.Value.String()
|
||||
}
|
||||
|
||||
func (this *Attr) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v = v.Visit(this)
|
||||
this.Field.Walk(v)
|
||||
this.Value.Walk(v)
|
||||
}
|
||||
|
||||
type Location interface {
|
||||
Elem
|
||||
Walkable
|
||||
isLocation()
|
||||
GetId() Id
|
||||
GetPort() Port
|
||||
IsNode() bool
|
||||
}
|
||||
|
||||
func (this *NodeId) isLocation() {}
|
||||
func (this *NodeId) IsNode() bool { return true }
|
||||
func (this *SubGraph) isLocation() {}
|
||||
func (this *SubGraph) IsNode() bool { return false }
|
||||
|
||||
type EdgeStmt struct {
|
||||
Source Location
|
||||
EdgeRHS EdgeRHS
|
||||
Attrs AttrList
|
||||
}
|
||||
|
||||
func NewEdgeStmt(id, e, attrs Elem) (*EdgeStmt, error) {
|
||||
var a AttrList = nil
|
||||
var err error = nil
|
||||
if attrs == nil {
|
||||
a, err = NewAttrList(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
a = attrs.(AttrList)
|
||||
}
|
||||
return &EdgeStmt{id.(Location), e.(EdgeRHS), a}, nil
|
||||
}
|
||||
|
||||
func (this EdgeStmt) String() string {
|
||||
return strings.TrimSpace(this.Source.String() + this.EdgeRHS.String() + this.Attrs.String())
|
||||
}
|
||||
|
||||
func (this EdgeStmt) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v = v.Visit(this)
|
||||
this.Source.Walk(v)
|
||||
this.EdgeRHS.Walk(v)
|
||||
this.Attrs.Walk(v)
|
||||
}
|
||||
|
||||
type EdgeRHS []*EdgeRH
|
||||
|
||||
func NewEdgeRHS(op, id Elem) (EdgeRHS, error) {
|
||||
return EdgeRHS{&EdgeRH{op.(EdgeOp), id.(Location)}}, nil
|
||||
}
|
||||
|
||||
func AppendEdgeRHS(e, op, id Elem) (EdgeRHS, error) {
|
||||
erhs := e.(EdgeRHS)
|
||||
erhs = append(erhs, &EdgeRH{op.(EdgeOp), id.(Location)})
|
||||
return erhs, nil
|
||||
}
|
||||
|
||||
func (this EdgeRHS) String() string {
|
||||
s := ""
|
||||
for i := range this {
|
||||
s += this[i].String()
|
||||
}
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
|
||||
func (this EdgeRHS) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v = v.Visit(this)
|
||||
for i := range this {
|
||||
this[i].Walk(v)
|
||||
}
|
||||
}
|
||||
|
||||
type EdgeRH struct {
|
||||
Op EdgeOp
|
||||
Destination Location
|
||||
}
|
||||
|
||||
func (this *EdgeRH) String() string {
|
||||
return strings.TrimSpace(this.Op.String() + this.Destination.String())
|
||||
}
|
||||
|
||||
func (this *EdgeRH) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v = v.Visit(this)
|
||||
this.Op.Walk(v)
|
||||
this.Destination.Walk(v)
|
||||
}
|
||||
|
||||
type NodeStmt struct {
|
||||
NodeId *NodeId
|
||||
Attrs AttrList
|
||||
}
|
||||
|
||||
func NewNodeStmt(id, attrs Elem) (*NodeStmt, error) {
|
||||
nid := id.(*NodeId)
|
||||
var a AttrList = nil
|
||||
var err error = nil
|
||||
if attrs == nil {
|
||||
a, err = NewAttrList(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
a = attrs.(AttrList)
|
||||
}
|
||||
return &NodeStmt{nid, a}, nil
|
||||
}
|
||||
|
||||
func (this NodeStmt) String() string {
|
||||
return strings.TrimSpace(this.NodeId.String() + ` ` + this.Attrs.String())
|
||||
}
|
||||
|
||||
func (this NodeStmt) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v = v.Visit(this)
|
||||
this.NodeId.Walk(v)
|
||||
this.Attrs.Walk(v)
|
||||
}
|
||||
|
||||
type EdgeOp bool
|
||||
|
||||
const (
|
||||
DIRECTED EdgeOp = true
|
||||
UNDIRECTED EdgeOp = false
|
||||
)
|
||||
|
||||
func (this EdgeOp) String() string {
|
||||
switch this {
|
||||
case DIRECTED:
|
||||
return "->"
|
||||
case UNDIRECTED:
|
||||
return "--"
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func (this EdgeOp) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v.Visit(this)
|
||||
}
|
||||
|
||||
type NodeId struct {
|
||||
Id Id
|
||||
Port Port
|
||||
}
|
||||
|
||||
func NewNodeId(id Elem, port Elem) (*NodeId, error) {
|
||||
if port == nil {
|
||||
return &NodeId{id.(Id), Port{"", ""}}, nil
|
||||
}
|
||||
return &NodeId{id.(Id), port.(Port)}, nil
|
||||
}
|
||||
|
||||
func MakeNodeId(id string, port string) *NodeId {
|
||||
p := Port{"", ""}
|
||||
if len(port) > 0 {
|
||||
ps := strings.Split(port, ":")
|
||||
p.Id1 = Id(ps[1])
|
||||
if len(ps) > 2 {
|
||||
p.Id2 = Id(ps[2])
|
||||
}
|
||||
}
|
||||
return &NodeId{Id(id), p}
|
||||
}
|
||||
|
||||
func (this *NodeId) String() string {
|
||||
return this.Id.String() + this.Port.String()
|
||||
}
|
||||
|
||||
func (this *NodeId) GetId() Id {
|
||||
return this.Id
|
||||
}
|
||||
|
||||
func (this *NodeId) GetPort() Port {
|
||||
return this.Port
|
||||
}
|
||||
|
||||
func (this *NodeId) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v = v.Visit(this)
|
||||
this.Id.Walk(v)
|
||||
this.Port.Walk(v)
|
||||
}
|
||||
|
||||
//TODO semantic analysis should decide which Id is an Id and which is a Compass Point
|
||||
type Port struct {
|
||||
Id1 Id
|
||||
Id2 Id
|
||||
}
|
||||
|
||||
func NewPort(id1, id2 Elem) (Port, error) {
|
||||
port := Port{Id(""), Id("")}
|
||||
if id1 != nil {
|
||||
port.Id1 = id1.(Id)
|
||||
}
|
||||
if id2 != nil {
|
||||
port.Id2 = id2.(Id)
|
||||
}
|
||||
return port, nil
|
||||
}
|
||||
|
||||
func (this Port) String() string {
|
||||
if len(this.Id1) == 0 {
|
||||
return ""
|
||||
}
|
||||
s := ":" + this.Id1.String()
|
||||
if len(this.Id2) > 0 {
|
||||
s += ":" + this.Id2.String()
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (this Port) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v = v.Visit(this)
|
||||
this.Id1.Walk(v)
|
||||
this.Id2.Walk(v)
|
||||
}
|
||||
|
||||
type Id string
|
||||
|
||||
func NewId(id Elem) (Id, error) {
|
||||
if id == nil {
|
||||
return Id(""), nil
|
||||
}
|
||||
id_lit := string(id.(*token.Token).Lit)
|
||||
return Id(id_lit), nil
|
||||
}
|
||||
|
||||
func (this Id) String() string {
|
||||
return string(this)
|
||||
}
|
||||
|
||||
func (this Id) Walk(v Visitor) {
|
||||
if v == nil {
|
||||
return
|
||||
}
|
||||
v.Visit(this)
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package gographviz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
)
|
||||
|
||||
//Represents attributes for an Edge, Node or Graph.
|
||||
type Attrs map[string]string
|
||||
|
||||
//Creates an empty Attributes type.
|
||||
func NewAttrs() Attrs {
|
||||
return make(Attrs)
|
||||
}
|
||||
|
||||
//Adds an attribute name and value.
|
||||
func (this Attrs) Add(field string, value string) {
|
||||
prev, ok := this[field]
|
||||
if ok {
|
||||
fmt.Fprintf(os.Stderr, "WARNING: overwriting field %v value %v, with value %v\n", field, prev, value)
|
||||
}
|
||||
this[field] = value
|
||||
}
|
||||
|
||||
//Adds the attributes into this Attrs type overwriting duplicates.
|
||||
func (this Attrs) Extend(more Attrs) {
|
||||
for key, value := range more {
|
||||
this.Add(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
//Only adds the missing attributes to this Attrs type.
|
||||
func (this Attrs) Ammend(more Attrs) {
|
||||
for key, value := range more {
|
||||
if _, ok := this[key]; !ok {
|
||||
this.Add(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (this Attrs) SortedNames() []string {
|
||||
keys := make([]string, 0)
|
||||
for key := range this {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func (this Attrs) Copy() Attrs {
|
||||
attrs := make(Attrs)
|
||||
for k, v := range this {
|
||||
attrs[k] = v
|
||||
}
|
||||
return attrs
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package gographviz
|
||||
|
||||
import (
|
||||
"github.com/awalterschulze/gographviz/ast"
|
||||
"github.com/awalterschulze/gographviz/parser"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type bugSubGraphWorldVisitor struct {
|
||||
t *testing.T
|
||||
found bool
|
||||
}
|
||||
|
||||
func (this *bugSubGraphWorldVisitor) Visit(v ast.Elem) ast.Visitor {
|
||||
edge, ok := v.(ast.EdgeStmt)
|
||||
if !ok {
|
||||
return this
|
||||
}
|
||||
if edge.Source.GetId().String() != "2" {
|
||||
return this
|
||||
}
|
||||
dst := edge.EdgeRHS[0].Destination
|
||||
if _, ok := dst.(*ast.SubGraph); !ok {
|
||||
this.t.Fatalf("2 -> Not SubGraph")
|
||||
} else {
|
||||
this.found = true
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
func TestBugSubGraphWorld(t *testing.T) {
|
||||
g := analtest(t, "world.gv.txt")
|
||||
st, err := parser.ParseString(g.String())
|
||||
check(t, err)
|
||||
s := &bugSubGraphWorldVisitor{
|
||||
t: t,
|
||||
}
|
||||
st.Walk(s)
|
||||
if !s.found {
|
||||
t.Fatalf("2 -> SubGraph not found")
|
||||
}
|
||||
}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
//This bnf has been derived from http://www.graphviz.org/content/dot-language
|
||||
//The rules have been copied and are shown in the comments, with their derived bnf rules below.
|
||||
|
||||
//graph : [ strict ] (graph | digraph) [ ID ] '{' stmt_list '}'
|
||||
DotGraph
|
||||
: Graph "{" "}" << ast.NewGraph(ast.GRAPH, ast.FALSE, nil, nil) >>
|
||||
| Strict Graph "{" "}" << ast.NewGraph(ast.GRAPH, ast.TRUE, nil, nil) >>
|
||||
| Graph Id "{" "}" << ast.NewGraph(ast.GRAPH, ast.FALSE, $1, nil) >>
|
||||
| Strict Graph Id "{" "}" << ast.NewGraph(ast.GRAPH, ast.TRUE, $2, nil) >>
|
||||
| Graph "{" StmtList "}" << ast.NewGraph(ast.GRAPH, ast.FALSE, nil, $2) >>
|
||||
| Graph Id "{" StmtList "}" << ast.NewGraph(ast.GRAPH, ast.FALSE, $1, $3) >>
|
||||
| Strict Graph "{" StmtList "}" << ast.NewGraph(ast.GRAPH, ast.TRUE, nil, $3) >>
|
||||
| Strict Graph Id "{" StmtList "}" << ast.NewGraph(ast.GRAPH, ast.TRUE, $2, $4) >>
|
||||
| Digraph "{" "}" << ast.NewGraph(ast.DIGRAPH, ast.FALSE, nil, nil) >>
|
||||
| Strict Digraph "{" "}" << ast.NewGraph(ast.DIGRAPH, ast.TRUE, nil, nil) >>
|
||||
| Digraph Id "{" "}" << ast.NewGraph(ast.DIGRAPH, ast.FALSE, $1, nil) >>
|
||||
| Strict Digraph Id "{" "}" << ast.NewGraph(ast.DIGRAPH, ast.TRUE, $2, nil) >>
|
||||
| Digraph "{" StmtList "}" << ast.NewGraph(ast.DIGRAPH, ast.FALSE, nil, $2) >>
|
||||
| Digraph Id "{" StmtList "}" << ast.NewGraph(ast.DIGRAPH, ast.FALSE, $1, $3) >>
|
||||
| Strict Digraph "{" StmtList "}" << ast.NewGraph(ast.DIGRAPH, ast.TRUE, nil, $3) >>
|
||||
| Strict Digraph Id "{" StmtList "}" << ast.NewGraph(ast.DIGRAPH, ast.TRUE, $2, $4) >>
|
||||
;
|
||||
|
||||
|
||||
//stmt_list : [ stmt [ ';' ] [ stmt_list ] ]
|
||||
StmtList
|
||||
: Stmt1 << ast.NewStmtList($0) >>
|
||||
| StmtList Stmt1 << ast.AppendStmtList($0, $1) >>
|
||||
;
|
||||
|
||||
Stmt1
|
||||
: Stmt << $0, nil >>
|
||||
| Stmt ";" << $0, nil >>
|
||||
;
|
||||
|
||||
//stmt : node_stmt | edge_stmt | attr_stmt | (ID '=' ID) | subgraph
|
||||
Stmt
|
||||
: Id "=" Id << ast.NewAttr($0, $2) >>
|
||||
| NodeStmt << $0, nil >>
|
||||
| EdgeStmt << $0, nil >>
|
||||
| AttrStmt << $0, nil >>
|
||||
| SubGraphStmt << $0, nil >>
|
||||
;
|
||||
|
||||
//attr_stmt : (graph | node | edge) attr_list
|
||||
AttrStmt
|
||||
: Graph AttrList << ast.NewGraphAttrs($1) >>
|
||||
| Node AttrList << ast.NewNodeAttrs($1) >>
|
||||
| Edge AttrList << ast.NewEdgeAttrs($1) >>
|
||||
;
|
||||
|
||||
//attr_list : '[' [ a_list ] ']' [ attr_list ]
|
||||
AttrList
|
||||
: "[" "]" << ast.NewAttrList(nil) >>
|
||||
| "[" AList "]" << ast.NewAttrList($1) >>
|
||||
| AttrList "[" "]" << ast.AppendAttrList($0, nil) >>
|
||||
| AttrList "[" AList "]" << ast.AppendAttrList($0, $2) >>
|
||||
;
|
||||
|
||||
//a_list : ID [ '=' ID ] [ ',' ] [ a_list ]
|
||||
AList
|
||||
: Attr << ast.NewAList($0) >>
|
||||
| AList Attr << ast.AppendAList($0, $1) >>
|
||||
| AList "," Attr << ast.AppendAList($0, $2) >>
|
||||
;
|
||||
|
||||
//An a_list clause of the form ID is equivalent to ID=true.
|
||||
Attr
|
||||
: Id << ast.NewAttr($0, nil) >>
|
||||
| Id "=" Id << ast.NewAttr($0, $2) >>
|
||||
;
|
||||
|
||||
//edge_stmt : (node_id | subgraph) edgeRHS [ attr_list ]
|
||||
EdgeStmt
|
||||
: NodeId EdgeRHS << ast.NewEdgeStmt($0, $1, nil) >>
|
||||
| NodeId EdgeRHS AttrList << ast.NewEdgeStmt($0, $1, $2) >>
|
||||
| SubGraphStmt EdgeRHS << ast.NewEdgeStmt($0, $1, nil) >>
|
||||
| SubGraphStmt EdgeRHS AttrList << ast.NewEdgeStmt($0, $1, $2) >>
|
||||
;
|
||||
|
||||
//edgeRHS : edgeop (node_id | subgraph) [ edgeRHS ]
|
||||
EdgeRHS
|
||||
: EdgeOp NodeId << ast.NewEdgeRHS($0, $1) >>
|
||||
| EdgeOp SubGraphStmt << ast.NewEdgeRHS($0, $1) >>
|
||||
| EdgeRHS EdgeOp NodeId << ast.AppendEdgeRHS($0, $1, $2) >>
|
||||
| EdgeRHS EdgeOp SubGraphStmt << ast.AppendEdgeRHS($0, $1, $2) >>
|
||||
;
|
||||
|
||||
//node_stmt : node_id [ attr_list ]
|
||||
NodeStmt
|
||||
: NodeId << ast.NewNodeStmt($0, nil) >>
|
||||
| NodeId AttrList << ast.NewNodeStmt($0, $1) >>
|
||||
;
|
||||
|
||||
//node_id : ID [ port ]
|
||||
NodeId
|
||||
: Id << ast.NewNodeId($0, nil) >>
|
||||
| Id Port << ast.NewNodeId($0, $1) >>
|
||||
;
|
||||
|
||||
//compass_pt : (n | ne | e | se | s | sw | w | nw | c | _)
|
||||
//Note also that the allowed compass point values are not keywords,
|
||||
//so these strings can be used elsewhere as ordinary identifiers and,
|
||||
//conversely, the parser will actually accept any identifier.
|
||||
//port : ':' ID [ ':' compass_pt ]
|
||||
// | ':' compass_pt
|
||||
Port
|
||||
: ":" Id << ast.NewPort($1, nil) >>
|
||||
| ":" Id ":" Id << ast.NewPort($1, $3) >>
|
||||
;
|
||||
|
||||
//subgraph : [ subgraph [ ID ] ] '{' stmt_list '}'
|
||||
SubGraphStmt
|
||||
: "{" StmtList "}" << ast.NewSubGraph(nil, $1) >>
|
||||
| Subgraph "{" StmtList "}" << ast.NewSubGraph(nil, $2) >>
|
||||
| Subgraph Id "{" StmtList "}" << ast.NewSubGraph($1, $3) >>
|
||||
;
|
||||
|
||||
//An edgeop is -> in directed graphs and -- in undirected graphs.
|
||||
EdgeOp
|
||||
: "->" << ast.DIRECTED, nil >>
|
||||
| "--" << ast.UNDIRECTED, nil >>
|
||||
;
|
||||
|
||||
//The keywords node, edge, graph, digraph, subgraph, and strict are case-independent.
|
||||
Graph
|
||||
: "graph" << $0, nil >>
|
||||
| "Graph" << $0, nil >>
|
||||
| "GRAPH" << $0, nil >>
|
||||
;
|
||||
|
||||
Strict
|
||||
: "strict" << $0, nil >>
|
||||
| "Strict" << $0, nil >>
|
||||
| "STRICT" << $0, nil >>
|
||||
;
|
||||
|
||||
Digraph
|
||||
: "digraph" << $0, nil >>
|
||||
| "Digraph" << $0, nil >>
|
||||
| "DiGraph" << $0, nil >>
|
||||
| "DIGRAPH" << $0, nil >>
|
||||
;
|
||||
|
||||
Node
|
||||
: "node" << $0, nil >>
|
||||
| "Node" << $0, nil >>
|
||||
| "NODE" << $0, nil >>
|
||||
;
|
||||
|
||||
Edge
|
||||
: "edge" << $0, nil >>
|
||||
| "Edge" << $0, nil >>
|
||||
| "EDGE" << $0, nil >>
|
||||
;
|
||||
|
||||
Subgraph
|
||||
: "subgraph" << $0, nil >>
|
||||
| "Subgraph" << $0, nil >>
|
||||
| "SubGraph" << $0, nil >>
|
||||
| "SUBGRAPH" << $0, nil >>
|
||||
;
|
||||
|
||||
// An ID is one of the following:
|
||||
// Any string of alphabetic ([a-zA-Z'200-'377]) characters, underscores ('_') or digits ([0-9]), not beginning with a digit; //CHECK 200-377
|
||||
// a numeral [-]?(.[0-9]+ | [0-9]+(.[0-9]*)? );
|
||||
// any double-quoted string ("...") possibly containing escaped quotes ('")1; //TODO
|
||||
// an HTML string (<...>).
|
||||
// An ID is just a string; the lack of quote characters in the first two forms is just for simplicity.
|
||||
// There is no semantic difference between abc_2 and "abc_2", or between 2.34 and "2.34".
|
||||
// Obviously, to use a keyword as an ID, it must be quoted. Note that, in HTML strings, angle brackets must occur in matched pairs, and unescaped newlines are allowed.
|
||||
// In addition, the content must be legal XML, so that the special XML escape sequences for ", &, <, and > may be necessary in order to embed these characters in attribute values or raw text.
|
||||
// Both quoted strings and HTML strings are scanned as a unit, so any embedded comments will be treated as part of the strings.
|
||||
Id
|
||||
: id << ast.NewId($0) >>
|
||||
| string_lit << ast.NewId($0) >>
|
||||
| int_lit << ast.NewId($0) >>
|
||||
| float_lit << ast.NewId($0) >>
|
||||
| html_lit << ast.NewId($0) >>
|
||||
;
|
||||
|
||||
//The language supports C++-style comments: /* */ and //.
|
||||
//In addition, a line beginning with a '#' character is considered a line output from a C preprocessor (e.g., # 34 to indicate line 34 ) and discarded.
|
||||
|
||||
//TODO (if dot file is not parsable, it might be because of these points)
|
||||
|
||||
//Semicolons aid readability but are not required except in the rare case that a named subgraph with no body immediately preceeds an anonymous subgraph,
|
||||
//since the precedence rules cause this sequence to be parsed as a subgraph with a heading and a body. Also, any amount of whitespace may be inserted between terminals.
|
||||
|
||||
//TODO
|
||||
//As another aid for readability, dot allows single logical lines to span multiple physical lines using the standard C convention of a backslash immediately preceding a newline character.
|
||||
//In addition, double-quoted strings can be concatenated using a '+' operator. As HTML strings can contain newline characters, they do not support the concatenation operator.
|
||||
|
||||
//TODO
|
||||
//Note there are still 3 sections on the webpage which have not been included (Subgraphs and Clusters, Lexical and Semantic Notes, and Character Encodings)
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package gographviz
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
//Represents an Edge.
|
||||
type Edge struct {
|
||||
Src string
|
||||
SrcPort string
|
||||
Dst string
|
||||
DstPort string
|
||||
Dir bool
|
||||
Attrs Attrs
|
||||
}
|
||||
|
||||
//Represents a set of Edges.
|
||||
type Edges struct {
|
||||
SrcToDsts map[string]map[string][]*Edge
|
||||
DstToSrcs map[string]map[string][]*Edge
|
||||
Edges []*Edge
|
||||
}
|
||||
|
||||
//Creates a blank set of Edges.
|
||||
func NewEdges() *Edges {
|
||||
return &Edges{make(map[string]map[string][]*Edge), make(map[string]map[string][]*Edge), make([]*Edge, 0)}
|
||||
}
|
||||
|
||||
//Adds an Edge to the set of Edges.
|
||||
func (this *Edges) Add(edge *Edge) {
|
||||
if _, ok := this.SrcToDsts[edge.Src]; !ok {
|
||||
this.SrcToDsts[edge.Src] = make(map[string][]*Edge)
|
||||
}
|
||||
if _, ok := this.SrcToDsts[edge.Src][edge.Dst]; !ok {
|
||||
this.SrcToDsts[edge.Src][edge.Dst] = make([]*Edge, 0)
|
||||
}
|
||||
this.SrcToDsts[edge.Src][edge.Dst] = append(this.SrcToDsts[edge.Src][edge.Dst], edge)
|
||||
|
||||
if _, ok := this.DstToSrcs[edge.Dst]; !ok {
|
||||
this.DstToSrcs[edge.Dst] = make(map[string][]*Edge)
|
||||
}
|
||||
if _, ok := this.DstToSrcs[edge.Dst][edge.Src]; !ok {
|
||||
this.DstToSrcs[edge.Dst][edge.Src] = make([]*Edge, 0)
|
||||
}
|
||||
this.DstToSrcs[edge.Dst][edge.Src] = append(this.DstToSrcs[edge.Dst][edge.Src], edge)
|
||||
|
||||
this.Edges = append(this.Edges, edge)
|
||||
}
|
||||
|
||||
//Returns a sorted list of Edges.
|
||||
func (this Edges) Sorted() []*Edge {
|
||||
es := make(edgeSorter, len(this.Edges))
|
||||
copy(es, this.Edges)
|
||||
sort.Sort(es)
|
||||
return es
|
||||
}
|
||||
|
||||
type edgeSorter []*Edge
|
||||
|
||||
func (es edgeSorter) Len() int { return len(es) }
|
||||
func (es edgeSorter) Swap(i, j int) { es[i], es[j] = es[j], es[i] }
|
||||
func (es edgeSorter) Less(i, j int) bool {
|
||||
if es[i].Src < es[j].Src {
|
||||
return true
|
||||
} else if es[i].Src > es[j].Src {
|
||||
return false
|
||||
}
|
||||
|
||||
if es[i].Dst < es[j].Dst {
|
||||
return true
|
||||
} else if es[i].Dst > es[j].Dst {
|
||||
return false
|
||||
}
|
||||
|
||||
if es[i].SrcPort < es[j].SrcPort {
|
||||
return true
|
||||
} else if es[i].SrcPort > es[j].SrcPort {
|
||||
return false
|
||||
}
|
||||
|
||||
if es[i].DstPort < es[j].DstPort {
|
||||
return true
|
||||
} else if es[i].DstPort > es[j].DstPort {
|
||||
return false
|
||||
}
|
||||
|
||||
if es[i].Dir != es[j].Dir {
|
||||
return es[i].Dir
|
||||
}
|
||||
|
||||
attrs := es[i].Attrs.Copy()
|
||||
for k, v := range es[j].Attrs {
|
||||
attrs[k] = v
|
||||
}
|
||||
|
||||
for _, k := range attrs.SortedNames() {
|
||||
if es[i].Attrs[k] < es[j].Attrs[k] {
|
||||
return true
|
||||
} else if es[i].Attrs[k] > es[j].Attrs[k] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
+177
@@ -0,0 +1,177 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package gographviz
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEdges_Sorted(t *testing.T) {
|
||||
var tts = map[string]struct {
|
||||
edges []*Edge
|
||||
expected []*Edge
|
||||
}{
|
||||
"empty": {
|
||||
edges: []*Edge{},
|
||||
expected: []*Edge{},
|
||||
},
|
||||
"one edge": {
|
||||
edges: []*Edge{
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "abc"}},
|
||||
},
|
||||
expected: []*Edge{
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "abc"}},
|
||||
},
|
||||
},
|
||||
"two non parallel edges": {
|
||||
edges: []*Edge{
|
||||
{Src: "0", Dst: "2", Attrs: map[string]string{"label": "hello"}},
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "abc"}},
|
||||
},
|
||||
expected: []*Edge{
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "abc"}},
|
||||
{Src: "0", Dst: "2", Attrs: map[string]string{"label": "hello"}},
|
||||
},
|
||||
},
|
||||
"two parallel edges": {
|
||||
edges: []*Edge{
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "hello"}},
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "abc"}},
|
||||
},
|
||||
expected: []*Edge{
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "abc"}},
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "hello"}},
|
||||
},
|
||||
},
|
||||
"two parallel edges - one without label": {
|
||||
edges: []*Edge{
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "abc"}},
|
||||
{Src: "0", Dst: "1"},
|
||||
},
|
||||
expected: []*Edge{
|
||||
{Src: "0", Dst: "1"},
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "abc"}},
|
||||
},
|
||||
},
|
||||
"several non parallel edges": {
|
||||
edges: []*Edge{
|
||||
{Src: "0", Dst: "2", Attrs: map[string]string{"label": "hello"}},
|
||||
{Src: "1", Dst: "1", Attrs: map[string]string{"label": "world"}},
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "abc"}},
|
||||
{Src: "1", Dst: "0", Attrs: map[string]string{"label": "golang"}},
|
||||
},
|
||||
expected: []*Edge{
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "abc"}},
|
||||
{Src: "0", Dst: "2", Attrs: map[string]string{"label": "hello"}},
|
||||
{Src: "1", Dst: "0", Attrs: map[string]string{"label": "golang"}},
|
||||
{Src: "1", Dst: "1", Attrs: map[string]string{"label": "world"}},
|
||||
},
|
||||
},
|
||||
"several with parallel edges": {
|
||||
edges: []*Edge{
|
||||
{Src: "0", Dst: "2", Attrs: map[string]string{"label": "hello"}},
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "cba"}},
|
||||
{Src: "1", Dst: "1", Attrs: map[string]string{"label": "world"}},
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "abc"}},
|
||||
{Src: "1", Dst: "0", Attrs: map[string]string{"label": "gopher"}},
|
||||
{Src: "1", Dst: "0", Attrs: map[string]string{"label": "golang"}},
|
||||
},
|
||||
expected: []*Edge{
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "abc"}},
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "cba"}},
|
||||
{Src: "0", Dst: "2", Attrs: map[string]string{"label": "hello"}},
|
||||
{Src: "1", Dst: "0", Attrs: map[string]string{"label": "golang"}},
|
||||
{Src: "1", Dst: "0", Attrs: map[string]string{"label": "gopher"}},
|
||||
{Src: "1", Dst: "1", Attrs: map[string]string{"label": "world"}},
|
||||
},
|
||||
},
|
||||
"edges with ports": {
|
||||
edges: []*Edge{
|
||||
{Src: "0", Dst: "1", SrcPort: "a", DstPort: "b"},
|
||||
{Src: "0", Dst: "1", SrcPort: "a", DstPort: "a"},
|
||||
{Src: "0", Dst: "1", SrcPort: "b", DstPort: "a"},
|
||||
},
|
||||
expected: []*Edge{
|
||||
{Src: "0", Dst: "1", SrcPort: "a", DstPort: "a"},
|
||||
{Src: "0", Dst: "1", SrcPort: "a", DstPort: "b"},
|
||||
{Src: "0", Dst: "1", SrcPort: "b", DstPort: "a"},
|
||||
},
|
||||
},
|
||||
"directed edges before non directed edges": {
|
||||
edges: []*Edge{
|
||||
{Src: "0", Dst: "1", Dir: false},
|
||||
{Src: "0", Dst: "1", Dir: true},
|
||||
},
|
||||
expected: []*Edge{
|
||||
{Src: "0", Dst: "1", Dir: true},
|
||||
{Src: "0", Dst: "1", Dir: false},
|
||||
},
|
||||
},
|
||||
"the theory of everything": {
|
||||
edges: []*Edge{
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "cba"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "a", Dir: false, Attrs: map[string]string{"label": "gopher"}},
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "abc"}},
|
||||
{Src: "0", Dst: "2", Attrs: map[string]string{"label": "hello"}},
|
||||
{Src: "1", Dst: "0", Attrs: map[string]string{"label": "gopher"}},
|
||||
{Src: "1", Dst: "0", Attrs: map[string]string{"label": "golang"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "b", Attrs: map[string]string{"label": "gopher"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "a", DstPort: "b", Attrs: map[string]string{"label": "golang"}},
|
||||
{Src: "1", Dst: "1", Attrs: map[string]string{"comment": "test", "label": "world"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "a", DstPort: "a", Attrs: map[string]string{"label": "golang"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "a", Attrs: map[string]string{"label": "golang"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "b", Dir: false, Attrs: map[string]string{"label": "gopher"}},
|
||||
{Src: "1", Dst: "1", Attrs: map[string]string{"label": "world"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "a", DstPort: "b", Dir: true, Attrs: map[string]string{"label": "golang"}},
|
||||
{Src: "1", Dst: "1", Attrs: map[string]string{"comment": "test", "label": "hello"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "a", Dir: true, Attrs: map[string]string{"label": "golang"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "a", DstPort: "b", Dir: true, Attrs: map[string]string{"label": "graphviz"}},
|
||||
},
|
||||
expected: []*Edge{
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "abc"}},
|
||||
{Src: "0", Dst: "1", Attrs: map[string]string{"label": "cba"}},
|
||||
{Src: "0", Dst: "2", Attrs: map[string]string{"label": "hello"}},
|
||||
{Src: "1", Dst: "0", Attrs: map[string]string{"label": "golang"}},
|
||||
{Src: "1", Dst: "0", Attrs: map[string]string{"label": "gopher"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "a", Dir: true, Attrs: map[string]string{"label": "golang"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "a", Attrs: map[string]string{"label": "golang"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "a", Dir: false, Attrs: map[string]string{"label": "gopher"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "a", DstPort: "a", Attrs: map[string]string{"label": "golang"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "a", DstPort: "b", Dir: true, Attrs: map[string]string{"label": "golang"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "a", DstPort: "b", Dir: true, Attrs: map[string]string{"label": "graphviz"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "a", DstPort: "b", Attrs: map[string]string{"label": "golang"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "b", Dir: false, Attrs: map[string]string{"label": "gopher"}},
|
||||
{Src: "1", Dst: "0", SrcPort: "b", Attrs: map[string]string{"label": "gopher"}},
|
||||
{Src: "1", Dst: "1", Attrs: map[string]string{"label": "world"}},
|
||||
{Src: "1", Dst: "1", Attrs: map[string]string{"comment": "test", "label": "hello"}},
|
||||
{Src: "1", Dst: "1", Attrs: map[string]string{"comment": "test", "label": "world"}},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tt := range tts {
|
||||
edges := NewEdges()
|
||||
for _, e := range tt.edges {
|
||||
edges.Add(e)
|
||||
}
|
||||
s := edges.Sorted()
|
||||
if !reflect.DeepEqual(tt.expected, s) {
|
||||
t.Errorf("%s - Sorted invalid: expected %v got %v", name, tt.expected, s)
|
||||
} else if !reflect.DeepEqual(edges.Edges, tt.edges) {
|
||||
t.Errorf("%s - Sorted should not have changed original order: expected %v got %v", name, tt.edges, edges.Edges)
|
||||
}
|
||||
}
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package gographviz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/awalterschulze/gographviz/scanner"
|
||||
"github.com/awalterschulze/gographviz/token"
|
||||
"strings"
|
||||
"text/template"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type Escape struct {
|
||||
*Graph
|
||||
}
|
||||
|
||||
//Returns a graph which will try to escape some strings when required
|
||||
func NewEscape() *Escape {
|
||||
return &Escape{NewGraph()}
|
||||
}
|
||||
|
||||
func isHtml(s string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
ss := strings.TrimSpace(s)
|
||||
if ss[0] != '<' {
|
||||
return false
|
||||
}
|
||||
count := 0
|
||||
for _, c := range ss {
|
||||
if c == '<' {
|
||||
count += 1
|
||||
}
|
||||
if c == '>' {
|
||||
count -= 1
|
||||
}
|
||||
}
|
||||
if count == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isLetter(ch rune) bool {
|
||||
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' ||
|
||||
ch >= 0x80 && unicode.IsLetter(ch) && ch != 'ε'
|
||||
}
|
||||
|
||||
func isId(s string) bool {
|
||||
i := 0
|
||||
pos := false
|
||||
for _, c := range s {
|
||||
if i == 0 {
|
||||
if !isLetter(c) {
|
||||
return false
|
||||
}
|
||||
pos = true
|
||||
}
|
||||
if unicode.IsSpace(c) {
|
||||
return false
|
||||
}
|
||||
if c == '-' {
|
||||
return false
|
||||
}
|
||||
i++
|
||||
}
|
||||
return pos
|
||||
}
|
||||
|
||||
func isDigit(ch rune) bool {
|
||||
return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)
|
||||
}
|
||||
|
||||
func isNumber(s string) bool {
|
||||
state := 0
|
||||
for _, c := range s {
|
||||
if state == 0 {
|
||||
if isDigit(c) || c == '.' {
|
||||
state = 2
|
||||
} else if c == '-' {
|
||||
state = 1
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
} else if state == 1 {
|
||||
if isDigit(c) || c == '.' {
|
||||
state = 2
|
||||
}
|
||||
} else if c != '.' && !isDigit(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return (state == 2)
|
||||
}
|
||||
|
||||
func isStringLit(s string) bool {
|
||||
lex := &scanner.Scanner{}
|
||||
lex.Init([]byte(s), token.DOTTokens)
|
||||
tok, _ := lex.Scan()
|
||||
if tok.Type != token.DOTTokens.Type("string_lit") {
|
||||
return false
|
||||
}
|
||||
tok, _ = lex.Scan()
|
||||
if tok.Type != token.EOF {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func esc(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
if isHtml(s) {
|
||||
return s
|
||||
}
|
||||
ss := strings.TrimSpace(s)
|
||||
if ss[0] == '<' {
|
||||
return fmt.Sprintf("\"%s\"", strings.Replace(s, "\"", "\\\"", -1))
|
||||
}
|
||||
if isId(s) {
|
||||
return s
|
||||
}
|
||||
if isNumber(s) {
|
||||
return s
|
||||
}
|
||||
if isStringLit(s) {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("\"%s\"", template.HTMLEscapeString(s))
|
||||
}
|
||||
|
||||
func escAttrs(attrs map[string]string) map[string]string {
|
||||
newAttrs := make(map[string]string)
|
||||
for k, v := range attrs {
|
||||
newAttrs[esc(k)] = esc(v)
|
||||
}
|
||||
return newAttrs
|
||||
}
|
||||
|
||||
func (this *Escape) SetName(name string) {
|
||||
this.Graph.SetName(esc(name))
|
||||
}
|
||||
|
||||
func (this *Escape) AddPortEdge(src, srcPort, dst, dstPort string, directed bool, attrs map[string]string) {
|
||||
this.Graph.AddPortEdge(esc(src), srcPort, esc(dst), dstPort, directed, escAttrs(attrs))
|
||||
}
|
||||
|
||||
func (this *Escape) AddEdge(src, dst string, directed bool, attrs map[string]string) {
|
||||
this.AddPortEdge(src, "", dst, "", directed, attrs)
|
||||
}
|
||||
|
||||
func (this *Escape) AddNode(parentGraph string, name string, attrs map[string]string) {
|
||||
this.Graph.AddNode(esc(parentGraph), esc(name), escAttrs(attrs))
|
||||
}
|
||||
|
||||
func (this *Escape) AddAttr(parentGraph string, field, value string) {
|
||||
this.Graph.AddAttr(esc(parentGraph), esc(field), esc(value))
|
||||
}
|
||||
|
||||
func (this *Escape) AddSubGraph(parentGraph string, name string, attrs map[string]string) {
|
||||
this.Graph.AddSubGraph(esc(parentGraph), esc(name), escAttrs(attrs))
|
||||
}
|
||||
|
||||
func (this *Escape) IsNode(name string) bool {
|
||||
return this.Graph.IsNode(esc(name))
|
||||
}
|
||||
|
||||
func (this *Escape) IsSubGraph(name string) bool {
|
||||
return this.Graph.IsSubGraph(esc(name))
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package gographviz
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestEscape(t *testing.T) {
|
||||
g := NewEscape()
|
||||
g.SetName("asdf adsf")
|
||||
g.SetDir(true)
|
||||
g.AddNode("asdf asdf", "kasdf99 99", map[string]string{
|
||||
"<asfd": "1",
|
||||
})
|
||||
g.AddNode("asdf asdf", "7", map[string]string{
|
||||
"<asfd": "1",
|
||||
})
|
||||
g.AddNode("asdf asdf", "a << b", nil)
|
||||
g.AddEdge("kasdf99 99", "7", true, nil)
|
||||
s := g.String()
|
||||
if !strings.HasPrefix(s, `digraph "asdf adsf" {
|
||||
"kasdf99 99"->7;
|
||||
"a << b";
|
||||
"kasdf99 99" [ "<asfd"=1 ];
|
||||
7 [ "<asfd"=1 ];
|
||||
|
||||
}`) {
|
||||
t.Fatalf("%s", s)
|
||||
}
|
||||
if !g.IsNode("a << b") {
|
||||
t.Fatalf("should be a node")
|
||||
}
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package gographviz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func ExampleRead() {
|
||||
g, err := Read([]byte(`digraph G {Hello->World}`))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s := g.String()
|
||||
fmt.Println(s)
|
||||
// Output: digraph G {
|
||||
// Hello->World;
|
||||
// Hello;
|
||||
// World;
|
||||
//
|
||||
//}
|
||||
}
|
||||
|
||||
func ExampleNewGraph() {
|
||||
g := NewGraph()
|
||||
g.SetName("G")
|
||||
g.SetDir(true)
|
||||
g.AddNode("G", "Hello", nil)
|
||||
g.AddNode("G", "World", nil)
|
||||
g.AddEdge("Hello", "World", true, nil)
|
||||
s := g.String()
|
||||
fmt.Println(s)
|
||||
// Output: digraph G {
|
||||
// Hello->World;
|
||||
// Hello;
|
||||
// World;
|
||||
//
|
||||
//}
|
||||
}
|
||||
|
||||
type MyOwnGraphStructure struct {
|
||||
weights map[int]map[int]int
|
||||
max int
|
||||
}
|
||||
|
||||
func NewMyOwnGraphStructure() *MyOwnGraphStructure {
|
||||
return &MyOwnGraphStructure{
|
||||
make(map[int]map[int]int),
|
||||
0,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *MyOwnGraphStructure) SetStrict(strict bool) {}
|
||||
func (this *MyOwnGraphStructure) SetDir(directed bool) {}
|
||||
func (this *MyOwnGraphStructure) SetName(name string) {}
|
||||
func (this *MyOwnGraphStructure) AddPortEdge(src, srcPort, dst, dstPort string, directed bool, attrs map[string]string) {
|
||||
srci, err := strconv.Atoi(src)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
dsti, err := strconv.Atoi(dst)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
ai, err := strconv.Atoi(attrs["label"])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if _, ok := this.weights[srci]; !ok {
|
||||
this.weights[srci] = make(map[int]int)
|
||||
}
|
||||
this.weights[srci][dsti] = ai
|
||||
if srci > this.max {
|
||||
this.max = srci
|
||||
}
|
||||
if dsti > this.max {
|
||||
this.max = dsti
|
||||
}
|
||||
|
||||
}
|
||||
func (this *MyOwnGraphStructure) AddEdge(src, dst string, directed bool, attrs map[string]string) {
|
||||
this.AddPortEdge(src, "", dst, "", directed, attrs)
|
||||
}
|
||||
func (this *MyOwnGraphStructure) AddNode(parentGraph string, name string, attrs map[string]string) {}
|
||||
func (this *MyOwnGraphStructure) AddAttr(parentGraph string, field, value string) {}
|
||||
func (this *MyOwnGraphStructure) AddSubGraph(parentGraph string, name string, attrs map[string]string) {
|
||||
}
|
||||
func (this *MyOwnGraphStructure) String() string { return "" }
|
||||
|
||||
//An Example of how to parse into your own simpler graph structure and output it back to graphviz.
|
||||
//This example reads in only numbers and outputs a matrix graph.
|
||||
func ExampleMyOwnGraphStructure() {
|
||||
name := "matrix"
|
||||
parsed, err := Parse([]byte(`
|
||||
digraph G {
|
||||
1 -> 2 [ label = 5 ];
|
||||
4 -> 2 [ label = 1 ];
|
||||
4 -> 1 [ label = 2 ];
|
||||
1 -> 1 [ label = 0 ];
|
||||
}
|
||||
|
||||
`))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
mine := NewMyOwnGraphStructure()
|
||||
Analyse(parsed, mine)
|
||||
output := NewGraph()
|
||||
output.SetName(name)
|
||||
output.SetDir(true)
|
||||
for i := 1; i <= mine.max; i++ {
|
||||
output.AddNode(name, fmt.Sprintf("%v", i), nil)
|
||||
if _, ok := mine.weights[i]; !ok {
|
||||
mine.weights[i] = make(map[int]int)
|
||||
}
|
||||
}
|
||||
for i := 1; i <= mine.max; i++ {
|
||||
for j := 1; j <= mine.max; j++ {
|
||||
output.AddEdge(fmt.Sprintf("%v", i), fmt.Sprintf("%v", j), true, map[string]string{"label": fmt.Sprintf("%v", mine.weights[i][j])})
|
||||
}
|
||||
}
|
||||
s := output.String()
|
||||
fmt.Println(s)
|
||||
// Output: digraph matrix {
|
||||
// 1->1[ label=0 ];
|
||||
// 1->2[ label=5 ];
|
||||
// 1->3[ label=0 ];
|
||||
// 1->4[ label=0 ];
|
||||
// 2->1[ label=0 ];
|
||||
// 2->2[ label=0 ];
|
||||
// 2->3[ label=0 ];
|
||||
// 2->4[ label=0 ];
|
||||
// 3->1[ label=0 ];
|
||||
// 3->2[ label=0 ];
|
||||
// 3->3[ label=0 ];
|
||||
// 3->4[ label=0 ];
|
||||
// 4->1[ label=2 ];
|
||||
// 4->2[ label=1 ];
|
||||
// 4->3[ label=0 ];
|
||||
// 4->4[ label=0 ];
|
||||
// 1;
|
||||
// 2;
|
||||
// 3;
|
||||
// 4;
|
||||
//
|
||||
//}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
gocc -o ../ -p . dot.bnf
|
||||
cp ./parser/parser.temp ./parser/parser.go
|
||||
gofix parser/tables.go
|
||||
gofix token/token.go
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
//Package gographviz provides parsing for the DOT grammar into
|
||||
//an abstract syntax tree representing a graph,
|
||||
//analysis of the abstract syntax tree into a more usable structure,
|
||||
//and writing back of this structure into the DOT format.
|
||||
package gographviz
|
||||
|
||||
import (
|
||||
"github.com/awalterschulze/gographviz/ast"
|
||||
"github.com/awalterschulze/gographviz/parser"
|
||||
)
|
||||
|
||||
var _ Interface = NewGraph()
|
||||
|
||||
//Implementing this interface allows you to parse the graph into your own structure.
|
||||
type Interface interface {
|
||||
SetStrict(strict bool)
|
||||
SetDir(directed bool)
|
||||
SetName(name string)
|
||||
AddPortEdge(src, srcPort, dst, dstPort string, directed bool, attrs map[string]string)
|
||||
AddEdge(src, dst string, directed bool, attrs map[string]string)
|
||||
AddNode(parentGraph string, name string, attrs map[string]string)
|
||||
AddAttr(parentGraph string, field, value string)
|
||||
AddSubGraph(parentGraph string, name string, attrs map[string]string)
|
||||
String() string
|
||||
}
|
||||
|
||||
//Parses the buffer into a abstract syntax tree representing the graph.
|
||||
func Parse(buf []byte) (*ast.Graph, error) {
|
||||
return parser.ParseBytes(buf)
|
||||
}
|
||||
|
||||
//Parses and creates a new Graph from the data.
|
||||
func Read(buf []byte) (Interface, error) {
|
||||
st, err := Parse(buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewAnalysedGraph(st), nil
|
||||
}
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package gographviz
|
||||
|
||||
//The analysed representation of the Graph parsed from the DOT format.
|
||||
type Graph struct {
|
||||
Attrs Attrs
|
||||
Name string
|
||||
Directed bool
|
||||
Strict bool
|
||||
Nodes *Nodes
|
||||
Edges *Edges
|
||||
SubGraphs *SubGraphs
|
||||
Relations *Relations
|
||||
}
|
||||
|
||||
//Creates a new empty graph, ready to be populated.
|
||||
func NewGraph() *Graph {
|
||||
return &Graph{
|
||||
Attrs: make(Attrs),
|
||||
Name: "",
|
||||
Directed: false,
|
||||
Strict: false,
|
||||
Nodes: NewNodes(),
|
||||
Edges: NewEdges(),
|
||||
SubGraphs: NewSubGraphs(),
|
||||
Relations: NewRelations(),
|
||||
}
|
||||
}
|
||||
|
||||
//If the graph is strict then multiple edges are not allowed between the same pairs of nodes,
|
||||
//see dot man page.
|
||||
func (this *Graph) SetStrict(strict bool) {
|
||||
this.Strict = strict
|
||||
}
|
||||
|
||||
//Sets whether the graph is directed (true) or undirected (false).
|
||||
func (this *Graph) SetDir(dir bool) {
|
||||
this.Directed = dir
|
||||
}
|
||||
|
||||
//Sets the graph name.
|
||||
func (this *Graph) SetName(name string) {
|
||||
this.Name = name
|
||||
}
|
||||
|
||||
//Adds an edge to the graph from node src to node dst.
|
||||
//srcPort and dstPort are the port the node ports, leave as empty strings if it is not required.
|
||||
//This does not imply the adding of missing nodes.
|
||||
func (this *Graph) AddPortEdge(src, srcPort, dst, dstPort string, directed bool, attrs map[string]string) {
|
||||
this.Edges.Add(&Edge{src, srcPort, dst, dstPort, directed, attrs})
|
||||
}
|
||||
|
||||
//Adds an edge to the graph from node src to node dst.
|
||||
//This does not imply the adding of missing nodes.
|
||||
//If directed is set to true then SetDir(true) must also be called or there will be a syntax error in the output.
|
||||
func (this *Graph) AddEdge(src, dst string, directed bool, attrs map[string]string) {
|
||||
this.AddPortEdge(src, "", dst, "", directed, attrs)
|
||||
}
|
||||
|
||||
//Adds a node to a graph/subgraph.
|
||||
//If not subgraph exists use the name of the main graph.
|
||||
//This does not imply the adding of a missing subgraph.
|
||||
func (this *Graph) AddNode(parentGraph string, name string, attrs map[string]string) {
|
||||
this.Nodes.Add(&Node{name, attrs})
|
||||
this.Relations.Add(parentGraph, name)
|
||||
}
|
||||
|
||||
func (this *Graph) getAttrs(graphName string) Attrs {
|
||||
if this.Name == graphName {
|
||||
return this.Attrs
|
||||
}
|
||||
g, ok := this.SubGraphs.SubGraphs[graphName]
|
||||
if !ok {
|
||||
panic("graph or subgraph " + graphName + " does not exist")
|
||||
}
|
||||
return g.Attrs
|
||||
}
|
||||
|
||||
//Adds an attribute to a graph/subgraph.
|
||||
func (this *Graph) AddAttr(parentGraph string, field string, value string) {
|
||||
this.getAttrs(parentGraph).Add(field, value)
|
||||
}
|
||||
|
||||
//Adds a subgraph to a graph/subgraph.
|
||||
func (this *Graph) AddSubGraph(parentGraph string, name string, attrs map[string]string) {
|
||||
this.SubGraphs.Add(name)
|
||||
for key, value := range attrs {
|
||||
this.AddAttr(name, key, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *Graph) IsNode(name string) bool {
|
||||
_, ok := this.Nodes.Lookup[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (this *Graph) IsSubGraph(name string) bool {
|
||||
_, ok := this.SubGraphs.SubGraphs[name]
|
||||
return ok
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package gographviz
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
//Represents a Node.
|
||||
type Node struct {
|
||||
Name string
|
||||
Attrs Attrs
|
||||
}
|
||||
|
||||
//Represents a set of Nodes.
|
||||
type Nodes struct {
|
||||
Lookup map[string]*Node
|
||||
Nodes []*Node
|
||||
}
|
||||
|
||||
//Creates a new set of Nodes.
|
||||
func NewNodes() *Nodes {
|
||||
return &Nodes{make(map[string]*Node), make([]*Node, 0)}
|
||||
}
|
||||
|
||||
//Adds a Node to the set of Nodes, ammending the attributes of an already existing node.
|
||||
func (this *Nodes) Add(node *Node) {
|
||||
n, ok := this.Lookup[node.Name]
|
||||
if ok {
|
||||
n.Attrs.Ammend(node.Attrs)
|
||||
return
|
||||
}
|
||||
this.Lookup[node.Name] = node
|
||||
this.Nodes = append(this.Nodes, node)
|
||||
}
|
||||
|
||||
//Returns a sorted list of nodes.
|
||||
func (this Nodes) Sorted() []*Node {
|
||||
keys := make([]string, 0, len(this.Lookup))
|
||||
for key := range this.Lookup {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
nodes := make([]*Node, len(keys))
|
||||
for i := range keys {
|
||||
nodes[i] = this.Lookup[keys[i]]
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
//A parser for the DOT grammar.
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/awalterschulze/gographviz/ast"
|
||||
"github.com/awalterschulze/gographviz/scanner"
|
||||
"github.com/awalterschulze/gographviz/token"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
//Parses a DOT string and outputs the
|
||||
//abstract syntax tree representing the graph.
|
||||
func ParseString(dotString string) (*ast.Graph, error) {
|
||||
return ParseBytes([]byte(dotString))
|
||||
}
|
||||
|
||||
//Parses the bytes representing a DOT string
|
||||
//and outputs the abstract syntax tree representing the graph.
|
||||
func ParseBytes(dotBytes []byte) (*ast.Graph, error) {
|
||||
lex := &scanner.Scanner{}
|
||||
lex.Init(dotBytes, token.DOTTokens)
|
||||
parser := NewParser(ActionTable, GotoTable, ProductionsTable, token.DOTTokens)
|
||||
st, err := parser.Parse(lex)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g, ok := st.(*ast.Graph)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Parser did not return an *ast.Graph, but rather a %T", st))
|
||||
}
|
||||
return g, err
|
||||
}
|
||||
|
||||
//Parses a reader which contains a DOT string
|
||||
//and outputs the abstract syntax tree representing the graph.
|
||||
func Parse(r io.Reader) (*ast.Graph, error) {
|
||||
bytes, err := ioutil.ReadAll(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ParseBytes(bytes)
|
||||
}
|
||||
|
||||
//Parses a file which contains a DOT string
|
||||
//and outputs the abstract syntax tree representing the graph.
|
||||
func ParseFile(filename string) (*ast.Graph, error) {
|
||||
f, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return Parse(f)
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
import "github.com/awalterschulze/gographviz/token"
|
||||
|
||||
type (
|
||||
ActionTab []ActionRow
|
||||
ActionRow map[token.Type]Action
|
||||
)
|
||||
|
||||
func (R ActionRow) String() string {
|
||||
s := ""
|
||||
for t, a := range R {
|
||||
s += strconv.Itoa(int(t)) + " : " + a.String() + "\n"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type (
|
||||
Accept int
|
||||
Shift State
|
||||
Reduce int
|
||||
|
||||
Action interface {
|
||||
Act()
|
||||
String() string
|
||||
}
|
||||
)
|
||||
|
||||
func (this Accept) Act() {}
|
||||
func (this Shift) Act() {}
|
||||
func (this Reduce) Act() {}
|
||||
|
||||
func (this Accept) String() string { return "Accept(0)" }
|
||||
func (this Shift) String() string { return "Shift(" + strconv.Itoa(int(this)) + ")" }
|
||||
func (this Reduce) String() string { return "Reduce(" + strconv.Itoa(int(this)) + ")" }
|
||||
|
||||
type (
|
||||
GotoTab []GotoRow
|
||||
GotoRow map[NT]State
|
||||
State int
|
||||
NT string
|
||||
)
|
||||
|
||||
type (
|
||||
ProdTab []ProdTabEntry
|
||||
ProdTabEntry struct {
|
||||
String string
|
||||
Head NT
|
||||
NumSymbols int
|
||||
ReduceFunc func([]Attrib) (Attrib, error)
|
||||
}
|
||||
Attrib interface {
|
||||
String() string
|
||||
}
|
||||
)
|
||||
|
||||
/*** Stack ***/
|
||||
|
||||
type stack struct {
|
||||
state []State
|
||||
attrib []Attrib
|
||||
}
|
||||
|
||||
const INITIAL_STACK_SIZE = 100
|
||||
|
||||
func NewStack() *stack {
|
||||
return &stack{state: make([]State, 0, INITIAL_STACK_SIZE),
|
||||
attrib: make([]Attrib, 0, INITIAL_STACK_SIZE),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *stack) Push(s State, a Attrib) {
|
||||
this.state = append(this.state, s)
|
||||
this.attrib = append(this.attrib, a)
|
||||
}
|
||||
|
||||
func (this *stack) Top() State {
|
||||
return this.state[len(this.state)-1]
|
||||
}
|
||||
|
||||
func (this *stack) PopN(items int) []Attrib {
|
||||
lo, hi := len(this.state)-items, len(this.state)
|
||||
|
||||
attrib := this.attrib[lo:hi]
|
||||
|
||||
this.state = this.state[:lo]
|
||||
this.attrib = this.attrib[:lo]
|
||||
|
||||
return attrib
|
||||
}
|
||||
|
||||
func (S *stack) String() string {
|
||||
res := "stack:\n"
|
||||
for i, st := range S.state {
|
||||
res += "\t" + strconv.Itoa(i) + ": " + strconv.Itoa(int(st))
|
||||
res += " , "
|
||||
if S.attrib[i] == nil {
|
||||
res += "nil"
|
||||
} else {
|
||||
res += S.attrib[i].String()
|
||||
}
|
||||
res += "\n"
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/*** Parser ***/
|
||||
|
||||
type Parser struct {
|
||||
actTab ActionTab
|
||||
gotoTab GotoTab
|
||||
prodTab ProdTab
|
||||
stack *stack
|
||||
nextToken *token.Token
|
||||
pos token.Position
|
||||
tokenMap *token.TokenMap
|
||||
}
|
||||
|
||||
type Scanner interface {
|
||||
Scan() (*token.Token, token.Position)
|
||||
}
|
||||
|
||||
func NewParser(act ActionTab, gto GotoTab, prod ProdTab, tm *token.TokenMap) *Parser {
|
||||
p := &Parser{actTab: act, gotoTab: gto, prodTab: prod, stack: NewStack(), tokenMap: tm}
|
||||
p.stack.Push(0, nil) //TODO: which attribute should be pushed here?
|
||||
return p
|
||||
}
|
||||
|
||||
func (P *Parser) Error(err error) error {
|
||||
errmsg := "Error: " + P.TokString(P.nextToken) + " @ " + P.pos.String()
|
||||
if err != nil {
|
||||
errmsg += " " + err.Error()
|
||||
} else {
|
||||
errmsg += ", expected one of: "
|
||||
actRow := P.actTab[P.stack.Top()]
|
||||
i := 0
|
||||
for t := range actRow {
|
||||
errmsg += P.tokenMap.TokenString(t)
|
||||
if i < len(actRow)-1 {
|
||||
errmsg += " "
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
return errors.New(errmsg)
|
||||
}
|
||||
|
||||
func (P *Parser) TokString(tok *token.Token) string {
|
||||
msg := P.tokenMap.TokenString(tok.Type) + "(" + strconv.Itoa(int(tok.Type)) + ")"
|
||||
msg += " " + string(tok.Lit)
|
||||
return msg
|
||||
}
|
||||
|
||||
func (this *Parser) Parse(scanner Scanner) (res interface{}, err error) {
|
||||
this.nextToken, this.pos = scanner.Scan()
|
||||
for acc := false; !acc; {
|
||||
action, ok := this.actTab[this.stack.Top()][this.nextToken.Type]
|
||||
if !ok {
|
||||
return nil, this.Error(nil)
|
||||
}
|
||||
switch act := action.(type) {
|
||||
case Accept:
|
||||
res = this.stack.PopN(1)[0]
|
||||
acc = true
|
||||
case Shift:
|
||||
this.stack.Push(State(act), this.nextToken)
|
||||
this.nextToken, this.pos = scanner.Scan()
|
||||
case Reduce:
|
||||
prod := this.prodTab[int(act)]
|
||||
attrib, err := prod.ReduceFunc(this.stack.PopN(prod.NumSymbols))
|
||||
if err != nil {
|
||||
return nil, this.Error(err)
|
||||
} else {
|
||||
this.stack.Push(this.gotoTab[this.stack.Top()][prod.Head], attrib)
|
||||
}
|
||||
default:
|
||||
panic("unknown action")
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
import "github.com/awalterschulze/gographviz/token"
|
||||
|
||||
type (
|
||||
ActionTab []ActionRow
|
||||
ActionRow map[token.Type]Action
|
||||
)
|
||||
|
||||
func (R ActionRow) String() string {
|
||||
s := ""
|
||||
for t, a := range R {
|
||||
s += strconv.Itoa(int(t)) + " : " + a.String() + "\n"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
type (
|
||||
Accept int
|
||||
Shift State
|
||||
Reduce int
|
||||
|
||||
Action interface {
|
||||
Act()
|
||||
String() string
|
||||
}
|
||||
)
|
||||
|
||||
func (this Accept) Act() {}
|
||||
func (this Shift) Act() {}
|
||||
func (this Reduce) Act() {}
|
||||
|
||||
func (this Accept) String() string { return "Accept(0)" }
|
||||
func (this Shift) String() string { return "Shift(" + strconv.Itoa(int(this)) + ")" }
|
||||
func (this Reduce) String() string { return "Reduce(" + strconv.Itoa(int(this)) + ")" }
|
||||
|
||||
type (
|
||||
GotoTab []GotoRow
|
||||
GotoRow map[NT]State
|
||||
State int
|
||||
NT string
|
||||
)
|
||||
|
||||
type (
|
||||
ProdTab []ProdTabEntry
|
||||
ProdTabEntry struct {
|
||||
String string
|
||||
Head NT
|
||||
NumSymbols int
|
||||
ReduceFunc func([]Attrib) (Attrib, error)
|
||||
}
|
||||
Attrib interface {
|
||||
String() string
|
||||
}
|
||||
)
|
||||
|
||||
/*** Stack ***/
|
||||
|
||||
type stack struct {
|
||||
state []State
|
||||
attrib []Attrib
|
||||
}
|
||||
|
||||
const INITIAL_STACK_SIZE = 100
|
||||
|
||||
func NewStack() *stack {
|
||||
return &stack{state: make([]State, 0, INITIAL_STACK_SIZE),
|
||||
attrib: make([]Attrib, 0, INITIAL_STACK_SIZE),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *stack) Push(s State, a Attrib) {
|
||||
this.state = append(this.state, s)
|
||||
this.attrib = append(this.attrib, a)
|
||||
}
|
||||
|
||||
func (this *stack) Top() State {
|
||||
return this.state[len(this.state)-1]
|
||||
}
|
||||
|
||||
func (this *stack) PopN(items int) []Attrib {
|
||||
lo, hi := len(this.state)-items, len(this.state)
|
||||
|
||||
attrib := this.attrib[lo:hi]
|
||||
|
||||
this.state = this.state[:lo]
|
||||
this.attrib = this.attrib[:lo]
|
||||
|
||||
return attrib
|
||||
}
|
||||
|
||||
func (S *stack) String() string {
|
||||
res := "stack:\n"
|
||||
for i, st := range S.state {
|
||||
res += "\t" + strconv.Itoa(i) + ": " + strconv.Itoa(int(st))
|
||||
res += " , "
|
||||
if S.attrib[i] == nil {
|
||||
res += "nil"
|
||||
} else {
|
||||
res += S.attrib[i].String()
|
||||
}
|
||||
res += "\n"
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
/*** Parser ***/
|
||||
|
||||
type Parser struct {
|
||||
actTab ActionTab
|
||||
gotoTab GotoTab
|
||||
prodTab ProdTab
|
||||
stack *stack
|
||||
nextToken *token.Token
|
||||
pos token.Position
|
||||
tokenMap *token.TokenMap
|
||||
}
|
||||
|
||||
type Scanner interface {
|
||||
Scan() (*token.Token, token.Position)
|
||||
}
|
||||
|
||||
func NewParser(act ActionTab, gto GotoTab, prod ProdTab, tm *token.TokenMap) *Parser {
|
||||
p := &Parser{actTab: act, gotoTab: gto, prodTab: prod, stack: NewStack(), tokenMap: tm}
|
||||
p.stack.Push(0, nil) //TODO: which attribute should be pushed here?
|
||||
return p
|
||||
}
|
||||
|
||||
func (P *Parser) Error(err error) error {
|
||||
errmsg := "Error: " + P.TokString(P.nextToken) + " @ " + P.pos.String()
|
||||
if err != nil {
|
||||
errmsg += " " + err.Error()
|
||||
} else {
|
||||
errmsg += ", expected one of: "
|
||||
actRow := P.actTab[P.stack.Top()]
|
||||
i := 0
|
||||
for t := range actRow {
|
||||
errmsg += P.tokenMap.TokenString(t)
|
||||
if i < len(actRow)-1 {
|
||||
errmsg += " "
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
return errors.New(errmsg)
|
||||
}
|
||||
|
||||
func (P *Parser) TokString(tok *token.Token) string {
|
||||
msg := P.tokenMap.TokenString(tok.Type) + "(" + strconv.Itoa(int(tok.Type)) + ")"
|
||||
msg += " " + string(tok.Lit)
|
||||
return msg
|
||||
}
|
||||
|
||||
func (this *Parser) Parse(scanner Scanner) (res interface{}, err error) {
|
||||
this.nextToken, this.pos = scanner.Scan()
|
||||
for acc := false; !acc; {
|
||||
action, ok := this.actTab[this.stack.Top()][this.nextToken.Type]
|
||||
if !ok {
|
||||
return nil, this.Error(nil)
|
||||
}
|
||||
switch act := action.(type) {
|
||||
case Accept:
|
||||
res = this.stack.PopN(1)[0]
|
||||
acc = true
|
||||
case Shift:
|
||||
this.stack.Push(State(act), this.nextToken)
|
||||
this.nextToken, this.pos = scanner.Scan()
|
||||
case Reduce:
|
||||
prod := this.prodTab[int(act)]
|
||||
attrib, err := prod.ReduceFunc(this.stack.PopN(prod.NumSymbols))
|
||||
if err != nil {
|
||||
return nil, this.Error(err)
|
||||
} else {
|
||||
this.stack.Push(this.gotoTab[this.stack.Top()][prod.Head], attrib)
|
||||
}
|
||||
default:
|
||||
panic("unknown action")
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
+293
@@ -0,0 +1,293 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/awalterschulze/gographviz/ast"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func check(t *testing.T, err error) {
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func assert(t *testing.T, msg string, v1 interface{}, v2 interface{}) {
|
||||
if v1 != v2 {
|
||||
t.Fatalf("%v\n%v\n!=\n%v", msg, v1, v2)
|
||||
}
|
||||
}
|
||||
|
||||
func parseTest(t *testing.T, filename string) {
|
||||
localFilename := "../testdata/" + filename
|
||||
s, err := ioutil.ReadFile(localFilename)
|
||||
check(t, err)
|
||||
t.Logf("Input String = %v", string(s))
|
||||
g, err := ParseFile(localFilename)
|
||||
t.Logf("First Parse = %v", g)
|
||||
check(t, err)
|
||||
gstr := g.String()
|
||||
g2, err := ParseString(gstr)
|
||||
t.Logf("Second Parse = %v", g2)
|
||||
check(t, err)
|
||||
gstr2 := g2.String()
|
||||
assert(t, "output strings", gstr, gstr2)
|
||||
}
|
||||
|
||||
func parseStringTest(t *testing.T, s string) {
|
||||
t.Logf("Input String = %v", s)
|
||||
g, err := ParseString(s)
|
||||
t.Logf("First Parse = %v", g)
|
||||
check(t, err)
|
||||
gstr := g.String()
|
||||
g2, err := ParseString(gstr)
|
||||
t.Logf("Second Parse = %v", g2)
|
||||
check(t, err)
|
||||
gstr2 := g2.String()
|
||||
assert(t, "output strings", gstr, gstr2)
|
||||
}
|
||||
|
||||
func TestHelloWorldString(t *testing.T) {
|
||||
input := `digraph G {Hello->World}`
|
||||
g, err := ParseString(input)
|
||||
check(t, err)
|
||||
fmt.Printf("%#v", g)
|
||||
}
|
||||
|
||||
func TestHelloWorldFile(t *testing.T) {
|
||||
g, err := ParseFile("../testdata/helloworld.gv.txt")
|
||||
check(t, err)
|
||||
fmt.Printf("%#v", g)
|
||||
}
|
||||
|
||||
func TestAttr(t *testing.T) {
|
||||
parseStringTest(t,
|
||||
"digraph finite_state { rankdir = LR }")
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
parseStringTest(t,
|
||||
`digraph finite_state { rankdir = "LR" }`)
|
||||
}
|
||||
|
||||
func TestAttrList(t *testing.T) {
|
||||
parseStringTest(t, `
|
||||
digraph { node [ shape = doublecircle ] }`)
|
||||
}
|
||||
|
||||
func TestStringLit(t *testing.T) {
|
||||
parseStringTest(t, `digraph finite_state_machine {
|
||||
size= "8" ; }`)
|
||||
}
|
||||
|
||||
func TestHashComments(t *testing.T) {
|
||||
parseStringTest(t, `## bla \n
|
||||
digraph G {Hello->World}`)
|
||||
}
|
||||
|
||||
func TestIntLit(t *testing.T) {
|
||||
parseStringTest(t, `graph G {
|
||||
1 -- 30 [f=1];}`)
|
||||
}
|
||||
|
||||
func TestFloat1(t *testing.T) {
|
||||
parseStringTest(t, `digraph { bla = 2.0 }`)
|
||||
}
|
||||
|
||||
func TestFloat2(t *testing.T) {
|
||||
parseStringTest(t, `digraph { bla = .1 }`)
|
||||
}
|
||||
|
||||
func TestNegative(t *testing.T) {
|
||||
parseStringTest(t, `digraph { -2 -> -1 }`)
|
||||
}
|
||||
|
||||
func TestUnderscore(t *testing.T) {
|
||||
parseStringTest(t, `digraph { a_b = 1 }`)
|
||||
}
|
||||
|
||||
func TestNonAscii(t *testing.T) {
|
||||
parseStringTest(t, `digraph { label=T�th }`)
|
||||
}
|
||||
|
||||
func TestPorts(t *testing.T) {
|
||||
parseStringTest(t, `digraph { "node6":f0 -> "node9":f1 }`)
|
||||
}
|
||||
|
||||
func TestHtml(t *testing.T) {
|
||||
parseStringTest(t, `digraph { a = <<table></table>> }`)
|
||||
}
|
||||
|
||||
func TestIdWithKeyword(t *testing.T) {
|
||||
parseStringTest(t, `digraph { edgeURL = "a" }`)
|
||||
}
|
||||
|
||||
func TestSubGraph(t *testing.T) {
|
||||
parseStringTest(t, `digraph { subgraph { a -> b } }`)
|
||||
}
|
||||
|
||||
func TestImplicitSubGraph(t *testing.T) {
|
||||
parseStringTest(t, `digraph { { a -> b } }`)
|
||||
}
|
||||
|
||||
func TestEdges(t *testing.T) {
|
||||
parseStringTest(t, `digraph { a0 -> a1 -> a2 -> a3 }`)
|
||||
}
|
||||
|
||||
func TestNodes(t *testing.T) {
|
||||
parseStringTest(t, `digraph { a0 a1 }`)
|
||||
}
|
||||
|
||||
func TestTwoAttributes(t *testing.T) {
|
||||
g, err := ParseString(`digraph { a0 [shape = circle bla = bla]}`)
|
||||
check(t, err)
|
||||
t.Logf("Parsed String = %v", g)
|
||||
for _, stmt := range g.StmtList {
|
||||
node := stmt.(*ast.NodeStmt)
|
||||
if len(node.Attrs[0]) != 2 {
|
||||
t.Fatalf("Not enough attributes, expected two, but found %v in %v", len(node.Attrs), node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEasyFsm(t *testing.T) {
|
||||
parseStringTest(t, `digraph finite_state_machine {
|
||||
rankdir=LR;
|
||||
size="8,5";
|
||||
node [shape = doublecircle]; LR_0 LR_3 LR_4 LR_8;
|
||||
node [shape = circle];
|
||||
LR_0 -> LR_2 [ label = "SS(B)" ];
|
||||
LR_0 -> LR_1 [ label = "SS(S)" ];
|
||||
LR_1 -> LR_3 [ label = "S($end)" ];
|
||||
LR_2 -> LR_6 [ label = "SS(b)" ];
|
||||
LR_2 -> LR_5 [ label = "SS(a)" ];
|
||||
LR_2 -> LR_4 [ label = "S(A)" ];
|
||||
LR_5 -> LR_7 [ label = "S(b)" ];
|
||||
LR_5 -> LR_5 [ label = "S(a)" ];
|
||||
LR_6 -> LR_6 [ label = "S(b)" ];
|
||||
LR_6 -> LR_5 [ label = "S(a)" ];
|
||||
LR_7 -> LR_8 [ label = "S(b)" ];
|
||||
LR_7 -> LR_5 [ label = "S(a)" ];
|
||||
LR_8 -> LR_6 [ label = "S(b)" ];
|
||||
LR_8 -> LR_5 [ label = "S(a)" ];
|
||||
}`)
|
||||
}
|
||||
|
||||
func TestEmptyAttrList(t *testing.T) {
|
||||
parseStringTest(t, `digraph g { edge [ ] }`)
|
||||
}
|
||||
|
||||
func TestHelloWorld(t *testing.T) {
|
||||
parseTest(t, "helloworld.gv.txt")
|
||||
}
|
||||
|
||||
func TestCluster(t *testing.T) {
|
||||
parseTest(t, "cluster.gv.txt")
|
||||
}
|
||||
|
||||
func TestPsg(t *testing.T) {
|
||||
parseTest(t, "psg.gv.txt")
|
||||
}
|
||||
|
||||
func TestTransparency(t *testing.T) {
|
||||
parseTest(t, "transparency.gv.txt")
|
||||
}
|
||||
|
||||
func TestCrazy(t *testing.T) {
|
||||
parseTest(t, "crazy.gv.txt")
|
||||
}
|
||||
|
||||
func TestKennedyanc(t *testing.T) {
|
||||
parseTest(t, "kennedyanc.gv.txt")
|
||||
}
|
||||
|
||||
func TestRoot(t *testing.T) {
|
||||
parseTest(t, "root.gv.txt")
|
||||
}
|
||||
|
||||
func TestTwpoi(t *testing.T) {
|
||||
parseTest(t, "twopi.gv.txt")
|
||||
}
|
||||
|
||||
func TestDataStruct(t *testing.T) {
|
||||
parseTest(t, "datastruct.gv.txt")
|
||||
}
|
||||
|
||||
func TestLionShare(t *testing.T) {
|
||||
parseTest(t, "lion_share.gv.txt")
|
||||
}
|
||||
|
||||
func TestSdh(t *testing.T) {
|
||||
parseTest(t, "sdh.gv.txt")
|
||||
}
|
||||
|
||||
func TestUnix(t *testing.T) {
|
||||
parseTest(t, "unix.gv.txt")
|
||||
}
|
||||
|
||||
func TestEr(t *testing.T) {
|
||||
parseTest(t, "er.gv.txt")
|
||||
}
|
||||
|
||||
func TestNerworkMapTwopi(t *testing.T) {
|
||||
parseTest(t, "networkmap_twopi.gv.txt")
|
||||
}
|
||||
|
||||
func TestSibling(t *testing.T) {
|
||||
parseTest(t, "siblings.gv.txt")
|
||||
}
|
||||
|
||||
func TestWorld(t *testing.T) {
|
||||
parseTest(t, "world.gv.txt")
|
||||
}
|
||||
|
||||
func TestFdpclust(t *testing.T) {
|
||||
parseTest(t, "fdpclust.gv.txt")
|
||||
}
|
||||
|
||||
func TestPhilo(t *testing.T) {
|
||||
parseTest(t, "philo.gv.txt")
|
||||
}
|
||||
|
||||
func TestSoftmaint(t *testing.T) {
|
||||
parseTest(t, "softmaint.gv.txt")
|
||||
}
|
||||
|
||||
func TestFsm(t *testing.T) {
|
||||
parseTest(t, "fsm.gv.txt")
|
||||
}
|
||||
|
||||
func TestProcess(t *testing.T) {
|
||||
parseTest(t, "process.gv.txt")
|
||||
}
|
||||
|
||||
func TestSwitchGv(t *testing.T) {
|
||||
parseTest(t, "switch.gv.txt")
|
||||
}
|
||||
|
||||
func TestGd19942007(t *testing.T) {
|
||||
parseTest(t, "gd_1994_2007.gv.txt")
|
||||
}
|
||||
|
||||
func TestProfile(t *testing.T) {
|
||||
parseTest(t, "profile.gv.txt")
|
||||
}
|
||||
|
||||
func TestTrafficLights(t *testing.T) {
|
||||
parseTest(t, "traffic_lights.gv.txt")
|
||||
}
|
||||
+4002
File diff suppressed because it is too large
Load Diff
+52
@@ -0,0 +1,52 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package gographviz
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
//Represents the relations between graphs and nodes.
|
||||
//Each node belongs the main graph or a subgraph.
|
||||
type Relations struct {
|
||||
ParentToChildren map[string]map[string]bool
|
||||
ChildToParents map[string]map[string]bool
|
||||
}
|
||||
|
||||
//Creates an empty set of relations.
|
||||
func NewRelations() *Relations {
|
||||
return &Relations{make(map[string]map[string]bool), make(map[string]map[string]bool)}
|
||||
}
|
||||
|
||||
//Adds a node to a parent graph.
|
||||
func (this *Relations) Add(parent string, child string) {
|
||||
if _, ok := this.ParentToChildren[parent]; !ok {
|
||||
this.ParentToChildren[parent] = make(map[string]bool)
|
||||
}
|
||||
this.ParentToChildren[parent][child] = true
|
||||
if _, ok := this.ChildToParents[child]; !ok {
|
||||
this.ChildToParents[child] = make(map[string]bool)
|
||||
}
|
||||
this.ChildToParents[child][parent] = true
|
||||
}
|
||||
|
||||
func (this *Relations) SortedChildren(parent string) []string {
|
||||
keys := make([]string, 0)
|
||||
for key := range this.ParentToChildren[parent] {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
+570
@@ -0,0 +1,570 @@
|
||||
// Copyright 2009 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// A scanner for Go source text. Takes a []byte as source which can
|
||||
// then be tokenized through repeated calls to the Scan function.
|
||||
// For a sample use of a scanner, see the implementation of Tokenize.
|
||||
//
|
||||
|
||||
//A scanner for the DOT grammar that has been derived from the standard
|
||||
//golang scanner with extra added literals.
|
||||
package scanner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
import "github.com/awalterschulze/gographviz/token"
|
||||
|
||||
// A Scanner holds the scanner's internal state while processing
|
||||
// a given text. It can be allocated as part of another data
|
||||
// structure but must be initialized via Init before use. For
|
||||
// a sample use, see the implementation of Tokenize.
|
||||
//
|
||||
type Scanner struct {
|
||||
// immutable state
|
||||
src []byte // source
|
||||
tokenMap *token.TokenMap
|
||||
|
||||
// scanning state
|
||||
pos token.Position // previous reading position (position before ch)
|
||||
offset int // current reading offset (position after ch)
|
||||
ch rune // one char look-ahead
|
||||
|
||||
// public state - ok to modify
|
||||
ErrorCount int // number of errors encountered
|
||||
}
|
||||
|
||||
// Read the next Unicode char into S.ch.
|
||||
// S.ch < 0 means end-of-file.
|
||||
//
|
||||
func (S *Scanner) next() {
|
||||
if S.offset < len(S.src) {
|
||||
S.pos.Offset = S.offset
|
||||
S.pos.Column++
|
||||
if S.ch == '\n' {
|
||||
// next character starts a new line
|
||||
S.pos.Line++
|
||||
S.pos.Column = 1
|
||||
}
|
||||
r, w := rune(S.src[S.offset]), 1
|
||||
switch {
|
||||
case r == 0:
|
||||
S.error(S.pos, "illegal character NUL")
|
||||
case r >= 0x80:
|
||||
// not ASCII
|
||||
r, w = utf8.DecodeRune(S.src[S.offset:])
|
||||
if r == utf8.RuneError && w == 1 {
|
||||
S.error(S.pos, "illegal UTF-8 encoding")
|
||||
}
|
||||
}
|
||||
S.offset += w
|
||||
S.ch = r
|
||||
} else {
|
||||
S.pos.Offset = len(S.src)
|
||||
S.ch = -1 // eof
|
||||
}
|
||||
}
|
||||
|
||||
// Init prepares the scanner S to tokenize the text src. Calls to Scan
|
||||
// will use the error handler err if they encounter a syntax error and
|
||||
// err is not nil. Also, for each error encountered, the Scanner field
|
||||
// ErrorCount is incremented by one. The filename parameter is used as
|
||||
// filename in the token.Position returned by Scan for each token. The
|
||||
// mode parameter determines how comments and illegal characters are
|
||||
// handled.
|
||||
//
|
||||
func (S *Scanner) Init(src []byte, tokenMap *token.TokenMap) {
|
||||
// Explicitly initialize all fields since a scanner may be reused.
|
||||
S.src = src
|
||||
S.tokenMap = tokenMap
|
||||
S.pos = token.Position{0, 1, 0}
|
||||
S.offset = 0
|
||||
S.ErrorCount = 0
|
||||
S.next()
|
||||
}
|
||||
|
||||
func charString(ch rune) string {
|
||||
var s string
|
||||
switch ch {
|
||||
case -1:
|
||||
return "EOF"
|
||||
case '\a':
|
||||
s = "\\a"
|
||||
case '\b':
|
||||
s = "\\b"
|
||||
case '\f':
|
||||
s = "\\f"
|
||||
case '\n':
|
||||
s = "\\n"
|
||||
case '\r':
|
||||
s = "\\r"
|
||||
case '\t':
|
||||
s = "\\t"
|
||||
case '\v':
|
||||
s = "\\v"
|
||||
case '\\':
|
||||
s = "\\\\"
|
||||
case '\'':
|
||||
s = "\\'"
|
||||
default:
|
||||
s = string(ch)
|
||||
}
|
||||
return "'" + s + "' (U+" + strconv.FormatInt(int64(ch), 16) + ")"
|
||||
}
|
||||
|
||||
func (S *Scanner) error(pos token.Position, msg string) {
|
||||
S.ErrorCount++
|
||||
}
|
||||
|
||||
func (S *Scanner) expect(ch rune) {
|
||||
if S.ch != ch {
|
||||
S.error(S.pos, "expected "+charString(ch)+", found "+charString(S.ch))
|
||||
}
|
||||
S.next() // always make progress
|
||||
}
|
||||
|
||||
var prefix = []byte("line ")
|
||||
|
||||
func (S *Scanner) scanComment(pos token.Position) {
|
||||
// first '/' or '#' already consumed
|
||||
if S.ch == '*' {
|
||||
/*-style comment */
|
||||
S.expect('*')
|
||||
for S.ch >= 0 {
|
||||
ch := S.ch
|
||||
S.next()
|
||||
if ch == '*' && S.ch == '/' {
|
||||
S.next()
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//-style comment or #-style comment
|
||||
for S.ch >= 0 {
|
||||
S.next()
|
||||
if S.ch == '\n' {
|
||||
// '\n' is not part of the comment for purposes of scanning
|
||||
// (the comment ends on the same line where it started)
|
||||
if pos.Column == 1 {
|
||||
text := S.src[pos.Offset+2 : S.pos.Offset]
|
||||
if bytes.HasPrefix(text, prefix) {
|
||||
// comment starts at beginning of line with "//line ";
|
||||
// get filename and line number, if any
|
||||
i := bytes.Index(text, []byte{':'})
|
||||
if i >= 0 {
|
||||
if line, err := strconv.Atoi(string(text[i+1:])); err == nil && line > 0 {
|
||||
// valid //line filename:line comment;
|
||||
// update scanner position
|
||||
S.pos.Line = line - 1 // -1 since the '\n' has not been consumed yet
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
S.error(pos, "comment not terminated")
|
||||
}
|
||||
|
||||
func (S *Scanner) findNewline(pos token.Position) bool {
|
||||
// first '/' already consumed; assume S.ch == '/' || S.ch == '*'
|
||||
|
||||
// read ahead until a newline or non-comment token is found
|
||||
newline := false
|
||||
for pos1 := pos; S.ch >= 0; {
|
||||
if S.ch == '/' {
|
||||
//-style comment always contains a newline
|
||||
newline = true
|
||||
break
|
||||
}
|
||||
S.scanComment(pos1)
|
||||
if pos1.Line < S.pos.Line {
|
||||
/*-style comment contained a newline */
|
||||
newline = true
|
||||
break
|
||||
}
|
||||
S.skipWhitespace() // S.insertSemi is set
|
||||
if S.ch == '\n' {
|
||||
newline = true
|
||||
break
|
||||
}
|
||||
if S.ch != '/' {
|
||||
// non-comment token
|
||||
break
|
||||
}
|
||||
pos1 = S.pos
|
||||
S.next()
|
||||
if S.ch != '/' && S.ch != '*' {
|
||||
// non-comment token
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// reset position to where it was upon calling findNewline
|
||||
S.pos = pos
|
||||
S.offset = pos.Offset + 1
|
||||
S.next()
|
||||
|
||||
return newline
|
||||
}
|
||||
|
||||
func isLetter(ch rune) bool {
|
||||
return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' ||
|
||||
ch >= 0x80 && unicode.IsLetter(ch) && ch != 'ε'
|
||||
}
|
||||
|
||||
func isDigit(ch rune) bool {
|
||||
return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch)
|
||||
}
|
||||
|
||||
func digitVal(ch rune) int {
|
||||
switch {
|
||||
case '0' <= ch && ch <= '9':
|
||||
return int(ch) - '0'
|
||||
case 'a' <= ch && ch <= 'f':
|
||||
return int(ch) - 'a' + 10
|
||||
case 'A' <= ch && ch <= 'F':
|
||||
return int(ch) - 'A' + 10
|
||||
}
|
||||
return 16 // larger than any legal digit val
|
||||
}
|
||||
|
||||
func (S *Scanner) scanEscape(quote rune) {
|
||||
pos := S.pos
|
||||
|
||||
var i, base, max uint32
|
||||
switch S.ch {
|
||||
case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote:
|
||||
S.next()
|
||||
return
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7':
|
||||
i, base, max = 3, 8, 255
|
||||
case 'x':
|
||||
S.next()
|
||||
i, base, max = 2, 16, 255
|
||||
case 'u':
|
||||
S.next()
|
||||
i, base, max = 4, 16, unicode.MaxRune
|
||||
case 'U':
|
||||
S.next()
|
||||
i, base, max = 8, 16, unicode.MaxRune
|
||||
default:
|
||||
S.next() // always make progress
|
||||
S.error(pos, "unknown escape sequence")
|
||||
return
|
||||
}
|
||||
|
||||
var x uint32
|
||||
for ; i > 0; i-- {
|
||||
d := uint32(digitVal(S.ch))
|
||||
if d > base {
|
||||
S.error(S.pos, "illegal character in escape sequence")
|
||||
return
|
||||
}
|
||||
x = x*base + d
|
||||
S.next()
|
||||
}
|
||||
if x > max || 0xd800 <= x && x < 0xe000 {
|
||||
S.error(pos, "escape sequence is invalid Unicode code point")
|
||||
}
|
||||
}
|
||||
|
||||
func (S *Scanner) scanChar(pos token.Position) {
|
||||
// '\'' already consumed
|
||||
|
||||
n := 0
|
||||
for S.ch != '\'' {
|
||||
ch := S.ch
|
||||
n++
|
||||
S.next()
|
||||
if ch == '\n' || ch < 0 {
|
||||
S.error(pos, "character literal not terminated")
|
||||
n = 1
|
||||
break
|
||||
}
|
||||
if ch == '\\' {
|
||||
S.scanEscape('\'')
|
||||
}
|
||||
}
|
||||
|
||||
S.next()
|
||||
|
||||
if n != 1 {
|
||||
S.error(pos, "illegal character literal")
|
||||
}
|
||||
}
|
||||
|
||||
func (S *Scanner) isToken(str string) bool {
|
||||
return S.tokenMap.Type(str) != token.ILLEGAL
|
||||
}
|
||||
|
||||
func (S *Scanner) scanIdentifier() token.Type {
|
||||
pos := S.pos.Offset
|
||||
for !S.isToken(string(S.ch)) && !unicode.IsSpace(S.ch) && S.ch != '-' {
|
||||
S.next()
|
||||
}
|
||||
if tok := S.tokenMap.Type(string(S.src[pos:S.pos.Offset])); tok != token.ILLEGAL {
|
||||
return tok
|
||||
}
|
||||
return S.tokenMap.Type("id")
|
||||
}
|
||||
|
||||
func (S *Scanner) scanNumber() token.Type {
|
||||
for isDigit(S.ch) {
|
||||
S.next()
|
||||
}
|
||||
if S.ch == '.' {
|
||||
S.next()
|
||||
for isDigit(S.ch) {
|
||||
S.next()
|
||||
}
|
||||
return S.tokenMap.Type("float_lit")
|
||||
}
|
||||
return S.tokenMap.Type("int_lit")
|
||||
}
|
||||
|
||||
func (S *Scanner) scanHTML() token.Type {
|
||||
count := 1
|
||||
for count > 0 {
|
||||
if S.ch == '<' {
|
||||
count += 1
|
||||
}
|
||||
if S.ch == '>' {
|
||||
count -= 1
|
||||
}
|
||||
S.next()
|
||||
}
|
||||
return S.tokenMap.Type("html_lit")
|
||||
}
|
||||
|
||||
func (S *Scanner) scanSDTLit(pos token.Position) {
|
||||
// '<' already consumed
|
||||
S.next() // consume second <
|
||||
for cmp := false; !cmp; {
|
||||
if S.ch < 0 {
|
||||
S.error(pos, "SDT not terminated")
|
||||
break
|
||||
}
|
||||
if S.ch == '>' {
|
||||
S.next()
|
||||
if S.ch == '>' {
|
||||
break
|
||||
}
|
||||
}
|
||||
S.next()
|
||||
}
|
||||
S.next()
|
||||
}
|
||||
|
||||
func (S *Scanner) scanString(pos token.Position) {
|
||||
// '"' already consumed
|
||||
|
||||
for S.ch != '"' {
|
||||
ch := S.ch
|
||||
S.next()
|
||||
if ch == '\n' || ch < 0 {
|
||||
S.error(pos, "string not terminated")
|
||||
break
|
||||
}
|
||||
if ch == '\\' {
|
||||
S.scanEscape('"')
|
||||
}
|
||||
}
|
||||
|
||||
S.next()
|
||||
}
|
||||
|
||||
func (S *Scanner) scanRawString(pos token.Position) {
|
||||
// '\140' already consumed
|
||||
|
||||
for S.ch != '\140' {
|
||||
ch := S.ch
|
||||
S.next()
|
||||
if ch < 0 {
|
||||
S.error(pos, "string not terminated")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
S.next()
|
||||
}
|
||||
|
||||
func (S *Scanner) skipWhitespace() {
|
||||
for S.ch == ' ' || S.ch == '\t' || S.ch == '\n' || S.ch == '\r' {
|
||||
S.next()
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions for scanning multi-byte tokens such as >> += >>= .
|
||||
// Different routines recognize different length tok_i based on matches
|
||||
// of ch_i. If a token ends in '=', the result is tok1 or tok3
|
||||
// respectively. Otherwise, the result is tok0 if there was no other
|
||||
// matching character, or tok2 if the matching character was ch2.
|
||||
|
||||
func (S *Scanner) switch2(tok0, tok1 token.Type) token.Type {
|
||||
if S.ch == '=' {
|
||||
S.next()
|
||||
return tok1
|
||||
}
|
||||
return tok0
|
||||
}
|
||||
|
||||
func (S *Scanner) switch3(tok0, tok1 token.Type, ch2 rune, tok2 token.Type) token.Type {
|
||||
if S.ch == '=' {
|
||||
S.next()
|
||||
return tok1
|
||||
}
|
||||
if S.ch == ch2 {
|
||||
S.next()
|
||||
return tok2
|
||||
}
|
||||
return tok0
|
||||
}
|
||||
|
||||
func (S *Scanner) switch4(tok0, tok1 token.Type, ch2 rune, tok2, tok3 token.Type) token.Type {
|
||||
if S.ch == '=' {
|
||||
S.next()
|
||||
return tok1
|
||||
}
|
||||
if S.ch == ch2 {
|
||||
S.next()
|
||||
if S.ch == '=' {
|
||||
S.next()
|
||||
return tok3
|
||||
}
|
||||
return tok2
|
||||
}
|
||||
return tok0
|
||||
}
|
||||
|
||||
var semicolon = []byte{';'}
|
||||
|
||||
// Scan scans the next token and returns the token position pos,
|
||||
// the token tok, and the literal text lit corresponding to the
|
||||
// token. The source end is indicated by token.EOF.
|
||||
//
|
||||
// For more tolerant parsing, Scan will return a valid token if
|
||||
// possible even if a syntax error was encountered. Thus, even
|
||||
// if the resulting token sequence contains no illegal tokens,
|
||||
// a client may not assume that no error occurred. Instead it
|
||||
// must check the scanner's ErrorCount or the number of calls
|
||||
// of the error handler, if there was one installed.
|
||||
//
|
||||
func (S *Scanner) Scan() (*token.Token, token.Position) {
|
||||
scanAgain:
|
||||
S.skipWhitespace()
|
||||
|
||||
// current token start
|
||||
pos, tok := S.pos, token.ILLEGAL
|
||||
|
||||
// determine token value
|
||||
switch ch := S.ch; {
|
||||
case isLetter(ch):
|
||||
tok = S.scanIdentifier()
|
||||
case isDigit(ch), ch == '.':
|
||||
tok = S.scanNumber()
|
||||
default:
|
||||
S.next() // always make progress
|
||||
switch ch {
|
||||
case -1:
|
||||
tok = S.tokenMap.Type("$")
|
||||
case '"':
|
||||
tok = S.tokenMap.Type("string_lit")
|
||||
S.scanString(pos)
|
||||
case '\'':
|
||||
tok = S.tokenMap.Type("char")
|
||||
S.scanChar(pos)
|
||||
case '\140':
|
||||
tok = S.tokenMap.Type("string_lit")
|
||||
S.scanRawString(pos)
|
||||
case ',':
|
||||
tok = S.tokenMap.Type(",")
|
||||
case '{':
|
||||
tok = S.tokenMap.Type("{")
|
||||
case '}':
|
||||
tok = S.tokenMap.Type("}")
|
||||
case ':':
|
||||
tok = S.tokenMap.Type(":")
|
||||
case ';':
|
||||
tok = S.tokenMap.Type(";")
|
||||
case '+':
|
||||
tok = S.tokenMap.Type("+")
|
||||
case '-':
|
||||
if S.ch == '-' {
|
||||
tok = S.tokenMap.Type("--")
|
||||
S.next()
|
||||
} else if S.ch == '>' {
|
||||
tok = S.tokenMap.Type("->")
|
||||
S.next()
|
||||
} else if isDigit(S.ch) {
|
||||
tok = S.scanNumber()
|
||||
} else {
|
||||
tok = S.tokenMap.Type("-")
|
||||
}
|
||||
case '=':
|
||||
tok = S.tokenMap.Type("=")
|
||||
case '[':
|
||||
tok = S.tokenMap.Type("[")
|
||||
case ']':
|
||||
tok = S.tokenMap.Type("]")
|
||||
case '(':
|
||||
tok = S.tokenMap.Type("(")
|
||||
case ')':
|
||||
tok = S.tokenMap.Type(")")
|
||||
case 'ε':
|
||||
tok = S.tokenMap.Type("ε")
|
||||
case '#':
|
||||
S.scanComment(pos)
|
||||
goto scanAgain
|
||||
case '/':
|
||||
if S.ch == '/' || S.ch == '*' {
|
||||
// comment
|
||||
S.scanComment(pos)
|
||||
goto scanAgain
|
||||
} else {
|
||||
tok = S.tokenMap.Type("/")
|
||||
}
|
||||
case '|':
|
||||
tok = S.tokenMap.Type("|")
|
||||
case '<':
|
||||
tok = S.scanHTML()
|
||||
default:
|
||||
S.error(pos, "illegal character "+charString(ch))
|
||||
}
|
||||
}
|
||||
//fmt.Fprintf(os.Stderr, "return tok %v, %v\n", token.NewToken(tok, S.src[pos.Offset:S.pos.Offset]), pos)
|
||||
return token.NewToken(tok, S.src[pos.Offset:S.pos.Offset]), pos
|
||||
}
|
||||
|
||||
// An implementation of an ErrorHandler may be provided to the Scanner.
|
||||
// If a syntax error is encountered and a handler was installed, Error
|
||||
// is called with a position and an error message. The position points
|
||||
// to the beginning of the offending token.
|
||||
//
|
||||
type ErrorHandler interface {
|
||||
Error(pos token.Position, msg string)
|
||||
}
|
||||
|
||||
// Within ErrorVector, an error is represented by an Error node. The
|
||||
// position Pos, if valid, points to the beginning of the offending
|
||||
// token, and the error condition is described by Msg.
|
||||
//
|
||||
type Error struct {
|
||||
Pos token.Position
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (e *Error) String() string {
|
||||
if e.Pos.IsValid() {
|
||||
// don't print "<unknown position>"
|
||||
// TODO(gri) reconsider the semantics of Position.IsValid
|
||||
return e.Pos.String() + ": " + e.Msg
|
||||
}
|
||||
return e.Msg
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package gographviz
|
||||
|
||||
import (
|
||||
"sort"
|
||||
)
|
||||
|
||||
//Represents a Subgraph.
|
||||
type SubGraph struct {
|
||||
Attrs Attrs
|
||||
Name string
|
||||
}
|
||||
|
||||
//Creates a new Subgraph.
|
||||
func NewSubGraph(name string) *SubGraph {
|
||||
return &SubGraph{
|
||||
Attrs: make(Attrs),
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
//Represents a set of SubGraphs.
|
||||
type SubGraphs struct {
|
||||
SubGraphs map[string]*SubGraph
|
||||
}
|
||||
|
||||
//Creates a new blank set of SubGraphs.
|
||||
func NewSubGraphs() *SubGraphs {
|
||||
return &SubGraphs{make(map[string]*SubGraph)}
|
||||
}
|
||||
|
||||
//Adds and creates a new Subgraph to the set of SubGraphs.
|
||||
func (this *SubGraphs) Add(name string) {
|
||||
if _, ok := this.SubGraphs[name]; !ok {
|
||||
this.SubGraphs[name] = NewSubGraph(name)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *SubGraphs) Sorted() []*SubGraph {
|
||||
keys := make([]string, 0)
|
||||
for key := range this.SubGraphs {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
s := make([]*SubGraph, len(keys))
|
||||
for i, key := range keys {
|
||||
s[i] = this.SubGraphs[key]
|
||||
}
|
||||
return s
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
These test graphs have been copied from the graphviz gallery found here http://www.graphviz.org/Gallery.php
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
digraph G {
|
||||
|
||||
subgraph cluster_0 {
|
||||
style=filled;
|
||||
color=lightgrey;
|
||||
node [style=filled,color=white];
|
||||
a0 -> a1 -> a2 -> a3;
|
||||
label = "process #1";
|
||||
}
|
||||
|
||||
subgraph cluster_1 {
|
||||
node [style=filled];
|
||||
b0 -> b1 -> b2 -> b3;
|
||||
label = "process #2";
|
||||
color=blue
|
||||
}
|
||||
start -> a0;
|
||||
start -> b0;
|
||||
a1 -> b3;
|
||||
b2 -> a3;
|
||||
a3 -> a0;
|
||||
a3 -> end;
|
||||
b3 -> end;
|
||||
|
||||
start [shape=Mdiamond];
|
||||
end [shape=Msquare];
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
digraph "unix" {
|
||||
graph [ fontname = "Helvetica-Oblique",
|
||||
fontsize = 36,
|
||||
label = "\n\n\n\nObject Oriented Graphs\nStephen North, 3/19/93",
|
||||
size = "6,6" ];
|
||||
node [ shape = polygon,
|
||||
sides = 4,
|
||||
distortion = "0.0",
|
||||
orientation = "0.0",
|
||||
skew = "0.0",
|
||||
color = white,
|
||||
style = filled,
|
||||
fontname = "Helvetica-Outline" ];
|
||||
"5th Edition" [sides=9, distortion="0.936354", orientation=28, skew="-0.126818", color=salmon2];
|
||||
"6th Edition" [sides=5, distortion="0.238792", orientation=11, skew="0.995935", color=deepskyblue];
|
||||
"PWB 1.0" [sides=8, distortion="0.019636", orientation=79, skew="-0.440424", color=goldenrod2];
|
||||
LSX [sides=9, distortion="-0.698271", orientation=22, skew="-0.195492", color=burlywood2];
|
||||
"1 BSD" [sides=7, distortion="0.265084", orientation=26, skew="0.403659", color=gold1];
|
||||
"Mini Unix" [distortion="0.039386", orientation=2, skew="-0.461120", color=greenyellow];
|
||||
Wollongong [sides=5, distortion="0.228564", orientation=63, skew="-0.062846", color=darkseagreen];
|
||||
Interdata [distortion="0.624013", orientation=56, skew="0.101396", color=dodgerblue1];
|
||||
"Unix/TS 3.0" [sides=8, distortion="0.731383", orientation=43, skew="-0.824612", color=thistle2];
|
||||
"PWB 2.0" [sides=6, distortion="0.592100", orientation=34, skew="-0.719269", color=darkolivegreen3];
|
||||
"7th Edition" [sides=10, distortion="0.298417", orientation=65, skew="0.310367", color=chocolate];
|
||||
"8th Edition" [distortion="-0.997093", orientation=50, skew="-0.061117", color=turquoise3];
|
||||
"32V" [sides=7, distortion="0.878516", orientation=19, skew="0.592905", color=steelblue3];
|
||||
V7M [sides=10, distortion="-0.960249", orientation=32, skew="0.460424", color=navy];
|
||||
"Ultrix-11" [sides=10, distortion="-0.633186", orientation=10, skew="0.333125", color=darkseagreen4];
|
||||
Xenix [sides=8, distortion="-0.337997", orientation=52, skew="-0.760726", color=coral];
|
||||
"UniPlus+" [sides=7, distortion="0.788483", orientation=39, skew="-0.526284", color=darkolivegreen3];
|
||||
"9th Edition" [sides=7, distortion="0.138690", orientation=55, skew="0.554049", color=coral3];
|
||||
"2 BSD" [sides=7, distortion="-0.010661", orientation=84, skew="0.179249", color=blanchedalmond];
|
||||
"2.8 BSD" [distortion="-0.239422", orientation=44, skew="0.053841", color=lightskyblue1];
|
||||
"2.9 BSD" [distortion="-0.843381", orientation=70, skew="-0.601395", color=aquamarine2];
|
||||
"3 BSD" [sides=10, distortion="0.251820", orientation=18, skew="-0.530618", color=lemonchiffon];
|
||||
"4 BSD" [sides=5, distortion="-0.772300", orientation=24, skew="-0.028475", color=darkorange1];
|
||||
"4.1 BSD" [distortion="-0.226170", orientation=38, skew="0.504053", color=lightyellow1];
|
||||
"4.2 BSD" [sides=10, distortion="-0.807349", orientation=50, skew="-0.908842", color=darkorchid4];
|
||||
"4.3 BSD" [sides=10, distortion="-0.030619", orientation=76, skew="0.985021", color=lemonchiffon2];
|
||||
"Ultrix-32" [distortion="-0.644209", orientation=21, skew="0.307836", color=goldenrod3];
|
||||
"PWB 1.2" [sides=7, distortion="0.640971", orientation=84, skew="-0.768455", color=cyan];
|
||||
"USG 1.0" [distortion="0.758942", orientation=42, skew="0.039886", color=blue];
|
||||
"CB Unix 1" [sides=9, distortion="-0.348692", orientation=42, skew="0.767058", color=firebrick];
|
||||
"USG 2.0" [distortion="0.748625", orientation=74, skew="-0.647656", color=chartreuse4];
|
||||
"CB Unix 2" [sides=10, distortion="0.851818", orientation=32, skew="-0.020120", color=greenyellow];
|
||||
"CB Unix 3" [sides=10, distortion="0.992237", orientation=29, skew="0.256102", color=bisque4];
|
||||
"Unix/TS++" [sides=6, distortion="0.545461", orientation=16, skew="0.313589", color=mistyrose2];
|
||||
"PDP-11 Sys V" [sides=9, distortion="-0.267769", orientation=40, skew="0.271226", color=cadetblue1];
|
||||
"USG 3.0" [distortion="-0.848455", orientation=44, skew="0.267152", color=bisque2];
|
||||
"Unix/TS 1.0" [distortion="0.305594", orientation=75, skew="0.070516", color=orangered];
|
||||
"TS 4.0" [sides=10, distortion="-0.641701", orientation=50, skew="-0.952502", color=crimson];
|
||||
"System V.0" [sides=9, distortion="0.021556", orientation=26, skew="-0.729938", color=darkorange1];
|
||||
"System V.2" [sides=6, distortion="0.985153", orientation=33, skew="-0.399752", color=darkolivegreen4];
|
||||
"System V.3" [sides=7, distortion="-0.687574", orientation=58, skew="-0.180116", color=lightsteelblue1];
|
||||
"5th Edition" -> "6th Edition";
|
||||
"5th Edition" -> "PWB 1.0";
|
||||
"6th Edition" -> LSX;
|
||||
"6th Edition" -> "1 BSD";
|
||||
"6th Edition" -> "Mini Unix";
|
||||
"6th Edition" -> Wollongong;
|
||||
"6th Edition" -> Interdata;
|
||||
Interdata -> "Unix/TS 3.0";
|
||||
Interdata -> "PWB 2.0";
|
||||
Interdata -> "7th Edition";
|
||||
"7th Edition" -> "8th Edition";
|
||||
"7th Edition" -> "32V";
|
||||
"7th Edition" -> V7M;
|
||||
"7th Edition" -> "Ultrix-11";
|
||||
"7th Edition" -> Xenix;
|
||||
"7th Edition" -> "UniPlus+";
|
||||
V7M -> "Ultrix-11";
|
||||
"8th Edition" -> "9th Edition";
|
||||
"1 BSD" -> "2 BSD";
|
||||
"2 BSD" -> "2.8 BSD";
|
||||
"2.8 BSD" -> "Ultrix-11";
|
||||
"2.8 BSD" -> "2.9 BSD";
|
||||
"32V" -> "3 BSD";
|
||||
"3 BSD" -> "4 BSD";
|
||||
"4 BSD" -> "4.1 BSD";
|
||||
"4.1 BSD" -> "4.2 BSD";
|
||||
"4.1 BSD" -> "2.8 BSD";
|
||||
"4.1 BSD" -> "8th Edition";
|
||||
"4.2 BSD" -> "4.3 BSD";
|
||||
"4.2 BSD" -> "Ultrix-32";
|
||||
"PWB 1.0" -> "PWB 1.2";
|
||||
"PWB 1.0" -> "USG 1.0";
|
||||
"PWB 1.2" -> "PWB 2.0";
|
||||
"USG 1.0" -> "CB Unix 1";
|
||||
"USG 1.0" -> "USG 2.0";
|
||||
"CB Unix 1" -> "CB Unix 2";
|
||||
"CB Unix 2" -> "CB Unix 3";
|
||||
"CB Unix 3" -> "Unix/TS++";
|
||||
"CB Unix 3" -> "PDP-11 Sys V";
|
||||
"USG 2.0" -> "USG 3.0";
|
||||
"USG 3.0" -> "Unix/TS 3.0";
|
||||
"PWB 2.0" -> "Unix/TS 3.0";
|
||||
"Unix/TS 1.0" -> "Unix/TS 3.0";
|
||||
"Unix/TS 3.0" -> "TS 4.0";
|
||||
"Unix/TS++" -> "TS 4.0";
|
||||
"CB Unix 3" -> "TS 4.0";
|
||||
"TS 4.0" -> "System V.0";
|
||||
"System V.0" -> "System V.2";
|
||||
"System V.2" -> "System V.3";
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
digraph g {
|
||||
graph [
|
||||
rankdir = "LR"
|
||||
];
|
||||
node [
|
||||
fontsize = "16"
|
||||
shape = "ellipse"
|
||||
];
|
||||
edge [
|
||||
];
|
||||
"node0" [
|
||||
label = "<f0> 0x10ba8| <f1>"
|
||||
shape = "record"
|
||||
];
|
||||
"node1" [
|
||||
label = "<f0> 0xf7fc4380| <f1> | <f2> |-1"
|
||||
shape = "record"
|
||||
];
|
||||
"node2" [
|
||||
label = "<f0> 0xf7fc44b8| | |2"
|
||||
shape = "record"
|
||||
];
|
||||
"node3" [
|
||||
label = "<f0> 3.43322790286038071e-06|44.79998779296875|0"
|
||||
shape = "record"
|
||||
];
|
||||
"node4" [
|
||||
label = "<f0> 0xf7fc4380| <f1> | <f2> |2"
|
||||
shape = "record"
|
||||
];
|
||||
"node5" [
|
||||
label = "<f0> (nil)| | |-1"
|
||||
shape = "record"
|
||||
];
|
||||
"node6" [
|
||||
label = "<f0> 0xf7fc4380| <f1> | <f2> |1"
|
||||
shape = "record"
|
||||
];
|
||||
"node7" [
|
||||
label = "<f0> 0xf7fc4380| <f1> | <f2> |2"
|
||||
shape = "record"
|
||||
];
|
||||
"node8" [
|
||||
label = "<f0> (nil)| | |-1"
|
||||
shape = "record"
|
||||
];
|
||||
"node9" [
|
||||
label = "<f0> (nil)| | |-1"
|
||||
shape = "record"
|
||||
];
|
||||
"node10" [
|
||||
label = "<f0> (nil)| <f1> | <f2> |-1"
|
||||
shape = "record"
|
||||
];
|
||||
"node11" [
|
||||
label = "<f0> (nil)| <f1> | <f2> |-1"
|
||||
shape = "record"
|
||||
];
|
||||
"node12" [
|
||||
label = "<f0> 0xf7fc43e0| | |1"
|
||||
shape = "record"
|
||||
];
|
||||
"node0":f0 -> "node1":f0 [
|
||||
id = 0
|
||||
];
|
||||
"node0":f1 -> "node2":f0 [
|
||||
id = 1
|
||||
];
|
||||
"node1":f0 -> "node3":f0 [
|
||||
id = 2
|
||||
];
|
||||
"node1":f1 -> "node4":f0 [
|
||||
id = 3
|
||||
];
|
||||
"node1":f2 -> "node5":f0 [
|
||||
id = 4
|
||||
];
|
||||
"node4":f0 -> "node3":f0 [
|
||||
id = 5
|
||||
];
|
||||
"node4":f1 -> "node6":f0 [
|
||||
id = 6
|
||||
];
|
||||
"node4":f2 -> "node10":f0 [
|
||||
id = 7
|
||||
];
|
||||
"node6":f0 -> "node3":f0 [
|
||||
id = 8
|
||||
];
|
||||
"node6":f1 -> "node7":f0 [
|
||||
id = 9
|
||||
];
|
||||
"node6":f2 -> "node9":f0 [
|
||||
id = 10
|
||||
];
|
||||
"node7":f0 -> "node3":f0 [
|
||||
id = 11
|
||||
];
|
||||
"node7":f1 -> "node1":f0 [
|
||||
id = 12
|
||||
];
|
||||
"node7":f2 -> "node8":f0 [
|
||||
id = 13
|
||||
];
|
||||
"node10":f1 -> "node11":f0 [
|
||||
id = 14
|
||||
];
|
||||
"node10":f2 -> "node12":f0 [
|
||||
id = 15
|
||||
];
|
||||
"node11":f2 -> "node1":f0 [
|
||||
id = 16
|
||||
];
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
graph ER {
|
||||
node [shape=box]; course; institute; student;
|
||||
node [shape=ellipse]; {node [label="name"] name0; name1; name2;}
|
||||
code; grade; number;
|
||||
node [shape=diamond,style=filled,color=lightgrey]; "C-I"; "S-C"; "S-I";
|
||||
|
||||
name0 -- course;
|
||||
code -- course;
|
||||
course -- "C-I" [label="n",len=1.00];
|
||||
"C-I" -- institute [label="1",len=1.00];
|
||||
institute -- name1;
|
||||
institute -- "S-I" [label="1",len=1.00];
|
||||
"S-I" -- student [label="n",len=1.00];
|
||||
student -- grade;
|
||||
student -- name2;
|
||||
student -- number;
|
||||
student -- "S-C" [label="m",len=1.00];
|
||||
"S-C" -- course [label="n",len=1.00];
|
||||
|
||||
label = "\n\nEntity Relation Diagram\ndrawn by NEATO";
|
||||
fontsize=20;
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
graph G {
|
||||
e
|
||||
subgraph clusterA {
|
||||
a -- b;
|
||||
subgraph clusterC {
|
||||
C -- D;
|
||||
}
|
||||
}
|
||||
subgraph clusterB {
|
||||
d -- f
|
||||
}
|
||||
d -- D
|
||||
e -- clusterB
|
||||
clusterC -- clusterB
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
digraph finite_state_machine {
|
||||
rankdir=LR;
|
||||
size="8,5"
|
||||
node [shape = doublecircle]; LR_0 LR_3 LR_4 LR_8;
|
||||
node [shape = circle];
|
||||
LR_0 -> LR_2 [ label = "SS(B)" ];
|
||||
LR_0 -> LR_1 [ label = "SS(S)" ];
|
||||
LR_1 -> LR_3 [ label = "S($end)" ];
|
||||
LR_2 -> LR_6 [ label = "SS(b)" ];
|
||||
LR_2 -> LR_5 [ label = "SS(a)" ];
|
||||
LR_2 -> LR_4 [ label = "S(A)" ];
|
||||
LR_5 -> LR_7 [ label = "S(b)" ];
|
||||
LR_5 -> LR_5 [ label = "S(a)" ];
|
||||
LR_6 -> LR_6 [ label = "S(b)" ];
|
||||
LR_6 -> LR_5 [ label = "S(a)" ];
|
||||
LR_7 -> LR_8 [ label = "S(b)" ];
|
||||
LR_7 -> LR_5 [ label = "S(a)" ];
|
||||
LR_8 -> LR_6 [ label = "S(b)" ];
|
||||
LR_8 -> LR_5 [ label = "S(a)" ];
|
||||
}
|
||||
+1903
File diff suppressed because it is too large
Load Diff
+1
@@ -0,0 +1 @@
|
||||
digraph G {Hello->World}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
Note: All images in the file is found at
|
||||
|
||||
http://www.graphviz.org/Gallery/directed/images/
|
||||
|
||||
-----------
|
||||
|
||||
from Kaarle Kaila:
|
||||
|
||||
|
||||
I have implemented Genealogic descendant and ancestor graphs using Graphviz in FinFamily. I have made som description on how to use it with FinFamily at FinFamily wiki-pages at
|
||||
|
||||
http://www.finfamily.fi/index.php/Handbook
|
||||
|
||||
I attach a descendant graph from Joseph Patrick Kennedy and an ancestor graph from Caroline Bouvier Kennedy as samples from FinFamily. The file georg.jpg is a descendant graph w/o pictures from an imaginary person Georg af Charlow (who has some common attributes with my father) from my testdatabase like the person Charles Charlow has some resemblance to myself.
|
||||
|
||||
If you wish to display the attached pictures or wish me to create another ones then feel free to do so. I wish to thank you for Graphviz that let's me create such nice graphs with FinFamily.
|
||||
|
||||
regards
|
||||
Kaarle Kaila
|
||||
|
||||
I have this little kennedy database as a sample gedcom file on the download webpage to give international users a few wellknown persons to play with if they wish to try out my software. I originally got it from Michael Kay who is among others Editor at http://www.w3.org/TR/xslt20/. I added the pictures and some data myself.
|
||||
|
||||
Attached are both the kennedyanc and kennedydesc files as you requested. I made them as zip-files so that tehy contain both source and destination files. As you email server does not accept zip-files I renamed them to anc.zip ->anc.files and desc.zip to desc.files. Hope these com through your filters.
|
||||
|
||||
Graphviz dot program is called from withing FinFamily with a command line such as:
|
||||
|
||||
dot -Tjpeg kennedyanc.txt -o kennedyanc.jpg
|
||||
|
||||
On page http://www.finfamily.fi/index.php/Graphviz is a description on the different colors together with instructions for finFamily users how to create Graphviz reports.
|
||||
|
||||
Kaarle Kaila
|
||||
|
||||
|
||||
Colors and forms symbolize following
|
||||
|
||||
* Blue box - man
|
||||
* Red ellipse - woman
|
||||
* Blue line - Father/Child relation
|
||||
* Red line - Mother/Child relation
|
||||
* Green line - Spouse relation
|
||||
* Orange line - Ancestors (other) children
|
||||
* Violet line - Ancestors (other) spouse
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/* ancestor graph from Caroline Bouvier Kennedy */
|
||||
|
||||
graph G {
|
||||
I5 [shape=ellipse,color=red,style=bold,label="Caroline Bouvier Kennedy\nb. 27.11.1957 New York",image="images/165px-Caroline_Kennedy.jpg",labelloc=b];
|
||||
I1 [shape=box,color=blue,style=bold,label="John Fitzgerald Kennedy\nb. 29.5.1917 Brookline\nd. 22.11.1963 Dallas",image="images/kennedyface.jpg",labelloc=b];
|
||||
I6 [shape=box,color=blue,style=bold,label="John Fitzgerald Kennedy\nb. 25.11.1960 Washington\nd. 16.7.1999 over the Atlantic Ocean, near Aquinnah, MA, USA",image="images/180px-JFKJr2.jpg",labelloc=b];
|
||||
I7 [shape=box,color=blue,style=bold,label="Patrick Bouvier Kennedy\nb. 7.8.1963\nd. 9.8.1963"];
|
||||
I2 [shape=ellipse,color=red,style=bold,label="Jaqueline Lee Bouvier\nb. 28.7.1929 Southampton\nd. 19.5.1994 New York City",image="images/jacqueline-kennedy-onassis.jpg",labelloc=b];
|
||||
I8 [shape=box,color=blue,style=bold,label="Joseph Patrick Kennedy\nb. 6.9.1888 East Boston\nd. 16.11.1969 Hyannis Port",image="images/1025901671.jpg",labelloc=b];
|
||||
I10 [shape=box,color=blue,style=bold,label="Joseph Patrick Kennedy Jr\nb. 1915\nd. 1944"];
|
||||
I11 [shape=ellipse,color=red,style=bold,label="Rosemary Kennedy\nb. 13.9.1918\nd. 7.1.2005",image="images/rosemary.jpg",labelloc=b];
|
||||
I12 [shape=ellipse,color=red,style=bold,label="Kathleen Kennedy\nb. 1920\nd. 1948"];
|
||||
I13 [shape=ellipse,color=red,style=bold,label="Eunice Mary Kennedy\nb. 10.7.1921 Brookline"];
|
||||
I9 [shape=ellipse,color=red,style=bold,label="Rose Elizabeth Fitzgerald\nb. 22.7.1890 Boston\nd. 22.1.1995 Hyannis Port",image="images/Rose_kennedy.JPG",labelloc=b];
|
||||
I15 [shape=box,color=blue,style=bold,label="Aristotle Onassis"];
|
||||
I3 [shape=box,color=blue,style=bold,label="John Vernou Bouvier III\nb. 1891\nd. 1957",image="images/BE037819.jpg",labelloc=b];
|
||||
I4 [shape=ellipse,color=red,style=bold,label="Janet Norton Lee\nb. 2.10.1877\nd. 3.1.1968",image="images/n48862003257_1275276_1366.jpg",labelloc=b];
|
||||
I1 -- I5 [style=bold,color=blue];
|
||||
I1 -- I6 [style=bold,color=orange];
|
||||
I2 -- I6 [style=bold,color=orange];
|
||||
I1 -- I7 [style=bold,color=orange];
|
||||
I2 -- I7 [style=bold,color=orange];
|
||||
I1 -- I2 [style=bold,color=violet];
|
||||
I8 -- I1 [style=bold,color=blue];
|
||||
I8 -- I10 [style=bold,color=orange];
|
||||
I9 -- I10 [style=bold,color=orange];
|
||||
I8 -- I11 [style=bold,color=orange];
|
||||
I9 -- I11 [style=bold,color=orange];
|
||||
I8 -- I12 [style=bold,color=orange];
|
||||
I9 -- I12 [style=bold,color=orange];
|
||||
I8 -- I13 [style=bold,color=orange];
|
||||
I9 -- I13 [style=bold,color=orange];
|
||||
I8 -- I9 [style=bold,color=violet];
|
||||
I9 -- I1 [style=bold,color=red];
|
||||
I2 -- I5 [style=bold,color=red];
|
||||
I2 -- I15 [style=bold,color=violet];
|
||||
I3 -- I2 [style=bold,color=blue];
|
||||
I3 -- I4 [style=bold,color=violet];
|
||||
I4 -- I2 [style=bold,color=red];
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
##"A few people in the field of genetics are using dot to draw "marriage node diagram" pedigree drawings. Here is one I have done of a test pedigree from the FTREE pedigree drawing package (Lion Share was a racehorse)." Contributed by David Duffy.
|
||||
|
||||
##Command to get the layout: "dot -Tpng thisfile > thisfile.png"
|
||||
|
||||
digraph Ped_Lion_Share {
|
||||
# page = "8.2677165,11.692913" ;
|
||||
ratio = "auto" ;
|
||||
mincross = 2.0 ;
|
||||
label = "Pedigree Lion_Share" ;
|
||||
|
||||
"001" [shape=box , regular=1,style=filled,fillcolor=white ] ;
|
||||
"002" [shape=box , regular=1,style=filled,fillcolor=white ] ;
|
||||
"003" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"004" [shape=box , regular=1,style=filled,fillcolor=white ] ;
|
||||
"005" [shape=box , regular=1,style=filled,fillcolor=white ] ;
|
||||
"006" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"007" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"009" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"014" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"015" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"016" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"ZZ01" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"ZZ02" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"017" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"012" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"008" [shape=box , regular=1,style=filled,fillcolor=white ] ;
|
||||
"011" [shape=box , regular=1,style=filled,fillcolor=white ] ;
|
||||
"013" [shape=box , regular=1,style=filled,fillcolor=white ] ;
|
||||
"010" [shape=box , regular=1,style=filled,fillcolor=white ] ;
|
||||
"023" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"020" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"021" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"018" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"025" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"019" [shape=box , regular=1,style=filled,fillcolor=white ] ;
|
||||
"022" [shape=box , regular=1,style=filled,fillcolor=white ] ;
|
||||
"024" [shape=box , regular=1,style=filled,fillcolor=white ] ;
|
||||
"027" [shape=circle , regular=1,style=filled,fillcolor=white ] ;
|
||||
"026" [shape=box , regular=1,style=filled,fillcolor=white ] ;
|
||||
"028" [shape=box , regular=1,style=filled,fillcolor=grey ] ;
|
||||
"marr0001" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"001" -> "marr0001" [dir=none,weight=1] ;
|
||||
"007" -> "marr0001" [dir=none,weight=1] ;
|
||||
"marr0001" -> "017" [dir=none, weight=2] ;
|
||||
"marr0002" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"001" -> "marr0002" [dir=none,weight=1] ;
|
||||
"ZZ02" -> "marr0002" [dir=none,weight=1] ;
|
||||
"marr0002" -> "012" [dir=none, weight=2] ;
|
||||
"marr0003" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"002" -> "marr0003" [dir=none,weight=1] ;
|
||||
"003" -> "marr0003" [dir=none,weight=1] ;
|
||||
"marr0003" -> "008" [dir=none, weight=2] ;
|
||||
"marr0004" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"002" -> "marr0004" [dir=none,weight=1] ;
|
||||
"006" -> "marr0004" [dir=none,weight=1] ;
|
||||
"marr0004" -> "011" [dir=none, weight=2] ;
|
||||
"marr0005" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"002" -> "marr0005" [dir=none,weight=1] ;
|
||||
"ZZ01" -> "marr0005" [dir=none,weight=1] ;
|
||||
"marr0005" -> "013" [dir=none, weight=2] ;
|
||||
"marr0006" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"004" -> "marr0006" [dir=none,weight=1] ;
|
||||
"009" -> "marr0006" [dir=none,weight=1] ;
|
||||
"marr0006" -> "010" [dir=none, weight=2] ;
|
||||
"marr0007" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"005" -> "marr0007" [dir=none,weight=1] ;
|
||||
"015" -> "marr0007" [dir=none,weight=1] ;
|
||||
"marr0007" -> "023" [dir=none, weight=2] ;
|
||||
"marr0008" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"005" -> "marr0008" [dir=none,weight=1] ;
|
||||
"016" -> "marr0008" [dir=none,weight=1] ;
|
||||
"marr0008" -> "020" [dir=none, weight=2] ;
|
||||
"marr0009" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"005" -> "marr0009" [dir=none,weight=1] ;
|
||||
"012" -> "marr0009" [dir=none,weight=1] ;
|
||||
"marr0009" -> "021" [dir=none, weight=2] ;
|
||||
"marr0010" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"008" -> "marr0010" [dir=none,weight=1] ;
|
||||
"017" -> "marr0010" [dir=none,weight=1] ;
|
||||
"marr0010" -> "018" [dir=none, weight=2] ;
|
||||
"marr0011" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"011" -> "marr0011" [dir=none,weight=1] ;
|
||||
"023" -> "marr0011" [dir=none,weight=1] ;
|
||||
"marr0011" -> "025" [dir=none, weight=2] ;
|
||||
"marr0012" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"013" -> "marr0012" [dir=none,weight=1] ;
|
||||
"014" -> "marr0012" [dir=none,weight=1] ;
|
||||
"marr0012" -> "019" [dir=none, weight=2] ;
|
||||
"marr0013" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"010" -> "marr0013" [dir=none,weight=1] ;
|
||||
"021" -> "marr0013" [dir=none,weight=1] ;
|
||||
"marr0013" -> "022" [dir=none, weight=2] ;
|
||||
"marr0014" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"019" -> "marr0014" [dir=none,weight=1] ;
|
||||
"020" -> "marr0014" [dir=none,weight=1] ;
|
||||
"marr0014" -> "024" [dir=none, weight=2] ;
|
||||
"marr0015" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"022" -> "marr0015" [dir=none,weight=1] ;
|
||||
"025" -> "marr0015" [dir=none,weight=1] ;
|
||||
"marr0015" -> "027" [dir=none, weight=2] ;
|
||||
"marr0016" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"024" -> "marr0016" [dir=none,weight=1] ;
|
||||
"018" -> "marr0016" [dir=none,weight=1] ;
|
||||
"marr0016" -> "026" [dir=none, weight=2] ;
|
||||
"marr0017" [shape=diamond,style=filled,label="",height=.1,width=.1] ;
|
||||
"026" -> "marr0017" [dir=none,weight=1] ;
|
||||
"027" -> "marr0017" [dir=none,weight=1] ;
|
||||
"marr0017" -> "028" [dir=none, weight=2] ;
|
||||
}
|
||||
+2140
File diff suppressed because it is too large
Load Diff
+40
@@ -0,0 +1,40 @@
|
||||
## "It encodes the so-called philosophers dilemma. Neato pretty much approximates the way how humans would layout the graph." Contributed by Manfred Jeusfield.
|
||||
## Command to generate the layout: "neato -Tpng thisfile > thisfile.png"
|
||||
|
||||
|
||||
digraph PhiloDilemma {
|
||||
node [shape=box]; bec3; rel3; bec2; rel2; acq2; acq3; bec1; rel1; acq1;
|
||||
node [shape=circle,fixedsize=true,width=0.9]; hu3; th3; ri3; ea3; hu2; th2; ri2; ea2; hu1; th1; ri1; ea1;
|
||||
ri3->acq2;
|
||||
ri3->acq3;
|
||||
hu3->acq3;
|
||||
bec3->hu3;
|
||||
th3->bec3;
|
||||
rel3->th3;
|
||||
rel3->ri3;
|
||||
ea3->rel3;
|
||||
acq3->ea3;
|
||||
ri2->acq1;
|
||||
ri2->acq2;
|
||||
hu2->acq2;
|
||||
bec2->hu2;
|
||||
th2->bec2;
|
||||
rel2->th2;
|
||||
rel2->ri2;
|
||||
ea2->rel2;
|
||||
acq2->ea2;
|
||||
ri1->acq3;
|
||||
ri1->acq1;
|
||||
hu1->acq1;
|
||||
bec1->hu1;
|
||||
th1->bec1;
|
||||
rel1->th1;
|
||||
rel1->ri1;
|
||||
ea1->rel1;
|
||||
acq1->ea1;
|
||||
|
||||
overlap=false
|
||||
label="PetriNet Model PhiloDilemma\nExtracted from ConceptBase and layed out by Graphviz "
|
||||
fontsize=12;
|
||||
}
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
graph G {
|
||||
run -- intr;
|
||||
intr -- runbl;
|
||||
runbl -- run;
|
||||
run -- kernel;
|
||||
kernel -- zombie;
|
||||
kernel -- sleep;
|
||||
kernel -- runmem;
|
||||
sleep -- swap;
|
||||
swap -- runswap;
|
||||
runswap -- new;
|
||||
runswap -- runmem;
|
||||
new -- runmem;
|
||||
sleep -- runmem;
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
digraph prof {
|
||||
size="6,4"; ratio = fill;
|
||||
node [style=filled];
|
||||
start -> main [color="0.002 0.999 0.999"];
|
||||
start -> on_exit [color="0.649 0.701 0.701"];
|
||||
main -> sort [color="0.348 0.839 0.839"];
|
||||
main -> merge [color="0.515 0.762 0.762"];
|
||||
main -> term [color="0.647 0.702 0.702"];
|
||||
main -> signal [color="0.650 0.700 0.700"];
|
||||
main -> sbrk [color="0.650 0.700 0.700"];
|
||||
main -> unlink [color="0.650 0.700 0.700"];
|
||||
main -> newfile [color="0.650 0.700 0.700"];
|
||||
main -> fclose [color="0.650 0.700 0.700"];
|
||||
main -> close [color="0.650 0.700 0.700"];
|
||||
main -> brk [color="0.650 0.700 0.700"];
|
||||
main -> setbuf [color="0.650 0.700 0.700"];
|
||||
main -> copyproto [color="0.650 0.700 0.700"];
|
||||
main -> initree [color="0.650 0.700 0.700"];
|
||||
main -> safeoutfil [color="0.650 0.700 0.700"];
|
||||
main -> getpid [color="0.650 0.700 0.700"];
|
||||
main -> sprintf [color="0.650 0.700 0.700"];
|
||||
main -> creat [color="0.650 0.700 0.700"];
|
||||
main -> rem [color="0.650 0.700 0.700"];
|
||||
main -> oldfile [color="0.650 0.700 0.700"];
|
||||
sort -> msort [color="0.619 0.714 0.714"];
|
||||
sort -> filbuf [color="0.650 0.700 0.700"];
|
||||
sort -> newfile [color="0.650 0.700 0.700"];
|
||||
sort -> fclose [color="0.650 0.700 0.700"];
|
||||
sort -> setbuf [color="0.650 0.700 0.700"];
|
||||
sort -> setfil [color="0.650 0.700 0.700"];
|
||||
msort -> qsort [color="0.650 0.700 0.700"];
|
||||
msort -> insert [color="0.650 0.700 0.700"];
|
||||
msort -> wline [color="0.650 0.700 0.700"];
|
||||
msort -> div [color="0.650 0.700 0.700"];
|
||||
msort -> cmpsave [color="0.650 0.700 0.700"];
|
||||
merge -> insert [color="0.650 0.700 0.700"];
|
||||
merge -> rline [color="0.650 0.700 0.700"];
|
||||
merge -> wline [color="0.650 0.700 0.700"];
|
||||
merge -> unlink [color="0.650 0.700 0.700"];
|
||||
merge -> fopen [color="0.650 0.700 0.700"];
|
||||
merge -> fclose [color="0.650 0.700 0.700"];
|
||||
merge -> setfil [color="0.650 0.700 0.700"];
|
||||
merge -> mul [color="0.650 0.700 0.700"];
|
||||
merge -> setbuf [color="0.650 0.700 0.700"];
|
||||
merge -> cmpsave [color="0.650 0.700 0.700"];
|
||||
insert -> cmpa [color="0.650 0.700 0.700"];
|
||||
wline -> flsbuf [color="0.649 0.700 0.700"];
|
||||
qsort -> cmpa [color="0.650 0.700 0.700"];
|
||||
rline -> filbuf [color="0.649 0.700 0.700"];
|
||||
xflsbuf -> write [color="0.650 0.700 0.700"];
|
||||
flsbuf -> xflsbuf [color="0.649 0.700 0.700"];
|
||||
filbuf -> read [color="0.650 0.700 0.700"];
|
||||
term -> unlink [color="0.650 0.700 0.700"];
|
||||
term -> signal [color="0.650 0.700 0.700"];
|
||||
term -> setfil [color="0.650 0.700 0.700"];
|
||||
term -> exit [color="0.650 0.700 0.700"];
|
||||
endopen -> open [color="0.650 0.700 0.700"];
|
||||
fopen -> endopen [color="0.639 0.705 0.705"];
|
||||
fopen -> findiop [color="0.650 0.700 0.700"];
|
||||
newfile -> fopen [color="0.634 0.707 0.707"];
|
||||
newfile -> setfil [color="0.650 0.700 0.700"];
|
||||
fclose -> fflush [color="0.642 0.704 0.704"];
|
||||
fclose -> close [color="0.650 0.700 0.700"];
|
||||
fflush -> xflsbuf [color="0.635 0.707 0.707"];
|
||||
malloc -> morecore [color="0.325 0.850 0.850"];
|
||||
malloc -> demote [color="0.650 0.700 0.700"];
|
||||
morecore -> sbrk [color="0.650 0.700 0.700"];
|
||||
morecore -> getfreehdr [color="0.650 0.700 0.700"];
|
||||
morecore -> free [color="0.650 0.700 0.700"];
|
||||
morecore -> getpagesize [color="0.650 0.700 0.700"];
|
||||
morecore -> putfreehdr [color="0.650 0.700 0.700"];
|
||||
morecore -> udiv [color="0.650 0.700 0.700"];
|
||||
morecore -> umul [color="0.650 0.700 0.700"];
|
||||
on_exit -> malloc [color="0.325 0.850 0.850"];
|
||||
signal -> sigvec [color="0.650 0.700 0.700"];
|
||||
moncontrol -> profil [color="0.650 0.700 0.700"];
|
||||
getfreehdr -> sbrk [color="0.650 0.700 0.700"];
|
||||
free -> insert [color="0.650 0.700 0.700"];
|
||||
insert -> getfreehdr [color="0.650 0.700 0.700"];
|
||||
setfil -> div [color="0.650 0.700 0.700"];
|
||||
setfil -> rem [color="0.650 0.700 0.700"];
|
||||
sigvec -> sigblock [color="0.650 0.700 0.700"];
|
||||
sigvec -> sigsetmask [color="0.650 0.700 0.700"];
|
||||
doprnt -> urem [color="0.650 0.700 0.700"];
|
||||
doprnt -> udiv [color="0.650 0.700 0.700"];
|
||||
doprnt -> strlen [color="0.650 0.700 0.700"];
|
||||
doprnt -> localeconv [color="0.650 0.700 0.700"];
|
||||
sprintf -> doprnt [color="0.650 0.700 0.700"];
|
||||
cmpa [color="0.000 1.000 1.000"];
|
||||
wline [color="0.201 0.753 1.000"];
|
||||
insert [color="0.305 0.625 1.000"];
|
||||
rline [color="0.355 0.563 1.000"];
|
||||
sort [color="0.408 0.498 1.000"];
|
||||
qsort [color="0.449 0.447 1.000"];
|
||||
write [color="0.499 0.386 1.000"];
|
||||
read [color="0.578 0.289 1.000"];
|
||||
msort [color="0.590 0.273 1.000"];
|
||||
merge [color="0.603 0.258 1.000"];
|
||||
unlink [color="0.628 0.227 1.000"];
|
||||
filbuf [color="0.641 0.212 1.000"];
|
||||
open [color="0.641 0.212 1.000"];
|
||||
sbrk [color="0.647 0.204 1.000"];
|
||||
signal [color="0.647 0.204 1.000"];
|
||||
moncontrol [color="0.647 0.204 1.000"];
|
||||
xflsbuf [color="0.650 0.200 1.000"];
|
||||
flsbuf [color="0.650 0.200 1.000"];
|
||||
div [color="0.650 0.200 1.000"];
|
||||
cmpsave [color="0.650 0.200 1.000"];
|
||||
rem [color="0.650 0.200 1.000"];
|
||||
setfil [color="0.650 0.200 1.000"];
|
||||
close [color="0.650 0.200 1.000"];
|
||||
fclose [color="0.650 0.200 1.000"];
|
||||
fflush [color="0.650 0.200 1.000"];
|
||||
setbuf [color="0.650 0.200 1.000"];
|
||||
endopen [color="0.650 0.200 1.000"];
|
||||
findiop [color="0.650 0.200 1.000"];
|
||||
fopen [color="0.650 0.200 1.000"];
|
||||
mul [color="0.650 0.200 1.000"];
|
||||
newfile [color="0.650 0.200 1.000"];
|
||||
sigblock [color="0.650 0.200 1.000"];
|
||||
sigsetmask [color="0.650 0.200 1.000"];
|
||||
sigvec [color="0.650 0.200 1.000"];
|
||||
udiv [color="0.650 0.200 1.000"];
|
||||
urem [color="0.650 0.200 1.000"];
|
||||
brk [color="0.650 0.200 1.000"];
|
||||
getfreehdr [color="0.650 0.200 1.000"];
|
||||
strlen [color="0.650 0.200 1.000"];
|
||||
umul [color="0.650 0.200 1.000"];
|
||||
doprnt [color="0.650 0.200 1.000"];
|
||||
copyproto [color="0.650 0.200 1.000"];
|
||||
creat [color="0.650 0.200 1.000"];
|
||||
demote [color="0.650 0.200 1.000"];
|
||||
exit [color="0.650 0.200 1.000"];
|
||||
free [color="0.650 0.200 1.000"];
|
||||
getpagesize [color="0.650 0.200 1.000"];
|
||||
getpid [color="0.650 0.200 1.000"];
|
||||
initree [color="0.650 0.200 1.000"];
|
||||
insert [color="0.650 0.200 1.000"];
|
||||
localeconv [color="0.650 0.200 1.000"];
|
||||
main [color="0.650 0.200 1.000"];
|
||||
malloc [color="0.650 0.200 1.000"];
|
||||
morecore [color="0.650 0.200 1.000"];
|
||||
oldfile [color="0.650 0.200 1.000"];
|
||||
on_exit [color="0.650 0.200 1.000"];
|
||||
profil [color="0.650 0.200 1.000"];
|
||||
putfreehdr [color="0.650 0.200 1.000"];
|
||||
safeoutfil [color="0.650 0.200 1.000"];
|
||||
sprintf [color="0.650 0.200 1.000"];
|
||||
term [color="0.650 0.200 1.000"];
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
##"I made a program to generate dot files representing the LR(0) state graph along with computed LALR(1) lookahead for an arbitrary context-free grammar, to make the diagrams I used in this article: http://blog.lab49.com/archives/2471. The program also highlights errant nodes in red if the grammar would produce a shift/reduce or reduce/reduce conflict -- you may be able to go to http://kthielen.dnsalias.com:8082/ to produce a graph more to your liking". Contributed by Kalani Thielen.
|
||||
|
||||
##Command to get the layout: "dot -Gsize=10,15 -Tpng thisfile > thisfile.png"
|
||||
|
||||
digraph g {
|
||||
graph [fontsize=30 labelloc="t" label="" splines=true overlap=false rankdir = "LR"];
|
||||
ratio = auto;
|
||||
"state0" [ style = "filled, bold" penwidth = 5 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #0</font></td></tr><tr><td align="left" port="r0">(0) s -> •e $ </td></tr><tr><td align="left" port="r1">(1) e -> •l '=' r </td></tr><tr><td align="left" port="r2">(2) e -> •r </td></tr><tr><td align="left" port="r3">(3) l -> •'*' r </td></tr><tr><td align="left" port="r4">(4) l -> •'n' </td></tr><tr><td align="left" port="r5">(5) r -> •l </td></tr></table>> ];
|
||||
"state1" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #1</font></td></tr><tr><td align="left" port="r3">(3) l -> •'*' r </td></tr><tr><td align="left" port="r3">(3) l -> '*' •r </td></tr><tr><td align="left" port="r4">(4) l -> •'n' </td></tr><tr><td align="left" port="r5">(5) r -> •l </td></tr></table>> ];
|
||||
"state2" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #2</font></td></tr><tr><td align="left" port="r4">(4) l -> 'n' •</td><td bgcolor="grey" align="right">=$</td></tr></table>> ];
|
||||
"state3" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #3</font></td></tr><tr><td align="left" port="r5">(5) r -> l •</td><td bgcolor="grey" align="right">=$</td></tr></table>> ];
|
||||
"state4" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #4</font></td></tr><tr><td align="left" port="r3">(3) l -> '*' r •</td><td bgcolor="grey" align="right">=$</td></tr></table>> ];
|
||||
"state5" [ style = "filled" penwidth = 1 fillcolor = "black" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="black"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #5</font></td></tr><tr><td align="left" port="r0"><font color="white">(0) s -> e •$ </font></td></tr></table>> ];
|
||||
"state6" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #6</font></td></tr><tr><td align="left" port="r1">(1) e -> l •'=' r </td></tr><tr><td align="left" port="r5">(5) r -> l •</td><td bgcolor="grey" align="right">$</td></tr></table>> ];
|
||||
"state7" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #7</font></td></tr><tr><td align="left" port="r1">(1) e -> l '=' •r </td></tr><tr><td align="left" port="r3">(3) l -> •'*' r </td></tr><tr><td align="left" port="r4">(4) l -> •'n' </td></tr><tr><td align="left" port="r5">(5) r -> •l </td></tr></table>> ];
|
||||
"state8" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #8</font></td></tr><tr><td align="left" port="r1">(1) e -> l '=' r •</td><td bgcolor="grey" align="right">$</td></tr></table>> ];
|
||||
"state9" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #9</font></td></tr><tr><td align="left" port="r2">(2) e -> r •</td><td bgcolor="grey" align="right">$</td></tr></table>> ];
|
||||
state0 -> state5 [ penwidth = 5 fontsize = 28 fontcolor = "black" label = "e" ];
|
||||
state0 -> state6 [ penwidth = 5 fontsize = 28 fontcolor = "black" label = "l" ];
|
||||
state0 -> state9 [ penwidth = 5 fontsize = 28 fontcolor = "black" label = "r" ];
|
||||
state0 -> state1 [ penwidth = 1 fontsize = 14 fontcolor = "grey28" label = "'*'" ];
|
||||
state0 -> state2 [ penwidth = 1 fontsize = 14 fontcolor = "grey28" label = "'n'" ];
|
||||
state1 -> state1 [ penwidth = 1 fontsize = 14 fontcolor = "grey28" label = "'*'" ];
|
||||
state1 -> state4 [ penwidth = 5 fontsize = 28 fontcolor = "black" label = "r" ];
|
||||
state1 -> state2 [ penwidth = 1 fontsize = 14 fontcolor = "grey28" label = "'n'" ];
|
||||
state1 -> state3 [ penwidth = 5 fontsize = 28 fontcolor = "black" label = "l" ];
|
||||
state6 -> state7 [ penwidth = 1 fontsize = 14 fontcolor = "grey28" label = "'='" ];
|
||||
state7 -> state8 [ penwidth = 5 fontsize = 28 fontcolor = "black" label = "r" ];
|
||||
state7 -> state1 [ penwidth = 1 fontsize = 14 fontcolor = "grey28" label = "'*'" ];
|
||||
state7 -> state2 [ penwidth = 1 fontsize = 14 fontcolor = "grey28" label = "'n'" ];
|
||||
state7 -> state3 [ penwidth = 5 fontsize = 28 fontcolor = "black" label = "l" ];
|
||||
}
|
||||
+2143
File diff suppressed because it is too large
Load Diff
+32
@@ -0,0 +1,32 @@
|
||||
##"I made a program to generate dot files representing the LR(0) state graph along with computed LALR(1) lookahead for an arbitrary context-free grammar, to make the diagrams I used in this article: http://blog.lab49.com/archives/2471. The program also highlights errant nodes in red if the grammar would produce a shift/reduce or reduce/reduce conflict -- you may be able to go to http://kthielen.dnsalias.com:8082/ to produce a graph more to your liking". Contributed by Kalani Thielen.
|
||||
|
||||
##Command to get the layout: "dot -Gsize=10,15 -Tpng thisfile > thisfile.png"
|
||||
|
||||
digraph g {
|
||||
graph [fontsize=30 labelloc="t" label="" splines=true overlap=false rankdir = "LR"];
|
||||
ratio = auto;
|
||||
"state0" [ style = "filled, bold" penwidth = 5 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #0</font></td></tr><tr><td align="left" port="r0">(0) s -> •e $ </td></tr><tr><td align="left" port="r1">(1) e -> •l '=' r </td></tr><tr><td align="left" port="r2">(2) e -> •r </td></tr><tr><td align="left" port="r3">(3) l -> •'*' r </td></tr><tr><td align="left" port="r4">(4) l -> •'n' </td></tr><tr><td align="left" port="r5">(5) r -> •l </td></tr></table>> ];
|
||||
"state1" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #1</font></td></tr><tr><td align="left" port="r3">(3) l -> •'*' r </td></tr><tr><td align="left" port="r3">(3) l -> '*' •r </td></tr><tr><td align="left" port="r4">(4) l -> •'n' </td></tr><tr><td align="left" port="r5">(5) r -> •l </td></tr></table>> ];
|
||||
"state2" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #2</font></td></tr><tr><td align="left" port="r4">(4) l -> 'n' •</td><td bgcolor="grey" align="right">=$</td></tr></table>> ];
|
||||
"state3" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #3</font></td></tr><tr><td align="left" port="r5">(5) r -> l •</td><td bgcolor="grey" align="right">=$</td></tr></table>> ];
|
||||
"state4" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #4</font></td></tr><tr><td align="left" port="r3">(3) l -> '*' r •</td><td bgcolor="grey" align="right">=$</td></tr></table>> ];
|
||||
"state5" [ style = "filled" penwidth = 1 fillcolor = "black" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="black"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #5</font></td></tr><tr><td align="left" port="r0"><font color="white">(0) s -> e •$ </font></td></tr></table>> ];
|
||||
"state6" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #6</font></td></tr><tr><td align="left" port="r1">(1) e -> l •'=' r </td></tr><tr><td align="left" port="r5">(5) r -> l •</td><td bgcolor="grey" align="right">$</td></tr></table>> ];
|
||||
"state7" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #7</font></td></tr><tr><td align="left" port="r1">(1) e -> l '=' •r </td></tr><tr><td align="left" port="r3">(3) l -> •'*' r </td></tr><tr><td align="left" port="r4">(4) l -> •'n' </td></tr><tr><td align="left" port="r5">(5) r -> •l </td></tr></table>> ];
|
||||
"state8" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #8</font></td></tr><tr><td align="left" port="r1">(1) e -> l '=' r •</td><td bgcolor="grey" align="right">$</td></tr></table>> ];
|
||||
"state9" [ style = "filled" penwidth = 1 fillcolor = "white" fontname = "Courier New" shape = "Mrecord" label =<<table border="0" cellborder="0" cellpadding="3" bgcolor="white"><tr><td bgcolor="black" align="center" colspan="2"><font color="white">State #9</font></td></tr><tr><td align="left" port="r2">(2) e -> r •</td><td bgcolor="grey" align="right">$</td></tr></table>> ];
|
||||
state0 -> state5 [ penwidth = 5 fontsize = 28 fontcolor = "black" label = "e" ];
|
||||
state0 -> state6 [ penwidth = 5 fontsize = 28 fontcolor = "black" label = "l" ];
|
||||
state0 -> state9 [ penwidth = 5 fontsize = 28 fontcolor = "black" label = "r" ];
|
||||
state0 -> state1 [ penwidth = 1 fontsize = 14 fontcolor = "grey28" label = "'*'" ];
|
||||
state0 -> state2 [ penwidth = 1 fontsize = 14 fontcolor = "grey28" label = "'n'" ];
|
||||
state1 -> state1 [ penwidth = 1 fontsize = 14 fontcolor = "grey28" label = "'*'" ];
|
||||
state1 -> state4 [ penwidth = 5 fontsize = 28 fontcolor = "black" label = "r" ];
|
||||
state1 -> state2 [ penwidth = 1 fontsize = 14 fontcolor = "grey28" label = "'n'" ];
|
||||
state1 -> state3 [ penwidth = 5 fontsize = 28 fontcolor = "black" label = "l" ];
|
||||
state6 -> state7 [ penwidth = 1 fontsize = 14 fontcolor = "grey28" label = "'='" ];
|
||||
state7 -> state8 [ penwidth = 5 fontsize = 28 fontcolor = "black" label = "r" ];
|
||||
state7 -> state1 [ penwidth = 1 fontsize = 14 fontcolor = "grey28" label = "'*'" ];
|
||||
state7 -> state2 [ penwidth = 1 fontsize = 14 fontcolor = "grey28" label = "'n'" ];
|
||||
state7 -> state3 [ penwidth = 5 fontsize = 28 fontcolor = "black" label = "l" ];
|
||||
}
|
||||
+512
@@ -0,0 +1,512 @@
|
||||
/*
|
||||
This is a graphviz-produced layout of the "family tree" of a fraternity and sorority.
|
||||
|
||||
Each member in the graph was assigned a "big brother" from one organization and a "big sister" from the other. Blue icons represent Brothers from the fraternity, Pink represents Sisters from the sorority (Purple members are in both organizations - like honoraries.)
|
||||
|
||||
Charter members (who can have no parent nodes) are outlined.
|
||||
|
||||
...
|
||||
|
||||
dot -Tgif -Goverlap=false -o siblings.gif siblings.dot
|
||||
|
||||
|
||||
We're experimenting with different ways of coloring and graphing, but found this the easiest for now. When we have more people in, we might look at different shades depending on generation number -- earlier people would get lighter colors, more recent members darker. Thumbnail images would be an interesting alteration as well.
|
||||
|
||||
from Japheth Cleaver
|
||||
*/
|
||||
|
||||
|
||||
digraph sdsu {
|
||||
size="36,36";
|
||||
node [color=grey, style=filled];
|
||||
node [fontname="Verdana", size="30,30"];
|
||||
graph [ fontname = "Arial",
|
||||
fontsize = 36,
|
||||
style = "bold",
|
||||
label = "\nKappa Kappa Psi/Tau Beta Sigma\nSan Diego State University\nEta Mu and Zeta Xi Family Tree\n\nto date: November 30th, 2008\n",
|
||||
ssize = "30,60" ];
|
||||
"Lori Brede" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=10"];
|
||||
"Michael Griffith" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=24"];
|
||||
"Amie Holston" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=30"];
|
||||
"Michael Griffith" -> "Lori Brede"
|
||||
"Amie Holston" -> "Lori Brede"
|
||||
"Casey Carter" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=11"];
|
||||
"Laura De'Armond" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=14"];
|
||||
"Laura De'Armond" -> "Casey Carter"
|
||||
"Japheth Cleaver" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=12"];
|
||||
"Chuk Gawlik" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=22"];
|
||||
"Stacy Snyder" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=309"];
|
||||
"Chuk Gawlik" -> "Japheth Cleaver"
|
||||
"Stacy Snyder" -> "Japheth Cleaver"
|
||||
"Jillian Clifton" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=13"];
|
||||
"David Guthrie" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=25"];
|
||||
"David Guthrie" -> "Jillian Clifton"
|
||||
"Japheth Cleaver" -> "Jillian Clifton"
|
||||
"Tony Sacco" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=55"];
|
||||
"Heather Smith" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=59"];
|
||||
"Tony Sacco" -> "Laura De'Armond"
|
||||
"Heather Smith" -> "Laura De'Armond"
|
||||
"Kevin Decker" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=15"];
|
||||
"Alex Hansen" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=26"];
|
||||
"Wanda Livelsberger" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=53"];
|
||||
"Alex Hansen" -> "Kevin Decker"
|
||||
"Wanda Livelsberger" -> "Kevin Decker"
|
||||
"Patrick Doerr" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=16"];
|
||||
"Deanna Jagow" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=23"];
|
||||
"Alex Hansen" -> "Patrick Doerr"
|
||||
"Deanna Jagow" -> "Patrick Doerr"
|
||||
"Lori Asaro" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=178"];
|
||||
"Mark Pearson" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=169"];
|
||||
"Lori Ball" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=167"];
|
||||
"Mark Pearson" -> "Lori Asaro"
|
||||
"Lori Ball" -> "Lori Asaro"
|
||||
"Ryan Farris" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=18"];
|
||||
"Rob Reiner" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=51"];
|
||||
"Cindy Teel" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=62"];
|
||||
"Rob Reiner" -> "Ryan Farris"
|
||||
"Cindy Teel" -> "Ryan Farris"
|
||||
"Ginger Palmer" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=180"];
|
||||
"Mark Newton-John" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=46"];
|
||||
"Mark Newton-John" -> "Ginger Palmer"
|
||||
"Matthew FitzGerald" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=19"];
|
||||
"Mervin Maniago" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=41"];
|
||||
"Mervin Maniago" -> "Matthew FitzGerald"
|
||||
"Amie Holston" -> "Matthew FitzGerald"
|
||||
"Tani Miller" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=195"];
|
||||
"Mark Pearson" -> "Tani Miller"
|
||||
"Vienna McMurtry" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=196"];
|
||||
"Robert Walwick" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=153"];
|
||||
"Robert Walwick" -> "Vienna McMurtry"
|
||||
"Ginger Palmer" -> "Vienna McMurtry"
|
||||
"Chuck Foster" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=20"];
|
||||
"Karen Saye" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=56"];
|
||||
"Kevin Decker" -> "Chuck Foster"
|
||||
"Karen Saye" -> "Chuck Foster"
|
||||
"Gary Frampton" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=201"];
|
||||
"Ginger Palmer" -> "Gary Frampton"
|
||||
"Pat Norris" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=207"];
|
||||
"Sean Tipps" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=204"];
|
||||
"Teresa Long" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=194"];
|
||||
"Sean Tipps" -> "Pat Norris"
|
||||
"Teresa Long" -> "Pat Norris"
|
||||
"Marc Martin-ez" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=208"];
|
||||
"Mark Pearson" -> "Marc Martin-ez"
|
||||
"Tani Miller" -> "Marc Martin-ez"
|
||||
"Kristen Villone" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=209"];
|
||||
"Kelly Erickson" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=199"];
|
||||
"Anna Pedroza" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=197"];
|
||||
"Kelly Erickson" -> "Kristen Villone"
|
||||
"Anna Pedroza" -> "Kristen Villone"
|
||||
"Geoff Frank" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=21"];
|
||||
"Chris Livelsberger" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=40"];
|
||||
"Amy Price" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=57"];
|
||||
"Chris Livelsberger" -> "Geoff Frank"
|
||||
"Amy Price" -> "Geoff Frank"
|
||||
"Tracy Murray" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=210"];
|
||||
"John FitzGibbon" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=92"];
|
||||
"Judy Dulcich" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=177"];
|
||||
"John FitzGibbon" -> "Tracy Murray"
|
||||
"Judy Dulcich" -> "Tracy Murray"
|
||||
"Ian McIntosh" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=215"];
|
||||
"Barbara Tollison" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=172"];
|
||||
"Robert Walwick" -> "Ian McIntosh"
|
||||
"Barbara Tollison" -> "Ian McIntosh"
|
||||
"Jayson Smith" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=58"];
|
||||
"Jayson Smith" -> "Chuk Gawlik"
|
||||
"Heather Smith" -> "Chuk Gawlik"
|
||||
"Kelly McKinney" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=222"];
|
||||
"Mark Nadeau" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=183"];
|
||||
"Mark Nadeau" -> "Kelly McKinney"
|
||||
"Judy Dulcich" -> "Kelly McKinney"
|
||||
"Chris Livelsberger" -> "Deanna Jagow"
|
||||
"Amy Price" -> "Deanna Jagow"
|
||||
"Renee Thompson" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=231"];
|
||||
"J. Angeles" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=3"];
|
||||
"Kelley Smith" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=223"];
|
||||
"J. Angeles" -> "Renee Thompson"
|
||||
"Kelley Smith" -> "Renee Thompson"
|
||||
"Steven Smith" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=233"];
|
||||
"John FitzGibbon" -> "Steven Smith"
|
||||
"Charlene Andrews" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=234"];
|
||||
"Diane Reoch" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=227"];
|
||||
"Diane Reoch" -> "Charlene Andrews"
|
||||
"Tonya Alexander" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=238"];
|
||||
"Gail Vasquez" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=225"];
|
||||
"Gail Vasquez" -> "Tonya Alexander"
|
||||
"Spencer Caldwell" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=239"];
|
||||
"Becky Bernal" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=218"];
|
||||
"Becky Bernal" -> "Spencer Caldwell"
|
||||
"Chuk Gawlik" -> "Michael Griffith"
|
||||
"Wanda Livelsberger" -> "Michael Griffith"
|
||||
"Russell Grant" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=242"];
|
||||
"Steven Smith" -> "Russell Grant"
|
||||
"Tiffany Worthington" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=66"];
|
||||
"Chuck Foster" -> "David Guthrie"
|
||||
"Tiffany Worthington" -> "David Guthrie"
|
||||
"Jerry Maya" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=250"];
|
||||
"John FitzGibbon" -> "Jerry Maya"
|
||||
"Melissa Schwartz" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=252"];
|
||||
"Russell Grant" -> "Melissa Schwartz"
|
||||
"Delphy Shaulis" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=255"];
|
||||
"Renee Thompson" -> "Delphy Shaulis"
|
||||
"Martin Naiman" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=45"];
|
||||
"Janean Angeles" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=86"];
|
||||
"Martin Naiman" -> "Alex Hansen"
|
||||
"Janean Angeles" -> "Alex Hansen"
|
||||
"Leslie Harlow" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=265"];
|
||||
"Dennis McColl" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=251"];
|
||||
"Denise Luna" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=236"];
|
||||
"Dennis McColl" -> "Leslie Harlow"
|
||||
"Denise Luna" -> "Leslie Harlow"
|
||||
"Jonathan Yudman" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=267"];
|
||||
"April Ortiz-cloninger" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=258"];
|
||||
"April Ortiz-cloninger" -> "Jonathan Yudman"
|
||||
"Michael Elgo" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=268"];
|
||||
"Carol Kropp" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=254"];
|
||||
"Spencer Caldwell" -> "Michael Elgo"
|
||||
"Carol Kropp" -> "Michael Elgo"
|
||||
"Denmark Vea" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=269"];
|
||||
"Marc Martin-ez" -> "Denmark Vea"
|
||||
"Kelley Smith" -> "Denmark Vea"
|
||||
"Kathleen Hansen" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=27"];
|
||||
"Martin Naiman" -> "Kathleen Hansen"
|
||||
"Heather Smith" -> "Kathleen Hansen"
|
||||
"Laura Stegner" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=274"];
|
||||
"April Ortiz-cloninger" -> "Laura Stegner"
|
||||
"Kathy Jones" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=28"];
|
||||
"J. Angeles" -> "Kathy Jones"
|
||||
"Eric Gates" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=282"];
|
||||
"Erick Sugimura" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=280"];
|
||||
"Erick Sugimura" -> "Eric Gates"
|
||||
"Laura Stegner" -> "Eric Gates"
|
||||
"Jennifer Stoewe" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=288"];
|
||||
"Eric Gates" -> "Jennifer Stoewe"
|
||||
"Karen Helbling" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=29"];
|
||||
"Regan Ashker" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=5"];
|
||||
"Kevin Decker" -> "Karen Helbling"
|
||||
"Regan Ashker" -> "Karen Helbling"
|
||||
"Scott Wood" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=295"];
|
||||
"Eric Gates" -> "Scott Wood"
|
||||
"Greg Flood" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=200"];
|
||||
"Greg Flood" -> "J. Angeles"
|
||||
"Ginger Palmer" -> "J. Angeles"
|
||||
"Lynn Reeves" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=48"];
|
||||
"Chuk Gawlik" -> "Amie Holston"
|
||||
"Lynn Reeves" -> "Amie Holston"
|
||||
"Susan Colwell" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=302"];
|
||||
"Michael Elgo" -> "Susan Colwell"
|
||||
"Christopher Jouan" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=306"];
|
||||
"Kevin Owens" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=245"];
|
||||
"Kevin Owens" -> "Christopher Jouan"
|
||||
"Kristianna Reynante" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=308"];
|
||||
"Michael Elgo" -> "Kristianna Reynante"
|
||||
"Janean Angeles" -> "Kristianna Reynante"
|
||||
"Amy Berner" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=300"];
|
||||
"Amy Berner" -> "Stacy Snyder"
|
||||
"Deanna Johnson" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=31"];
|
||||
"Alex Hansen" -> "Deanna Johnson"
|
||||
"Laura De'Armond" -> "Deanna Johnson"
|
||||
"Johnny Richardson" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=310"];
|
||||
"Russell Grant" -> "Johnny Richardson"
|
||||
"Nathan Fellhauer" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=313"];
|
||||
"James Rowland" [color=thistle, URL="http://sdsu.kkytbs.net/members/profile.html?who=52"];
|
||||
"James Rowland" -> "Nathan Fellhauer"
|
||||
"Kristianna Reynante" -> "Nathan Fellhauer"
|
||||
"Brian Raneses" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=314"];
|
||||
"Sean McHenry" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=42"];
|
||||
"Sean McHenry" -> "Brian Raneses"
|
||||
"Penny Lewis" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=315"];
|
||||
"Martin Naiman" -> "Penny Lewis"
|
||||
"Becky Graham" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=316"];
|
||||
"Kristen Elgo" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=7"];
|
||||
"Kristen Elgo" -> "Becky Graham"
|
||||
"Steven Gross" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=318"];
|
||||
"Rob Reiner" -> "Steven Gross"
|
||||
"Stacy Snyder" -> "Steven Gross"
|
||||
"Sedona Reynolds" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=32"];
|
||||
"Mark Newton-John" -> "Sedona Reynolds"
|
||||
"Cindy Teel" -> "Sedona Reynolds"
|
||||
"Klair Mayerchak" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=320"];
|
||||
"Nathan Fellhauer" -> "Klair Mayerchak"
|
||||
"Becky Graham" -> "Klair Mayerchak"
|
||||
"Shari VerBerkmoes" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=321"];
|
||||
"Sean McHenry" -> "Shari VerBerkmoes"
|
||||
"Janean Angeles" -> "Shari VerBerkmoes"
|
||||
"Anson Summers" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=326"];
|
||||
"James Rowland" -> "Anson Summers"
|
||||
"Dusty Jolliff" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=33"];
|
||||
"Rob Reiner" -> "Dusty Jolliff"
|
||||
"Stacy Snyder" -> "Dusty Jolliff"
|
||||
"Jennifer Garman" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=331"];
|
||||
"James Rowland" -> "Jennifer Garman"
|
||||
"Kelly Greenhill" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=333"];
|
||||
"Rob Reiner" -> "Kelly Greenhill"
|
||||
"Kristen Elgo" -> "Kelly Greenhill"
|
||||
"Lucinda Farless" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=334"];
|
||||
"J. Angeles" -> "Lucinda Farless"
|
||||
"Susan Colwell" -> "Lucinda Farless"
|
||||
"Alfredo Cardenas" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=335"];
|
||||
"Chuk Gawlik" -> "Alfredo Cardenas"
|
||||
"Kathleen Hansen" -> "Alfredo Cardenas"
|
||||
"Jennifer Jouan" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=34"];
|
||||
"Andrea Owens" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=276"];
|
||||
"Andrea Owens" -> "Jennifer Jouan"
|
||||
"Tamara Scrivner" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=345"];
|
||||
"Joseph Butler" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=69"];
|
||||
"Sarah Maltese" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=83"];
|
||||
"Joseph Butler" -> "Tamara Scrivner"
|
||||
"Sarah Maltese" -> "Tamara Scrivner"
|
||||
"Bradley Stouse" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=346"];
|
||||
"Ryan Underwood" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=74"];
|
||||
"Ryan Underwood" -> "Bradley Stouse"
|
||||
"Cindy Teel" -> "Bradley Stouse"
|
||||
"Casondra Brimmage" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=347"];
|
||||
"Kristopher Lininger" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=85"];
|
||||
"Ilana Melcher" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=73"];
|
||||
"Kristopher Lininger" -> "Casondra Brimmage"
|
||||
"Ilana Melcher" -> "Casondra Brimmage"
|
||||
"Cassiopeia Guthrie" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=348"];
|
||||
"Jeremy Frazier" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=79"];
|
||||
"Christine Mount" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=76"];
|
||||
"Jeremy Frazier" -> "Cassiopeia Guthrie"
|
||||
"Christine Mount" -> "Cassiopeia Guthrie"
|
||||
"Kathleen Moran" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=349"];
|
||||
"Matthew FitzGerald" -> "Kathleen Moran"
|
||||
"Lori Brede" -> "Kathleen Moran"
|
||||
"Tiffany Kalland" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=35"];
|
||||
"Tony Sacco" -> "Tiffany Kalland"
|
||||
"Karen Helbling" -> "Tiffany Kalland"
|
||||
"Kristen Anderson" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=350"];
|
||||
"Jennie Bogart" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=78"];
|
||||
"David Guthrie" -> "Kristen Anderson"
|
||||
"Jennie Bogart" -> "Kristen Anderson"
|
||||
"Laura Simonette" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=351"];
|
||||
"Jon Weisel" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=89"];
|
||||
"Jon Weisel" -> "Laura Simonette"
|
||||
"Japheth Cleaver" -> "Laura Simonette"
|
||||
"Nathan Williams" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=352"];
|
||||
"David Guthrie" -> "Nathan Williams"
|
||||
"Karen Helbling" -> "Nathan Williams"
|
||||
"Rebecca Hippert" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=353"];
|
||||
"Ryan Underwood" -> "Rebecca Hippert"
|
||||
"Tiffany Kalland" -> "Rebecca Hippert"
|
||||
"Samuel Wallace" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=354"];
|
||||
"Joseph Butler" -> "Samuel Wallace"
|
||||
"Deanna Jagow" -> "Samuel Wallace"
|
||||
"Scott Gardner" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=355"];
|
||||
"Jeremy Frazier" -> "Scott Gardner"
|
||||
"Christine Mount" -> "Scott Gardner"
|
||||
"Alberto Ayon" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=356"];
|
||||
"Bradley Stouse" -> "Alberto Ayon"
|
||||
"Jennie Bogart" -> "Alberto Ayon"
|
||||
"Susannah Clayton" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=357"];
|
||||
"Nathan Williams" -> "Susannah Clayton"
|
||||
"Karen Helbling" -> "Susannah Clayton"
|
||||
"Lisa Gochnauer" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=358"];
|
||||
"Scott Gardner" -> "Lisa Gochnauer"
|
||||
"Casondra Brimmage" -> "Lisa Gochnauer"
|
||||
"Jamie Jackson" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=359"];
|
||||
"Samuel Wallace" -> "Jamie Jackson"
|
||||
"Tamara Scrivner" -> "Jamie Jackson"
|
||||
"Christina Kelly" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=36"];
|
||||
"Matthew FitzGerald" -> "Christina Kelly"
|
||||
"Lori Brede" -> "Christina Kelly"
|
||||
"Gara Thornton" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=360"];
|
||||
"Mark Newton-John" -> "Gara Thornton"
|
||||
"Laura Simonette" -> "Gara Thornton"
|
||||
"Robert Winebarger" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=361"];
|
||||
"Robin Ellison" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=90"];
|
||||
"Scott Gardner" -> "Robert Winebarger"
|
||||
"Robin Ellison" -> "Robert Winebarger"
|
||||
"Jeremy Kirchner" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=37"];
|
||||
"Rob Reiner" -> "Jeremy Kirchner"
|
||||
"Sandy Konar" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=38"];
|
||||
"Jennifer Brandon" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=9"];
|
||||
"Jennifer Brandon" -> "Sandy Konar"
|
||||
"Dan Kuhlman" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=39"];
|
||||
"Rob Reiner" -> "Dan Kuhlman"
|
||||
"Dusty Jolliff" -> "Dan Kuhlman"
|
||||
"Lindsay Arehart" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=4"];
|
||||
"Martin Naiman" -> "Lindsay Arehart"
|
||||
"Jennifer Brandon" -> "Lindsay Arehart"
|
||||
"J. Angeles" -> "Mervin Maniago"
|
||||
"Kathy Jones" -> "Mervin Maniago"
|
||||
"Jarrod Monroe" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=43"];
|
||||
"Jamie Fratacci" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=44"];
|
||||
"Mark Newton-John" -> "Jarrod Monroe"
|
||||
"Jamie Fratacci" -> "Jarrod Monroe"
|
||||
"Chuk Gawlik" -> "Jamie Fratacci"
|
||||
"Tiffany Worthington" -> "Jamie Fratacci"
|
||||
"Russell Grant" -> "Martin Naiman"
|
||||
"Tonya Alexander" -> "Martin Naiman"
|
||||
"Edward Givens" [color=lightblue, outline=bold, style=bold, URL="http://sdsu.kkytbs.net/members/profile.html?who=106"];
|
||||
"Edward Givens" -> "Mark Newton-John"
|
||||
"Veronica Nickel" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=47"];
|
||||
"Regan Ashker" -> "Veronica Nickel"
|
||||
"Wanda Livelsberger" -> "Lynn Reeves"
|
||||
"Bryan Ransom" [color=thistle, URL="http://sdsu.kkytbs.net/members/profile.html?who=49"];
|
||||
"Jayson Smith" -> "Bryan Ransom"
|
||||
"Tony Sacco" -> "Regan Ashker"
|
||||
"Dusty Jolliff" -> "Regan Ashker"
|
||||
"Jennifer Stout" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=50"];
|
||||
"Matthew FitzGerald" -> "Jennifer Stout"
|
||||
"Deanna Jagow" -> "Jennifer Stout"
|
||||
"Sean McHenry" -> "James Rowland"
|
||||
"James Rowland" -> "Wanda Livelsberger"
|
||||
"Janean Angeles" -> "Wanda Livelsberger"
|
||||
"Melissa Roy" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=54"];
|
||||
"Mervin Maniago" -> "Melissa Roy"
|
||||
"Christina Kelly" -> "Melissa Roy"
|
||||
"Dennis McColl" -> "Tony Sacco"
|
||||
"April Ortiz-cloninger" -> "Tony Sacco"
|
||||
"Tony Sacco" -> "Karen Saye"
|
||||
"Tony Sacco" -> "Amy Price"
|
||||
"Kathleen Hansen" -> "Amy Price"
|
||||
"James Rowland" -> "Jayson Smith"
|
||||
"Brian Raneses" -> "Heather Smith"
|
||||
"Kristen Elgo" -> "Heather Smith"
|
||||
"Josh Atwood" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=6"];
|
||||
"David Guthrie" -> "Josh Atwood"
|
||||
"Lori Brede" -> "Josh Atwood"
|
||||
"Katie Browne" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=60"];
|
||||
"Patrick Doerr" -> "Katie Browne"
|
||||
"Jamie Fratacci" -> "Katie Browne"
|
||||
"Kristin Tang" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=61"];
|
||||
"James Rowland" -> "Kristin Tang"
|
||||
"Heather Smith" -> "Kristin Tang"
|
||||
"Mervin Maniago" -> "Cindy Teel"
|
||||
"Veronica Nickel" -> "Cindy Teel"
|
||||
"Mike Tulumello" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=63"];
|
||||
"Matthew FitzGerald" -> "Mike Tulumello"
|
||||
"Katie Browne" -> "Mike Tulumello"
|
||||
"Veronica Villanueva" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=64"];
|
||||
"Ryan Farris" -> "Veronica Villanueva"
|
||||
"Sedona Reynolds" -> "Veronica Villanueva"
|
||||
"Mervin Maniago" -> "Tiffany Worthington"
|
||||
"Jennifer Jouan" -> "Tiffany Worthington"
|
||||
"Scott Wright" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=67"];
|
||||
"James Rowland" -> "Scott Wright"
|
||||
"Kristen Elgo" -> "Scott Wright"
|
||||
"Jeremy Browne" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=68"];
|
||||
"Matthew FitzGerald" -> "Jeremy Browne"
|
||||
"Japheth Cleaver" -> "Jeremy Browne"
|
||||
"James Fogelman" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=688"];
|
||||
"Alberto Ayon" -> "James Fogelman"
|
||||
"Susannah Clayton" -> "James Fogelman"
|
||||
"Sandra Chase" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=689"];
|
||||
"David Guthrie" -> "Sandra Chase"
|
||||
"Japheth Cleaver" -> "Sandra Chase"
|
||||
"Patrick Doerr" -> "Joseph Butler"
|
||||
"Deanna Jagow" -> "Joseph Butler"
|
||||
"Laura Fisher" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=690"];
|
||||
"Nathan Williams" -> "Laura Fisher"
|
||||
"Casondra Brimmage" -> "Laura Fisher"
|
||||
"Katie Kozma" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=691"];
|
||||
"Scott Wright" -> "Katie Kozma"
|
||||
"Robin Ellison" -> "Katie Kozma"
|
||||
"Rachel Perkins" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=692"];
|
||||
"Joseph Butler" -> "Rachel Perkins"
|
||||
"Cassiopeia Guthrie" -> "Rachel Perkins"
|
||||
"Sarah Titilah" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=693"];
|
||||
"Robert Winebarger" -> "Sarah Titilah"
|
||||
"Karen Helbling" -> "Sarah Titilah"
|
||||
"Ashley Rehart" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=694"];
|
||||
"Laura Fisher" -> "Ashley Rehart"
|
||||
"Cara Yancey" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=695"];
|
||||
"Katie Kozma" -> "Cara Yancey"
|
||||
"Ashley Presley" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=698"];
|
||||
"Cara Yancey" -> "Ashley Presley"
|
||||
"Leila Wilhelm" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=699"];
|
||||
"Robin Ellison" -> "Leila Wilhelm"
|
||||
"Sean McHenry" -> "Kristen Elgo"
|
||||
"Stacy Snyder" -> "Kristen Elgo"
|
||||
"Greg Moody" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=70"];
|
||||
"Ryan Farris" -> "Greg Moody"
|
||||
"Jennifer Stout" -> "Greg Moody"
|
||||
"Lisa Fleck" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=700"];
|
||||
"Rachel Perkins" -> "Lisa Fleck"
|
||||
"Christine Coyne" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=701"];
|
||||
"Rachel Perkins" -> "Christine Coyne"
|
||||
"Jennifer Cooley" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=702"];
|
||||
"Laura Fisher" -> "Jennifer Cooley"
|
||||
"Elizabeth Larios" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=703"];
|
||||
"Ashley Rehart" -> "Elizabeth Larios"
|
||||
"Cate Threlkeld" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=707"];
|
||||
"Katie Kozma" -> "Cate Threlkeld"
|
||||
"Erika Tapia" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=71"];
|
||||
"Patrick Doerr" -> "Erika Tapia"
|
||||
"Melissa Roy" -> "Erika Tapia"
|
||||
"Robbyn Rozelle" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=72"];
|
||||
"Jarrod Monroe" -> "Robbyn Rozelle"
|
||||
"Tiffany Kalland" -> "Robbyn Rozelle"
|
||||
"Ryan Farris" -> "Ilana Melcher"
|
||||
"Veronica Villanueva" -> "Ilana Melcher"
|
||||
"Greg Moody" -> "Ryan Underwood"
|
||||
"Katie Browne" -> "Ryan Underwood"
|
||||
"Cameron Brown" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=75"];
|
||||
"Joseph Butler" -> "Cameron Brown"
|
||||
"Tiffany Kalland" -> "Cameron Brown"
|
||||
"Ryan Underwood" -> "Christine Mount"
|
||||
"Lori Brede" -> "Christine Mount"
|
||||
"Janay Rabe" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=77"];
|
||||
"Greg Moody" -> "Janay Rabe"
|
||||
"Cindy Teel" -> "Janay Rabe"
|
||||
"Jeremy Browne" -> "Jennie Bogart"
|
||||
"Tiffany Kalland" -> "Jennie Bogart"
|
||||
"Ryan Farris" -> "Jeremy Frazier"
|
||||
"Ilana Melcher" -> "Jeremy Frazier"
|
||||
"Crystal Bozak" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=8"];
|
||||
"Patrick Doerr" -> "Crystal Bozak"
|
||||
"Katie Browne" -> "Crystal Bozak"
|
||||
"Kameka Smith" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=80"];
|
||||
"Matthew FitzGerald" -> "Kameka Smith"
|
||||
"Ilana Melcher" -> "Kameka Smith"
|
||||
"Kyra Sacco" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=81"];
|
||||
"Joseph Butler" -> "Kyra Sacco"
|
||||
"Robbyn Rozelle" -> "Kyra Sacco"
|
||||
"Samuel Behar" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=82"];
|
||||
"Ryan Underwood" -> "Samuel Behar"
|
||||
"Lori Brede" -> "Samuel Behar"
|
||||
"Patrick Doerr" -> "Sarah Maltese"
|
||||
"Deanna Jagow" -> "Sarah Maltese"
|
||||
"David Bronson" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=84"];
|
||||
"Kristin Alongi-Hutchins" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=87"];
|
||||
"Tony Sacco" -> "David Bronson"
|
||||
"Kristin Alongi-Hutchins" -> "David Bronson"
|
||||
"Cameron Brown" -> "Kristopher Lininger"
|
||||
"Kameka Smith" -> "Kristopher Lininger"
|
||||
"Rakan Abu-Rahma" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=852"];
|
||||
"Christine Coyne" -> "Rakan Abu-Rahma"
|
||||
"Jennifer Berry" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=270"];
|
||||
"Jennifer Berry" -> "Janean Angeles"
|
||||
"Penny Lewis" -> "Kristin Alongi-Hutchins"
|
||||
"Melissa Bebak" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=88"];
|
||||
"Greg Moody" -> "Melissa Bebak"
|
||||
"Sarah Maltese" -> "Melissa Bebak"
|
||||
"Scott Wright" -> "Jennifer Brandon"
|
||||
"Japheth Cleaver" -> "Jennifer Brandon"
|
||||
"Samuel Behar" -> "Robin Ellison"
|
||||
"Kyra Sacco" -> "Robin Ellison"
|
||||
"Teresa Simms" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=91"];
|
||||
"Joseph Butler" -> "Teresa Simms"
|
||||
"Janay Rabe" -> "Teresa Simms"
|
||||
"Robert Schmidtke" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=188"];
|
||||
"Jean Newman" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=166"];
|
||||
"Robert Schmidtke" -> "John FitzGibbon"
|
||||
"Jean Newman" -> "John FitzGibbon"
|
||||
"Brittany DePew" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=928"];
|
||||
"Elizabeth Larios" -> "Brittany DePew"
|
||||
"Kathleen Halberg" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=929"];
|
||||
"Ashley Rehart" -> "Kathleen Halberg"
|
||||
"Terrance Hirsch" [color=lightblue, URL="http://sdsu.kkytbs.net/members/profile.html?who=96"];
|
||||
"J. Angeles" -> "Terrance Hirsch"
|
||||
"Susan Colwell" -> "Terrance Hirsch"
|
||||
"Monique Arellano" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=972"];
|
||||
"Ashley Presley" -> "Monique Arellano"
|
||||
"Anthony Henderson" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=973"];
|
||||
"Jennifer Cooley" -> "Anthony Henderson"
|
||||
"Amethyst Tagle" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=974"];
|
||||
"Cate Threlkeld" -> "Amethyst Tagle"
|
||||
"Mallory Williams" [color=lightpink, URL="http://sdsu.kkytbs.net/members/profile.html?who=975"];
|
||||
"Lisa Fleck" -> "Mallory Williams"
|
||||
}
|
||||
+377
@@ -0,0 +1,377 @@
|
||||
digraph G {
|
||||
size="7,10"
|
||||
page="8.5,11"
|
||||
center=""
|
||||
node[width=.25,height=.375,fontsize=9]
|
||||
fcfpr1_1_2t_17 -> 341411;
|
||||
fcfpr1_1t_1 -> 341411;
|
||||
rdlfpr2_0_rdlt_4 -> 341411;
|
||||
fpfpr1_0_1t_1 -> 341411;
|
||||
fpfpr1_1_2t_11 -> 341411;
|
||||
rtafpr1_1_2t_28 -> 341411;
|
||||
rtafpr1_1_3t_6 -> 341411;
|
||||
rdlfpr1_1t_1 -> 358866;
|
||||
rtafpr1_1_3t_6 -> 358866;
|
||||
tmfpr1_1_3t_5 -> 358930;
|
||||
fcfpr1_1_3t_9 -> 358930;
|
||||
pcfpr1_1_3t_7 -> 358930;
|
||||
fpfpr1_1_3g_1 -> 358930;
|
||||
fpfpr1_1_3t_1 -> 358930;
|
||||
aufpr1_1_3t_1 -> 358930;
|
||||
rtafpr1_0_3g_1 -> 358930;
|
||||
rtafpr1_1_3t_6 -> 358930;
|
||||
msgfpr1_1_1g_12 -> 371943;
|
||||
rtafpr1_1_1g_8 -> 371943;
|
||||
rtafpr1_1_1t_35 -> 371943;
|
||||
rtafpr1_1_1t_45 -> 371943;
|
||||
rtafpr1_1_3t_6 -> 371943;
|
||||
tlfpr2_0_rdlg_2 -> 374300;
|
||||
fcfpr1_1_3t_8 -> 374300;
|
||||
fcfpr1_1_3t_9 -> 374300;
|
||||
rtafpr1_1_3t_6 -> 374300;
|
||||
fcfpr1_0_5g_1 -> 371942;
|
||||
fcfpr1_1_1t_19 -> 371942;
|
||||
fcfpr1_1_3t_9 -> 371942;
|
||||
fcfpr1_1_3t_9 -> 374700;
|
||||
tymsgfpr1_1_3t_3 -> 374700;
|
||||
fpfpr1_1_3t_1 -> 374700;
|
||||
rtafpr1_1_3t_7 -> 374700;
|
||||
fcfpr1_1_3g_2 -> 374741;
|
||||
fcfpr1_1_3t_9 -> 374741;
|
||||
fpfpr1_1_3t_1 -> 374741;
|
||||
rtafpr1_1_3t_7 -> 374741;
|
||||
fcfpr1_1_1t_18 -> 374886;
|
||||
fcfpr1_1_3t_9 -> 374886;
|
||||
fpfpr1_1_3t_1 -> 374886;
|
||||
rtafpr1_1_3t_7 -> 374886;
|
||||
fcfpr1_1_3t_9 -> 375039;
|
||||
fpfpr1_1_3t_1 -> 375039;
|
||||
fcfpr1_1_3t_42 -> 375507;
|
||||
fcfpr1_1_3t_9 -> 375507;
|
||||
rdlfpr2_0_rdlt_158 -> 375507;
|
||||
rtafpr1_1_3t_7 -> 375507;
|
||||
rtafpr1_1_3t_71 -> 375507;
|
||||
dbfpr1_1_3t_2 -> 375507;
|
||||
fcfpr1_1_3t_9 -> 375508;
|
||||
rdlfpr1_1g_13 -> 375508;
|
||||
rtafpr1_1_3t_7 -> 375508;
|
||||
rtafpr2_1_rdlg_1 -> 375508;
|
||||
dbfpr1_1_3t_2 -> 375508;
|
||||
fcfpr1_1_3t_9 -> 375519;
|
||||
fpfpr1_1_3g_1 -> 375519;
|
||||
fpfpr1_1_3t_1 -> 375519;
|
||||
fcfpr1_1_3t_9 -> 377380;
|
||||
rdlfpr1_1g_16 -> 377380;
|
||||
rdlfpr1_1t_100 -> 377380;
|
||||
fcfpr1_0_2g_1 -> 377719;
|
||||
fcfpr1_1_3t_10 -> 377719;
|
||||
fcfpr1_1_3t_7 -> 377719;
|
||||
fcfpr1_1_3t_9 -> 377719;
|
||||
rdlfpr2_0_rdlg_12 -> 377719;
|
||||
rdlfpr2_0_rdlt_108 -> 377719;
|
||||
rdlfpr2_0_rdlt_27 -> 377719;
|
||||
rdlfpr2_0_rdlt_30 -> 377719;
|
||||
fcfpr1_1_3t_9 -> 377763;
|
||||
fcfpr1_1_3t_9 -> 379848;
|
||||
fpfpr1_1_3t_1 -> 379848;
|
||||
fcfpr1_1_3t_9 -> 380571;
|
||||
fcfpr1_1_3t_9 -> 380604;
|
||||
fpfpr1_1_3t_1 -> 380604;
|
||||
fcfpr1_1_3t_9 -> 381211;
|
||||
fpfpr1_1_3t_1 -> 381211;
|
||||
fcfpr1_1_3t_9 -> 381835;
|
||||
fcfpr1_1_3t_9 -> 381897;
|
||||
fcfpr1_1_3t_9 -> 381901;
|
||||
fpfpr1_1_3t_1 -> 381901;
|
||||
fcfpr1_1_3t_9 -> 382103;
|
||||
rtafpr1_1_3t_7 -> 382103;
|
||||
fcfpr1_1_3t_9 -> 382161;
|
||||
fcfpr1_1_3t_9 -> 383174;
|
||||
fpfpr1_1_3t_1 -> 383174;
|
||||
rtafpr1_1_3t_7 -> 383174;
|
||||
fpfpr1_1_3g_1 -> 352010;
|
||||
fpfpr1_1_3t_1 -> 352010;
|
||||
fpfpr1_1_3t_1 -> 382409;
|
||||
fpfpr1_1_3t_1 -> 382827;
|
||||
fpfpr1_1_3t_1 -> 382928;
|
||||
rtafpr1_1_3t_7 -> 382928;
|
||||
tlfpr1_1_1t_5 -> 358224;
|
||||
tymsgfpr1_1_1t_23 -> 358224;
|
||||
tymsgfpr1_1_3t_3 -> 358224;
|
||||
rcfpr0_0_1t_9 -> 358224;
|
||||
rcfpr1_1_1t_5 -> 358224;
|
||||
odfpr0_0_1t_8 -> 358224;
|
||||
odfpr1_1_1t_6 -> 358224;
|
||||
ecdsgfpr1_1_1t_4 -> 358224;
|
||||
tymsgfpr1_1_1t_18 -> 358900;
|
||||
tymsgfpr1_1_3t_3 -> 358900;
|
||||
rcfpr1_1_1t_100 -> 358900;
|
||||
rcfpr1_1_1t_22 -> 358900;
|
||||
rcfpr1_1_1t_37 -> 358900;
|
||||
odfpr1_1_1t_21 -> 358900;
|
||||
tymsgfpr1_1_3t_3 -> 372568;
|
||||
rcfpr1_1_1t_30 -> 372568;
|
||||
odfpr1_1_1t_31 -> 372568;
|
||||
tlfpr1_1_1t_20 -> 375557;
|
||||
tymsgfpr1_1_1t_24 -> 375557;
|
||||
tymsgfpr1_1_3t_3 -> 375557;
|
||||
rcfpr1_1_1t_11 -> 375557;
|
||||
odfpr1_1_1t_9 -> 375557;
|
||||
ecdsgfpr1_1_1t_19 -> 375557;
|
||||
rtafpr1_1_1g_14 -> 376956;
|
||||
rtafpr1_1_1t_64 -> 376956;
|
||||
rtafpr1_1_2t_18 -> 376956;
|
||||
rtafpr1_1_3t_30 -> 376956;
|
||||
rtafpr1_1_3t_7 -> 376956;
|
||||
rtafpr1_1_3t_7 -> 379339;
|
||||
rtafpr1_1_1t_14 -> 379422;
|
||||
rtafpr1_1_1t_20 -> 379422;
|
||||
rtafpr1_1_3t_7 -> 379422;
|
||||
rtafpr1_1_3t_7 -> 383039;
|
||||
fcfpr1_1_1t_18 -> 359471;
|
||||
fcfpr2_0_1t_1 -> 359471;
|
||||
fcfpr2_0_1t_2 -> 359471;
|
||||
ccsfpr2_0_1t_99 -> 359471;
|
||||
fcfpr1_1_3t_42 -> 384096;
|
||||
rtafpr1_1_3t_71 -> 384096;
|
||||
tlfpr1_0_4g_4 -> 354290;
|
||||
rcfpr0_0_1t_9 -> 354290;
|
||||
odfpr0_0_1t_8 -> 354290;
|
||||
pagfpr1_1_1t_23 -> 354290;
|
||||
rcfpr1_1_1t_5 -> 379864;
|
||||
rcfpr1_1_1t_100 -> 382574;
|
||||
rcfpr1_1_1t_22 -> 382574;
|
||||
rcfpr1_1_1t_37 -> 382574;
|
||||
rcfpr1_1_1t_30 -> 370706;
|
||||
rcfpr1_1_1t_30 -> 377908;
|
||||
rcfpr1_1_1t_30 -> 377924;
|
||||
rcfpr1_1_1t_30 -> 377971;
|
||||
rcfpr1_1_1t_30 -> 377980;
|
||||
odfpr1_1_1t_31 -> 377980;
|
||||
rcfpr1_1_1t_30 -> 378362;
|
||||
rcfpr1_1_1t_30 -> 378656;
|
||||
rcfpr1_1_1t_30 -> 378666;
|
||||
rcfpr1_1_1t_30 -> 379169;
|
||||
odfpr1_1_1t_31 -> 379169;
|
||||
rcfpr1_1_1t_110 -> 379341;
|
||||
rcfpr1_1_1t_30 -> 379341;
|
||||
rcfpr1_1_1t_62 -> 379341;
|
||||
odfpr1_1_1t_31 -> 379341;
|
||||
rcfpr1_1_1t_30 -> 379972;
|
||||
rcfpr1_1_1t_30 -> 380298;
|
||||
rcfpr1_1_1t_30 -> 380448;
|
||||
rcfpr1_1_1t_30 -> 380475;
|
||||
odfpr1_1_1t_31 -> 380475;
|
||||
rcfpr1_1_1t_30 -> 380526;
|
||||
odfpr1_1_1t_31 -> 357430;
|
||||
rcfpr1_1_1t_11 -> 379968;
|
||||
odfpr1_1_1t_9 -> 379968;
|
||||
ccsfpr2_0_1t_99 -> 359100;
|
||||
ccsfpr2_0_1t_99 -> 376529;
|
||||
ccsfpr2_0_1t_99 -> 377801;
|
||||
ccsfpr2_0_1t_99 -> 379126;
|
||||
ccsfpr2_0_1t_99 -> 379212;
|
||||
ccsfpr2_0_1t_99 -> 380285;
|
||||
ccsfpr2_0_1t_99 -> 380963;
|
||||
ccsfpr2_0_1t_99 -> 384909;
|
||||
tlfpr1_0_4g_4 -> 358471;
|
||||
odfpr0_0_1t_7 -> 358471;
|
||||
odfpr1_0_1t_36 -> 358471;
|
||||
odfpr1_0_3t_18 -> 358471;
|
||||
odfpr1_0_3t_21 -> 358471;
|
||||
tlfpr1_0_4g_4 -> 375024;
|
||||
tlfpr1_0_4g_4 -> 375027;
|
||||
rcfpr1_1_1t_110 -> 381710;
|
||||
rcfpr1_1_1t_62 -> 381710;
|
||||
rcfpr1_1_1t_110 -> 381775;
|
||||
rcfpr1_1_1t_62 -> 381775;
|
||||
rcfpr1_1_1t_110 -> 382436;
|
||||
fcfpr1_1_3t_34 -> 382528;
|
||||
rcfpr1_1_1t_110 -> 382528;
|
||||
rtafpr1_1_3t_48 -> 382528;
|
||||
rcfpr1_1_1t_110 -> 382566;
|
||||
rcfpr1_1_1t_110 -> 382572;
|
||||
odfpr0_0_1t_7 -> 353506;
|
||||
rcfpr1_0_1t_35 -> 370509;
|
||||
odfpr0_0_1t_7 -> 370509;
|
||||
odfpr0_0_1t_7 -> 370510;
|
||||
odfpr1_0_1t_38 -> 370510;
|
||||
tlfpr1_0_4g_5 -> 354546;
|
||||
rcfpr1_1_1t_61 -> 354546;
|
||||
odfpr1_0_3t_18 -> 354546;
|
||||
odfpr1_0_3t_20 -> 354546;
|
||||
odfpr1_0_3t_18 -> 354757;
|
||||
odfpr1_0_3t_20 -> 354757;
|
||||
odfpr1_0_3t_18 -> 354766;
|
||||
odfpr1_0_3t_20 -> 354766;
|
||||
odfpr1_0_3t_18 -> 354771;
|
||||
odfpr1_0_3t_20 -> 354771;
|
||||
odfpr1_0_3t_18 -> 354785;
|
||||
odfpr1_0_3t_23 -> 354785;
|
||||
odfpr1_0_3t_24 -> 354785;
|
||||
odfpr1_0_3t_18 -> 354878;
|
||||
odfpr1_0_3t_23 -> 354878;
|
||||
odfpr1_0_3t_24 -> 354878;
|
||||
odfpr1_0_3t_18 -> 355080;
|
||||
odfpr1_0_3t_23 -> 355080;
|
||||
odfpr1_0_3t_24 -> 355080;
|
||||
odfpr1_0_3t_18 -> 355288;
|
||||
odfpr1_0_3t_23 -> 355288;
|
||||
odfpr1_0_3t_24 -> 355288;
|
||||
odfpr2_0_03t_13 -> 355288;
|
||||
odfpr1_0_3t_18 -> 355800;
|
||||
odfpr1_0_3t_21 -> 355800;
|
||||
odfpr1_0_3t_18 -> 356116;
|
||||
odfpr1_0_3t_21 -> 356116;
|
||||
odfpr1_0_3t_18 -> 356741;
|
||||
odfpr1_0_3t_21 -> 356741;
|
||||
odfpr1_0_3t_18 -> 357340;
|
||||
odfpr1_0_3t_21 -> 357340;
|
||||
odfpr1_0_3t_18 -> 357538;
|
||||
odfpr1_0_3t_21 -> 357538;
|
||||
odfpr1_0_3t_18 -> 357769;
|
||||
odfpr1_0_3t_21 -> 357769;
|
||||
odfpr1_0_3t_18 -> 357793;
|
||||
odfpr1_0_3t_21 -> 357793;
|
||||
odfpr1_0_3t_18 -> 358155;
|
||||
odfpr1_0_3t_21 -> 358155;
|
||||
odfpr1_0_3t_18 -> 358157;
|
||||
odfpr1_0_3t_21 -> 358157;
|
||||
odfpr1_0_3t_18 -> 358159;
|
||||
odfpr1_0_3t_21 -> 358159;
|
||||
odfpr1_0_3t_18 -> 358584;
|
||||
odfpr1_0_3t_21 -> 358584;
|
||||
odfpr1_0_3t_18 -> 360104;
|
||||
odfpr1_0_3t_21 -> 360104;
|
||||
odfpr1_0_3t_18 -> 360144;
|
||||
odfpr1_0_3t_21 -> 360144;
|
||||
odfpr1_0_3t_18 -> 360672;
|
||||
odfpr1_0_3t_21 -> 360672;
|
||||
odfpr1_0_3t_5 -> 360672;
|
||||
odfpr1_0_3t_18 -> 360839;
|
||||
odfpr1_0_3t_21 -> 360839;
|
||||
odfpr1_0_3t_18 -> 371187;
|
||||
tlfpr1_0_3g_5 -> 373300;
|
||||
odfpr1_0_3t_12 -> 373300;
|
||||
odfpr1_0_3t_18 -> 373300;
|
||||
odfpr1_0_3t_18 -> 375134;
|
||||
odfpr1_0_5t_18 -> 375134;
|
||||
rcfpr0_0_1t_10 -> 375319;
|
||||
odfpr1_0_3t_18 -> 375319;
|
||||
odfpr1_0_3t_36 -> 375319;
|
||||
odfpr1_0_5t_17 -> 375319;
|
||||
odfpr1_0_5t_19 -> 375319;
|
||||
odfpr1_0_3t_18 -> 375499;
|
||||
odfpr1_0_3t_18 -> 377220;
|
||||
odfpr1_0_5t_21 -> 377220;
|
||||
tlfpr1_0_3g_7 -> 377562;
|
||||
tlfpr1_1_1t_3 -> 377562;
|
||||
odfpr1_0_3t_18 -> 377562;
|
||||
odfpr1_0_3t_36 -> 377562;
|
||||
odfpr1_0_5t_20 -> 377562;
|
||||
odfpr1_0_3t_18 -> 378108;
|
||||
odfpr1_0_3t_6 -> 378108;
|
||||
odfpr1_0_5t_20 -> 354221;
|
||||
|
||||
odfpr0_0_1t_7 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
tlfpr1_0_3g_5 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr0_0_1t_8 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rcfpr1_1_1t_61 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr1_1t_1 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_3t_18 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
tlfpr1_0_3g_7 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rcfpr1_1_1t_62 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
ccsfpr2_0_1t_99 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
tymsgfpr1_1_3t_3 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rcfpr0_0_1t_9 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_1_1t_14 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_1_3t_30 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rcfpr1_1_1t_110 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
dbfpr1_1_3t_2 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_1_1g_8 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rcfpr1_1_1t_30 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
tlfpr1_1_1t_20 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_1_1t_64 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
tlfpr2_0_rdlg_2 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_1_2t_28 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
tlfpr1_1_1t_3 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_1_1t_6 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fpfpr1_1_3t_1 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
aufpr1_1_3t_1 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr1_1_3t_34 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rcfpr1_1_1t_5 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr1_1_1t_18 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_3t_36 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
tlfpr1_1_1t_5 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr1_1_1t_19 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_1_1t_9 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr1_1_3t_7 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rcfpr1_1_1t_37 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr1_1_3t_8 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_1_1t_21 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr1_1_3t_9 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rdlfpr2_0_rdlt_27 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr1_1_3g_2 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_1_1t_35 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_5t_20 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fpfpr1_1_3g_1 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_5t_21 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fpfpr1_1_2t_11 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
ecdsgfpr1_1_1t_19 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_1t_36 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_1_1g_14 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
tymsgfpr1_1_1t_23 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
tymsgfpr1_1_1t_24 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_1t_38 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr1_0_2g_1 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rdlfpr1_1t_1 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rcfpr0_0_1t_10 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rcfpr1_1_1t_100 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rdlfpr2_0_rdlt_108 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
pcfpr1_1_3t_7 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_3t_20 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
ecdsgfpr1_1_1t_4 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
tmfpr1_1_3t_5 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_3t_21 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fpfpr1_0_1t_1 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_3t_23 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rcfpr1_1_1t_22 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
pagfpr1_1_1t_23 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_1_3t_71 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_1_2t_18 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rdlfpr2_0_rdlt_158 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_1_3t_6 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_3t_24 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_1_3t_7 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_0_3g_1 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_1_1t_20 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rdlfpr1_1g_13 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rcfpr1_0_1t_35 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr1_1_2t_17 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr2_1_rdlg_1 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rdlfpr2_0_rdlt_4 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rdlfpr1_1g_16 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr2_0_1t_1 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr2_0_1t_2 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rdlfpr1_1t_100 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
msgfpr1_1_1g_12 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rdlfpr2_0_rdlt_30 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_3t_5 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
tlfpr1_0_4g_4 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr1_1_3t_42 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_3t_6 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
tlfpr1_0_4g_5 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_1_3t_48 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_5t_17 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_5t_18 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
tymsgfpr1_1_1t_18 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_5t_19 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr1_1_3t_10 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
fcfpr1_0_5g_1 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_0_3t_12 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr2_0_03t_13 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rcfpr1_1_1t_11 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
odfpr1_1_1t_31 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rdlfpr2_0_rdlg_12 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
rtafpr1_1_1t_45 [label="",shape=circle,height=0.12,width=0.12,fontsize=1];
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
digraph G {
|
||||
graph [center rankdir=LR bgcolor="#808080"]
|
||||
edge [dir=none]
|
||||
node [width=0.3 height=0.3 label=""]
|
||||
{ node [shape=circle style=invis]
|
||||
1 2 3 4 5 6 7 8 10 20 30 40 50 60 70 80
|
||||
}
|
||||
{ node [shape=circle]
|
||||
a b c d e f g h i j k l m n o p q r s t u v w x
|
||||
}
|
||||
{ node [shape=diamond]
|
||||
A B C D E F G H I J K L M N O P Q R S T U V W X
|
||||
}
|
||||
1 -> a -> {A B} [color="#0000ff"]
|
||||
2 -> b -> {B A} [color="#ff0000"]
|
||||
3 -> c -> {C D} [color="#ffff00"]
|
||||
4 -> d -> {D C} [color="#00ff00"]
|
||||
5 -> e -> {E F} [color="#000000"]
|
||||
6 -> f -> {F E} [color="#00ffff"]
|
||||
7 -> g -> {G H} [color="#ffffff"]
|
||||
8 -> h -> {H G} [color="#ff00ff"]
|
||||
{ edge [color="#ff0000:#0000ff"]
|
||||
A -> i -> {I K}
|
||||
B -> j -> {J L}
|
||||
}
|
||||
{ edge [color="#00ff00:#ffff00"]
|
||||
C -> k -> {K I}
|
||||
D -> l -> {L J}
|
||||
}
|
||||
{ edge [color="#00ffff:#000000"]
|
||||
E -> m -> {M O}
|
||||
F -> n -> {N P}
|
||||
}
|
||||
{ edge [color="#ff00ff:#ffffff"]
|
||||
G -> o -> {O M}
|
||||
H -> p -> {P N}
|
||||
}
|
||||
{ edge [color="#00ff00:#ffff00:#ff0000:#0000ff"]
|
||||
I -> q -> {Q U}
|
||||
J -> r -> {R V}
|
||||
K -> s -> {S W}
|
||||
L -> t -> {T X}
|
||||
}
|
||||
{ edge [color="#ff00ff:#ffffff:#00ffff:#000000"]
|
||||
M -> u -> {U Q}
|
||||
N -> v -> {V R}
|
||||
O -> w -> {W S}
|
||||
P -> x -> {X T}
|
||||
}
|
||||
{ edge [color="#ff00ff:#ffffff:#00ffff:#000000:#00ff00:#ffff00:#ff0000:#0000ff"]
|
||||
Q -> 10
|
||||
R -> 20
|
||||
S -> 30
|
||||
T -> 40
|
||||
U -> 50
|
||||
V -> 60
|
||||
W -> 70
|
||||
X -> 80
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
##"I played some days with making an interface between our ConceptBase system (essentially a database system to store models) and graphviz. One example graph is attached. It is a so-called petri net for Dutch traffic lights. The example is actually taken from a book by Wil van der Aalst." Contributed by Manfred Jeusfeld.
|
||||
|
||||
##Command to produce the output: "neato -Tpng thisfile > thisfile.png"
|
||||
|
||||
digraph TrafficLights {
|
||||
node [shape=box]; gy2; yr2; rg2; gy1; yr1; rg1;
|
||||
node [shape=circle,fixedsize=true,width=0.9]; green2; yellow2; red2; safe2; safe1; green1; yellow1; red1;
|
||||
gy2->yellow2;
|
||||
rg2->green2;
|
||||
yr2->safe1;
|
||||
yr2->red2;
|
||||
safe2->rg2;
|
||||
green2->gy2;
|
||||
yellow2->yr2;
|
||||
red2->rg2;
|
||||
gy1->yellow1;
|
||||
rg1->green1;
|
||||
yr1->safe2;
|
||||
yr1->red1;
|
||||
safe1->rg1;
|
||||
green1->gy1;
|
||||
yellow1->yr1;
|
||||
red1->rg1;
|
||||
|
||||
overlap=false
|
||||
label="PetriNet Model TrafficLights\nExtracted from ConceptBase and layed out by Graphviz"
|
||||
fontsize=12;
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
graph G {
|
||||
// graph [splines=true overlap=false]
|
||||
graph [truecolor bgcolor="#ff00005f"]
|
||||
node [style=filled fillcolor="#00ff005f"]
|
||||
1 -- 30 [f=1];
|
||||
1 -- 40 [f=14];
|
||||
8 -- 46 [f=1];
|
||||
8 -- 16 [f=18];
|
||||
10 -- 25 [f=1];
|
||||
10 -- 19 [f=5];
|
||||
10 -- 33 [f=1];
|
||||
12 -- 8 [f=1];
|
||||
12 -- 36 [f=5];
|
||||
12 -- 17 [f=16];
|
||||
13 -- 38 [f=1];
|
||||
13 -- 24 [f=19];
|
||||
24 -- 49 [f=1];
|
||||
24 -- 13 [f=1];
|
||||
24 -- 47 [f=12];
|
||||
24 -- 12 [f=19];
|
||||
25 -- 27 [f=1];
|
||||
25 -- 12 [f=1];
|
||||
27 -- 12 [f=1];
|
||||
27 -- 14 [f=8];
|
||||
29 -- 10 [f=1];
|
||||
29 -- 8 [f=17];
|
||||
30 -- 24 [f=1];
|
||||
30 -- 44 [f=15];
|
||||
38 -- 29 [f=1];
|
||||
38 -- 35 [f=15];
|
||||
2 -- 42 [f=2];
|
||||
2 -- 35 [f=3];
|
||||
2 -- 11 [f=19];
|
||||
14 -- 18 [f=2];
|
||||
14 -- 24 [f=15];
|
||||
14 -- 38 [f=18];
|
||||
18 -- 49 [f=2];
|
||||
18 -- 47 [f=20];
|
||||
26 -- 41 [f=2];
|
||||
26 -- 42 [f=15];
|
||||
31 -- 39 [f=2];
|
||||
31 -- 47 [f=17];
|
||||
31 -- 25 [f=14];
|
||||
37 -- 26 [f=2];
|
||||
37 -- 16 [f=14];
|
||||
39 -- 50 [f=2];
|
||||
39 -- 14 [f=2];
|
||||
39 -- 18 [f=17];
|
||||
39 -- 47 [f=10];
|
||||
41 -- 31 [f=2];
|
||||
41 -- 8 [f=16];
|
||||
42 -- 44 [f=2];
|
||||
42 -- 29 [f=12];
|
||||
44 -- 37 [f=2];
|
||||
44 -- 32 [f=15];
|
||||
3 -- 20 [f=2];
|
||||
3 -- 28 [f=19];
|
||||
6 -- 45 [f=2];
|
||||
6 -- 28 [f=10];
|
||||
9 -- 6 [f=2];
|
||||
9 -- 16 [f=1];
|
||||
15 -- 16 [f=2];
|
||||
15 -- 48 [f=2];
|
||||
16 -- 50 [f=2];
|
||||
16 -- 32 [f=14];
|
||||
16 -- 39 [f=8];
|
||||
20 -- 33 [f=2];
|
||||
33 -- 9 [f=2];
|
||||
33 -- 46 [f=3];
|
||||
33 -- 48 [f=17];
|
||||
45 -- 15 [f=2];
|
||||
4 -- 17 [f=4];
|
||||
4 -- 15 [f=6];
|
||||
4 -- 12 [f=16];
|
||||
17 -- 21 [f=4];
|
||||
19 -- 35 [f=4];
|
||||
19 -- 15 [f=9];
|
||||
19 -- 43 [f=4];
|
||||
21 -- 19 [f=4];
|
||||
21 -- 50 [f=4];
|
||||
23 -- 36 [f=4];
|
||||
34 -- 23 [f=4];
|
||||
34 -- 24 [f=11];
|
||||
35 -- 34 [f=4];
|
||||
35 -- 16 [f=6];
|
||||
35 -- 18 [f=16];
|
||||
36 -- 46 [f=4];
|
||||
5 -- 7 [f=1];
|
||||
5 -- 36 [f=6];
|
||||
7 -- 32 [f=1];
|
||||
7 -- 11 [f=2];
|
||||
7 -- 14 [f=17];
|
||||
11 -- 40 [f=1];
|
||||
11 -- 50 [f=1];
|
||||
22 -- 46 [f=1];
|
||||
28 -- 43 [f=1];
|
||||
28 -- 8 [f=18];
|
||||
32 -- 28 [f=1];
|
||||
32 -- 39 [f=13];
|
||||
32 -- 42 [f=15];
|
||||
40 -- 22 [f=1];
|
||||
40 -- 47 [f=1];
|
||||
43 -- 11 [f=1];
|
||||
43 -- 17 [f=19];
|
||||
}
|
||||
+2212
File diff suppressed because it is too large
Load Diff
+54
@@ -0,0 +1,54 @@
|
||||
/* courtesy Ian Darwin and Geoff Collyer, Softquad Inc. */
|
||||
digraph unix {
|
||||
size="6,6";
|
||||
node [color=lightblue2, style=filled];
|
||||
"5th Edition" -> "6th Edition";
|
||||
"5th Edition" -> "PWB 1.0";
|
||||
"6th Edition" -> "LSX";
|
||||
"6th Edition" -> "1 BSD";
|
||||
"6th Edition" -> "Mini Unix";
|
||||
"6th Edition" -> "Wollongong";
|
||||
"6th Edition" -> "Interdata";
|
||||
"Interdata" -> "Unix/TS 3.0";
|
||||
"Interdata" -> "PWB 2.0";
|
||||
"Interdata" -> "7th Edition";
|
||||
"7th Edition" -> "8th Edition";
|
||||
"7th Edition" -> "32V";
|
||||
"7th Edition" -> "V7M";
|
||||
"7th Edition" -> "Ultrix-11";
|
||||
"7th Edition" -> "Xenix";
|
||||
"7th Edition" -> "UniPlus+";
|
||||
"V7M" -> "Ultrix-11";
|
||||
"8th Edition" -> "9th Edition";
|
||||
"1 BSD" -> "2 BSD";
|
||||
"2 BSD" -> "2.8 BSD";
|
||||
"2.8 BSD" -> "Ultrix-11";
|
||||
"2.8 BSD" -> "2.9 BSD";
|
||||
"32V" -> "3 BSD";
|
||||
"3 BSD" -> "4 BSD";
|
||||
"4 BSD" -> "4.1 BSD";
|
||||
"4.1 BSD" -> "4.2 BSD";
|
||||
"4.1 BSD" -> "2.8 BSD";
|
||||
"4.1 BSD" -> "8th Edition";
|
||||
"4.2 BSD" -> "4.3 BSD";
|
||||
"4.2 BSD" -> "Ultrix-32";
|
||||
"PWB 1.0" -> "PWB 1.2";
|
||||
"PWB 1.0" -> "USG 1.0";
|
||||
"PWB 1.2" -> "PWB 2.0";
|
||||
"USG 1.0" -> "CB Unix 1";
|
||||
"USG 1.0" -> "USG 2.0";
|
||||
"CB Unix 1" -> "CB Unix 2";
|
||||
"CB Unix 2" -> "CB Unix 3";
|
||||
"CB Unix 3" -> "Unix/TS++";
|
||||
"CB Unix 3" -> "PDP-11 Sys V";
|
||||
"USG 2.0" -> "USG 3.0";
|
||||
"USG 3.0" -> "Unix/TS 3.0";
|
||||
"PWB 2.0" -> "Unix/TS 3.0";
|
||||
"Unix/TS 1.0" -> "Unix/TS 3.0";
|
||||
"Unix/TS 3.0" -> "TS 4.0";
|
||||
"Unix/TS++" -> "TS 4.0";
|
||||
"CB Unix 3" -> "TS 4.0";
|
||||
"TS 4.0" -> "System V.0";
|
||||
"System V.0" -> "System V.2";
|
||||
"System V.2" -> "System V.3";
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
digraph world {
|
||||
size="7,7";
|
||||
{rank=same; S8 S24 S1 S35 S30;}
|
||||
{rank=same; T8 T24 T1 T35 T30;}
|
||||
{rank=same; 43 37 36 10 2;}
|
||||
{rank=same; 25 9 38 40 13 17 12 18;}
|
||||
{rank=same; 26 42 11 3 33 19 39 14 16;}
|
||||
{rank=same; 4 31 34 21 41 28 20;}
|
||||
{rank=same; 27 5 22 32 29 15;}
|
||||
{rank=same; 6 23;}
|
||||
{rank=same; 7;}
|
||||
|
||||
S8 -> 9;
|
||||
S24 -> 25;
|
||||
S24 -> 27;
|
||||
S1 -> 2;
|
||||
S1 -> 10;
|
||||
S35 -> 43;
|
||||
S35 -> 36;
|
||||
S30 -> 31;
|
||||
S30 -> 33;
|
||||
9 -> 42;
|
||||
9 -> T1;
|
||||
25 -> T1;
|
||||
25 -> 26;
|
||||
27 -> T24;
|
||||
2 -> {3 ; 16 ; 17 ; T1 ; 18}
|
||||
10 -> { 11 ; 14 ; T1 ; 13; 12;}
|
||||
31 -> T1;
|
||||
31 -> 32;
|
||||
33 -> T30;
|
||||
33 -> 34;
|
||||
42 -> 4;
|
||||
26 -> 4;
|
||||
3 -> 4;
|
||||
16 -> 15;
|
||||
17 -> 19;
|
||||
18 -> 29;
|
||||
11 -> 4;
|
||||
14 -> 15;
|
||||
37 -> {39 ; 41 ; 38 ; 40;}
|
||||
13 -> 19;
|
||||
12 -> 29;
|
||||
43 -> 38;
|
||||
43 -> 40;
|
||||
36 -> 19;
|
||||
32 -> 23;
|
||||
34 -> 29;
|
||||
39 -> 15;
|
||||
41 -> 29;
|
||||
38 -> 4;
|
||||
40 -> 19;
|
||||
4 -> 5;
|
||||
19 -> {21 ; 20 ; 28;}
|
||||
5 -> {6 ; T35 ; 23;}
|
||||
21 -> 22;
|
||||
20 -> 15;
|
||||
28 -> 29;
|
||||
6 -> 7;
|
||||
15 -> T1;
|
||||
22 -> T35;
|
||||
22 -> 23;
|
||||
29 -> T30;
|
||||
7 -> T8;
|
||||
23 -> T24;
|
||||
23 -> T1;
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package token
|
||||
|
||||
var DOTTokens = NewMapFromStrings([]string{
|
||||
"ε",
|
||||
"id",
|
||||
"{",
|
||||
"}",
|
||||
";",
|
||||
"=",
|
||||
"[",
|
||||
"]",
|
||||
",",
|
||||
":",
|
||||
"->",
|
||||
"--",
|
||||
"graph",
|
||||
"Graph",
|
||||
"GRAPH",
|
||||
"strict",
|
||||
"Strict",
|
||||
"STRICT",
|
||||
"digraph",
|
||||
"Digraph",
|
||||
"DiGraph",
|
||||
"DIGRAPH",
|
||||
"node",
|
||||
"Node",
|
||||
"NODE",
|
||||
"edge",
|
||||
"Edge",
|
||||
"EDGE",
|
||||
"subgraph",
|
||||
"Subgraph",
|
||||
"SubGraph",
|
||||
"SUBGRAPH",
|
||||
"string_lit",
|
||||
"int_lit",
|
||||
"float_lit",
|
||||
"html_lit",
|
||||
})
|
||||
+242
@@ -0,0 +1,242 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package token
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
Type Type
|
||||
Lit []byte
|
||||
}
|
||||
|
||||
func NewToken(typ Type, lit []byte) *Token {
|
||||
return &Token{typ, lit}
|
||||
}
|
||||
|
||||
func (this *Token) Equals(that *Token) bool {
|
||||
if this == nil || that == nil {
|
||||
return this == that
|
||||
}
|
||||
|
||||
if this.Type != that.Type {
|
||||
return false
|
||||
}
|
||||
|
||||
return bytes.Equal(this.Lit, that.Lit)
|
||||
}
|
||||
|
||||
func (this *Token) String() string {
|
||||
str := ""
|
||||
if this.Type == EOF {
|
||||
str += "\"$\""
|
||||
} else {
|
||||
str += "\"" + string(this.Lit) + "\""
|
||||
}
|
||||
str += "(" + strconv.Itoa(int(this.Type)) + ")"
|
||||
return str
|
||||
}
|
||||
|
||||
type Type int
|
||||
|
||||
const (
|
||||
ILLEGAL Type = iota - 1
|
||||
EOF
|
||||
)
|
||||
|
||||
func (T Type) String() string {
|
||||
return strconv.Itoa(int(T))
|
||||
}
|
||||
|
||||
// Position describes an arbitrary source position
|
||||
// including the file, line, and column location.
|
||||
// A Position is valid if the line number is > 0.
|
||||
//
|
||||
type Position struct {
|
||||
Offset int // offset, starting at 0
|
||||
Line int // line number, starting at 1
|
||||
Column int // column number, starting at 1 (character count)
|
||||
}
|
||||
|
||||
// IsValid returns true if the position is valid.
|
||||
func (pos *Position) IsValid() bool { return pos.Line > 0 }
|
||||
|
||||
// String returns a string in one of several forms:
|
||||
//
|
||||
// file:line:column valid position with file name
|
||||
// line:column valid position without file name
|
||||
// file invalid position with file name
|
||||
// - invalid position without file name
|
||||
//
|
||||
func (pos Position) String() string {
|
||||
s := ""
|
||||
if pos.IsValid() {
|
||||
s += fmt.Sprintf("%d:%d", pos.Line, pos.Column)
|
||||
}
|
||||
if s == "" {
|
||||
s = "-"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (T *Token) IntValue() (int64, error) {
|
||||
return strconv.ParseInt(string(T.Lit), 10, 64)
|
||||
}
|
||||
|
||||
func (T *Token) UintValue() (uint64, error) {
|
||||
return strconv.ParseUint(string(T.Lit), 10, 64)
|
||||
}
|
||||
|
||||
func (T *Token) SDTVal() string {
|
||||
sdt := string(T.Lit)
|
||||
rex, err := regexp.Compile("\\$[0-9]+")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
idx := rex.FindAllStringIndex(sdt, -1)
|
||||
res := ""
|
||||
if len(idx) <= 0 {
|
||||
res = sdt
|
||||
} else {
|
||||
for i, loc := range idx {
|
||||
if loc[0] > 0 {
|
||||
if i > 0 {
|
||||
res += sdt[idx[i-1][1]:loc[0]]
|
||||
} else {
|
||||
res += sdt[0:loc[0]]
|
||||
}
|
||||
}
|
||||
res += "X["
|
||||
res += sdt[loc[0]+1 : loc[1]]
|
||||
res += "]"
|
||||
}
|
||||
if idx[len(idx)-1][1] < len(sdt) {
|
||||
res += sdt[idx[len(idx)-1][1]:]
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(res[2 : len(res)-2])
|
||||
}
|
||||
|
||||
//*********** Tokenmap
|
||||
|
||||
type TokenMap struct {
|
||||
tokenMap []string
|
||||
stringMap map[string]Type
|
||||
}
|
||||
|
||||
func NewMap() *TokenMap {
|
||||
tm := &TokenMap{make([]string, 0, 10), make(map[string]Type)}
|
||||
tm.AddToken("$")
|
||||
tm.AddToken("ε")
|
||||
return tm
|
||||
}
|
||||
|
||||
func (this *TokenMap) AddToken(str string) {
|
||||
if _, exists := this.stringMap[str]; exists {
|
||||
return
|
||||
}
|
||||
this.stringMap[str] = Type(len(this.tokenMap))
|
||||
this.tokenMap = append(this.tokenMap, str)
|
||||
}
|
||||
|
||||
func NewMapFromFile(file string) (*TokenMap, error) {
|
||||
src, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewMapFromString(string(src)), nil
|
||||
}
|
||||
|
||||
func NewMapFromStrings(input []string) *TokenMap {
|
||||
tm := NewMap()
|
||||
for _, s := range input {
|
||||
tm.AddToken(s)
|
||||
}
|
||||
return tm
|
||||
}
|
||||
|
||||
func NewMapFromString(input string) *TokenMap {
|
||||
tokens := strings.Fields(input)
|
||||
return NewMapFromStrings(tokens)
|
||||
}
|
||||
|
||||
func (this *TokenMap) Type(key string) Type {
|
||||
tok, ok := this.stringMap[key]
|
||||
if !ok {
|
||||
return ILLEGAL
|
||||
}
|
||||
return tok
|
||||
}
|
||||
|
||||
func (this *TokenMap) TokenString(typ Type) string {
|
||||
tok := int(typ)
|
||||
if tok < 0 || tok >= len(this.tokenMap) {
|
||||
return "illegal " + strconv.Itoa(tok)
|
||||
}
|
||||
return this.tokenMap[tok]
|
||||
}
|
||||
|
||||
func (this *TokenMap) String() string {
|
||||
res := ""
|
||||
for str, tok := range this.stringMap {
|
||||
res += str + " : " + strconv.Itoa(int(tok)) + "\n"
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (this *TokenMap) Strings() []string {
|
||||
return this.tokenMap[1:]
|
||||
}
|
||||
|
||||
func (this *TokenMap) Equals(that *TokenMap) bool {
|
||||
if this == nil || that == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(this.stringMap) != len(that.stringMap) ||
|
||||
len(this.tokenMap) != len(that.tokenMap) {
|
||||
return false
|
||||
}
|
||||
|
||||
for str, tok := range this.stringMap {
|
||||
if tok1, ok := that.stringMap[str]; !ok || tok1 != tok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (this *TokenMap) Tokens() []*Token {
|
||||
res := make([]*Token, 0, len(this.stringMap))
|
||||
for typ, str := range this.tokenMap {
|
||||
res = append(res, &Token{Type(typ), []byte(str)})
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (this *TokenMap) WriteFile(file string) error {
|
||||
out := ""
|
||||
for i := 1; i < len(this.tokenMap); i++ {
|
||||
out += this.TokenString(Type(i)) + "\n"
|
||||
}
|
||||
return ioutil.WriteFile(file, []byte(out), 0644)
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
//Copyright 2013 GoGraphviz Authors
|
||||
//
|
||||
//Licensed under the Apache License, Version 2.0 (the "License");
|
||||
//you may not use this file except in compliance with the License.
|
||||
//You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//Unless required by applicable law or agreed to in writing, software
|
||||
//distributed under the License is distributed on an "AS IS" BASIS,
|
||||
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
//See the License for the specific language governing permissions and
|
||||
//limitations under the License.
|
||||
|
||||
package gographviz
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/awalterschulze/gographviz/ast"
|
||||
)
|
||||
|
||||
type writer struct {
|
||||
*Graph
|
||||
writtenLocations map[string]bool
|
||||
}
|
||||
|
||||
func newWriter(g *Graph) *writer {
|
||||
return &writer{g, make(map[string]bool)}
|
||||
}
|
||||
|
||||
func appendAttrs(list ast.StmtList, attrs Attrs) ast.StmtList {
|
||||
for _, name := range attrs.SortedNames() {
|
||||
stmt := &ast.Attr{
|
||||
Field: ast.Id(name),
|
||||
Value: ast.Id(attrs[name]),
|
||||
}
|
||||
list = append(list, stmt)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (this *writer) newSubGraph(name string) *ast.SubGraph {
|
||||
sub := this.SubGraphs.SubGraphs[name]
|
||||
this.writtenLocations[sub.Name] = true
|
||||
s := &ast.SubGraph{}
|
||||
s.Id = ast.Id(sub.Name)
|
||||
s.StmtList = appendAttrs(s.StmtList, sub.Attrs)
|
||||
children := this.Relations.SortedChildren(name)
|
||||
for _, child := range children {
|
||||
s.StmtList = append(s.StmtList, this.newNodeStmt(child))
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (this *writer) newNodeId(name string, port string) *ast.NodeId {
|
||||
node := this.Nodes.Lookup[name]
|
||||
return ast.MakeNodeId(node.Name, port)
|
||||
}
|
||||
|
||||
func (this *writer) newNodeStmt(name string) *ast.NodeStmt {
|
||||
node := this.Nodes.Lookup[name]
|
||||
id := ast.MakeNodeId(node.Name, "")
|
||||
this.writtenLocations[node.Name] = true
|
||||
return &ast.NodeStmt{
|
||||
id,
|
||||
ast.PutMap(node.Attrs),
|
||||
}
|
||||
}
|
||||
|
||||
func (this *writer) newLocation(name string, port string) ast.Location {
|
||||
if this.IsNode(name) {
|
||||
return this.newNodeId(name, port)
|
||||
} else if this.IsSubGraph(name) {
|
||||
if len(port) != 0 {
|
||||
panic(fmt.Sprintf("subgraph cannot have a port: %v", port))
|
||||
}
|
||||
return this.newSubGraph(name)
|
||||
}
|
||||
panic(fmt.Sprintf("%v is not a node or a subgraph", name))
|
||||
}
|
||||
|
||||
func (this *writer) newEdgeStmt(edge *Edge) *ast.EdgeStmt {
|
||||
src := this.newLocation(edge.Src, edge.SrcPort)
|
||||
dst := this.newLocation(edge.Dst, edge.DstPort)
|
||||
stmt := &ast.EdgeStmt{
|
||||
Source: src,
|
||||
EdgeRHS: ast.EdgeRHS{
|
||||
&ast.EdgeRH{
|
||||
ast.EdgeOp(edge.Dir),
|
||||
dst,
|
||||
},
|
||||
},
|
||||
Attrs: ast.PutMap(edge.Attrs),
|
||||
}
|
||||
return stmt
|
||||
}
|
||||
|
||||
func (this *writer) Write() *ast.Graph {
|
||||
t := &ast.Graph{}
|
||||
t.Strict = this.Strict
|
||||
t.Type = ast.GraphType(this.Directed)
|
||||
t.Id = ast.Id(this.Name)
|
||||
|
||||
t.StmtList = appendAttrs(t.StmtList, this.Attrs)
|
||||
|
||||
for _, edge := range this.Edges.Edges {
|
||||
t.StmtList = append(t.StmtList, this.newEdgeStmt(edge))
|
||||
}
|
||||
|
||||
subGraphs := this.SubGraphs.Sorted()
|
||||
for _, s := range subGraphs {
|
||||
if _, ok := this.writtenLocations[s.Name]; !ok {
|
||||
t.StmtList = append(t.StmtList, this.newSubGraph(s.Name))
|
||||
}
|
||||
}
|
||||
|
||||
nodes := this.Nodes.Sorted()
|
||||
for _, n := range nodes {
|
||||
if _, ok := this.writtenLocations[n.Name]; !ok {
|
||||
t.StmtList = append(t.StmtList, this.newNodeStmt(n.Name))
|
||||
}
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
|
||||
//Creates an Abstract Syntrax Tree from the Graph.
|
||||
func (g *Graph) WriteAst() *ast.Graph {
|
||||
w := newWriter(g)
|
||||
return w.Write()
|
||||
}
|
||||
|
||||
//Returns a DOT string representing the Graph.
|
||||
func (g *Graph) String() string {
|
||||
return g.WriteAst().String()
|
||||
}
|
||||
Reference in New Issue
Block a user