[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

PRETTY-PRINT-INTERFACE, version 4



Issue:		PRETTY-PRINT-INTERFACE

Full description of XP accompanying version 4 of the proposal

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

			   Pretty Printing

			  Richard C. Waters


Pretty printing has traditionally been a black box process, displaying
program code using a set of fixed layout rules.  Its utility can be greatly
enhanced by opening it up to user control.

By providing direct access to the mechanisms within the pretty printer that
make dynamic decisions about layout, the FORMAT directives ~_, ~I, and
~<...~:> 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
circularity detection and abbreviation based on length and nesting depth to
be supported by the function.  The construct DEFINE-PRINT-DISPATCH makes it
possible to associate a user-defined pretty printing function with any type
of object.  Together, these facilities enable users to redefine the way
code is displayed and allow the full power of pretty printing to be applied
to complex combinations of data structures.

		  Pretty Printing Control Variables

*PRINT-DISPATCH*                                                 [variable]

When *PRINT-PRETTY* is not NIL, the print dispatch table in
*PRINT-DISPATCH* controls the way objects are printed.  The initial value
of *PRINT-DISPATCH* causes traditional pretty printing of Lisp code.

*PRINT-RIGHT-MARGIN*                                             [variable]
*DEFAULT-RIGHT-MARGIN*                                           [variable]

The goal of dynamic layout decisions (when pretty printing or when directly
specified via ~_, ~I, and ~<...~:>) is to keep the output between a pair of
margins.  The left margin is set at the column where the output begins.  If
this cannot be determined, the left margin is set to zero.

When *PRINT-RIGHT-MARGIN* is not NIL, it specifies the right margin to use
when making layout decisions.  When *PRINT-RIGHT-MARGIN* is NIL (the
initial value), the right margin is set at the maximum line length that can
be displayed by the output stream without wraparound or truncation.  If
this cannot be determined, the right margin is set to
*DEFAULT-RIGHT-MARGIN*.  The initial value of *DEFAULT-RIGHT-MARGIN* is
implementation-dependent.

To allow for the possibility of variable-width fonts, both of these
variables are interpreted in terms of ems---the length of an "m"
in the font being used to display characters on the relevant output
stream at the moment when the variables are consulted.

*PRINT-MISER-WIDTH*                                              [variable]

If *PRINT-MISER-WIDTH* is not NIL, the pretty printer switches to a compact
style of output (called miser style) whenever the width available for
printing a substructure is less than or equal to *PRINT-MISER-WIDTH* ems.  The
initial value of *PRINT-MISER-WIDTH* is implementation-dependent.
!
*PRINT-LINES*                                                    [variable]

When given a value other than its initial value of NIL, *PRINT-LINES*
limits the number of output lines produced when something is printed.  If
an attempt is made to go beyond *PRINT-LINES* lines, " ---" is printed at
the end of the last line and printing stops.

(let ((*print-right-margin* 20) (*print-lines* 3))
  (pprint '(setq a 1 b 2 c 3 d 4)))

(SETQ A 1
      B 2
      C 3 ---

*LAST-ABBREVIATED-PRINTING*                                      [variable]

This variable records the last printing event where abbreviation occurred.
Funcalling its value (e.g., after turning off abbreviation) causes the
printing to happen a second time.

The function WRITE accepts keyword arguments :DISPATCH, :RIGHT-MARGIN,
:LINES, and :MISER-WIDTH corresponding to *PRINT-DISPATCH*,
*PRINT-RIGHT-MARGIN*, *PRINT-LINES*, and *PRINT-MISER-WIDTH*.


		   Compiling Format Control Strings

The control strings used by FORMAT are essentially programs that perform
printing.  The readmacro character #"..." provides the efficiency of using
a compiled function for printing without losing the conciseness of FORMAT
control strings.  In the notation #"...", the string following # is
identical to a FORMAT control string.  However, the readmacro translates it
into an equivalent sharp-quoted function.  The first expression below is
equivalent to the second.

#"~%~@{~S~â??, ~}"

#'(lambda (stream &rest args)
    (terpri stream)
    (loop (prin1 (pop args) stream)
          (if (null args) (return nil))
          (write-string ", " stream)))

In support of the above, FORMAT accepts functions as its second argument as
well as strings.  When a function is provided, it is called with the
appropriate output stream as its first argument and the data arguments to
FORMAT as its remaining arguments.  The function should perform whatever
output is necessary.  The values returned by the function are ignored.  The
directive ~? also accepts functions as well as control strings.
!
	     Dynamic Control of the Arrangement of Output

The following FORMAT directives 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 directives work---`logical blocks',
`conditional newlines', and `sections'.

The first line of Figure 1 shows a schematic piece of output.  The
characters in the output are 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 ~<...~:> directives.
Conditional newline positions are specified by ~_ directives.  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 Figure 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.


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

Figure 1: 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.
!
~W                                                          [format directive]

WRITE -- An arg, any Lisp object, is printed obeying every printer control
variable (as by WRITE).  In addition, ~W interacts correctly with depth
abbreviation, by not resetting the depth counter to zero.  ~W does not
accept parameters.  If given the colon modifier, ~W binds *PRINT-PRETTY* to
T.  If given the atsign modifier, ~W binds *PRINT-LEVEL* and *PRINT-LENGTH*
to NIL.

~W provides automatic support for circularity detection.  If *PRINT-CIRCLE*
is T and ~W is applied to an argument that has already been encountered
during the printing process, an appropriate #n# marker is inserted in the
output instead of printing the argument.

Circularity detection is supported by effectively doing the printing twice.
On the first pass, circularities 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.

~_                                                          [format directive]

CONDITIONAL NEWLINE -- Without any modifiers, ~_ 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.

~@_ 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 the starting column of the logical block is less
than or equal to *PRINT-MISER-WIDTH* columns from the right margin.

~:_ 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.

~:@_ 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
column as the first character in the immediately containing logical block.
The indentation can be changed via ~I.

There are a variety of ways unconditional newlines can be introduced into
the output (e.g., via ~% 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 ~<...~:>), this
prefix will always be printed no matter how a newline originates.
!
~<...~:>                                                    [format directive]

LOGICAL BLOCK -- If ~:> is used to terminate a ~<...~>, the directive
delimits a logical block.  In addition, ~<...~:> descends into the
corresponding FORMAT argument (a list) in the same way as the directive
~1{...~:}.  ~â?? can be used to exit from ~<...~:> just as it can be used to
exit from ~{...~}.

The portion of a FORMAT control string enclosed in ~<...~:> can be divided
into segments ~<prefix~;body~;suffix~:> by ~; directives.  It is an error
for the enclosed portion to be divided into more than three segments.  If
the enclosed portion is divided into only two segments, the suffix defaults
to the null string.  If the enclosed portion consists of only a single
segment, both the prefix and the suffix default to the null string.  If the
colon modifier is used with ~<...~:>, the prefix and suffix default to "("
and ")" (respectively) instead of to the null string.

The prefix and suffix must both be constant strings.  They cannot contain
FORMAT directives.  The body can be any arbitrary FORMAT control string.
The prefix is printed out just before the logical block is started and the
suffix is printed out just after the logical block ends.  This is done even
when the argument corresponding to ~<...~:> is an empty list.

If ~<...~:> is applied to an argument that is not a list, the directive is
ignored (suppressing the output of the prefix and suffix) and the offending
argument is printed using ~W.  This makes it easier to write FORMAT strings
that are robust in the face of malformed arguments.

During the processing of the FORMAT string nested in ~<...~:>, arguments
are taken one by one from the list passed to ~<...~:>.  If an attempt is
made to access an argument at a time when the remaining portion of this
argument list is not a cons, then ". " is inserted in the output, ~W is
used to print out the remaining argument list, and the processing of the
logical block is terminated, except for printing the suffix (if any).  This
makes it easier to write FORMAT strings that are robust in the face of
malformed argument lists.  (Note that ~â?? exits only when the remaining
argument list is NIL.)

~<...~:> provides automatic support for depth abbreviation.  If
*PRINT-LEVEL* is not NIL and ~<...~:> is encountered at a dynamic nesting
depth in logical blocks greater than *PRINT-LEVEL*, "#" is inserted in the
output and the ~<...~:> and its associated argument are ignored.

~<...~:> provides automatic support for length abbreviation.  If
*PRINT-LENGTH* is not NIL, a count is kept of the number of arguments used
within the ~<...~:>.  If this count ever reaches *PRINT-LENGTH*, " ..." is
inserted in the output and the processing of the logical block is
terminated, except for printing the suffix (if any).

~<...~:> also provides automatic support for circularity detection.  If
*PRINT-CIRCLE* is T and ~<...~:> (without the atsign modifier) is applied
to a list argument that has already been encountered during the printing
process, an appropriate #n# marker is inserted in the output and the
~<...~:> and its associated argument are ignored.
!
In addition, if an attempt is made to access an argument from the list
passed to ~<...~:>, at a time when the remaining portion of this list has
already been encountered during the printing process, ". #n#" is inserted
in the output and the processing of the logical block is terminated, except
for printing the suffix (if any).  This catches instances of CDR
circularity in lists.

For circularity detection to work correctly when printing an object, every
part of the object that is a cons must be printed using ~<...~:> and every
non-cons must be printed using ~W.  If some part is printed some other way
(e.g., using ~S), circularities involving this part will be missed.

If the atsign modifier is used with ~<...~:>, the entire remaining argument
list is passed to the directive as its argument.  Unlike ~1@{...~} all of
the remaining arguments are always consumed by ~@<...~:>, even if they are
not all used by the FORMAT string nested in the directive.

As an example of the interaction of conditional newlines and logical
blocks, consider the following.  The FORMAT string specifies how to pretty
print a LET.  The outermost ~:<...~:> decomposes the input and specifies
that parentheses should be printed in the output.  The
~:<~@{~:<~@{~W~â??~ _~}~:>~â?? ~:_~}~:> decomposes the list of binding pairs.
Each pair in the list is itself decomposed and printed using
~:<~@{~W~â?? ~_~}~:>.  (An iteration is used in this FORMAT string instead of
merely decomposing the pair into two elements so that a malformed pair
containing more than two elements will print readably.)  A space and a
fill-style conditional newline are placed after each pair except the last.
The ~@{~â??~_ ~W~} prints out the forms in the body of the LET separated by
spaces and linear-style conditional newlines.

(format T #"~:<~W~â?? ~:<~@{~:<~@{~W~â?? ~_~}~:>~â?? ~:_~}~:>~
            ~@{~â??~_ ~W~}~:>"
        '#1=(let (x (*print-length* (f (g 3))) 
                  (z . 2) (k (car y)))
              (setq x (sqrt z)) #1#))

Suppose that *PRINT-PRETTY* is T, *PRINT-LEVEL* is 4, and *PRINT-CIRCLE* is
T.  If the line length is greater than or equal to 77, the output produced
by the FORMAT above 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 fails
to be 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 as well.

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

If ~@; is used to terminate the prefix in ~<...~:>, the prefix is a
`per-line' prefix.  A per-line prefix is printed at the beginning of every
line in the logical block, rather than just before the start of the block
as a whole.  Each instance of the prefix is lined up below the occurrence
of the prefix on the first line.  With a line length of 25, the form below
produces the output shown.

(format T #"~<;;; ~@;Roads ~<= ~@;~W, ~:_~W~:>  ~:_ Town ~W~:>"
           '((elm cottonwood) boston))

;;; Roads = ELM,
;;;       = COTTONWOOD
;;;  Town BOSTON

If ~<...~:> is terminated with ~:@>, then a fill-style conditional newline
is automatically inserted after each group of blanks immediately contained
in the body (except for blanks after a ~<newline> directive).  This makes
it easy to achieve the equivalent of paragraph filling.  With a line length
of 12, the form below produces the output shown.

(format T #"~<~:(~W~) street goes to ~:(~W~).~:@>" 
        '(main boston))

Main street
goes to
Boston.

To a considerable extent, the basic form of the directive ~<...~> is
incompatible with the dynamic control of the arrangement of output by ~W,
~_, ~<...~:>, ~I, and ~:T.  As a result, it is an error for any of these
directives to be nested within ~<...~>.  Beyond this, it is also an error
for the ~<...~:;...~> form of ~<...~> to be used at all in conjunction with
any of these directives.
!
~I                                                          [format directive]

INDENT -- ~nI specifies that the indentation within the immediately
containing logical block should be set to the column position of the
first character in the block plus n ems.  If omitted, n defaults to
zero.  The parameter 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.  ~n:I is exactly the same as ~nI except
that it operates relative to the position in the output of the directive
itself, rather than relative to the first character in the block.  (For
robustness in the face of variable-width fonts, it is advisable to use
~:I with a parameter of zero instead of ~I whenever possible.)  Changes
in indentation caused by a ~I directive do not take effect until after
the next line break.  Consider the following example:

(format T #"~:<~W ~@_~:I~W ~:_~W ~1I~_~W~:>" 
        '(defun prod (x y) (* x y)))

If the line width available is 15, both the ~:_ and the ~_ are replaced by
line breaks.  The ~:I directive before the ~W that prints the function name
causes the argument list to be lined up under the function name.  The ~1I
directive before the ~_ specifies that the statement in the body of the
DEFUN should be printed at a relative indentation of 1 in the logical
block.

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

In miser style, all ~I directives are ignored, forcing the lines
corresponding to the logical block to line up under the first character in
the block.  If *PRINT-MISER-WIDTH* were greater than or equal to 14 (the
block begins in the second column, after the prefix "(" IS printed), the
example output above would have been as follows.

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


~:T                                                         [format directive]

TABULATE -- If the colon modifier is used with the ~T directive, the
tabbing computation is done relative to the column where the section
immediately containing the directive begins, rather than with respect to
column zero.  The numerical parameters are both interpreted as being in
units of ems.  Consider the following example.  Each street name is
followed by a ~8:T, which ensures that the total width taken up will be
a multiple of 8.  With a line width of 25, the output shown is produced.

(format T #"~<Roads ~:I~@_~@{~W~â??~8:T~:_~}~:>"
        '(elm main maple center))

Roads ELM     MAIN
      MAPLE   CENTER
!
~/name/                                                     [format directive]

CALL FUNCTION -- User defined functions can be called from within a FORMAT
string by using the directive ~/name/.  The colon modifier, the atsign
modifier, and arbitrarily many parameters can be specified with the ~/name/
directive.  The name can contain a package prefix, but it cannot contain
"/".  If the readmacro #"..." is used, the default package associated with
name will be the value of *PACKAGE* at the moment the #"..." is read.  If
an ordinary FORMAT control string is used, the default package will be the
value of *PACKAGE* at the moment the string is processed by FORMAT.

When a ~/name/ directive is encountered, the indicated function is called
with four or more arguments.  The first four arguments are: the output
stream, the FORMAT argument corresponding to the directive, the value T if
the colon modifier was used (NIL otherwise), and the value T if the atsign
modifier was used (NIL otherwise).  The remaining arguments consist of any
parameters specified with the directive.  The function should print the
argument appropriately.  Any values returned by the function are ignored.

~/LINEAR-STYLE/                                             [format directive]

An argument, a list, is printed so that either all of the elements are on
one line or each element is on a separate line.  Parentheses are printed
around the list if the colon modifier is specified.  As an example of a
function intended to be called from within a FORMAT string, the definition
of LINEAR-STYLE is shown below.

(defun linear-style (stream list &optional (colon? T) atsign?)
    (declare (ignore atsign?))
  (if colon?
      (format stream #"~:<~@{~W~â?? ~_~}~:>" list)
      (format stream #"~<~@{~W~â?? ~_~}~:>" list)))

~/FILL-STYLE/                                               [format directive]

An argument, a list, is printed with as many elements as possible on each
line.  Parentheses are printed around the list if the colon modifier is
specified.

~/TABULAR-STYLE/                                            [format directive]

An argument, a list, is printed in a tabular form with as many elements as
possible on each line.  In addition to the colon modifier, which causes
parentheses to be printed, ~/TABULAR-STYLE/ takes a parameter
(default 16) that specifies the width in ems of columns in the table.
!
                  Functional Interface  

The primary interface to operations for dynamically determining the
arrangement of output is provided through FORMAT.  This is done,
because FORMAT strings are typically the most convenient way of
interacting with pretty printing.  However, these operations have
nothing inherently to do with FORMAT per se.  In particular, they can
also be accessed via the six functions and macros below.

WITHIN-LOGICAL-BLOCK (STREAM-SYMBOL LIST                     [Macro]
                      :PREFIX :PER-LINE-PREFIX :SUFFIX)
                      &BODY BODY

In the manner of ~<...~:>, this macro causes printing to be
grouped into a logical block.  The value NIL is always returned.

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.  The logical block is printed into this destination stream.

The BODY can contain any arbitrary Lisp forms.  Within the BODY,
STREAM-SYMBOL is bound to a special kind of stream that supports dynamic
decisions about the arrangement of output and then forwards the output
to the destination stream.  All the standard printing functions (e.g.,
WRITE, PRINC, TERPRI) can be used to print output into STREAM-SYMBOL.
All and only the output sent to STREAM-SYMBOL is treated as being in the
logical block.  (It is an error to send any output directly to the
underlying destination stream.)

The :SUFFIX, :PREFIX, and :PER-LINE-PREFIX must all be expressions that
(at run time) evaluate to strings.  :SUFFIX (which defaults to the null
string) specifies a suffix that is printed just after the logical block.
:PREFIX specifies a prefix to be printed before the beginning of the
logical block.  :PER-LINE-PREFIX specifies a prefix that is printed
before the block and at the beginning of each new line in the block.  It
is an error for :PREFIX and :PRE-LINE-PREFIX to both be used. If neither
is used, a :PREFIX of the null string is assumed.

LIST is interpreted as being a list that BODY is responsible for
printing.  If LIST does not (at run time) evaluate to a list, it is
printed using WRITE.  If *PRINT-CIRCLE* is not NIL and LIST is a
circular reference to a cons, then an appropriate #n# marker is printed.
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.  If either of the three conditions above occures, the indicated
special 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.)
!
CONDITIONAL-NEWLINE KIND &OPTIONAL (STREAM *STANDARD-OUTPUT*)    [Function]

CONDITIONAL-NEWLINE is the functional equivalent of ~_.  STREAM (which
defaults to *STANDARD-OUTPUT*) follows the standard conventions for
stream arguments to printing functions (i.e., 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.  If STREAM is a special stream bound by
WITHIN-LOGICAL-BLOCK, a conditional newline is sent to it.  Otherwise,
CONDITIONAL-NEWLINE has no effect.  The value NIL is always returned.

LOGICAL-BLOCK-INDENT RELATIVE-TO N &OPTIONAL (STREAM *STANDARD-OUTPUT*) [Function]

LOGICAL-BLOCK-INDENT is the functional equivalent of ~I.  STREAM (which
defaults to *STANDARD-OUTPUT*) follows the standard conventions for
stream arguments to printing functions.  N specifies the indentation in
ems.  If RELATIVE-TO is :BLOCK, this indentation is relative to the
start of the enclosing block (as for ~I).  If RELATIVE-TO is :CURRENT,
the indentation is relative to the current output position (as for ~:I).
It is an error for RELATIVE-TO to take on any other value.  If STREAM is
a special stream bound by WITHIN-LOGICAL-BLOCK, LOGICAL-BLOCK-INDENT
sets the indentation in the innermost enclosing logical block.
Otherwise, LOGICAL-BLOCK-INDENT has no effect.  The value NIL is always
returned.

LOGICAL-BLOCK-TAB KIND COLNUM COLINC &OPTIONAL (STREAM *STANDARD-OUTPUT*)

LOGICAL-BLOCK-TAB is the functional equivalent of ~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 numeric parameters to ~T and are in terms of ems.
The KIND argument specifies the style of tabbing.  It must be one of
:LINE (tab using ~T), :BLOCK (tab using ~:T), :LINE-RELATIVE (tab using
~@T), or :BLOCK-RELATIVE (tab using ~:@T).  If STREAM is a special
stream bound by WITHIN-LOGICAL-BLOCK, tabbing is performed.  Otherwise,
LOGICAL-BLOCK-TAB has no effect.  The value NIL is always returned.

LOGICAL-BLOCK-POP ARGS &OPTIONAL (STREAM *STANDARD-OUTPUT*)      [Macro]
LOGICAL-BLOCK-COUNT &OPTIONAL (STREAM *STANDARD-OUTPUT*)         [Macro]

LOGICAL-BLOCK-POP is identical to POP except that it supports
*PRINT-LENGTH* and *PRINT-CIRCLE*.  It is an error to use
LOGICAL-BLOCK-POP anywhere other than syntactically nested within a
call on WITHIN-LOGICAL-BLOCK.

ARGS must be a symbol or expression acceptable to POP.  STREAM (which
defaults to *STANDARD-OUTPUT*) follows the standard conventions for
stream arguments to printing functions.  If STREAM is a special stream
bound by WITHIN-LOGICAL-BLOCK, then LOGICAL-BLOCK-POP performs the
special operations described below.  Otherwise, LOGICAL-BLOCK-POP is
identical to POP.
!
Each time LOGICAL-BLOCK-POP is called, it performs three tests.  if
ARGS is not a cons, ". " is printed followed by ARGS.  If
*PRINT-LENGTH* is NIL and LOGICAL-BLOCK-POP has already been called
*PRINT-LENGTH* times within the immediately containing logical block,
"..." is printed.  If *PRINT-CIRCLE* is not NIL, and ARGS is a circular
reference, then ". " is printed followed by an appropriate #n# marker.
If either of the three conditions above occurs, the special output is
printed on :STREAM and the execution of the immediately containing
WITHIN-LOGICAL-BLOCK is terminated except for the printing of the
suffix.  Otherwise, LOGICAL-BLOCK-POP pops the top value off of ARGS
and returns this value.

LOGICAL-BLOCK-COUNT is identical to LOGICAL-BLOCK-POP except that it
does not take an ARGS argument, always returns NIL, and only performs
the second test discussed above.  It is useful when the components of a
non-list are being printed.

Using the functions above, TABULAR-STYLE could be defined as follows.

  (defun tabular-style (s list &optional (colon? T) atsign? (tabsize nil))
      (declare (ignore atsign?))
    (if (null tabsize) (setq tabsize 16))
    (within-logical-block (s list :prefix (if colon? "(" "")
				  :suffix (if colon? ")" ""))
     (when list
       (loop (write (logical-block-pop list s) :stream s)
	     (if (null list) (return nil))
	     (write-char #\space s)
	     (logical-block-tab :block-relative 0 tabsize s)
	     (conditional-newline :fill s)))))
    
The function below prints a vector using #(...) notation.
    
  (defun print-vector (v *standard-output*)
    (within-logical-block (nil nil :prefix "#(" :suffix ")")
      (let ((end (length v)) (i 0))
	(when (plusp end)
	  (loop (logical-block-count)
		(write (aref v i))
		(if (= (incf i) end) (return nil))
		(write-char #\space)
		(conditional-newline :fill))))))

FILL-STYLE STREAM LIST &OPTIONAL (COLON? T) ATSIGN?
LINEAR-STYLE STREAM LIST &OPTIONAL (COLON? T) ATSIGN?
TABULAR-STYLE STREAM LIST &OPTIONAL (COLON? T) ATSIGN? (TABSIZE 16)

The directives ~/fill-style/, ~/linear-style/, and ~/tabular-style/ are
supported by the three functions above.  These functions can also be
called directly by the user.  Each function prints parentheses around
the output if an only if COLON? (default T) is not NIL.  Each function
ignores its ATSIGN? argument and returns NIL.  (These arguments are
optional to facilitate the direct use of the three functions.)  Each
function handles abbreviation and circularity detection correctly, and
uses WRITE to print LIST when given a non-list argument.

The function LINEAR-STYLE prints a list either all on one line, or with
each element on a separate line.  The function FILL-STYLE prints a list
with as many elements as possible on each line.  The function
TABULAR-STYLE is the same as FILL-STYLE 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.
!
			Print Dispatch Tables

Pretty printing is directed by print dispatch tables.

COPY-PRINT-DISPATCH &optional (table *PRINT-DISPATCH*)           [function]

A copy is made of table, which defaults to the current print dispatch
table.  If table is NIL, a copy is made of the initial print dispatch
table.

DEFINE-PRINT-DISPATCH type-specifier options &body function      [macro]

This puts an entry into a print dispatch table.  The type-specifier
(which is implicitly quoted) is the key of the entry.  The function
specifies how to pretty print the indicated type of object.  When
appropriate, the function is called with two arguments: an output
stream and the object to print.  Any values returned by the function are
ignored.  The options are a list of pairs containing a keyword and a
value.  Three different keywords are supported:

(:TABLE table) 
Specifies the table (default *PRINT-DISPATCH*) to put the dispatch entry
into.

(:PRIORITY number)
Specifies a priority (default 0) used to resolve conflicts when an object
matches more than one entry.

(:NAME name)
Specifies a name to be given to function.  This makes it possible to
reuse the function---e.g., in another call on DEFINE-PRINT-DISPATCH.


Before doing anything else, DEFINE-PRINT-DISPATCH removes any existing
entry with a type specifier EQUAL to the given type specifier.  A new
entry containing the given priority and function is then created.

The function in a DEFINE-PRINT-DISPATCH call can be specified in five
different ways.  First, the function can be NIL.  In this case, no new
entry is made after the old entry (if any) is removed.  Second, the
function can be omitted altogether.  In this case, the standard pretty
printing function (if any) corresponding to the type specifier is entered
into the table.  Third, the function can be an argument list followed by a
body consisting of one or more statements.  (The use of &REST X in the
argument list below makes it possible to use ~/RATIO-PRINT/ in a FORMAT
string.)

(define-print-dispatch ratio ((:name ratio-print)) 
                       (s obj &rest x)
    (declare (ignore x))
  (format s #"#.(/ ~W ~W)" (numerator obj) (denominator obj)))

(pprint '(2/3 -4/5)) prints: (#.(/ 2 3) #.(/ -4 5))
!
Fourth, the function can be an instance of #"...".

(define-print-dispatch (and ratio (satisfies plusp)) 
                       ((:priority 1))
  #"(+ ~/ratio-print/)")

(pprint '(2/3 -4/5)) prints: ((+ #.(/ 2 3)) #.(/ -4 5))

Fifth, the function can be a sharp-quoted function name.  The definition
below shows the default method used for printing lists that represent data,
rather than programs.  (As shown in the definition of LINEAR-STYLE above,
LINEAR-STYLE, FILL-STYLE, and TABULAR-STYLE are all defined with their
COLON? and ATSIGN? arguments optional so that they can be used as
DEFINE-PRINT-DISPATCH functions.)

(define-print-dispatch cons ((:priority -10)) #'fill-style)

The entry to use for printing an object is selected by looking at the
entries in *PRINT-DISPATCH* in the order of their priorities.  The first
entry whose type specifier matches the object is chosen.  If an object
matches two entries with the same priority, an arbitrary choice is made.
If no entry matches the object, the object is printed as if *PRINT-PRETTY*
were NIL.

(CONS car-type cdr-type)                                    [type specifier]

When used simply as the symbol CONS, this type specifier matches any
cons cell.  When used in the form above, it matches a cons cell only if the
car of the cell matches the type specifier car-type and the cdr of
the cell matches the type specifier cdr-type.  The cdr-type can
be omitted in which case it defaults to T.

The examples below show three of the predefined pretty printing functions
for Lisp code.  By default, function calls are printed in the standard
way---i.e, either all on one line or with the arguments one to a line
indented after the function name.

(define-print-dispatch (cons (and symbol (satisfies fboundp)))
                       ((:priority -5))
  #"~:<~W~â?? ~:I~@_~@{~W~â?? ~_~}~:>")

Lists beginning with COND are printed the same way as function calls,
except that the clauses are always printed in linear style, rather than in
the format suggested by their cars.

(define-print-dispatch (cons (member cond)) ()
  #"~:<~W~â?? ~:I~@_~@{~:/linear-style/~â?? ~_~}~:>")

Lists beginning with QUOTE are printed using the standard "'" syntax.  Note
the care taken to ensure that data lists that happen to begin with QUOTE
will be printed legibly.

(define-print-dispatch (cons (member quote)) () (s list)
  (if (and (consp (cdr list)) (null (cddr list)))
      (format s #"'~W" (cadr list))
      (fill-style s list)))
!
In addition to the last four entries shown above, the initial print
dispatch table contains approximately fifty additional entries with type
specifiers of the form (CONS (MEMBER symbol)) and priority zero for various
Lisp special forms and macros.  There are no other predefined pretty
printing functions for data structures other than lists.  However, as shown
below, such entries can easily be defined.

(defstruct family mom kids)

(define-print-dispatch family () (s f)
  (format s #"~@<#<~;~W and ~2I~_~/fill-style/~;>~:>" 
            (family-mom f) (family-kids f)))

The pretty printing function for the structure FAMILY specifies how to
adjust the layout of the output so that it can fit aesthetically into a
variety of line widths.  In addition, it obeys the printer control
variables *PRINT-LEVEL*, *PRINT-LENGTH*, *PRINT-LINES*, *PRINT-CIRCLE*, and
*PRINT-ESCAPE*, and can tolerate several different kinds of malformity in
the data structure.  The output below shows what happens with line width
25, *PRINT-PRETTY T, *PRINT-ESCAPE* NIL, and a malformed KIDS list.

(write (list 'principle-family
             (make-family :mom "Lucy"
                          :kids '("Mark" "Bob" . "Dan"))))

(PRINCIPLE-FAMILY
 #<Lucy and
     Mark Bob . Dan>)
 
Note that a pretty printing function for a structure is different from the
structure's print function.  While print functions are permanently
associated with a structure, pretty printing functions are stored in print
dispatch tables and can be rapidly changed to reflect different printing
needs.  If there is no pretty printing function for a structure in the
current print dispatch table, the print function (if any) is used instead.

[End of attached document]