Common Lisp the Language, 2nd Edition

Next: Format Directive Interface Up: Pretty Printing Previous: Pretty Printing Control

# 27.3. Dynamic Control of the Arrangement of Output

The following functions and macros support precise control of what should be done when a piece of output is too large to fit in the space available. Three concepts underlie the way these operations work: logical blocks, conditional newlines, and sections. Before proceeding further, it is important to define these terms.

The first line of figure 27-1 shows a schematic piece of output. The characters in the output are represented by hyphens. The positions of conditional newlines are indicated by digits. The beginnings and ends of logical blocks are indicated in the figure by ```<`'' and ```>`'' respectively.

The output as a whole is a logical block and the outermost section. This section is indicated by the `0`'s on the second line of figure 27-1. Logical blocks nested within the output are specified by the macro `pprint-logical-block`. Conditional newline positions are specified by calls on `pprint-newline`. Each conditional newline defines two sections (one before it and one after it) and is associated with a third (the section immediately containing it).

The section after a conditional newline consists of all the output up to, but not including, (a) the next conditional newline immediately contained in the same logical block; or if (a) is not applicable, (b) the next newline that is at a lesser level of nesting in logical blocks; or if (b) is not applicable, (c) the end of the output.

The section before a conditional newline consists of all the output back to, but not including, (a) the previous conditional newline that is immediately contained in the same logical block; or if (a) is not applicable, (b) the beginning of the immediately containing logical block. The last four lines in figure 27-1 indicate the sections before and after the four conditional newlines.

The section immediately containing a conditional newline is the shortest section that contains the conditional newline in question. In figure 27-1, the first conditional newline is immediately contained in the section marked with `0`'s, the second and third conditional newlines are immediately contained in the section before the fourth conditional newline, and the fourth conditional newline is immediately contained in the section after the first conditional newline.

    ----------------------------------------------------------------
    Figure 27-1: Example of Logical Blocks, Conditional Newlines, and Sections

                     <-1---<--<--2---3->--4-->->
                     000000000000000000000000000
                     11 111111111111111111111111
                               22 222
                                  333 3333
                            44444444444444 44444

    ----------------------------------------------------------------

Whenever possible, the pretty printer displays the entire contents of a section on a single line. However, if the section is too long to fit in the space available, line breaks are inserted at conditional newline positions within the section.

[Function]
`pprint-newline kind &optional stream`

The stream (which defaults to `*standard-output*`) follows the standard conventions for stream arguments to printing functions (that is, `nil` stands for `*standard-output*` and `t` stands for `*terminal-io*`). The kind argument specifies the style of conditional newline. It must be one of `:linear`, `:fill`, `:miser`, or `:mandatory`. An error is signaled if any other value is supplied. If stream is a pretty printing stream created by `pprint-logical-block`, a line break is inserted in the output when the appropriate condition below is satisfied. Otherwise, `pprint-newline` has no effect. The value `nil` is always returned.

If kind is `:linear`, it specifies a `linear-style' conditional newline. A line break is inserted if and only if the immediately containing section cannot be printed on one line. The effect of this is that line breaks are either inserted at every linear-style conditional newline in a logical block or at none of them.

If kind is `:miser`, it specifies a `miser-style' conditional newline. A line break is inserted if and only if the immediately containing section cannot be printed on one line and miser style is in effect in the immediately containing logical block. The effect of this is that miser-style conditional newlines act like linear-style conditional newlines, but only when miser style is in effect. Miser style is in effect for a logical block if and only if the starting position of the logical block is less than or equal to `*print-miser-width*` from the right margin.

If kind is `:fill`, it specifies a `fill-style' conditional newline. A line break is inserted if and only if either (a) the following section cannot be printed on the end of the current line, (b) the preceding section was not printed on a single line, or (c) the immediately containing section cannot be printed on one line and miser style is in effect in the immediately containing logical block. If a logical block is broken up into a number of subsections by fill-style conditional newlines, the basic effect is that the logical block is printed with as many subsections as possible on each line. However, if miser style is in effect, fill-style conditional newlines act like linear-style conditional newlines.

If kind is `:mandatory`, it specifies a `mandatory-style' conditional newline. A line break is always inserted. This implies that none of the containing sections can be printed on a single line and will therefore trigger the insertion of line breaks at linear-style conditional newlines in these sections.

