diff --git a/gap/dot.gd b/gap/dot.gd index 0296444..a684bdb 100644 --- a/gap/dot.gd +++ b/gap/dot.gd @@ -52,17 +52,18 @@ #! https://graphviz.readthedocs.io/en/stable/examples.html or #! https://graphviz.org/Gallery/directed/fsm.html. #! -#! @BeginLogSession +#! @BeginExampleSession #! gap> LoadPackage("GraphvizForGAP");; #! gap> f := GraphvizDigraph("finite_state_machine"); #! -#! gap> GraphvizSetAttr(f, "rankdir=LR"); +#! gap> GraphvizSetAttr(f, "rankdir", "LR"); #! -#! gap> GraphvizSetAttr(f, "size=\"8,5\""); +#! gap> GraphvizSetAttr(f, "size", "8,5"); #! #! gap> terminals := GraphvizAddContext(f, "terminals"); #! -#! gap> GraphvizSetAttr(terminals, "node [shape=doublecircle]"); +#! gap> GraphvizSetAttrs(terminals, rec(node := rec(shape := +#! > "doublecircle"))); #! #! gap> GraphvizAddNode(terminals, "LR_0"); #! @@ -74,7 +75,7 @@ #! #! gap> nodes := GraphvizAddContext(f, "nodes"); #! -#! gap> GraphvizSetAttr(nodes, "node [shape=circle]"); +#! gap> GraphvizSetAttrs(nodes, rec(node := rec(shape := "circle"))); #! #! gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_0", "LR_2"), "label", "\"SS(B)\""); #! @@ -107,19 +108,18 @@ #! gap> Print(AsString(f), "\n"); #! //dot #! digraph finite_state_machine { -#! rankdir=LR size="8,5" -#! // terminals context -#! { -#! node [shape=doublecircle] +#! graph [rankdir="LR", size="8,5"] +#! { +#! // context "terminals" +#! node [shape="doublecircle"] #! LR_0 #! LR_3 #! LR_4 #! LR_8 -#! rankdir=LR size="8,5" -#! } -#! // nodes context -#! { -#! node [shape=circle] +#! } +#! { +#! // context "nodes" +#! node [shape="circle"] #! LR_2 #! LR_0 -> LR_2 [label="SS(B)"] #! LR_1 @@ -139,12 +139,10 @@ #! LR_7 -> LR_5 [label="S(a)"] #! LR_8 -> LR_6 [label="S(b)"] #! LR_8 -> LR_5 [label="S(a)"] -#! rankdir=LR size="8,5" -#! } -#! +#! } #! } -#! gap> Splash(f); -#! @EndLogSession +#! gap> # Splash(f); +#! @EndExampleSession #! #! Provided that you have &Graphviz; installed on your computer, the last line #! of the example Splash(f) would produce the following picture: @@ -370,15 +368,24 @@ DeclareOperation("GraphvizNodes", [IsGraphvizGraphDigraphOrContext]); #! @EndExampleSession DeclareOperation("GraphvizNode", [IsGraphvizGraphDigraphOrContext, IsObject]); -#! @Arguments graph +#! @Arguments gv #! @Returns the number of nodes in a &GAPGraphviz; object. #! #! @Description This operations returns the number of nodes in the -#! &GAPGraphviz; graph, digraph, or context graph. +#! &GAPGraphviz; graph, digraph, or context gv. #! @BeginExampleSession #! @EndExampleSession DeclareOperation("GraphvizNumberOfNodes", [IsGraphvizGraphDigraphOrContext]); +#! @Arguments gv +#! @Returns the number of nodes in a &GAPGraphviz; object. +#! +#! @Description This operations returns the number of nodes in the +#! &GAPGraphviz; graph, digraph, or context gv. +#! @BeginExampleSession +#! @EndExampleSession +DeclareOperation("GraphvizNumberOfEdges", [IsGraphvizGraphDigraphOrContext]); + #! @Arguments graph #! @Returns a print record of &GAPGraphviz; graphs #! @Description gets the subgraphs of a provided graphviz graph. diff --git a/gap/dot.gi b/gap/dot.gi index d5305bd..5ec07ad 100644 --- a/gap/dot.gi +++ b/gap/dot.gi @@ -8,23 +8,23 @@ ############################################################################# ## +# TODO +# 2. ensure arg names are consistent (using gv, name, e, n, key, val, head, +# tail) +# 3. Use GraphvizStringify which for a string does nothing (no copy); for an +# object calls String; for a IsGraphvizObject returns its name; + ############################################################################# # Constructors ############################################################################# -InstallMethod(GraphvizGraph, "for a string", [IsString], -name -> GV_GraphOrDigraph(name, IsGraphvizGraph)); - InstallMethod(GraphvizGraph, "for an object", [IsObject], -obj -> GraphvizGraph(String(obj))); +obj -> GV_GraphOrDigraph(String(obj), IsGraphvizGraph)); InstallMethod(GraphvizGraph, "for no args", [], {} -> GraphvizGraph("")); -InstallMethod(GraphvizDigraph, "for a string", [IsString], -name -> GV_GraphOrDigraph(name, IsGraphvizDigraph)); - InstallMethod(GraphvizDigraph, "for an object", [IsObject], -obj -> GraphvizDigraph(String(obj))); +obj -> GV_GraphOrDigraph(String(obj), IsGraphvizDigraph)); InstallMethod(GraphvizDigraph, "for no args", [], {} -> GraphvizDigraph("")); @@ -40,22 +40,16 @@ e -> StringFormatted("", GraphvizName(e))); InstallMethod(PrintString, "for a graphviz (di)graph or context", [IsGraphvizGraphDigraphOrContext], -function(g) +function(gv) local result, edges, nodes, kind; result := ""; - edges := 0; - nodes := 0; - - GV_GraphSearchChildren(g, function(s) - nodes := nodes + Length(RecNames(GraphvizNodes(s))); - edges := edges + Length(GraphvizEdges(s)); - return false; - end); + edges := GraphvizNumberOfEdges(gv); + nodes := GraphvizNumberOfNodes(gv); - if IsGraphvizContext(g) then + if IsGraphvizContext(gv) then kind := "context"; - elif IsGraphvizDigraph(g) then + elif IsGraphvizDigraph(gv) then kind := "digraph"; else kind := "graph"; @@ -63,8 +57,8 @@ function(g) Append(result, StringFormatted(" "" then - Append(result, StringFormatted("\"{}\" ", GraphvizName(g))); + if GraphvizName(gv) <> "" then + Append(result, StringFormatted("\"{}\" ", GraphvizName(gv))); fi; Append(result, StringFormatted("with {} ", Pluralize(nodes, "node"))); @@ -79,32 +73,55 @@ end); ############################################################################# InstallMethod(GraphvizName, "for a graphviz object", [IsGraphvizObject], -x -> x!.Name); +gv -> gv!.Name); InstallMethod(GraphvizAttrs, "for a graphviz object", [IsGraphvizObject], -x -> x!.Attrs); +gv -> gv!.Attrs); InstallMethod(GraphvizNodes, "for a graphviz (di)graph or context", -[IsGraphvizGraphDigraphOrContext], x -> x!.Nodes); +[IsGraphvizGraphDigraphOrContext], gv -> gv!.Nodes); InstallMethod(GraphvizNode, "for a graphviz (di)graph or context and object", [IsGraphvizGraphDigraphOrContext, IsObject], -function(gv, val) - local graph; - val := String(val); - graph := GV_FindGraphWithNode(gv, val); - if graph = fail then +function(gv, obj) + local name, subgraph; + + name := String(obj); + subgraph := GV_FindGraphWithNode(gv, name); + if subgraph = fail then return fail; fi; - return GraphvizNodes(graph).(val); + return GraphvizNodes(subgraph).(name); end); -# FIXME the below only counts nodes in the root graph, not in its children InstallMethod(GraphvizNumberOfNodes, "for a graphviz (di)graph or context", -[IsGraphvizGraphDigraphOrContext], x -> Length(RecNames(GraphvizNodes(x)))); +[IsGraphvizGraphDigraphOrContext], +function(gv) + local nodes; + + nodes := 0; + GV_GraphSearchChildren(gv, function(subgraph) + nodes := nodes + Length(RecNames(GraphvizNodes(subgraph))); + return false; + end); + return nodes; +end); + +InstallMethod(GraphvizNumberOfEdges, "for a graphviz (di)graph or context", +[IsGraphvizGraphDigraphOrContext], +function(gv) + local edges; + + edges := 0; + GV_GraphSearchChildren(gv, function(subgraph) + edges := edges + Length(GraphvizEdges(subgraph)); + return false; + end); + return edges; +end); InstallMethod(GraphvizEdges, "for a graphviz (di)graph or context", -[IsGraphvizGraphDigraphOrContext], x -> x!.Edges); +[IsGraphvizGraphDigraphOrContext], gv -> gv!.Edges); InstallMethod(GraphvizEdges, "for a graphviz (di)graph or context, object, and object", @@ -132,52 +149,41 @@ function(gv, head, tail) end); InstallMethod(GraphvizSubgraphs, "for a graphviz (di)graph or context", -[IsGraphvizGraphDigraphOrContext], x -> x!.Subgraphs); +[IsGraphvizGraphDigraphOrContext], gv -> gv!.Subgraphs); InstallMethod(GraphvizContexts, "for a graphviz (di)graph or context", -[IsGraphvizGraphDigraphOrContext], x -> x!.Contexts); +[IsGraphvizGraphDigraphOrContext], gv -> gv!.Contexts); InstallMethod(GraphvizTail, "for a graphviz edge", [IsGraphvizEdge], -x -> x!.Tail); +e -> e!.Tail); InstallMethod(GraphvizHead, "for a graphviz edge", [IsGraphvizEdge], -x -> x!.Head); - -# Operators for nodes +e -> e!.Head); -InstallMethod(\=, "for graphviz nodes", -[IsGraphvizNode, IsGraphvizNode], IsIdenticalObj); +# Operators for graphviz objects -InstallMethod(\=, "for graphviz edges", -[IsGraphvizEdge, IsGraphvizEdge], IsIdenticalObj); +InstallMethod(\=, "for graphviz objects", +[IsGraphvizObject, IsGraphvizObject], IsIdenticalObj); # Accessor for graphs and digraphs InstallMethod(GraphvizFindSubgraphRecursive, -"for a graphviz (di)graph or context and a string", -[IsGraphvizGraphDigraphOrContext, IsString], -{g, s} -> GV_GraphTreeSearch(g, v -> GraphvizName(v) = s and - not IsGraphvizContext(v))); - -InstallMethod(GraphvizFindSubgraphRecursive, -"for a graphviz (di)graph or context and a string", +"for a graphviz (di)graph or context and an object", [IsGraphvizGraphDigraphOrContext, IsObject], -{g, o} -> GraphvizFindSubgraphRecursive(g, String(o))); +{gv, obj} -> GV_GraphTreeSearch(gv, subgraph -> GraphvizName(subgraph) = String(obj) + and not IsGraphvizContext(subgraph))); ############################################################################# # GraphvizSetName ############################################################################# -InstallMethod(GraphvizSetName, "for a graphviz (di)graph or context and string", -[IsGraphvizGraphDigraphOrContext, IsString], -function(x, name) - x!.Name := name; - return x; -end); - -InstallMethod(GraphvizSetName, "for a graphviz (di)graph or context and string", +InstallMethod(GraphvizSetName, +"for a graphviz (di)graph or context and object", [IsGraphvizGraphDigraphOrContext, IsObject], -{g, o} -> GraphvizSetName(g, String(o))); +function(gv, name) + gv!.Name := String(name); + return gv; +end); ############################################################################# # GraphvizSetAttr(s) @@ -283,16 +289,16 @@ end); InstallMethod(GraphvizAddNode, "for a graphviz (di)graph or context and object", [IsGraphvizGraphDigraphOrContext, IsObject], -function(gv, val) - local node; - val := String(val); +function(gv, obj) + local node, name; + name := String(obj); - if Length(val) = 0 then - ErrorNoReturn("the argument (the name of the node being added) cannot be empty"); + if Length(name) = 0 then + ErrorNoReturn("the value returned by `String(obj)` must not be empty"); fi; # Reuse existing node if available - node := GraphvizNode(gv, val); + node := GraphvizNode(gv, name); if node <> fail then if not IsIdenticalObj(GV_GetParent(node), gv) then ErrorFormatted("The 2nd argument \"{}\" is the name of a node in a ", @@ -301,24 +307,24 @@ function(gv, val) "(a graphviz (di)graph / context) named \"{}\" (", "adding the node \"{}\" to the 1st argument would ", "silently change the enclosing context of \"{}\")", - val, + name, GraphvizName(GV_GetParent(node)), GraphvizName(gv), - val, + name, GraphvizName(gv), - val); + name); fi; return node; fi; - node := GV_Node(gv, val); + node := GV_Node(gv, name); GV_AddNode(gv, node); return node; end); InstallMethod(GraphvizAddNode, "for a graphviz (di)graph or context and string", [IsGraphvizGraphDigraphOrContext, IsGraphvizNode], -function(gv, name) # gaplint: disable=unused-func-args +function(gv, n) # gaplint: disable=unused-func-args ErrorNoReturn("it is not currently possible to add Graphviz node ", "objects directly to Graphviz graphs or digraphs, use ", "the node's name instead"); @@ -331,10 +337,10 @@ end); InstallMethod(GraphvizAddEdge, "for a graphviz (di)graph or context and two graphviz nodes", [IsGraphvizGraphDigraphOrContext, IsGraphvizNode, IsGraphvizNode], -function(x, head, tail) +function(gv, head, tail) local edge, root_x, root_head, root_tail; - root_x := GV_GetRoot(x); + root_x := GV_GetRoot(gv); root_head := GV_GetRoot(head); root_tail := GV_GetRoot(tail); if not IsIdenticalObj(root_x, root_head) then @@ -343,7 +349,7 @@ function(x, head, tail) "(adding the edge would silently change the enclosing ", "context of \"{}\")", GraphvizName(head), - GraphvizName(x), + GraphvizName(gv), GraphvizName(head)); fi; if not IsIdenticalObj(root_x, root_tail) then @@ -352,45 +358,21 @@ function(x, head, tail) "(adding the edge would silently change the enclosing ", "context of \"{}\")", GraphvizName(tail), - GraphvizName(x), + GraphvizName(gv), GraphvizName(tail)); fi; - edge := GV_Edge(x, head, tail); - GV_AddEdge(x, edge); + edge := GV_Edge(gv, head, tail); + GV_AddEdge(gv, edge); return edge; end); -InstallMethod(GraphvizAddEdge, -"for a graphviz (di)graph or context and two strings", -[IsGraphvizGraphDigraphOrContext, IsString, IsString], -function(x, head, tail) - local head_node, tail_node; - - head_node := GraphvizNode(x, head); - if head_node = fail then - Info(InfoGraphviz, 3, "the head of the edge \"", head, - "\" is not an existing node, adding node \"", head, "\""); - head_node := GV_Node(x, head); - GV_AddNode(x, head_node); - fi; - - tail_node := GraphvizNode(x, tail); - if tail_node = fail then - Info(InfoGraphviz, 3, "the tail of the edge \"", tail, - "\" is not an existing node, adding node \"", tail, "\""); - tail_node := GV_Node(x, tail); - GV_AddNode(x, tail_node); - fi; - - return GraphvizAddEdge(x, head_node, tail_node); -end); - InstallMethod(GraphvizAddEdge, "for a graphviz (di)graph or context and two objects", [IsGraphvizGraphDigraphOrContext, IsObject, IsObject], function(gv, head, tail) - local head_name, tail_name; + local head_name, tail_name, head_node, tail_node; + if IsGraphvizNode(head) then head_name := GraphvizName(head); else @@ -401,7 +383,24 @@ function(gv, head, tail) else tail_name := String(tail); fi; - return GraphvizAddEdge(gv, head_name, tail_name); + + head_node := GraphvizNode(gv, head_name); + if head_node = fail then + Info(InfoGraphviz, 3, "the head of the edge \"", head_name, + "\" is not an existing node, adding node \"", head_name, "\""); + head_node := GV_Node(gv, head_name); + GV_AddNode(gv, head_node); + fi; + + tail_node := GraphvizNode(gv, tail_name); + if tail_node = fail then + Info(InfoGraphviz, 3, "the tail of the edge \"", tail_name, + "\" is not an existing node, adding node \"", tail_name, "\""); + tail_node := GV_Node(gv, tail_name); + GV_AddNode(gv, tail_node); + fi; + + return GraphvizAddEdge(gv, head_node, tail_node); end); ############################################################################# @@ -409,10 +408,12 @@ end); ############################################################################# InstallMethod(GraphvizAddSubgraph, -"for a graphviz (di)graph or context and string", -[IsGraphvizGraphDigraphOrContext, IsString], -function(gv, name) - local subgraphs, subgraph; +"for a graphviz (di)graph or context and object", +[IsGraphvizGraphDigraphOrContext, IsObject], +function(gv, obj) + local subgraphs, subgraph, name; + + name := String(obj); subgraphs := GraphvizSubgraphs(gv); if IsBound(subgraphs.(name)) then @@ -426,58 +427,47 @@ function(gv, name) return subgraph; end); -InstallMethod(GraphvizAddSubgraph, -"for a graphviz (di)graph or context and an object", -[IsGraphvizGraphDigraphOrContext, IsObject], -{g, o} -> GraphvizAddSubgraph(g, String(o))); - InstallMethod(GraphvizAddSubgraph, "for a graphviz (di)graph or context", [IsGraphvizGraphDigraphOrContext], -graph -> GraphvizAddSubgraph(graph, - StringFormatted("no_name_{}", - GV_GetCounter(graph)))); +gv -> GraphvizAddSubgraph(gv, StringFormatted("no_name_{}", GV_GetCounter(gv)))); InstallMethod(GraphvizAddContext, -"for a graphviz (di)graph or context and a string", -[IsGraphvizGraphDigraphOrContext, IsString], -function(graph, name) - local contexts, ctx; +"for a graphviz (di)graph or context and object", +[IsGraphvizGraphDigraphOrContext, IsObject], +function(gv, obj) + local contexts, ctx, name; - contexts := GraphvizContexts(graph); + name := String(obj); + contexts := GraphvizContexts(gv); if IsBound(contexts.(name)) then ErrorFormatted("the 1st argument (a graphviz (di)graph/context) ", "already has a context with name \"{}\"", name); fi; - ctx := GV_Context(graph, name); + ctx := GV_Context(gv, name); contexts.(name) := ctx; return ctx; end); -InstallMethod(GraphvizAddContext, -"for a graphviz (di)graph or context", +InstallMethod(GraphvizAddContext, "for a graphviz (di)graph or context", [IsGraphvizGraphDigraphOrContext], -g -> GraphvizAddContext(g, StringFormatted("no_name_{}", GV_GetCounter(g)))); - -InstallMethod(GraphvizAddContext, -"for a graphviz (di)graph or context and an object", -[IsGraphvizGraphDigraphOrContext, IsObject], -{g, o} -> GraphvizAddContext(g, String(o))); +gv -> GraphvizAddContext(gv, StringFormatted("no_name_{}", GV_GetCounter(gv)))); # Removal InstallMethod(GraphvizRemoveNode, "for a graphviz (di)graph or context and node", [IsGraphvizGraphDigraphOrContext, IsGraphvizNode], -{g, node} -> GraphvizRemoveNode(g, GraphvizName(node))); +{gv, n} -> GraphvizRemoveNode(gv, GraphvizName(n))); InstallMethod(GraphvizRemoveNode, "for a graphviz (di)graph or context and a string", -[IsGraphvizGraphDigraphOrContext, IsString], -function(g, name) - local nodes; - nodes := GraphvizNodes(g); +[IsGraphvizGraphDigraphOrContext, IsObject], +function(gv, obj) + local nodes, name; + name := String(obj); + nodes := GraphvizNodes(gv); if IsBound(nodes.(name)) then Unbind(nodes.(name)); else @@ -488,7 +478,7 @@ function(g, name) fi; # remove incident edges - GraphvizFilterEdges(g, + GraphvizFilterEdges(gv, function(e) local head, tail; head := GraphvizHead(e); @@ -496,42 +486,41 @@ function(g, name) return name <> GraphvizName(tail) and name <> GraphvizName(head); end); - return g; + return gv; end); -InstallMethod(GraphvizRemoveNode, -"for a graphviz (di)graph or context and a string", -[IsGraphvizGraphDigraphOrContext, IsObject], -{g, o} -> GraphvizRemoveNode(g, String(o))); - InstallMethod(GraphvizFilterEdges, "for a graphviz (di)graph or context and edge filter", [IsGraphvizGraphDigraphOrContext, IsFunction], -function(g, filter) +function(gv, func) local edge, idx, edges; + # TODO check func has correct number of args - edges := GraphvizEdges(g); + edges := GraphvizEdges(gv); idx := Length(edges); while idx > 0 do edge := edges[idx]; - if not filter(edge) then + if not func(edge) then Remove(edges, idx); fi; idx := idx - 1; od; - return g; + return gv; end); InstallMethod(GraphvizRemoveEdges, -"for a graphviz (di)graph or context, string, and string", -[IsGraphvizGraphDigraphOrContext, IsString, IsString], -function(g, hn, tn) - local lh, lt, len; +"for a graphviz (di)graph or context, object, and object", +[IsGraphvizGraphDigraphOrContext, IsObject, IsObject], +function(gv, head_obj, tail_obj) + local lh, lt, len, hn, tn; + + hn := String(head_obj); + tn := String(tail_obj); # if no such nodes exist -> error out - lh := GraphvizNode(g, hn) = fail; - lt := GraphvizNode(g, tn) = fail; + lh := GraphvizNode(gv, hn) = fail; + lt := GraphvizNode(gv, tn) = fail; if lh and lt then ErrorFormatted("no nodes with names \"{}\" or \"{}\"", hn, tn); elif lh then @@ -540,32 +529,27 @@ function(g, hn, tn) ErrorFormatted("no node with name \"{}\"", tn); fi; - len := Length(GraphvizEdges(g)); - GraphvizFilterEdges(g, + len := Length(GraphvizEdges(gv)); + GraphvizFilterEdges(gv, function(e) local head, tail, tmp; head := GraphvizHead(e); tail := GraphvizTail(e); - if IsGraphvizDigraph(g) then + if IsGraphvizDigraph(gv) then return tn <> GraphvizName(tail) or hn <> GraphvizName(head); else tmp := tn <> GraphvizName(tail) or hn <> GraphvizName(head); return tmp and (hn <> GraphvizName(tail) or tn <> GraphvizName(head)); fi; end); - if len - Length(GraphvizEdges(g)) = 0 then + if len - Length(GraphvizEdges(gv)) = 0 then ErrorFormatted("no edges exist from \"{}\" to \"{}\"", tn, hn); fi; - return g; + return gv; end); -InstallMethod(GraphvizRemoveEdges, -"for a graphviz (di)graph or context, object, and object", -[IsGraphvizGraphDigraphOrContext, IsObject, IsObject], -{gv, o1, o2} -> GraphvizRemoveEdges(gv, String(o1), String(o2))); - ############################################################################# # Stringify ############################################################################# @@ -575,7 +559,7 @@ InstallMethod(GraphvizRemoveEdges, # immutable, and hence cannot be changed after it is first set. InstallMethod(AsString, "for a graphviz (di)graph", -[IsGraphvizGraphDigraphOrContext], graph -> GV_StringifyGraph(graph, false)); +[IsGraphvizGraphDigraphOrContext], gv -> GV_StringifyGraph(gv, false)); # Can't do the following because it conflicts with the PrintString above, we # leave this here as a reminder. diff --git a/gap/gv.gd b/gap/gv.gd index f10049e..b21c07e 100644 --- a/gap/gv.gd +++ b/gap/gv.gd @@ -39,4 +39,4 @@ DeclareOperation("GV_AddEdge", [IsGraphvizGraphDigraphOrContext, IsGraphvizEdge]); DeclareGlobalFunction("GV_IsValidColor"); -DeclareGlobalFunction("GV_ErrorIfNotNodeColoring"); +DeclareOperation("GV_ErrorIfNotNodeColoring", [IsGraphvizObject, IsList]); diff --git a/gap/gv.gi b/gap/gv.gi index bf123de..14b6204 100644 --- a/gap/gv.gi +++ b/gap/gv.gi @@ -8,8 +8,6 @@ ############################################################################# ## -# TODO remove non-public functions from gv.gd - ############################################################################### # Family + type ############################################################################### @@ -40,46 +38,43 @@ BindGlobal("GV_GraphDigraphOrContextType", # Graph child counter functions BindGlobal("GV_IncCounter", -function(x) - x!.Counter := x!.Counter + 1; +function(gv) + Assert(0, IsGraphvizObject(gv)); + gv!.Counter := gv!.Counter + 1; end); InstallMethod(GV_GetCounter, "for a graphviz graph", -[IsGraphvizGraphDigraphOrContext], -x -> x!.Counter); +[IsGraphvizGraphDigraphOrContext], gv -> gv!.Counter); InstallMethod(GV_Node, "for a string", [IsGraphvizGraphDigraphOrContext, IsString], -function(graph, name) - local out; +function(gv, name) + local result; Assert(0, not IsEmpty(name)); - out := Objectify(GV_NodeType, - rec( - Name := name, - Attrs := rec(), - Parent := graph, - Idx := GV_GetCounter(graph))); - GV_IncCounter(graph); - return out; + result := Objectify(GV_NodeType, + rec(Name := name, + Attrs := rec(), + Parent := gv, + Idx := GV_GetCounter(gv))); + GV_IncCounter(gv); + return result; end); InstallMethod(GV_Edge, "for two graphviz nodes", [IsGraphvizGraphDigraphOrContext, IsGraphvizNode, IsGraphvizNode], -function(graph, head, tail) - local out; - - out := Objectify(GV_EdgeType, - rec( - Name := StringFormatted("({}, {})", - GraphvizName(head), - GraphvizName(tail)), - Head := head, - Tail := tail, - Attrs := rec(), - Parent := graph, - Idx := GV_GetCounter(graph))); - GV_IncCounter(graph); - return out; +function(gv, head, tail) + local result; + result := Objectify(GV_EdgeType, + rec(Name := StringFormatted("({}, {})", + GraphvizName(head), + GraphvizName(tail)), + Head := head, + Tail := tail, + Attrs := rec(), + Parent := gv, + Idx := GV_GetCounter(gv))); + GV_IncCounter(gv); + return result; end); # Graph constructors @@ -90,97 +85,100 @@ function(name, filt) local result; Assert(0, filt = IsGraphvizGraph or filt = IsGraphvizDigraph); - result := Objectify( - GV_GraphDigraphOrContextType, - rec( - # Note that the following aren't attributes because we require them to be - # mutable. - Name := name, - Subgraphs := rec(), - Contexts := rec(), - Nodes := rec(), - Edges := [], - Attrs := rec(graph := rec(), - edge := rec(), - node := rec()), - Parent := fail, - Idx := 1, - Counter := 1)); + # Note that the components of the record below aren't attributes because we + # require them to be mutable. + result := Objectify(GV_GraphDigraphOrContextType, + rec(Name := name, + Subgraphs := rec(), + Contexts := rec(), + Nodes := rec(), + Edges := [], + Attrs := rec(graph := rec(), + edge := rec(), + node := rec()), + Parent := fail, + Idx := 1, + Counter := 1)); SetFilterObj(result, filt); SetGV_Filter(result, filt); return result; end); -InstallMethod(GV_Subgraph, -"for a graphviz graph and a string", +InstallMethod(GV_Subgraph, "for a graphviz graph and a string", [IsGraphvizGraphDigraphOrContext, IsString], -function(parent, name) - local out; +function(gv, name) + local result; - out := GV_GraphOrDigraph(name, GV_Filter(parent)); - out!.Parent := parent; - out!.Idx := GV_GetCounter(parent); + result := GV_GraphOrDigraph(name, GV_Filter(gv)); + result!.Parent := gv; + result!.Idx := GV_GetCounter(gv); - GV_IncCounter(parent); - return out; + GV_IncCounter(gv); + return result; end); -InstallMethod(GV_Context, -"for a string and a string", +InstallMethod(GV_Context, "for a graphviz (di)graph or context and a string", [IsGraphvizGraphDigraphOrContext, IsString], -function(parent, name) - local out; - out := GV_Subgraph(parent, name); - SetFilterObj(out, IsGraphvizContext); - return out; +function(gv, name) + local result; + result := GV_Subgraph(gv, name); + SetFilterObj(result, IsGraphvizContext); + return result; end); # Nodes -InstallMethod(GV_GetParent, -"for a graphviz object", -[IsGraphvizObject], graph -> graph!.Parent); +InstallMethod(GV_GetParent, "for a graphviz object", +[IsGraphvizObject], gv -> gv!.Parent); +# TODO: GV_GraphTreeSearch apparently searches both up and down in the tree, +# while GV_GraphSearchChildren only searches down. These could be amalgamated +# by just initialising to_visit with all the ancestors of the gv, then calling +# GV_GraphSearchChildren InstallMethod(GV_GraphTreeSearch, -"for a graphviz graph and a predicate", +"for a graphviz (di)graph or context and a predicate", [IsGraphvizGraphDigraphOrContext, IsFunction], -function(graph, pred) - local seen, to_visit, g, key, subgraph, context, parent; - seen := [graph]; - to_visit := [graph]; +function(gv, pred) + local seen, to_visit, subgraph, subsubgraph, context, parent, key; + + # TODO this shouldn't be necessary, the subgraphs should be a tree, so we + # shouldn't be able to see a subgraph occurring in more than one object. + seen := [gv]; + to_visit := [gv]; while Length(to_visit) > 0 do - g := Remove(to_visit, Length(to_visit)); + subgraph := Remove(to_visit, Length(to_visit)); - # Check this graph - if pred(g) then - return g; + if pred(subgraph) then + return subgraph; fi; # add subgraphs to list of to visit if not visited - for key in RecNames(GraphvizSubgraphs(g)) do - subgraph := GraphvizSubgraphs(g).(key); - if not ForAny(seen, s -> IsIdenticalObj(s, subgraph)) then - Add(seen, subgraph); - Add(to_visit, subgraph); + for key in RecNames(GraphvizSubgraphs(subgraph)) do + subsubgraph := GraphvizSubgraphs(subgraph).(key); + if not subsubgraph in seen then + Add(seen, subsubgraph); + Add(to_visit, subsubgraph); fi; od; # add contexts to list of to visit if not visited - for key in RecNames(GraphvizContexts(g)) do - context := GraphvizContexts(g).(key); - if not ForAny(seen, s -> IsIdenticalObj(s, context)) then + for key in RecNames(GraphvizContexts(subgraph)) do + context := GraphvizContexts(subgraph).(key); + if not context in seen then Add(seen, context); Add(to_visit, context); fi; od; # add parent if not visited - parent := GV_GetParent(g); + parent := GV_GetParent(subgraph); + if not IsGraphvizGraphDigraphOrContext(parent) then + # TODO Not sure this can ever occur continue; - fi; - if not ForAny(seen, s -> IsIdenticalObj(s, parent)) then + elif not parent in seen then + # TODO Not sure this can ever occur Add(seen, parent); Add(to_visit, parent); fi; @@ -193,10 +191,12 @@ end); InstallMethod(GV_GraphSearchChildren, "for a graphviz graph and a predicate", [IsGraphvizGraphDigraphOrContext, IsFunction], -function(graph, pred) +function(gv, func) local _, curr, queue, count, nexts, key; - queue := [graph]; + # TODO check func has right number of args + + queue := [gv]; while Length(queue) > 0 do count := Length(queue); @@ -205,7 +205,7 @@ function(graph, pred) curr := Remove(queue, 1); # Check this graph (visit) - if pred(curr) then + if func(curr) then return curr; fi; @@ -227,22 +227,27 @@ end); InstallMethod(GV_FindGraphWithNode, "for a graphviz graph and a node", [IsGraphvizGraphDigraphOrContext, IsString], -{g, n} -> GV_GraphTreeSearch(g, v -> IsBound(GraphvizNodes(v).(n)))); +{gv, name} -> GV_GraphTreeSearch(gv, subgraph -> IsBound(GraphvizNodes(subgraph).(name)))); InstallMethod(GV_GetRoot, "for a graphviz graph", [IsGraphvizObject], -function(graph) - while GV_GetParent(graph) <> fail do - graph := GV_GetParent(graph); +function(gv) + while GV_GetParent(gv) <> fail do + gv := GV_GetParent(gv); od; - return graph; + return gv; end); -BindGlobal("GV_Indent", lvl -> ListWithIdenticalEntries(2 * lvl, ' ')); +BindGlobal("GV_Indent", +function(lvl) + Assert(0, IsInt(lvl)); + return ListWithIdenticalEntries(2 * lvl, ' '); +end); BindGlobal("GV_Depth", function(gv) local depth; + Assert(0, IsGraphvizObject(gv)); depth := 0; while GV_GetParent(gv) <> fail do gv := GV_GetParent(gv); @@ -254,53 +259,59 @@ end); InstallMethod(GV_AddNode, "for a graphviz graph and node", [IsGraphvizGraphDigraphOrContext, IsGraphvizNode], -function(x, node) +function(gv, node) local name, nodes; name := GraphvizName(node); - nodes := GraphvizNodes(x); + nodes := GraphvizNodes(gv); nodes.(name) := node; - return x; + return gv; end); InstallMethod(GV_AddEdge, "for a graphviz graph and edge", [IsGraphvizGraphDigraphOrContext, IsGraphvizEdge], -function(x, edge) - Add(x!.Edges, edge); - return x; +function(gv, edge) + Add(gv!.Edges, edge); + return gv; end); ############################################################################### # Stringifying ############################################################################### -# @ Return DOT graph head line. BindGlobal("GV_StringifyGraphHead", -graph -> StringFormatted("graph {} {{\n", GraphvizName(graph))); +function(gv) + Assert(0, IsGraphvizGraph(gv)); + return StringFormatted("graph {} {{\n", GraphvizName(gv)); +end); -# @ Return DOT digraph head line. BindGlobal("GV_StringifyDigraphHead", -graph -> StringFormatted("digraph {} {{\n", GraphvizName(graph))); +function(gv) + Assert(0, IsGraphvizDigraph(gv)); + return StringFormatted("digraph {} {{\n", GraphvizName(gv)); +end); -# @ Return DOT subgraph head line. BindGlobal("GV_StringifySubgraphHead", -gv -> StringFormatted("{}subgraph {} {{\n", GV_Indent(GV_Depth(gv)), GraphvizName(gv))); +function(gv) + Assert(0, IsGraphvizDigraph(gv) or IsGraphvizGraph(gv)); + return StringFormatted("{}subgraph {} {{\n", + GV_Indent(GV_Depth(gv)), + GraphvizName(gv)); +end); -# @ Return DOT subgraph head line. BindGlobal("GV_StringifyContextHead", function(gv) - local indent; - indent := GV_Indent(GV_Depth(gv)); + Assert(0, IsGraphvizContext(gv)); return StringFormatted("{1}{{\n{1} // context \"{2}\" \n", - indent, + GV_Indent(GV_Depth(gv)), GraphvizName(gv)); end); BindGlobal("GV_StringifyNodeName", function(node) local name, old; - Assert(0, IsGraphvizNode(node)); + name := GraphvizName(node); if (ForAny("- .+", x -> x in name) or (IsDigitChar(First(name)) and IsAlphaChar(Last(name)))) @@ -345,10 +356,10 @@ function(attrs) return result; end); -# @ Return DOT node statement line. BindGlobal("GV_StringifyNode", function(node) local name, attrs; + Assert(0, IsGraphvizNode(node)); name := GV_StringifyNodeName(node); attrs := GraphvizAttrs(node); return StringFormatted("{}{}{}\n", @@ -357,7 +368,6 @@ function(node) GV_StringifyAttrList(attrs)); end); -# @ Return DOT graph edge statement line. BindGlobal("GV_StringifyEdge", function(edge, edge_str) local head, tail, attrs; @@ -376,17 +386,22 @@ function(edge, edge_str) GV_StringifyAttrList(attrs)); end); -BindGlobal("GV_GetIdx", x -> x!.Idx); +BindGlobal("GV_GetIdx", +function(gv) + Assert(0, IsGraphvizObject(gv)); + return gv!.Idx; +end); BindGlobal("GV_ConstructHistory", -function(graph) +function(gv) local ctxs, nodes, edges, subs, ctxs_hist, node_hist, edge_hist, subs_hist, hist; + Assert(0, IsGraphvizGraphDigraphOrContext(gv)); - nodes := GraphvizNodes(graph); - edges := GraphvizEdges(graph); - subs := GraphvizSubgraphs(graph); - ctxs := GraphvizContexts(graph); + nodes := GraphvizNodes(gv); + edges := GraphvizEdges(gv); + subs := GraphvizSubgraphs(gv); + ctxs := GraphvizContexts(gv); node_hist := List(RecNames(nodes), n -> [GV_GetIdx(nodes.(n)), nodes.(n)]); subs_hist := List(RecNames(subs), s -> [GV_GetIdx(subs.(s)), subs.(s)]); @@ -403,48 +418,48 @@ end); InstallMethod(GV_StringifyGraph, "for a graphviz graph and a string", [IsGraphvizGraphDigraphOrContext, IsBool], -function(graph, is_subgraph) +function(gv, is_subgraph) local result, record, type, obj; result := ""; # get the correct head to use if is_subgraph then - if IsGraphvizContext(graph) then - Append(result, GV_StringifyContextHead(graph)); + if IsGraphvizContext(gv) then + Append(result, GV_StringifyContextHead(gv)); else - Append(result, GV_StringifySubgraphHead(graph)); + Append(result, GV_StringifySubgraphHead(gv)); fi; - elif IsGraphvizDigraph(graph) then + elif IsGraphvizDigraph(gv) then Append(result, "//dot\n"); - Append(result, GV_StringifyDigraphHead(graph)); - elif IsGraphvizGraph(graph) then + Append(result, GV_StringifyDigraphHead(gv)); + elif IsGraphvizGraph(gv) then Append(result, "//dot\n"); - Append(result, GV_StringifyGraphHead(graph)); + Append(result, GV_StringifyGraphHead(gv)); else - ErrorFormatted("Unknown graph category, ", - "expected a context, digraph or graph."); + ErrorFormatted("Unknown gv category, ", + "expected a context, digraph or gv."); fi; for type in ["graph", "node", "edge"] do - record := GraphvizAttrs(graph).(type); + record := GraphvizAttrs(gv).(type); if Length(RecNames(record)) > 0 then Append(result, StringFormatted("{}{}{}\n", - GV_Indent(GV_Depth(graph) + 1), + GV_Indent(GV_Depth(gv) + 1), type, GV_StringifyAttrList(record))); fi; od; # Add child graphviz objects - for obj in GV_ConstructHistory(graph) do + for obj in GV_ConstructHistory(gv) do if IsGraphvizGraphDigraphOrContext(obj) then Append(result, GV_StringifyGraph(obj, true)); elif IsGraphvizNode(obj) then Append(result, GV_StringifyNode(obj)); elif IsGraphvizEdge(obj) then - if (IsGraphvizDigraph(GV_GetRoot(graph))) then + if (IsGraphvizDigraph(GV_GetRoot(gv))) then Append(result, GV_StringifyEdge(obj, "->")); else Append(result, GV_StringifyEdge(obj, "--")); @@ -452,8 +467,8 @@ function(graph, is_subgraph) fi; od; - Append(result, StringFormatted("{}}}", GV_Indent(GV_Depth(graph)))); - if GV_Depth(graph) > 0 then + Append(result, StringFormatted("{}}}", GV_Indent(GV_Depth(gv)))); + if GV_Depth(gv) > 0 then Append(result, "\n"); fi; return result; @@ -579,6 +594,8 @@ BindGlobal("GV_IsValidRGBColor", function(str) local valid, i; + Assert(0, IsString(str)); + valid := "0123456789ABCDEFabcdef"; if Length(str) <> 7 or str[1] <> '#' then @@ -596,7 +613,9 @@ end); InstallGlobalFunction(GV_IsValidColor, c -> IsString(c) and (GV_IsValidRGBColor(c) or c in GV_ValidColorNames)); -InstallGlobalFunction(GV_ErrorIfNotNodeColoring, +InstallMethod(GV_ErrorIfNotNodeColoring, +"for a graphviz (di)graph or context and list", +[IsGraphvizGraphDigraphOrContext, IsList], function(gv, colors) local N; N := GraphvizNumberOfNodes(gv); diff --git a/tst/graphvizforgap01.tst b/tst/graphvizforgap01.tst index d77be25..b6fdcd1 100644 --- a/tst/graphvizforgap01.tst +++ b/tst/graphvizforgap01.tst @@ -1,4 +1,4 @@ -# GraphvizForGAP, chapter 2 +# GraphvizForGAP, chapter 1 # # DO NOT EDIT THIS FILE - EDIT EXAMPLES IN THE SOURCE INSTEAD! # @@ -10,157 +10,96 @@ # gap> START_TEST("graphvizforgap01.tst"); -# doc/_Chapter_Full_Reference.xml:78-89 -gap> gv := GraphvizGraph("GraphyMcGraphFace"); - -gap> GraphvizName(gv); -"GraphyMcGraphFace" -gap> GraphvizGraph(666); - -gap> gv := GraphvizGraph(); - -gap> GraphvizName(gv); -"" - -# doc/_Chapter_Full_Reference.xml:131-142 -gap> gv := GraphvizDigraph("GraphyMcGraphFace"); - -gap> GraphvizName(gv); -"GraphyMcGraphFace" -gap> GraphvizDigraph(666); - -gap> gv := GraphvizDigraph(); - -gap> GraphvizName(gv); -"" - -# doc/_Chapter_Full_Reference.xml:167-178 -gap> dot := GraphvizDigraph("The Round Table");; -gap> GraphvizName(dot); -"The Round Table" -gap> n := GraphvizSetAttr(GraphvizAddNode(dot, "A"), "label", "King Arthur"); - -gap> GraphvizName(n); -"A" -gap> e := GraphvizAddEdge(dot, "A", "B");; -gap> GraphvizName(e); -"(A, B)" - -# doc/_Chapter_Full_Reference.xml:191-192 - -# doc/_Chapter_Full_Reference.xml:220-231 -gap> gv := GraphvizDigraph(); - -gap> context := GraphvizAddContext(gv); - -gap> GraphvizAddNode(context, "a"); - -gap> GraphvizNodes(gv); # There are no nodes in gv directly, only in context -rec( ) -gap> GraphvizNodes(context); -rec( a := ) - -# doc/_Chapter_Full_Reference.xml:252-261 -gap> gv := GraphvizDigraph(); - -gap> context := GraphvizAddContext(gv); - -gap> GraphvizAddNode(context, "a"); - -gap> GraphvizNode(gv, "a"); - - -# doc/_Chapter_Full_Reference.xml:275-276 - -# doc/_Chapter_Full_Reference.xml:289-290 - -# doc/_Chapter_Full_Reference.xml:304-305 - -# doc/_Chapter_Full_Reference.xml:321-322 - -# doc/_Chapter_Full_Reference.xml:339-346 -gap> gv := GraphvizGraph();; -gap> GraphvizAddEdge(gv, "a", "b");; -gap> GraphvizEdges(gv); -[ ] -gap> GraphvizEdges(gv, "a", "b"); -[ ] - -# doc/_Chapter_Full_Reference.xml:360-366 -gap> gv := GraphvizGraph();; -gap> e := GraphvizAddEdge(gv, "a", "b"); - -gap> GraphvizHead(e); - - -# doc/_Chapter_Full_Reference.xml:379-385 -gap> gv := GraphvizGraph();; -gap> e := GraphvizAddEdge(gv, "a", "b"); - -gap> GraphvizTail(e); - - -# doc/_Chapter_Full_Reference.xml:407-412 -gap> gv := GraphvizGraph(); - -gap> GraphvizSetName(gv, "newname"); - - -# doc/_Chapter_Full_Reference.xml:428-435 -gap> gv := GraphvizGraph(); - -gap> GraphvizAddNode(gv, "a"); - -gap> gv; - - -# doc/_Chapter_Full_Reference.xml:457-464 -gap> gv := GraphvizGraph(); - -gap> GraphvizAddEdge(gv, "a", "b"); - -gap> gv; - - -# doc/_Chapter_Full_Reference.xml:482-483 - -# doc/_Chapter_Full_Reference.xml:510-511 - -# doc/_Chapter_Full_Reference.xml:530-537 -gap> gv := GraphvizGraph();; -gap> GraphvizAddNode(gv, "a");; -gap> gv; - -gap> GraphvizRemoveNode(gv, "a"); - - -# doc/_Chapter_Full_Reference.xml:550-551 - -# doc/_Chapter_Full_Reference.xml:566-573 -gap> gv := GraphvizGraph();; -gap> GraphvizAddEdge(gv, "a", "b");; -gap> gv; - -gap> GraphvizRemoveEdges(gv, "a", "b"); - - -# doc/_Chapter_Full_Reference.xml:596-597 - -# doc/_Chapter_Full_Reference.xml:615-616 - -# doc/_Chapter_Full_Reference.xml:631-632 - -# doc/_Chapter_Full_Reference.xml:648-649 - -# doc/_Chapter_Full_Reference.xml:668-669 - -# doc/_Chapter_Full_Reference.xml:683-684 - -# doc/_Chapter_Full_Reference.xml:799-800 - -# doc/_Chapter_Full_Reference.xml:817-818 - -# doc/_Chapter_Full_Reference.xml:833-834 +# doc/_Chapter_.xml:58-148 +gap> LoadPackage("GraphvizForGAP");; +gap> f := GraphvizDigraph("finite_state_machine"); + +gap> GraphvizSetAttr(f, "rankdir", "LR"); + +gap> GraphvizSetAttr(f, "size", "8,5"); + +gap> terminals := GraphvizAddContext(f, "terminals"); + +gap> GraphvizSetAttrs(terminals, rec(node := rec(shape := +> "doublecircle"))); + +gap> GraphvizAddNode(terminals, "LR_0"); + +gap> GraphvizAddNode(terminals, "LR_3"); + +gap> GraphvizAddNode(terminals, "LR_4"); + +gap> GraphvizAddNode(terminals, "LR_8"); + +gap> nodes := GraphvizAddContext(f, "nodes"); + +gap> GraphvizSetAttrs(nodes, rec(node := rec(shape := "circle"))); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_0", "LR_2"), "label", "\"SS(B)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_0", "LR_1"), "label", "\"SS(S)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_1", "LR_3"), "label", "\"S($end)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_2", "LR_6"), "label", "\"SS(b)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_2", "LR_5"), "label", "\"SS(a)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_2", "LR_4"), "label", "\"S(A)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_5", "LR_7"), "label", "\"S(b)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_5", "LR_5"), "label", "\"S(a)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_6", "LR_6"), "label", "\"S(b)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_6", "LR_5"), "label", "\"S(a)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_7", "LR_8"), "label", "\"S(b)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_7", "LR_5"), "label", "\"S(a)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_8", "LR_6"), "label", "\"S(b)\""); + +gap> GraphvizSetAttr(GraphvizAddEdge(nodes, "LR_8", "LR_5"), "label", "\"S(a)\""); + +gap> Print(AsString(f), "\n"); +//dot +digraph finite_state_machine { + graph [rankdir="LR", size="8,5"] + { + // context "terminals" + node [shape="doublecircle"] + LR_0 + LR_3 + LR_4 + LR_8 + } + { + // context "nodes" + node [shape="circle"] + LR_2 + LR_0 -> LR_2 [label="SS(B)"] + LR_1 + LR_0 -> LR_1 [label="SS(S)"] + LR_1 -> LR_3 [label="S($end)"] + LR_6 + LR_2 -> LR_6 [label="SS(b)"] + LR_5 + LR_2 -> LR_5 [label="SS(a)"] + LR_2 -> LR_4 [label="S(A)"] + LR_7 + 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)"] + } +} +gap> # Splash(f); # gap> STOP_TEST("graphvizforgap01.tst", 1); diff --git a/tst/graphvizforgap02.tst b/tst/graphvizforgap02.tst new file mode 100644 index 0000000..74271a8 --- /dev/null +++ b/tst/graphvizforgap02.tst @@ -0,0 +1,168 @@ +# GraphvizForGAP, chapter 2 +# +# DO NOT EDIT THIS FILE - EDIT EXAMPLES IN THE SOURCE INSTEAD! +# +# This file has been generated by AutoDoc. It contains examples extracted from +# the package documentation. Each example is preceded by a comment which gives +# the name of a GAPDoc XML file and a line range from which the example were +# taken. Note that the XML file in turn may have been generated by AutoDoc +# from some other input. +# +gap> START_TEST("graphvizforgap02.tst"); + +# doc/_Chapter_Full_Reference.xml:79-90 +gap> gv := GraphvizGraph("GraphyMcGraphFace"); + +gap> GraphvizName(gv); +"GraphyMcGraphFace" +gap> GraphvizGraph(666); + +gap> gv := GraphvizGraph(); + +gap> GraphvizName(gv); +"" + +# doc/_Chapter_Full_Reference.xml:133-144 +gap> gv := GraphvizDigraph("GraphyMcGraphFace"); + +gap> GraphvizName(gv); +"GraphyMcGraphFace" +gap> GraphvizDigraph(666); + +gap> gv := GraphvizDigraph(); + +gap> GraphvizName(gv); +"" + +# doc/_Chapter_Full_Reference.xml:170-181 +gap> dot := GraphvizDigraph("The Round Table");; +gap> GraphvizName(dot); +"The Round Table" +gap> n := GraphvizSetAttr(GraphvizAddNode(dot, "A"), "label", "King Arthur"); + +gap> GraphvizName(n); +"A" +gap> e := GraphvizAddEdge(dot, "A", "B");; +gap> GraphvizName(e); +"(A, B)" + +# doc/_Chapter_Full_Reference.xml:194-195 + +# doc/_Chapter_Full_Reference.xml:223-234 +gap> gv := GraphvizDigraph(); + +gap> context := GraphvizAddContext(gv); + +gap> GraphvizAddNode(context, "a"); + +gap> GraphvizNodes(gv); # There are no nodes in gv directly, only in context +rec( ) +gap> GraphvizNodes(context); +rec( a := ) + +# doc/_Chapter_Full_Reference.xml:255-264 +gap> gv := GraphvizDigraph(); + +gap> context := GraphvizAddContext(gv); + +gap> GraphvizAddNode(context, "a"); + +gap> GraphvizNode(gv, "a"); + + +# doc/_Chapter_Full_Reference.xml:278-279 + +# doc/_Chapter_Full_Reference.xml:293-294 + +# doc/_Chapter_Full_Reference.xml:307-308 + +# doc/_Chapter_Full_Reference.xml:322-323 + +# doc/_Chapter_Full_Reference.xml:339-340 + +# doc/_Chapter_Full_Reference.xml:357-364 +gap> gv := GraphvizGraph();; +gap> GraphvizAddEdge(gv, "a", "b");; +gap> GraphvizEdges(gv); +[ ] +gap> GraphvizEdges(gv, "a", "b"); +[ ] + +# doc/_Chapter_Full_Reference.xml:378-384 +gap> gv := GraphvizGraph();; +gap> e := GraphvizAddEdge(gv, "a", "b"); + +gap> GraphvizHead(e); + + +# doc/_Chapter_Full_Reference.xml:397-403 +gap> gv := GraphvizGraph();; +gap> e := GraphvizAddEdge(gv, "a", "b"); + +gap> GraphvizTail(e); + + +# doc/_Chapter_Full_Reference.xml:425-430 +gap> gv := GraphvizGraph(); + +gap> GraphvizSetName(gv, "newname"); + + +# doc/_Chapter_Full_Reference.xml:446-453 +gap> gv := GraphvizGraph(); + +gap> GraphvizAddNode(gv, "a"); + +gap> gv; + + +# doc/_Chapter_Full_Reference.xml:475-482 +gap> gv := GraphvizGraph(); + +gap> GraphvizAddEdge(gv, "a", "b"); + +gap> gv; + + +# doc/_Chapter_Full_Reference.xml:500-501 + +# doc/_Chapter_Full_Reference.xml:528-529 + +# doc/_Chapter_Full_Reference.xml:548-555 +gap> gv := GraphvizGraph();; +gap> GraphvizAddNode(gv, "a");; +gap> gv; + +gap> GraphvizRemoveNode(gv, "a"); + + +# doc/_Chapter_Full_Reference.xml:568-569 + +# doc/_Chapter_Full_Reference.xml:584-591 +gap> gv := GraphvizGraph();; +gap> GraphvizAddEdge(gv, "a", "b");; +gap> gv; + +gap> GraphvizRemoveEdges(gv, "a", "b"); + + +# doc/_Chapter_Full_Reference.xml:614-615 + +# doc/_Chapter_Full_Reference.xml:633-634 + +# doc/_Chapter_Full_Reference.xml:649-650 + +# doc/_Chapter_Full_Reference.xml:666-667 + +# doc/_Chapter_Full_Reference.xml:686-687 + +# doc/_Chapter_Full_Reference.xml:701-702 + +# doc/_Chapter_Full_Reference.xml:817-818 + +# doc/_Chapter_Full_Reference.xml:835-836 + +# doc/_Chapter_Full_Reference.xml:851-852 + +# +gap> STOP_TEST("graphvizforgap02.tst", 1); diff --git a/tst/node.tst b/tst/node.tst index 89e6235..443f0e9 100644 --- a/tst/node.tst +++ b/tst/node.tst @@ -28,7 +28,7 @@ gap> n := GraphvizAddNode(GraphvizGraph(), " "); # Test making a node with empty name fails gap> n := GraphvizAddNode(GraphvizGraph(), ""); -Error, the argument (the name of the node being added) cannot be empty +Error, the value returned by `String(obj)` must not be empty # Test whitespace in node names gap> n := GraphvizAddNode(GraphvizGraph(), "a a ");