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