When a line break is inserted by any type of conditional newline, any blanks that immediately precede the conditional newline are omitted from the output and indentation is introduced at the beginning of the next line. By default, the indentation causes the following line to begin in the same horizontal position as the first character in the immediately containing logical block. (The indentation can be changed via `pprint-indent`.)

There are a variety of ways unconditional newlines can be introduced into the output (for example, via `terpri` or by printing a string containing a newline character). As with mandatory conditional newlines, this prevents any of the containing sections from being printed on one line. In general, when an unconditional newline is encountered, it is printed out without suppression of the preceding blanks and without any indentation following it. However, if a per-line prefix has been specified (see `pprint-logical-block`), that prefix will always be printed no matter how a newline originates.

[Macro]

    pprint-logical-block (stream-symbol list
           [[ { :prefix | :per-line-prefix} p | :suffix s ]])
           {form}*

This macro causes printing to be grouped into a logical block. It returns `nil`.

The stream-symbol must be a symbol. If it is `nil`, it is treated the same as if it were `*standard-output*`. If it is `t`, it is treated the same as if it were `*terminal-io*`. The run-time value of stream-symbol must be a stream (or `nil` standing for `*standard-output*` or `t` standing for `*terminal-io*`). The logical block is printed into this destination stream.

The body (which consists of the forms) can contain any arbitrary Lisp forms. Within the body, stream-symbol (or `*standard-output*` if stream-symbol is `nil`, or `*terminal-io*` if stream-symbol is `t`) is bound to a ``pretty printing'' stream that supports decisions about the arrangement of output and then forwards the output to the destination stream. All the standard printing functions (for example, `write`, `princ`, `terpri`) can be used to send output to the pretty printing stream created by `pprint-logical-block`. All and only the output sent to this pretty printing stream is treated as being in the logical block.

`pprint-logical-block` and the pretty printing stream it creates have dynamic extent. It is undefined what happens if output is attempted outside of this extent to the pretty printing stream created. It is unspecified what happens if, within this extent, any output is sent directly to the underlying destination stream (by calling `write-char`, for example).

The `:suffix`, `:prefix`, and `:per-line-prefix` arguments must all be expressions that (at run time) evaluate to strings. The `:suffix` argument s (which defaults to the null string) specifies a suffix that is printed just after the logical block. The `:prefix` and `:per-line-prefix` arguments are mutually exclusive. If neither `:prefix` nor `:per-line-prefix` is specified, a `:prefix` of the null string is assumed. The `:prefix` argument specifies a prefix p that is printed before the beginning of the logical block. The `:per-line-prefix` specifies a prefix p that is printed before the block and at the beginning of each subsequent line in the block. An error is signaled if `:prefix` and `:per-line-prefix` are both used or if a `:suffix`, `:prefix`, or `:pre-line-prefix` argument does not evaluate to a string.

The list is interpreted as being a list that the body is responsible for printing. (See `pprint-exit-if-list-exhausted` and `pprint-pop`.) If list does not (at run time) evaluate to a list, it is printed using `write`. (This makes it easier to write printing functions that are robust in the face of malformed arguments.) If `*print-circle*` (and possibly also `*print-shared*`) is not `nil` and list is a circular (or shared) reference to a cons, then an appropriate ```#n#`'' marker is printed. (This makes it easy to write printing functions that provide full support for circularity and sharing abbreviation.) If `*print-level*` is not `nil` and the logical block is at a dynamic nesting depth of greater than `*print-level*` in logical blocks, ```#`'' is printed. (This makes it easy to write printing functions that provide full support for depth abbreviation.)

If any of the three preceding conditions occurs, the indicated output is printed on stream-symbol and the body is skipped along with the printing of the prefix and suffix. (If the body is not responsible for printing a list, then the first two tests above can be turned off by supplying `nil` for the list argument.)

In addition to the list argument of `pprint-logical-block`, the arguments of the standard printing functions such as `write`, `print`, `pprint`, `print1`, and `pprint`, as well as the arguments of the standard `format` directives such as `~A`, `~S`, (and `~W`) are all checked (when necessary) for circularity and sharing. However, such checking is not applied to the arguments of the functions `write-line`, `write-string`, and `write-char` or to the literal text output by `format`. A consequence of this is that you must use one of the latter functions if you want to print some literal text in the output that is not supposed to be checked for circularity or sharing. (See the examples below.)

