Next: , Up: The Lisp Pretty Printer


22.2.1 Pretty Printer Concepts

The facilities provided by the pretty printer permit programs to redefine the way in which code is displayed, and allow the full power of pretty printing to be applied to complex combinations of data structures.

Whether any given style of output is in fact “pretty” is inherently a somewhat subjective issue. However, since the effect of the pretty printer can be customized by conforming programs, the necessary flexibility is provided for individual programs to achieve an arbitrary degree of aesthetic control.

By providing direct access to the mechanisms within the pretty printer that make dynamic decisions about layout, the macros and functions pprint-logical-block, pprint-newline, and pprint-indent make it possible to specify pretty printing layout rules as a part of any function that produces output. They also make it very easy for the detection of circularity and sharing, and abbreviation based on length and nesting depth to be supported by the function.

The pretty printer is driven entirely by dispatch based on the value of *print-pprint-dispatch*. The function set-pprint-dispatch makes it possible for conforming programs to associate new pretty printing functions with a type.

22.2.1.1 Dynamic Control of the Arrangement of Output

The actions of the pretty printer when a piece of output is too large to fit in the space available can be precisely controlled. 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 the next figure shows a schematic piece of output. Each of the characters in the output is represented by “-”. The positions of conditional newlines are indicated by digits. The beginnings and ends of logical blocks are indicated 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 1. Logical blocks nested within the output are specified by the macro pprint-logical-block. Conditional newline positions are specified by calls to 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 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 the next figure, 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.

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

Figure 22.3: Example of Logical Blocks, Conditional Newlines, and Sections

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.

22.2.1.2 Format Directive Interface

The primary interface to operations for dynamically determining the arrangement of output is provided through the functions and macros of the pretty printer. The next figure shows the defined names related to pretty printing.

*print-lines* pprint-dispatch pprint-pop
*print-miser-width* pprint-exit-if-list-exhausted pprint-tab
*print-pprint-dispatch* pprint-fill pprint-tabular
*print-right-margin* pprint-indent set-pprint-dispatch
copy-pprint-dispatch pprint-linear write
format pprint-logical-block
formatter pprint-newline

Figure 22.4: Defined names related to pretty printing.

The next figure identifies a set of format directives which serve as an alternate interface to the same pretty printing operations in a more textually compact form.

~I~W~<... :>
~:T~/.../~_

Figure 22.5: Format directives related to Pretty Printing

22.2.1.3 Compiling Format Strings

A format string is essentially a program in a special-purpose language that performs printing, and that is interpreted by the function format. The formatter macro provides the efficiency of using a compiled function to do that same printing but without losing the textual compactness of format strings.

A format control is either a format string or a function that was returned by the the formatter macro.

22.2.1.4 Pretty Print Dispatch Tables

A pprint dispatch table is a mapping from keys to pairs of values. Each key is a type specifier. The values associated with a key are a “function” (specifically, a function designator or nil) and a “numerical priority” (specifically, a real). Basic insertion and retrieval is done based on the keys with the equality of keys being tested by equal.

When *print-pretty* is true, the current pprint dispatch table (in *print-pprint-dispatch*) controls how objects are printed. The information in this table takes precedence over all other mechanisms for specifying how to print objects. In particular, it has priority over user-defined print-object methods because the current pprint dispatch table is consulted first.

The function is chosen from the current pprint dispatch table by finding the highest priority function that is associated with a type specifier that matches the object; if there is more than one such function, it is implementation-dependent which is used.

However, if there is no information in the table about how to pretty print a particular kind of object, a function is invoked which uses print-object to print the object. The value of *print-pretty* is still true when this function is called, and individual methods for print-object might still elect to produce output in a special format conditional on the value of *print-pretty*.

22.2.1.5 Pretty Printer Margins

A primary goal of pretty printing is to keep the output between a pair of margins. The column where the output begins is taken as the left margin. If the current column cannot be determined at the time output begins, the left margin is assumed to be zero. The right margin is controlled by *print-right-margin*.