Version 3 (by Guy Steele Jr) supersedes version 2 and is changed fromversion 1 as follows: adds a functional interface to supplement the
interface through FORMAT, and reflects comments by Barrett and
Pierson.
Version 4 (by Dick Waters) is changed from version 3 as follows: The
short summary is updated to reflect the functional interface. The
functional interface is changed following suggestions made by Dave Moon.
The proposal is amended in a few minor ways to increase the
compatibility with variable width fonts. Additional discussion has been
added with regard to the advantages of XP with respect to handling
detection and abbreviation, the interaction with CLOS, and
the extended type specifier CONS used by XP.
Version 5 (by Waters, Haflich, Laubsch, Loosemore, Pierson, and van Roggen).
On June 27 it was voted 12 to 4 in a plenary session of X3J13 that an add
hoc committee on pretty printing be created containing the above members
with the charter of ironing out the final details of the pretty print
interface and that if this committee could come to a unanimous decision on
the interface, the interface would become part of the draft Common Lisp
standard without the need of a further vote by the full committee. The
unanimity of the add hoc committee has been signaled by having each member
of the committee send a message to X3J13 to this effect.
In addition to the main vote above, five straw polls were taken. Three
of these votes were overwhelming; however, two appeared too close to be
definitive. In order of the clarity of the outcome the straw votes were:
16-0 in favor of the basic functional interface,
10-4 against the inclusion of #"...",
12-7 in favor of having some way to convert a FORMAT string into a function,
11-8 in favor of allowing FORMAT to take a function as well as a string,
10-8 against the pretty printer extensions of FORMAT.
The subcommittee followed these recommendations except for the last.
(See the end of the discussion section.)
Version 5 is changed from version 4 as follows: The names of the
functions and variables have been changed to better reflect that (for the
most part) they are a group that applies only to pretty printing. The
variables *DEFAULT-RIGHT-MARGIN* and *LAST-ABBREVIATED-PRINTING* have been
eliminated. The readmacro construct #"..." has been eliminated and a new
macro FORMATTER introduced in its place. The way *PRINT-LINES*
abbreviation is indicated has been improved to ensure that the delimiters
will be balanced in the output and to ensure that the reader will complain
if you later try to read the output. The macros LOGICAL-BLOCK-POP and
LOGICAL-BLOCK-COUNT have been eliminated and the functionality they
supported provided in a slightly different form by two new macros
PPRINT-POP and PPRINT-EXIT-IF-LIST-EXHAUSTED. The macro
DEFINE-PRINT-DISPATCH has been replaced by a function SET-PPRINT-DISPATCH.
The proposal has been reorganized to show that the functional interface is
more fundamental than the FORMAT interface, to clarify exactly what happens
in error situations, and to indicate that the examples are merely examples
and not part of the proposal.
================================================================================
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
Version 2, 15-Mar-89 by Steele and Waters
Version 3, 15-Mar-89 by Steele
Version 4, 22-Mar-89 by Waters
Version 5, 20-Jul-89 by Waters et al
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 document. Here is a very brief summary
of the proposal.
New variables: *PRINT-PPRINT-DISPATCH*
New functions: COPY-PPRINT-DISPATCH
New macros: PPRINT-LOGICAL-BLOCK
New FORMAT directives: ~W ~_ ~I ~:T ~/name/ ~<...~:>
The function WRITE is extended to accept four additional keyword arguments
:PPRINT-DISPATCH, :RIGHT-MARGIN, :LINES, and :MISER-WIDTH corresponding to
the four new variables.
The function FORMAT is extended so that it can accept a function instead of
a FORMAT string. (This change is also made in the other functions that
accept FORMAT strings such as ERROR and WARN.)
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 (several man-weeks at most).
Source code for XP is available to all comers from Dick Waters. It has
been arranged with MIT for anyone who wants to to get a non-exclusive
royalty-free license for XP from MIT. The system is documented in great
detail in:
Waters, Richard C., "XP: A Common Lisp Pretty Printing System",
Artificial Intelligence Laboratory Technical Memo 1102a,
Massachusetts Institute of Technology, Cambridge MA, July 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.
Aesthetics:
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.
Dan Pierson comments: You might mention that some people will undoubtedly
find piling more hair on FORMAT ugly (of course these same people may
well find FORMAT in general ugly :-)).
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 appreciated.
The ~/.../ directive is already in Zetalisp, and is not an idea new
to this proposal. However, it should be noted that the proposal for
~/.../ here is simpler than, and incompatible with, the current Zatalisp
practice.
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.)
---
Dan Pierson comments: You can add me to the list of strong supporters of
this proposal. While the proposal is long and complex, it is supported by
a long history of usage in several different Lisp environments.
The utility of *PRINT-LINES* becomes more obvious if it is pointed out
that Dick's pretty printers are implemented to print each line as it
is computed. This means that a small value for *PRINT-LINES* saves
significant time as well as output medium space.
The advantages of compiled format strings (format functions) should be
brought out as benefits in their own right. The current proposal just
mentions them as a minor feature of XP.
At first this struck me a very cute end run around the failure of
STREAM-INFO, then I realized that one of the problems with STREAM-INFO
may have been that it was a standard at the wrong level. STREAM-INFO
permitted people to use XP, but not to count on it. This proposal
makes it possible to write portable code whose new data structures and
language elements print correctly in whatever Common Lisp environment
they're run in. [End of comments by Pierson]
---
It has been noted by Guy Steele that some places in the initial document
where it says that circularity detection is handled correctly, this is
true a fortiori following the decision on PRINT-CIRCLE-STRUCTURE.
However, Waters notes that the vote on PRINT-CIRCLE-STRUCTURE said
nothing about the handling of *PRINT-LEVEL*. Therefore, the fact that
XP handles *PRINT-LEVEL* correctly is an improvement.
In addition, PRINT-CIRCLE-STRUCTURE is also silent on what is supposed
to happen if a user program decomposes a list itself (e.g., with DOLIST
or ~{~}) rather than calling a print function. Assumedly *PRINT-CIRCLE*
etc. is not handled in this case. In contrast, if one uses
PPRINT-LOGICAL-BLOCK or ~<~:>, then *PRINT-CIRCLE*, *PRINT-LEVEL*, and
*PRINT-LENGTH* are all automatically handled correctly.
For example, (format nil "-~1{~A ~A ~A ~A ~A ~}-" '#1=(1 #1# 2 . #1#))
produces "-1 #1=(1 #1# 2 . #1#) 2 1 #1=(1 #1# 2 . #1#) -"
even under PRINT-CIRCLE-STRUCTURE and
(format nil "-~1{~A ~}-" '#1=(1 #1# 2 . #1#))
causes infinite looping. However, in XP,
(format nil "-~:<~W ~W ~W ~W ~W~:>-" '#1=(1 #1# 2 . #1#))
produces "-#1=(1 #1# 2 . #1#)-".
This proves to be very useful when writing pretty printing functions for
things. Note also that ~<~:> supports *PRINT-LEVEL* and *PRINT-LENGTH*
correctly. All the same things can be said about the functional interface
and using PPRINT-LOGICAL-BLOCK rather than traversing a list yourself in
some fashion.
All in all, Waters claims that PRINT-CIRCLE-STRUCTURE covers at most 1/4
of what XP does in support of *PRINT-CIRCLE* and does not cover anything
of what XP does to support *PRINT-LEVEL*, *PRINT-LENGTH*, and
robustness in the face of malformed arguments. These are vital
features for writing printing functions that really work right all the time.
---
It has been suggested that objects should be looked up in pprint dispatch
tables by looking for the most specific type specifier that applies, rather
than looking for the highest priority type specifier that applies. There
are two problems with this. First, it is possible for two type specifiers
to apply without one being more specific than the other. For example,
consider the type specifiers (OR INTEGER FLOAT) and (OR INTEGER RATIONAL)
or the type specifiers (NOT COMPLEX) and (NOT INTEGER).
A much more serious problem is that the only way to tell whether one type
specifier is more specific than another is to use SUBTYPEP. Unfortunately,
even with the clarifications and extensions introduced by X3J13 with regard
to SUBTYPEP in particular and the type system in general, SUBTYPEP is not
very dependable. There are critical situations where it cannot reasonably
work in any implementation (e.g., when a type specifier contains SATISFIES)
and many other situations where it can vary from one implementation to
another. Unfortunately, pprint dispatch tables are expected to contain
type specifiers containing SATISFIES on a regular basis. In addition, the
variability in SUBTYPEP would reduce the portability of user-defined pprint
dispatch tables if dispatching through them depended on SUBTYPEP. All in
all, it seems much better for the pretty printing to rely on priorities
rather than on SUBTYPEP
---
It has been noted by Dave Moon that things would be much more elegant if
SET-PPRINT-DISPATCH could be expressed directly as a CLOS DEFMETHOD for
an appropriate generic function. Dick Waters agrees with this. However,
SET-PPRINT-DISPATCH depends on type specifiers that are more complex than
the ones CLOS deals with and ones that do not have clear subtype/supertype
relationships, compensating for the latter problem by supporting numerical
priorities to disambiguate things. (The defaulting behavior is a key
feature of the pretty printer.) At the very least, this means that
SET-PPRINT-DISPATCH will not fit into CLOS in a simple way.
Given the problems, Moon suggests that "it does seem that right now it
might be best to keep a separate SET-PPRINT-DISPATCH macro, with the
idea that the expansion is implementation-dependent at the moment, but
might some day be changed to be defined to expand into DEFMETHOD. I
haven't looked to see whether any syntactic changes would be appropriate
to make that transition smoother."
(Waters also worries that the overhead needed to locate the right CLOS
method would seriously degrade the pretty printer, because the printer
has to do this for every part of every object printed. This dispatching is
currently done by very fast code that is tuned to take advantage of the
observed distribution of kinds of objects that have special pretty
printers attached to them. Even with this special purpose code,
dispatching takes a significant part of the pretty printer's time.)
---
Dave Moon also comments that it is not good to have something that looks
like a type specifier (i.e., the extended form of the CONS type specifier
used by SET-PPRINT-DISPATCH) and yet is not a real type specifier. He
suggests that we should either amend Common Lisp to accept the extended
form of the CONS type specifier, or stop having SET-PPRINT-DISPATCH use it.
Many of the members of the ad hoc pretty printing subcommittee agree that
(CONS car-type cdr-type) should be made a full fledged type specifier. In
fact it has been suggested that there should be a much wider variety of
ways to talk about lists and their contents than currently exists.
However, the subcommittee feels that it would be going significantly beyond
our charter to change anything about the type system.
Therefore, we opted for the opposite tack of making it very clear in this
proposal that while the function SET-PPRINT-DISPATCH accepts the form
(CONS car-type cdr-type), this form is not a type specifier.
---
To a considerable extent, the design of the XP interface is completely
neutral about the issue of variable- versus fixed- width fonts. In
particular, most of the discussion of how the formating proceeds either
talks about absolute positions of zero or talks about something being
in the same horizontal position as something else. These statements are
all font-independent. (Further, although Waters' current implementation
does not support variable-width fonts, the algorithms used could be
extended to support them without radical changes.)
Nevertheless, there are 8 places where users specify explicit non-zero
lengths: the variables *PRINT-RIGHT-MARGIN* and *PRINT-MISER-WIDTH*,
the numeric arguments to ~T, ~I, and ~/pprint-tabular/ and their associated
functions PPRINT-TAB, PPRINT-INDENT, and PPRINT-TABULAR.
It is proposed that all of these lengths be in the same units, and that
this unit be ems (the length of an "m" in the font currently being used
to output characters to the relevant output stream at the moment that
the command is encountered or a variable is consulted).
It is further proposed that users and implementors be advised to set things
up so that explicit lengths do not have to be specified. For implementors,
this means making streams smart enough that they know how wide they are.
(This avoids the use of *PRINT-RIGHT-MARGIN*.) For users, this means
relying on streams knowing their own widths (which is a good idea for
adaptability in any case) and using ~:I (instead of just ~I) to specify
indentations wherever possible. Further, it should be noted that since
*PRINT-MISER-WIDTH* is essentially heuristic in nature, it does not
matter if its value is only an approximate length and users will only need
to change the value of *PRINT-MISER-WIDTH* in unusual situations.
This leaves only tabbing as an area where explicit lengths have to be
specified on a regular basis. Fortunately, approximate lengths are often
acceptable in this situation as well.
---
The currently proposed syntax for PPRINT-LOGICAL-BLOCK was suggested by
Dave Moon based on his experience with similar constructs at Symbolics.
Waters had originally suggested using a standard binding pair to specify
the underlying stream separately from the variable used to hold the special
stream used within the logical block. However, this invites user mistakes.
The problem is that peculiar results will be obtained if any I/O is sent
directly to the underlying stream from within the logical block. By
arranging the syntax as proposed, the same variable is always used for the
special stream within the logical block as is used for the underlying
stream outside of the block. As a result, it is syntactically impossible
to directly access the underlying stream (unless it is stored in two
different variables) and the user is painlessly saved from making mistakes.
Also, the PPRINT-LOGICAL-BLOCK macro is rendered more compact.
---
Another interesting issue is the interaction between pretty-printing and
*PRINT-READABLY*. Note first, that WITH-STANDARD-IO-SYNTAX binds
*PRINT-PRETTY* to NIL. In general one should expect that maximum machine
readability will be achieved when *PRINT-PRETTY* is nil. However, in
analogy with issue DATA-IO, it is reasonable to require that
PPRINT-DISPATCH functions must obey *PRINT-READABLY*. Note that XP
supports a number of features (e.g., automatic handling of malformed lists)
that are explicitly designed to make it easier to write pprint functions
that reliably produce machine readable output.
---
Steve Haflich comments that the macro FORMATTER provides an opportunity at
some later time to do something about making FORMAT control strings more
readable. Specifically, when using FORMATTER, one should not hesitate to
insert lots of newlines, because they will not waste any space since the
FORMAT string will be converted into a function in any case. Second, it
would be nice to add some feature to format strings that would allow
comments to appear in a FORMAT string. (This is what ~; probably should
have been reserved to mean.)
---
Walter van Roggen notes that the automatic handling of *PRINT-LEVEL* by XP
obsoletes the need for the third argument to print functions for
structures. (This argument was very seldom used in any case.) It might be
appropriate at some future time to get rid of the third argument to print
functions for structures. However, this would not be an upward compatible
change.
---
The pretty printer control interface is included in this proposal for two
key reasons:
(A) Something like the following is probably a key part of the argument
against the pretty-printer-control FORMAT interface in the minds of those
that oppose it. "Including the pretty-printer-control FORMAT interface
might be a gain or a loss for Common Lisp, but nothing could possibly be
lost by not including it. You can always just use FORMAT as it is now, and
you can get pretty printing control with the new functions." However, this
argument is not quite true.
As can be seen in the example below, pretty printer control information has
to be highly intertwined with the rest of the output functions. As a
practical matter, this means that if there is no pretty-printer-control
FORMAT interface you cannot make much if any use of FORMAT when specifying
output that contains pretty-printer-control information. (For instance in
the 20 line example below, there is not even one place where there are two
printing operations in a row that could be combined into a call on FORMAT
if there were no pretty-printer-control FORMAT interface. This means that
there would be no benefit to using FORMAT anywhere in this example.)
The consequence of the above is that, even though pretty printer control
and FORMAT inherently have nothing to do with each other, adding pretty
printer control facilities without adding a pretty-printer-control FORMAT
interface, effectively requires people to choose between FORMAT and pretty
printer control. In order to allow people to separately decide whether
they do or do not want to user FORMAT and do or do not want to get involved
with controlling the pretty printer, you have to provide a
pretty-printer-control FORMAT interface.
This issue is intensified given a pretty printer such as XP that is
within epsilon of just as fast as the ordinary non-pretty printer,
because many people choose to set *PRINT-PRETTY* to T all of the time.
They are therefore naturally led to wanting to specify at least some
pretty printing control information whenever defining a print function
for a structure, or an error message, or in fact essentially any kind
of output. If there is no pretty-printer-control FORMAT interface,
then this would effectively bar the use of FORMAT for these users.
In summary, rather than pushing FORMAT into new territory, the
pretty-printer-control FORMAT interface is required to prevent FORMAT from
sliding into obsolescence.
(B) Although it probably is not convincing to those that do not like
FORMAT, a additional reason for having a pretty-printer-control FORMAT
interface is exactly the same as the reason for having FORMAT at all:
compactness. For example, as shown in the main proposal, the format string:
"~:<~W~^ ~:<~@{~:<~@{~W~^ ~_~}~:>~^ ~:_~}~:>~1I~@{~^ ~_~W~}~:>"
is the same as the expression:
(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)))))
It is of course also true that a line of FORMAT control string is a
lot harder to read than a line of Lisp code. However, FORMAT has
become a permanent fixture of Common Lisp, because a line of FORMAT
control string is a lot easier to read and work with than 20 lines of
Lisp code.
(C) It should also be noted that the way the XP proposal was first written
up made the FORMAT interface look a lot more complex than it is. Only six
things are being added. Two of these just fix problems with FORMAT and do
not have anything to do with pretty printing. Of the remaining four, three
are very simple. Only ~<...~:> could be called complex.
~A and ~S both force the value of *PRINT-ESCAPE*. ~W is needed in
order to write FORMAT strings that obey *PRINT-ESCAPE*.
~/.../ restores (in a simplified form) a feature of FORMAT that was
lost in the first version of Common Lisp. A number of people have
commented that this is something that they like, having nothing to do
with pretty printing. (Note that ~/PPRINT-LINEAR/, ~/PPRINT-FILL/,
and ~/PPRINT-TABULAR/ are not new directives, they are just a
consequence of the fact that ~/.../ can call the functions
PPRINT-LINEAR, PPRINT-FILL, and PPRINT-TABULAR.)
========================= ``Attached Document'' =========================
Proposal (PRETTY-PRINT-INTERFACE:XP):
Full description of XP accompanying version 5 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 macros and functions
PPRINT-LOGICAL-BLOCK, PPRINT-NEWLINE, and PPRINT-INDENT make it possible to
specify pretty printing layout rules as a part of any function that
produces output. They also make it very easy for the detection of
circularity and sharing, and abbreviation based on length and nesting depth
to be supported by the function. The function SET-PPRINT-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-RIGHT-MARGIN* [variable]
A primary goal of pretty printing 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 an implementation
dependent value.
To allow for the possibility of variable-width fonts, *PRINT-RIGHT-MARGIN*
is 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 pretty
printed. If an attempt is made to go beyond *PRINT-LINES* lines, "
.." is printed at the end of the last line followed by all of the
suffixes (closing delimiters) that are pending to be printed.
(let ((*print-right-margin* 25) (*print-lines* 3))
(pprint '(progn (setq a 1 b 2 c 3 d 4))))
B 2
C 3 ..))
(The symbol ".." is printed out to ensure that a reader error will occur
if the output is later read. A symbol different than "..." is used to
indicate that a different kind of abbreviation has occurred.)
*PRINT-PPRINT-DISPATCH* [variable]
When *PRINT-PRETTY* is not NIL, printing is controlled by the `pprint
dispatch table' stored in the variable *PRINT-PPRINT-DISPATCH*. The
initial value of *PRINT-PPRINT-DISPATCH* is implementation dependent and
causes traditional pretty printing of Lisp code. The last section of this
proposal explains how the contents of this table can be changed.
The function WRITE accepts keyword arguments :PPRINT-DISPATCH,
:RIGHT-MARGIN, :LINES, and :MISER-WIDTH corresponding to
*PRINT-PPRINT-DISPATCH*, *PRINT-RIGHT-MARGIN*, *PRINT-LINES*, and
Dynamic Control of the Arrangement of Output
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 1 shows a schematic piece of output. The
characters in the output are represented by "-"s. The positions of
conditional newlines are indicated by digits. The beginnings and ends of
logical blocks are indicated by "<" and ">" respectively.
The output as a whole is a logical block and the outermost section. This
section is indicated by the 0's on the second line of Figure 1. Logical
blocks nested within the output are specified by the macro
PPRINT-LOGICAL-BLOCK. Conditional newline positions are specified by calls
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 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.
PPRINT-NEWLINE kind &OPTIONAL (stream *STANDARD-OUTPUT*) [Function]
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. An error is signalled 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 (e.g., 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), this prefix will always be printed no matter how a
newline originates.
PPRINT-LOGICAL-BLOCK (stream-symbol list [Macro]
&KEY :prefix :per-line-prefix :suffix)
&BODY body
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 (or NIL standing for *STANDARD-OUTPUT* or T standing for *TERMINAL-IO*).
The logical block is printed into this destination stream.
The BODY 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
(e.g., WRITE, PRINC, TERPRI) can be used to print output 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.
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. The
:PREFIX and :PRE-LINE-PREFIX arguments are mutually exclusive. If neither
:PREFIX or :PER-LINE-PREFIX is specified, a :PREFIX of the null string is
assumed. :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. An
error is signalled if :PREFIX and :PRE-LINE-PREFIX are both used or if the
:SUFFIX, :PREFIX, or :PER-LINE-PREFIX do not evaluate to strings.
LIST is interpreted as being a list that 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 *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 easy
to write printing functions that provide full support for depth
abbreviation.)
If either of the three conditions above 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 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-EXIT-IF-LIST-EXHAUSTED [Macro]
PPRINT-EXIT-IF-LIST-EXHAUSTED tests whether or not the LIST passed to
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-IF-LIST-EXHAUSTED is executed outside
of the dynamic extent of this PPRINT-LOGICAL-BLOCK.
PPRINT-POP [Macro]
PPRINT-POP pops elements one at a time off the LIST passed to
PPRINT-LOGICAL-BLOCK obeying *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 PPRINT-LOGICAL-BLOCK.
Each time PPRINT-POP is called, it pops the next value off the LIST
passed to PPRINT-LOGICAL-BLOCK and returns it. However, before doing
this, it performs three tests. If the remaining list is not a list
(i.e., a cons or 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 *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 either of the three conditions above 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.
PPRINT-INDENT relative-to n &OPTIONAL (stream *STANDARD-OUTPUT*) [Function]
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. 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.
(For robustness in the face of variable-width fonts, it is advisable to use
:CURRENT with an N of zero whenever possible.)
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 signalled 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.
PPRINT-TAB kind colnum colinc &OPTIONAL (stream *STANDARD-OUTPUT*) [function]
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 signalled 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.
PPRINT-FILL STREAM LIST &OPTIONAL (COLON? T) ATSIGN? [function]
PPRINT-LINEAR STREAM LIST &OPTIONAL (COLON? T) ATSIGN? [function]
PPRINT-TABULAR STREAM LIST &OPTIONAL (COLON? T) ATSIGN? (TABSIZE 16) [function]
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 SIMPLE-PPRINT-DEFUN below. This
function prints out lists whose cars are DEFUN in the standard way assuming
that the list has exactly length 4.
(defun simple-pprint-defun (*standard-output* list)
(pprint-logical-block (*standard-output* list :prefix "(" :suffix ")")
(write-char #\space)
(pprint-newline :miser)
(pprint-indent :current 0)
(write-char #\space)
(pprint-newline :fill)
(pprint-indent :block 1)
(write-char #\space)
(pprint-newline :linear)
Suppose that one evaluates the following:
(simple-pprint-defun *standard-output* '(defun prod (x y) (* x y)))
If the line width available is greater than or equal to 26, then all of the
output appears on one line. If the line width available is reduced to 25,
a line break is inserted at the linear-style conditional newline before the
expression (* X Y), producing the output shown. 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 line width available is 15, a line break is also inserted at the
fill style conditional newline before the argument list. The call on
(PPRINT-INDENT :CURRENT 0) causes the argument list to line up under the
function name.
(DEFUN PROD
(X Y)
(* X Y))
If *PRINT-MISER-WIDTH* were greater than or equal to 14, the example output
above would have been as follows, because all indentation changes are
ignored in miser mode and line breaks are inserted at miser-style
conditional newlines.
PROD
(X Y)
(* X Y))
---
As an example of a per-line prefix, consider that evaluating the following
produces the output shown with a line width of 20 and *PRINT-MISER-WIDTH*
of NIL.
(pprint-logical-block (*standard-output* nil :per-line-prefix ";;; ")
(simple-pprint-defun *standard-output* '(defun prod (x y) (* x y))))
;;; (DEFUN PROD
;;; (X Y)
;;; (* X Y))
---
As a more complex (and realistic) example, consider the function PPRINT-LET
below. This specifies how to print a LET in the standard style. It is more
complex than the example above, because it has to deal with nested structure.
Also, unlike the example above it contains complete code to readably print 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.
(defun pprint-let (*standard-output* 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 one evaluates the following with *PRINT-LEVEL* 4, and
(pprint-let *standard-output*
'#1=(let (x (*print-length* (f (g 3)))
(z . 2) (k (car y)))
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)))
#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)))
#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
(F #))
(Z . 2) ...)
...)
---
The function PPRINT-TABULAR could be defined as follows.
(defun pprint-tabular (s list &optional (colon? T) atsign? (tabsize nil))
(when (null tabsize) (setq tabsize 16))
(pprint-logical-block (s list :prefix (if colon? "(" "")
:suffix (if colon? ")" ""))
(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 tabsize s)
(pprint-newline :fill s))))
Evaluating the following with a line length of 25 produces the output shown.
(princ "Roads ")
(pprint-tabular *standard-output* '(elm main maple center) nil nil 8)
Roads ELM MAIN
MAPLE CENTER
---
The function below prints a vector using #(...) notation.
(defun pprint-vector (*standard-output* v)
(pprint-logical-block (nil nil :prefix "#(" :suffix ")")
(loop (pprint-pop)
(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 *standard-output* '#(12 34 567 8 9012 34 567 89 0 1 23))
#(12 34 567 8
9012 34 567
89 0 1 23)
Format Directive Interface
The primary interface to operations for dynamically determining the
arrangement of output is provided through the functions above. However, an
additional interface is provided via a set of new format directives.
This is done, because as shown by the examples in this section and the
next, FORMAT strings are typically a much more compact way to specify
pretty printing. In addition, without such an interface, one would have to
abandon the use of FORMAT when interacting with the pretty printer.
~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 the detection of circularity and
sharing. If *PRINT-CIRCLE* (and possibly *PRINT-SHARED*) is not NIL
and ~W is applied to an argument that is a circular (or shared)
reference, an appropriate #n# marker is inserted in the output instead
of printing the argument.
~_ [format directive]
CONDITIONAL-NEWLINE -- Without any modifiers, ~_ is the same as
(PPRINT-NEWLINE :LINEAR). ~@_ is the same as (PPRINT-NEWLINE :MISER).
~:_ is the same as (PPRINT-NEWLINE :FILL). ~:@_ is the same as
(PPRINT-NEWLINE :MANDATORY).
~<...~:> [format directive]
LOGICAL BLOCK -- If ~:> is used to terminate a ~<...~>, the directive
is equivalent to a call on PPRINT-LOGICAL-BLOCK. The FORMAT argument
corresponding to the ~<...~:> directive is treated in the same way as
the LIST argument to PPRINT-LOGICAL-BLOCK, thereby providing automatic
support for non-list arguments and the detection of circularity,
sharing, and depth abbreviation. The portion of the FORMAT control
string nested within the ~<...~:> specifies the :PREFIX (or
:PER-LINE-PREFIX), :suffix}, and body of the PPRINT-LOGICAL-BLOCK.
The FORMAT string portion enclosed by ~<...~:> can be divided into
segments ~<prefix~;body~;suffix~:> by ~; directives. If the first
section is terminated by ~@;, it specifies a per-line prefix rather
than a simple prefix. The prefix and suffix cannot contain FORMAT
directives. An error is signalled if either the prefix or suffix
fails to be a constant string or if the enclosed portion is 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 (i.e., ~:<...~:>), the prefix
and suffix default to "(" and ")" (respectively) instead of the null
string.
The body segment can be any arbitrary FORMAT control string. This
FORMAT control string is applied to the elements of the list
corresponding to the ~<...~:> directive as a whole. Elements are
extracted from this list using PPRINT-POP, thereby providing automatic
support for malformed lists, and the detection of circularity,
sharing, and length abbreviation. Within the body segment, ~^ acts
like PPRINT-EXIT-IF-LIST-EXHAUSTED.
~<...~:> supports a feature not supported by PPRINT-LOGICAL-BLOCK. If
~:@> is used to terminate the directive (i.e., ~<...~:@>), 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.
If the atsign modifier is used with ~<...~:>, the entire remaining argument
list is passed to the directive as its argument. All of the remaining
arguments are always consumed by ~@<...~:>, even if they are not all used
by the FORMAT string nested in the directive. Other than the difference in
its argument, ~@<...~:> is exactly the same as ~<...~:> except that
circularity detection is not applied if ~@<...~:> is encountered at top
level in a {\cd format} string. This ensures that circularity detection is
applied only to data lists, not to {\cd format} argument lists.
" . #n#" is printed if circularity or sharing has
to be indicated for its argument as a whole.
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, an error is signalled if
any of these directives is nested within ~<...~>. Beyond this, an
error is also signalled if the ~<...~:;...~> form of ~<...~> is used
in the same FORMAT string with ~W, ~_, ~<...~:>, ~I, or ~:T.
~I [format directive]
INDENT -- ~nI is the same as (PPRINT-INDENT :BLOCK N).
~n:I is the same as (PPRINT-INDENT :CURRENT N). In both cases, N defaults
to zero, if it is omitted.
~:T [format directive]
TABULATE -- If the colon modifier is used with the ~T directive, the
tabbing computation is done relative to the horizontal position where the
section immediately containing the directive begins, rather than with
respect to a horizontal position of zero. The numerical parameters are
both interpreted as being in units of ems and both default to 1.
~n,m:T is the same as (PPRINT-TAB :SECTION N M).
~n,m:@T is the same as (PPRINT-TAB :SECTION-RELATIVE N M).
~/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. NAME can be any arbitrary string that does not contain a "/".
All of the characters in NAME are treated as if they were upper case. If
NAME contains a ":" or "::", then everything up to but not including the
first ":" or "::" is taken to be a string that names a package. Everything
after the first ":" or "::" (if any) is taken to be a string that names a
symbol. The function corresponding to a ~/name/ directive is obtained by
looking up the symbol that has the indicated name in the indicated package.
If NAME does not contain a ":" or "::", then the whole name string is
looked up in the USER package.
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.
The three functions PPRINT-LINEAR, PPRINT-FILL, and PPRINT-TABULAR are
specifically designed so that they can be called by ~/.../ (i.e.,
~/PPRINT-LINEAR/, ~/PPRINT-FILL/, and ~/PPRINT-TABULAR/). In particular
they take colon and atsign arguments.
---
As examples of the convenience of specifying pretty printing with FORMAT
strings, consider that the first two functions used as examples in the last
section can be compactly defined as follows. The function PPRINT-VECTOR
cannot be defined using FORMAT, because the data structure it traverses is
not a list. The function PPRINT-TABULAR is inconvenient to define using
FORMAT, because of the need to pass its TABSIZE argument through to a ~:T
directive nested within an iteration over a list.
(defun simple-pprint-defun (*standard-output* list)
(format T "~:<~W ~@_~:I~W ~:_~W~1I ~_~W~:>" list))
(defun pprint-let (*standard-output* list)
(format T "~:<~W~^ ~:<~@{~:<~@{~W~^ ~_~}~:>~^ ~:_~}~:>~1I~@{~^ ~_~W~}~:>" list))
Compiling Format Control Strings
The control strings used by FORMAT are essentially programs that perform
printing. The macro FORMATTER provides the efficiency of using a compiled
function for printing without losing the compactness of FORMAT control
strings.
FORMATTER control-string [macro]
CONTROL-STRING must be a literal string. An error is signalled if
CONTROL-STRING is not a valid FORMAT control string. The macro FORMATTER
expands into an expression of the form (FUNCTION (LAMBDA (STREAM &REST
ARGS) ...)) that does the printing specified by CONTROL-STRING. The
LAMBDA created accepts an output stream as its first argument and zero or
more data values as its remaining arguments. The value returned by the
LAMBDA is the tail (if any) of the data values that are not printed out by
CONTROL-STRING. (E.g., if the CONTROL-STRING is "~A~A" the CDDR (if any)
of the data values is returned.)
For instance: (formatter "~%~2@{~S, ~}") is equivalent to
#'(lambda (stream &rest args)
(dotimes (n 2)
(write-string ", " stream))
args)
In support of the above, FORMAT is extended so that it accepts functions as
its second argument as well as strings. When a function is provided, it
must be a function of the form created by FORMATTER. The function 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 and return the unused tail of the
arguments (if any). The directives ~? and ~{~} with no body are also
extended so that they accept functions as well as control strings.
Every other standard function that takes a FORMAT string as an argument
(e.g., ERROR and WARN) are also extended so that they can accept functions
of the form above instead.
Pretty Print Dispatch Tables
When *PRINT-PRETTY* is not NIL, the pprint dispatch table in the variable
*PRINT-PPRINT-DISPATCH* controls how objects are printed. The information
in this table takes precedence over all other mechanisms for specifying how
to print objects. In particular, it overrides user-defined PRINT-OBJECT
methods and print functions for structures. However, if there is no
specification for how to pretty print a particular kind of object, it is then
printed using the standard mechanisms as if *PRINT-PRETTY* were NIL.
Pprint dispatch tables are mappings from keys to pairs of values. The keys
are type specifiers. The values are functions and numerical priorities.
Basic insertion and retrieval is done based on the keys with the equality
of keys being tested by EQUAL. The function to use when pretty printing an
object is chosen by finding the highest priority function from
*PRINT-PPRINT-DISPATCH* that is associated with a type specifier that
matches the object.
COPY-PPRINT-DISPATCH &optional (table *PRINT-PPRINT-DISPATCH*) [function]
A copy is made of TABLE, which defaults to the current pprint dispatch
table. If TABLE is NIL, a copy is returned of the initial value of
PPRINT-DISPATCH object &optional (table *PRINT-PPRINT-DISPATCH*) [function]
This retrieves the highest priority function from a pprint table that is
associated with a type specifier in the table that matches OBJECT. The
function is chosen by finding all the type specifiers in TABLE that match
the object and selecting the highest priority function associated with any
of these type specifiers. If there is more than one highest priority
function, an arbitrary choice is made. If no type specifiers match the
object, a function is returned that prints object with *PRINT-PRETTY* bound
to NIL.
As a second return value, PPRINT-DISPATCH returns a flag that is T if a
matching type specifier was found in TABLE and NIL if not.
TABLE (which defaults to *PRINT-PPRINT-DISPATCH*) must be a pprint dispatch
table. TABLE can be NIL, in which case retrieval is done in the initial
value of *PRINT-PPRINT-DISPATCH*.
When *PRINT-PRETTY* is T, (WRITE OBJECT :STREAM S) is equivalent to
(FUNCALL (PPRINT-DISPATCH OBJECT) S OBJECT).
SET-PPRINT-DISPATCH type-specifier function [function]
&optional (priority 0) (table *PRINT-PPRINT-DISPATCH*)
This puts an entry into a pprint dispatch table and returns NIL.
TYPE-SPECIFIER must be a valid type specifier and is the key of the entry.
The first action of SET-PPRINT-DISPATCH is to remove any pre-existing entry
associated with TYPE-SPECIFIER. This guarantees that there will never be
two entries associated with the same type specifier in a given pprint
dispatch table. Equality of type specifiers is tested by EQUAL.
Two values are associated with each type specifier in a pprint dispatch
table: a function and a priority. FUNCTION must accept two arguments: the
stream to send output to and the object to be printed. FUNCTION should
pretty print the object on the stream. FUNCTION can assume that object
satisfies TYPE-SPECIFIER. Function must obey *PRINT-READABLY* (see issue
DATA-IO). Any values returned by FUNCTION are ignored.
PRIORITY (which defaults to 0) must be a non-complex number. This
number is used as a priority to resolve conflicts when an object
matches more than one entry. An error is signalled if priority fails
to be a non-complex number.
TABLE (which defaults to *PRINT-PPRINT-DISPATCH*) must be a pprint dispatch
table. The specified entry is placed in this table.
It is permissible for FUNCTION to be NIL. In this situation, there will be
no TYPE-SPECIFIER entry in TABLE after SET-PPRINT-DISPATCH is evaluated.
To facilitate the use of pprint dispatch tables for controlling the pretty
printing of Lisp code, the TYPE-SPECIFIER argument of the function
SET-PPRINT-DISPATCH is allowed to contain constructs of the form
(CONS car-type cdr-type)
This signifies that the corresponding object must be a cons cell whose car
matches the type specifier CAR-TYPE and whose cdr matches the type specifier
CDR-TYPE. The CDR-TYPE can be omitted in which case it defaults to T.
The initial value of *PRINT-PPRINT-DISPATCH* is implementation dependent.
However, the initial entries all use a special class of priorities that
have the property that they are less than every priority that can be
specified using SET-PPRINT-DISPATCH. The benefit of this is that it
guarantees that any pretty printing functions users specify will override
everything in the initial value of *PRINT-PPRINT-DISPATCH*.
----------------------------------------------------------------------
Implementation note: The restriction above is very useful to users
without actually limiting what Common Lisp implementors can do. It is
possible for implementors to set up any kind of pretty printing they
desire using the range of priorities available to them.
----------------------------------------------------------------------
Consider the following examples. The first form restores
*PRINT-PPRINT-DISPATCH* to its initial value. The next two forms then set
up a special way to pretty print ratios. Note that the more specific type
specifier has to be associated with a higher priority.
(setq *print-pprint-dispatch* (copy-pprint-dispatch nil))
#'(lambda (s obj)
(format s "#.(/ ~W ~W)" (numerator obj) (denominator obj))))
(set-pprint-dispatch '(and ratio (satisfies minusp))
#'(lambda (s obj)
(format s "#.(- (/ ~W ~W))" (- (numerator obj)) (denominator obj)))
5)
(pprint '(1/3 -2/3)) prints: (#.(/ 1 3) #.(- (/ 2 3)))
The following two forms illustrate the definition of pretty printing
functions for types of Lisp code. The first form illustrates how to
specify the traditional method for printing quoted objects using "'"
syntax. Note the care taken to ensure that data lists that happen to begin
with QUOTE will be printed readably. The second form specifies that lists
beginning with the symbol MY-LET should print the same way that lists
beginning with LET print when the initial pprint dispatch table is in effect.
(set-pprint-dispatch '(cons (member quote)) ()
(if (and (consp (cdr list)) (null (cddr list)))
(funcall (formatter "'~W") s (cadr list))
(pprint-fill s list)))))
(set-pprint-dispatch '(cons (member my-let)) (pprint-dispatch '(let) nil))
The next example specifies a default method for printing lists that do not
correspond to function calls. Note that, as shown in the definition of
PPRINT-TABULAR above, PPRINT-LINEAR, PPRINT-FILL, and PPRINT-TABULAR are
all defined with optional COLON? and ATSIGN? arguments so that they can be
used as pprint dispatch functions as well as ~/.../ functions.
(set-pprint-dispatch '(cons (not (and symbol (satisfies fboundp))))
#'pprint-fill -5)
with a line length of 9, (pprint '(0 b c d e f g h i j k)) prints:
(0 b c d
e f g h
i j k)
This final example shows how to define a pretty printing function for a
user defined data structure.
(defstruct family mom kids)
(set-pprint-dispatch 'family
#'(lambda (s f)
(funcall (formatter "~@<#<~;~W and ~2I~_~/pprint-fill/~;>~:>")
s (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*, *PRINT-SHARED* and *PRINT-ESCAPE*, and can tolerate
several different kinds of malformity in the data structure. The
output below shows what is printed out with a right margin of 25,
*PRINT-PRETTY* T, *PRINT-ESCAPE* NIL, and a malformed KIDS list.
(write (list 'principal-family
(make-family :mom "Lucy"
:kids '("Mark" "Bob" . "Dan")))
:right-margin 25 :pretty T :escape nil :miser-width nil)
(PRINCIPAL-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 pprint
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.