Implementation note: Detection of circularity and sharing is supported by the pretty printer by in essence performing the requested output twice. On the first pass, circularities and sharing are detected and the actual outputting of characters is suppressed. On the second pass, the appropriate ```#n=`'' and ```#n#`'' markers are inserted and characters are output.

A consequence of this two-pass approach to the detection of circularity and sharing is that the body of a `pprint-logical-block` must not perform any side-effects on the surrounding environment. This includes not modifying any variables that are bound outside of its scope. Obeying this restriction is facilitated by using `pprint-pop`, instead of an ordinary `pop` when traversing a list being printed by the body of a `pprint-logical-block`.)

[Macro]
`pprint-exit-if-list-exhausted`

`pprint-exit-if-list-exhausted` tests whether or not the list argument of `pprint-logical-block` has been exhausted (see `pprint-pop`). If this list has been reduced to `nil`, `pprint-exit-if-list-exhausted` terminates the execution of the immediately containing `pprint-logical-block` except for the printing of the suffix. Otherwise `pprint-exit-if-list-exhausted` returns `nil`. An error message is issued if `pprint-exit-if-list-exhausted` is used anywhere other than syntactically nested within a call on `pprint-logical-block`. It is undefined what happens if `pprint-pop` is executed outside of the dynamic extent of this `pprint-logical-block`.

[Macro]
`pprint-pop`

`pprint-pop` pops elements one at a time off the list argument of `pprint-logical-block`, taking care to obey `*print-length*`, `*print-circle*`, and `*print-shared*`. An error message is issued if it is used anywhere other than syntactically nested within a call on `pprint-logical-block`. It is undefined what happens if `pprint-pop` is executed outside of the dynamic extent of this call on `pprint-logical-block`.

Each time `pprint-pop` is called, it pops the next value off the list argument of `pprint-logical-block` and returns it. However, before doing this, it performs three tests. If the remaining list is not a list (neither a cons nor `nil`), ```.` '' is printed followed by the remaining list. (This makes it easier to write printing functions that are robust in the face of malformed arguments.) If `*print-length*` is `nil` and `pprint-pop` has already been called `*print-length*` times within the immediately containing logical block, ```...`'' is printed. (This makes it easy to write printing functions that properly handle `*print-length*`.) If `*print-circle*` (and possibly also `*print-shared*`) is not `nil`, and the remaining list is a circular (or shared) reference, then ```.` '' is printed followed by an appropriate ```#n#`'' marker. (This catches instances of cdr circularity and sharing in lists.)

If any of the three preceding conditions occurs, the indicated output is printed on the pretty printing stream created by the immediately containing `pprint-logical-block` and the execution of the immediately containing `pprint-logical-block` is terminated except for the printing of the suffix.

If `pprint-logical-block` is given a list argument of `nil`-because it is not processing a list-`pprint-pop` can still be used to obtain support for `*print-length*` (see the example function `pprint-vector` below). In this situation, the first and third tests above are disabled and `pprint-pop` always returns `nil`.

[Function]
`pprint-indent relative-to n &optional stream`

`pprint-indent` specifies the indentation to use in a logical block. Stream (which defaults to `*standard-output*`) follows the standard conventions for stream arguments to printing functions. The argument n specifies the indentation in ems. If relative-to is `:block`, the indentation is set to the horizontal position of the first character in the block plus n ems. If relative-to is `:current`, the indentation is set to the current output position plus n ems.

The argument n can be negative; however, the total indentation cannot be moved left of the beginning of the line or left of the end of the rightmost per-line prefix. Changes in indentation caused by `pprint-indent` do not take effect until after the next line break. In addition, in miser mode all calls on `pprint-indent` are ignored, forcing the lines corresponding to the logical block to line up under the first character in the block.

An error is signaled if a value other than `:block` or `:current` is supplied for relative-to. If stream is a pretty printing stream created by `pprint-logical-block`, `pprint-indent` sets the indentation in the innermost dynamically enclosing logical block. Otherwise, `pprint-indent` has no effect. The value `nil` is always returned.

[Function]
`pprint-tab kind colnum colinc &optional stream`

