graphAsync
will traverse the objects representing a
generator or async and render a graph of its structure
using Graphviz (if it is installed.)
graphAsync(
obj,
basename = if (is.name(substitute(obj))) as.character(substitute(obj)) else
stop("Please specify basename"),
type = "pdf",
...,
envs = TRUE,
vars = FALSE,
handlers = FALSE,
orphans = FALSE,
dot = find_dot(),
filename = paste0(basename, ".", type),
dotfile = if (type == "dot") filename else paste0(basename, ".dot")
)
The base file name. If basename="X"
and
type="pdf"
you will end up with two files, "X.dot"
and
"X.pdf"
.
the output format. If "dot", we will just write a
Graphviz dot file. If another extension like "pdf" or "svg", will
write a DOT file and then attempt to invoke Graphviz dot
(if it
is available according to Sys.which
) to produce the image.
If type=""
graphAsync
will return graphviz DOT language as a
character vector
Unused.
If TRUE
, multiple nodes that share the same
environment will be grouped together in clusters.
If TRUE
, context variables used in each state node
will be included on the graph, with edges indicating
reads/stores.
If TRUE
, state nodes will have thin edges
connecting to trampoline handlers they call, in addition to the
dashed edges connecting to the next transition.
If TRUE
, nodes will be included even if there are
no connections to them (this mostly being interface methods and
unused handlers).
Optional path to the dot
executable.
Optionally specify the output picture file name.
Optionally specify the output DOT file name.
If type=""
, a character vector of DOT source. Else
The name of the file that was created.
graphAsync
will write a Graphviz DOT format file describing the
given generator or async/await block. The graph shows the
generator as a state machine with nodes that connect to each other.
If type
is something other than dot
graphAsync will then try to invoke Graphviz
dot` to turn the graph description into an image
file.
The green octagonal node is where the program starts, while red "stop" and blue "return" are where it ends. Nodes in green type on dark background show code that runs in the host language unmodified; gray nodes implement control flow. Dark arrows carry a value; gray edges carry no value. A "semicolon" node receives a value and discards it.
Some nodes share a context with other nodes, shown by an enclosing box. Contexts can have state variables, shown as a rectangular record; orange edges from functions to variables represent writes; blue edges represent reads.
Dashed edges represent a state transition that goes through a trampoline handler. Dashed edges have a Unicode symbol representing the type of trampoline; (DOUBLE VERTICAL BAR) for await/yield; (TOP ARC ANTICLOCKWISE ARROW WITH PLUS) or (TOP ARC CLOCKWISE ARROW WITH MINUS) to wind on or off an exception handler; (ANTICLOCKWISE TRIANGLE-HEADED BOTTOM U-SHAPED ARROW) for a plain trampoline with no side effects (done once per loop, to avoid overflowing the stack.) Meanwhile, a thin edge connects to the trampoline handler. (So the user-facing "yield" function registers a continuation to the next step but actually calls the generator's yield handler.)
randomWalk <- gen({x <- 0; repeat {yield(x); x <- x + rnorm(1)}})
if (FALSE) {
graphAsync(randomWalk, "pdf")
# writes "randomWalk.dot" and invokes dot to make "randomWalk.pdf"
#or, display it in an R window with the Rgraphviz package:
g <- Rgraphviz::agread("randomWalk.dot")
Rgraphviz::plot(g)
}
#Or render an HTML sidget using DiagrammeR:
if (FALSE) {
dot <- graphAsync(randomWalk, type="")
DiagrammeR::DiagrammeR(paste0(dot, collapse="\n"), type="grViz")
}