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

PRETTY-PRINT-INTERFACE



Issue:		PRETTY-PRINT-INTERFACE

References:	Description of XP by Dick Waters (attached)
		*PRINT-PRETTY* (CLtL p. 371)
		WRITE (CLtL p. 382)
		PPRINT (CLtL p. 383)
		FORMAT (CLtL pp. 385-407)
		FORMAT ~T directive (CLtL pp. 398-399)
		FORMAT ~< directive (CLtL pp. 404-406)

Related issues: 

Category:	CLARIFICATION CHANGE ADDITION

Edit history:	Version 1, 24-Feb-89 by Steele

Problem description:

At present Common Lisp provides no specification whatsoever of how
pretty-printing is to be accomplished, and no way for the user to control
it.  In particular, there is no protocol by which a user can write a
print-function for a structure, or a method for PRINT-OBJECT, that will
interact smoothly with the built-in pretty-printer in a portable manner.

Proposal (PRETTY-PRINT-INTERFACE:XP):

Adopt the interfaces and protocols of the XP pretty-printer by Dick Waters,
described in full in the attached 12-page document.  Here is a very brief
summary of the proposal.

New variables:	*PRINT-DISPATCH*
		*PRINT-RIGHT-MARGIN*
		*DEFAULT-RIGHT-MARGIN*
		*PRINT-MISER-WIDTH*
		*PRINT-LINES*
		*LAST-ABBREVIATED-PRINTING*

New function:	COPY-PRINT-DISPATCH

New macro:	DEFINE-PRINT-DISPATCH

New FORMAT directives:	~W  ~_  ~I  ~:T  ~/name/  ~<...~:>

New # reader macro:  #"..."

The function WRITE is extended to accept additional keyword arguments
:DISPATCH, :RIGHT-MARGIN, :LINES, and :MISER-WIDTH corresponding to the
first four of the new variables.

Examples:	See attached document.


Rationale:

There ought to be a good user interface to the pretty printer.
This is the only proposal for which there is a portable implementation
that has seen extensive use and is being made freely available.


Current practice:

XP son of PP son of GPRINT son of PRINT* is the latest in a line of pretty
printers that goes back 13 years.  All of these printers use essentially
the same basic algorithm and conceptual interface.  Further, except for
PRINT*, which was implemented solely to satisfy the author's personal
needs, each of these printers has had extensive use.  XP has been in
experimental use as the pretty printer in CMU Common Lisp for 6 months.  PP
has been the pretty printer in DEC Common Lisp for the past 3 years.  Prior
to three years ago, GPRINT was used for 2 years as the pretty printer in
DEC Common Lisp.  In addition, GPRINT has been the pretty printer in
various generations of Symbolics Lisp for upwards of 5 years.
(See Waters R.C., "User Format Control in a Lisp Prettyprinter", ACM TOPLAS,
5(4):513--531, October 1983.)


Cost to Implementors:

A fair amount of effort (perhaps a few man-weeks at most).
Source code for XP is available to all comers from Dick Waters, and
the system is documented in great detail:

Waters, Richard C., "XP: A Common Lisp Pretty Printing System",
Artificial Intelligence Laboratory Technical Memo 1102,
Massachusetts Institute of Technology, Cambridge MA, March 1989.


Cost to Users:  None (I think).  This is an upward-compatible extension.

Cost of non-adoption:  Continued inability for user print-functions
to interact with the pretty-printer in a useful and portable manner.


Performance impact:  XP is claimed to be quite fast.

Benefits:  User control of pretty-printing in a portable manner.

Esthetics:

Using ~<...~:> may strike some as uncomfortably close in the syntactic
space of FORMAT directives to the existing ~<...~>.  However, it is very
unlikely that both of these directives (pretty-print logical block and
columnar justification, respectively) will be used in the same call to
FORMAT.  Previous versions of XP used ~!...~. instead of ~<...~:> but this
made FORMAT strings very difficult to read; it is preferable to have
a directive that looks like matching brackets of some sort.

Discussion:

Zetalisp used ~:T to mean pixelwise tabulation, so the use of ~:T
suggested here may be a problem.  If so, another suggestion for naming
this directive would be appropriate.

The ~/.../ directive is already in Zetalisp, and is not an idea new
to this proposal.

Guy Steele and Dick Waters strongly support this proposal.  (As an example,
Guy Steele has a portable simulator for Connection Machine Lisp, and would
like very much to have xappings and xectors pretty-print properly.)
!
			   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.

*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*.  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.  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.  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.  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 of columns in the table.
!
			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]