`pprint-tab` specifies tabbing as performed by the standard `format` directive `~T`. Stream (which defaults to `*standard-output*`) follows the standard conventions for stream arguments to printing functions. The arguments colnum and colinc correspond to the two parameters to `~T` and are in terms of ems. The kind argument specifies the style of tabbing. It must be one of `:line` (tab as by `~T`) `:section` (tab as by `~T`, but measuring horizontal positions relative to the start of the dynamically enclosing section), `:line-relative` (tab as by `~@T`), or `:section-relative` (tab as by `~@T`, but measuring horizontal positions relative to the start of the dynamically enclosing section). An error is signaled if any other value is supplied for kind. If stream is a pretty printing stream created by `pprint-logical-block`, tabbing is performed. Otherwise, `pprint-tab` has no effect. The value `nil` is always returned.

[Function]
`pprint-fill stream list &optional colon? atsign?
``pprint-linear stream list &optional colon? atsign?
``pprint-tabular stream list &optional colon? atsign? tabsize`

These three functions specify particular ways of pretty printing lists. Stream follows the standard conventions for stream arguments to printing functions. Each function prints parentheses around the output if and only if colon? (default `t`) is not `nil`. Each function ignores its atsign? argument and returns `nil`. (These two arguments are included in this way so that these functions can be used via `~/.../` and as `set-pprint-dispatch` functions as well as directly.) Each function handles abbreviation and the detection of circularity and sharing correctly and uses `write` to print list when given a non-list argument.

The function `pprint-linear` prints a list either all on one line or with each element on a separate line. The function `pprint-fill` prints a list with as many elements as possible on each line. The function `pprint-tabular` is the same as `pprint-fill` except that it prints the elements so that they line up in columns. This function takes an additional argument `tabsize` (default 16) that specifies the column spacing in ems.

