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")
)

Arguments

obj

A generator, async or stream object.

basename

The base file name. If basename="X" and type="pdf" you will end up with two files, "X.dot" and "X.pdf".

type

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.

envs

If TRUE, multiple nodes that share the same environment will be grouped together in clusters.

vars

If TRUE, context variables used in each state node will be included on the graph, with edges indicating reads/stores.

handlers

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.

orphans

If TRUE, nodes will be included even if there are no connections to them (this mostly being interface methods and unused handlers).

dot

Optional path to the dot executable.

filename

Optionally specify the output picture file name.

dotfile

Optionally specify the output DOT file name.

Value

If type="", a character vector of DOT source. Else The name of the file that was created.

Details

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.)

Examples

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")
}