Common Lisp the Language, 2nd Edition
Next: Format Directive
Interface Up: Pretty
Printing Previous: Pretty
Printing Control
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
AI.Repository@cs.cmu.edu