As an example of the interaction of logical blocks, conditional newlines, and indentation, consider the function `pprint-defun` below. This function pretty prints a list whose car is `defun` in the standard way assuming that the length of the list is exactly 4.

    ;;; Pretty printer function for DEFUN forms.

    (defun pprint-defun (list)
      (pprint-logical-block (nil list :prefix "(" :suffix ")")
        (write (first list))
        (write-char #\space)
        (pprint-newline :miser)
        (pprint-indent :current 0)
        (write (second list))
        (write-char #\space)
        (pprint-newline :fill)
        (write (third list))
        (pprint-indent :block 1)
        (write-char #\space)
        (pprint-newline :linear)
        (write (fourth list))))

Suppose that one evaluates the following:

    (pprint-defun '(defun prod (x y) (* x y)))

If the line width available is greater than or equal to 26, all of the output appears on one line. If the width is reduced to 25, a line break is inserted at the linear-style conditional newline before `(* X Y)`, producing the output shown below. The `(pprint-indent :block 1)` causes `(* X Y)` to be printed at a relative indentation of 1 in the logical block.

    (DEFUN PROD (X Y)
      (* X Y))

If the width is 15, a line break is also inserted at the fill-style conditional newline before the argument list. The argument list lines up under the function name because of the call on `(pprint-indent :current 0)` before the printing of the function name.

    (DEFUN PROD
           (X Y)
      (* X Y))

If `*print-miser-width*` were greater than or equal to 14, the output would have been entirely in miser mode. All indentation changes are ignored in miser mode and line breaks are inserted at miser-style conditional newlines. The result would have been as follows:

    (DEFUN
     PROD
     (X Y)
     (* X Y))

As an example of the use of a per-line prefix, consider that evaluating the expression

    (pprint-logical-block (nil nil :per-line-prefix ";;; ")
      (pprint-defun '(defun prod (x y) (* x y))))

produces the output

    ;;; (DEFUN PROD
    ;;;        (X Y)
    ;;;   (* X Y))

with a line width of 20 and `nil` as the value of the printer control variable `*print-miser-width*`.

(If `*print-miser-width*` were not `nil` the output

    ;;; (DEFUN
    ;;;  PROD
    ;;;  (X Y)
    ;;;  (* X Y))

might appear instead.)

As a more complex (and realistic) example, consider the function `pprint-let` below. This specifies how to pretty print a `let` in the standard style. It is more complex than `pprint-defun` because it has to deal with nested structure. Also, unlike `pprint-defun`, it contains complete code to print readably any possible list that begins with the symbol `let`. The outermost `pprint-logical-block` handles the printing of the input list as a whole and specifies that parentheses should be printed in the output. The second `pprint-logical-block` handles the list of binding pairs. Each pair in the list is itself printed by the innermost `pprint-logical-block`. (A `loop` is used instead of merely decomposing the pair into two elements so that readable output will be produced no matter whether the list corresponding to the pair has one element, two elements, or (being malformed) has more than two elements.) A space and a fill-style conditional newline are placed after each pair except the last. The loop at the end of the topmost `pprint-logical-block` prints out the forms in the body of the `let` separated by spaces and linear-style conditional newlines.

    ;;; Pretty printer function for LET forms,
    ;;; carefully coded to handle malformed binding pairs.

    (defun pprint-let (list)
      (pprint-logical-block (nil list :prefix "(" :suffix ")")
        (write (pprint-pop))
        (pprint-exit-if-list-exhausted)
        (write-char #\space)
        (pprint-logical-block
            (nil (pprint-pop) :prefix "(" :suffix ")")
          (pprint-exit-if-list-exhausted)
          (loop (pprint-logical-block
                    (nil (pprint-pop) :prefix "(" :suffix ")")
                  (pprint-exit-if-list-exhausted)
                  (loop (write (pprint-pop))
                        (pprint-exit-if-list-exhausted)
                        (write-char #\space)
                        (pprint-newline :linear)))
                (pprint-exit-if-list-exhausted)
                (write-char #\space)
                (pprint-newline :fill)))
        (pprint-indent :block 1)
        (loop (pprint-exit-if-list-exhausted)
              (write-char #\space)
              (pprint-newline :linear)
              (write (pprint-pop)))))

Suppose that the following is evaluated with `*print-level*` having the value `4` and `*print-circle*` having the value `t`.

    (pprint-let '#1=(let (x (*print-length* (f (g 3)))
                          (z . 2) (k (car y)))
                      (setq x (sqrt z)) #1#))

If the line length is greater than or equal to 77, the output produced appears on one line. However, if the line length is 76, line breaks are inserted at the linear-style conditional newlines separating the forms in the body and the output below is produced. Note that the degenerate binding pair `X` is printed readably even though it fails to be a list; a depth abbreviation marker is printed in place of `(G 3)`; the binding pair `(Z . 2)` is printed readably even though it is not a proper list; and appropriate circularity markers are printed.

    #1=(LET (X (*PRINT-LENGTH* (F #)) (Z . 2) (K (CAR Y)))
         (SETQ X (SQRT Z))
         #1#)

If the line length is reduced to 35, a line break is inserted at one of the fill-style conditional newlines separating the binding pairs.

    #1=(LET (X (*PRINT-PRETTY* (F #))
             (Z . 2) (K (CAR Y)))
         (SETQ X (SQRT Z))
         #1#)

Suppose that the line length is further reduced to 22 and `*print-length*` is set to 3. In this situation, line breaks are inserted after both the first and second binding pairs. In addition, the second binding pair is itself broken across two lines. Clause (b) of the description of fill-style conditional newlines prevents the binding pair `(Z . 2)` from being printed at the end of the third line. Note that the length abbreviation hides the circularity from view and therefore the printing of circularity markers disappears.

    (LET (X
          (*PRINT-LENGTH*
           (F #))
          (Z . 2) ...)
      (SETQ X (SQRT Z))
      ...)

The function `pprint-tabular` could be defined as follows:

    (defun pprint-tabular (s list &optional (c? t) a? (size 16))
      (declare (ignore a?))
      (pprint-logical-block
          (s list :prefix (if c? "(" "") :suffix (if c? ")" ""))
        (pprint-exit-if-list-exhausted)
        (loop (write (pprint-pop) :stream s)
              (pprint-exit-if-list-exhausted)
              (write-char #\space s)
              (pprint-tab :section-relative 0 size s)
              (pprint-newline :fill s))))

Evaluating the following with a line length of 25 produces the output shown.

    (princ "Roads ")
    (pprint-tabular nil '(elm main maple center) nil nil 8)

    Roads ELM     MAIN
          MAPLE   CENTER

The function below prints a vector using `#(...)` notation.

    (defun pprint-vector (v)
      (pprint-logical-block (nil nil :prefix "#(" :suffix ")")
        (let ((end (length v)) (i 0))
          (when (plusp end)
            (loop (pprint-pop)
                  (write (aref v i))
                  (if (= (incf i) end) (return nil))
                  (write-char #\space)
                  (pprint-newline :fill))))))

Evaluating the following with a line length of 15 produces the output shown.

    (pprint-vector '#(12 34 567 8 9012 34 567 89 0 1 23))

    #(12 34 567 8
      9012 34 567
      89 0 1 23)

Next: Format Directive Interface Up: Pretty Printing Previous: Pretty Printing Control