liveBook · Manning

Doc

As explained in Fabulous Adventures in Data Structures and Algorithms

A doc is the book's immutable pretty-printing document representation that encodes text, optional breaks, grouping, indentation, and concatenation.

A doc is an immutable description of formatting choices, not a formatted string. A pretty printer first turns a parse tree into a doc, then renders that doc at a requested width. Keeping those phases separate makes it possible to change line-breaking rules without changing the underlying expression. section 9.5

Five node types supply the vocabulary:

  • TextDoc holds text with no line breaks. OptBreakDoc holds text—often a space or an empty string—that may become a line break. section 9.3.1
  • GroupDoc chooses whether its optional breaks become line breaks together, excluding breaks inside nested groups. IndentDoc adds indentation after breaks in its child. section 9.3.1
  • ConcatDoc joins two docs; the + operator is a convenient synonym. These immutable nodes form a directed graph, which can be surprisingly deep. section 9.3.1 section 9.3.2

One expression, several widths

The doc for Foo.Bar(xx + yy * zz) allows a break before .Bar and after the opening parenthesis, while nested groups govern breaks around the operators. At width 30 it prints on one line; at width 20 it breaks after the parenthesis; at width 15 it also breaks after +. The expression stays the same—only the available width changes. section 9.3.1 section 9.3.4

Rendering uses two methods. Fits checks whether a doc fits in the remaining width with its optional breaks left as text. Pretty traverses the doc, testing groups against the remaining line width and carrying each descendant's break decision and indentation. Both use explicit stacks rather than recursive calls: even a 21-character example produces a graph 17 levels deep. Pretty builds the output with StringBuilder to avoid repeatedly concatenating small strings. section 9.3.2 section 9.3.3 section 9.3.4

To construct docs from expressions, a parse-tree visitor assigns formatting rules to each expression type: identifiers become text, operators get surrounding break opportunities, member access may break before a dot, and nonempty argument lists may break after commas. Those choices are formatting policy, not properties of the expression itself. The printer does not perform parsing; it starts from an existing parse tree or doc. section 9.4.2 section 9.5

Foo.Bar(xx + yy * zz)

Foo.Bar(
    xx + yy * zz)

Foo.Bar(
    xx +
        yy * zz)

Compare the same doc rendered at three widths to see how nested groups expose additional break opportunities. section 9.3.4

How Fabulous Adventures in Data Structures and Algorithms covers it

How deep it goes
Defines five immutable node types and implements iterative fitting and rendering; leaves parsing outside scope.
Examples
Formats a method call at widths 30, 20, and 15, then builds docs for larger expression trees.
What it assumes
Assumes familiarity with C# and an available parse tree; does not teach parsing.
What you can do after
Build it from scratch as a C# doc hierarchy and render parse-tree expressions at a chosen width.
How it connects
Connects pretty printing to parse trees, the visitor pattern, greedy algorithms, and explicit-stack traversal.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Fabulous Adventures in Data Structures and Algorithms. The section links point to the parts of the book it draws on.