Skip to content

Commit 10f849f

Browse files
tomfaulhaberstuarthalloway
authored andcommitted
Added the pretty printer, clojure.pprint
Signed-off-by: Stuart Halloway <[email protected]>
1 parent ab6fc90 commit 10f849f

File tree

15 files changed

+4823
-0
lines changed

15 files changed

+4823
-0
lines changed

build.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
<arg value="clojure.test"/>
105105
<arg value="clojure.test.tap"/>
106106
<arg value="clojure.test.junit"/>
107+
<arg value="clojure.pprint"/>
107108
</java>
108109
</target>
109110

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# A Common Lisp-compatible Format Function
2+
cl-format is an implementation of the incredibly baroque Common Lisp format function as specified
3+
in [Common Lisp, the Language, 2nd edition, Chapter 22](http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/clm/node200.html#SECTION002633000000000000000).
4+
5+
Format gives you an easy and powerful way to format text and data for output. It supports rich
6+
formatting of strings and numbers, loops, conditionals, embedded formats, etc. It is really a
7+
domain-specific language for formatting.
8+
9+
This implementation for clojure has the following goals:
10+
11+
* Support the full feature set of the Common Lisp format function (including the X3J13 extensions) with the only exception being concepts that make no sense or are differently interpreted in Clojure.
12+
* Make porting code from Common Lisp easier.
13+
* Provide a more native feeling solution for Clojure programmers than the Java format method and its relatives.
14+
* Be fast. This includes the ability to precompile formats that are going to be used reptitively.
15+
* Include useful error handling and comprehensive documentation.
16+
17+
## Why would I use cl-format?
18+
19+
For some people the answer to this question is that they are used to
20+
Common Lisp and, therefore, they already know the syntax of format
21+
strings and all the directives.
22+
23+
A more interesting answer is that cl-format provides a way of
24+
rendering strings that is much more suited to Lisp and its data
25+
structures.
26+
27+
Because iteration and conditionals are built into the directive
28+
structure of cl-format, it is possible to render sequences and other
29+
complex data structures directly without having to loop over the data
30+
structure.
31+
32+
For example, to print the elements of a sequence separated by commas,
33+
you simply say:
34+
35+
(cl-format true "~{~a~^, ~}" aseq)
36+
37+
(This example is taken from
38+
[Practical Common Lisp](http://www.gigamonkeys.com/book/)
39+
by Peter Seibel.)
40+
41+
The corresponding output using Clojure's Java-based _format_ function
42+
would involve a nasty loop/recur with some code to figure out about
43+
the commas. Yuck!
44+
45+
## Current Status of cl-format
46+
47+
cl-format is 100% compatible with the Common Lisp standard as
48+
specified in CLtLv2.
49+
This includes all of the functionality of Common
50+
Lisp's format function including iteration, conditionals,
51+
text justification and rich
52+
options for displaying real and integer values. It also includes the
53+
directives to support pretty printing structured output.
54+
55+
If you find a bug in a directive, drop me a line
56+
with a chunk of code that exhibits the bug and the version of
57+
cl-format you found it in and I'll try to get it fixed.
58+
59+
I also intend to have good built-in documentation for the directives,
60+
but I haven't built that yet.
61+
62+
The following directives are
63+
not yet supported: ~:T and ~@:T (but all other forms of ~T work)
64+
and extensions with ~/.
65+
66+
The pretty printer interface is similar, but not identical to the
67+
interface in Common Lisp.
68+
69+
Next up:
70+
71+
* Support for ~/
72+
* True compiled formats
73+
* Restructure unit tests into modular chunks.
74+
* Import tests from CLISP and SBCL.
75+
* Unit tests for exception conditions.
76+
* Interactive documentation
77+
78+
## How to use cl-format
79+
80+
### Loading cl-format in your program
81+
82+
Once cl-format is in your path, adding it to your code is easy:
83+
84+
(ns your-namespace-here
85+
(:use [clojure.pprint :only (cl-format)]))
86+
87+
If you want to refer to the cl-format function as "format" (rather
88+
than using the clojure function of that name), you can use this idiom:
89+
90+
(ns your-namespace-here
91+
(:refer-clojure :exclude [format])
92+
(:use clojure.pprint))
93+
94+
(def format cl-format)
95+
96+
You might want to do this in code that you've ported from Common Lisp,
97+
for instance, or maybe just because old habits die hard.
98+
99+
From the REPL, you can grab it using (use):
100+
101+
(use 'clojure.pprint)
102+
103+
### Calling cl-format
104+
105+
cl-format is a standard clojure function that takes a variable number
106+
of arguments. You call it like this:
107+
108+
(cl-format stream format args...)
109+
110+
_stream_ can be any Java Writer (that is java.io.Writer) or the values
111+
_true_, _false_, or _nil_. The argument _true_ is identical to using
112+
`*`out`*` while _false_ or _nil_ indicate that cl-format should return
113+
its result as a string rather than writing it to a stream.
114+
115+
_format_ is either a format string or a compiled format (see
116+
below). The format string controls the output that's written in a way
117+
that's similar to (but much more powerful than) the standard Clojure
118+
API format function (which is based on Java's
119+
java.lang.String.Format).
120+
121+
Format strings consist of characters that are to be written to the
122+
output stream plus directives (which are marked by ~) as in "The
123+
answer is ~,2f". Format strings are documented in detail in
124+
[*Common Lisp the Language*, 2nd edition, Chapter 22](http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/clm/node200.html#SECTION002633000000000000000).
125+
126+
_args_ is a set of arguments whose use is defined by the format.
127+
128+
## Using column aware streams across format invocations
129+
130+
Writers in Java have no real idea of current column or device page width, so the format
131+
directives that want to work relative to the current position on the
132+
page have nothing to work with. To deal with this, cl-format contains
133+
an extension to writer called pretty-writer. A pretty-writer watches the
134+
output and keeps track of what column the current output is going to.
135+
136+
When you call format and your format includes a directive that cares
137+
about what column it's in (~T, ~&, ~<...~>), cl-format will
138+
automatically wrap the Writer you passed in with a pretty-writer. This
139+
means that by default all cl-format statements act like they begin on
140+
a fresh line and have a page width of 72.
141+
142+
For many applications, these assumptions are fine and you need to do
143+
nothing more. But sometimes you want to use multiple cl-format calls
144+
that output partial lines. You may also want to mix cl-format calls
145+
with the native clojure calls like print. If you want stay
146+
column-aware while doing this you need to create a pretty-writer of
147+
your own (and possibly bind it to `*`out`*`).
148+
149+
As an example of this, this function takes a nested list and prints it
150+
as a table (returning the result as a string):
151+
152+
(defn list-to-table [aseq column-width]
153+
(let [string-writer (java.io.StringWriter.)
154+
stream (get-pretty-writer string-writer)]
155+
(binding [*out* stream]
156+
(doseq [row aseq]
157+
(doseq [col row]
158+
(cl-format true "~4D~7,vT" col column-width))
159+
(prn)))
160+
(.flush stream)
161+
(.toString string-writer)))
162+
163+
(In reality, you'd probably do this as a single call to cl-format.)
164+
165+
The get-pretty-writer function takes the Writer to wrap and
166+
(optionally) the page width (in columns) for use with ~<...~>.
167+
168+
## Examples
169+
170+
The following function uses cl-format to dump a columnized table of the Java system properties:
171+
172+
(defn show-props [stream]
173+
(let [p (mapcat
174+
#(vector (key %) (val %))
175+
(sort-by key (System/getProperties)))]
176+
(cl-format stream "~30A~A~%~{~20,,,'-A~10A~}~%~{~30A~S~%~}"
177+
"Property" "Value" ["" "" "" ""] p)))
178+
179+
There are some more examples in the pretty print examples gallery at
180+
http://github.com/tomfaulhaber/pprint-examples:
181+
182+
* hexdump - a program that uses cl-format to create a standard formatted hexdump of the requested stream.
183+
* multiply - a function to show a formatted multipication table in a very "first-order" way.
184+
* props - the show-props example shown above.
185+
* show_doc - some utilities for showing documentation from various name spaces.
186+
187+
## Differences from the Common Lisp format function
188+
189+
The floating point directives that show exponents (~E, ~G) show E for
190+
the exponent character in all cases (unless overridden with an
191+
_exponentchar_). Clojure does not distinguish between floats and
192+
doubles in its printed representation and neither does cl-format.
193+
194+
The ~A and ~S directives accept the colon prefix, but ignore it since
195+
() and nil are not equivalent in Clojure.
196+
197+
Clojure has 3 different reader syntaxes for characters. The ~@c
198+
directive to cl-format has an argument extension to let you choose:
199+
200+
* ~@c (with no argument) prints "\c" (backslash followed by the printed representation of the character or \newline, \space, \tab, \backspace, \return)
201+
* ~'o@c prints "\oDDD" where DDD are the octal digits representing the character.
202+
* ~'u@c prints "\uXXXX" prints the hex Unicode representation of the character.

0 commit comments

Comments
 (0)