1 ; PP.LSP -- a pretty-printer for XLISP.
3 ; Adapted by Jim Chapman (Bix: jchapman) from a program written originally
4 ; for IQLISP by Don Cohen. Copyright (c) 1984, Don Cohen; (c) 1987, Jim
5 ; Chapman. Permission for non-commercial use and distribution is hereby
6 ; granted. Modified for XLISP 2.0 by David Betz.
8 ; In addition to the pretty-printer itself, this file contains a few functions
9 ; that illustrate some simple but useful applications.
11 ; The basic function accepts two arguments:
15 ; where OBJECT is any Lisp expression, and STREAM optionally specifies the
16 ; output (default is *standard-output*).
18 ; PP-FILE pretty-prints an entire file. It is what I used to produce this
19 ; file (before adding the comments manually). The syntax is:
21 ; (PP-FILE "filename" STREAM)
23 ; where the file name must be a string or quoted, and STREAM, again, is the
24 ; optional output destination.
26 ; PP-DEF works just like PP, except its first argument is assumed to be the
27 ; name of a function or macro, which is translated back into the original
28 ; DEFUN or DEFMACRO form before printing.
31 ; MISCELLANEOUS USAGE AND CUSTOMIZATION NOTES:
33 ; 1. The program uses tabs whenever possible for indentation.
34 ; This greatly reduces the cost of the blank space. If your output
35 ; device doesn't support tabs, set TABSIZE to NIL -- which is what I
36 ; did when I pretty-printed this file, because of uncertainty
37 ; about the result after uploading.
39 ; 2. Printmacros are used to handle special forms. A printmacro is not
40 ; really a macro, just an ordinary lambda form that is stored on the
41 ; target symbol's property list. The default printer handles the form
42 ; if there is no printmacro or if the printmacro returns NIL.
44 ; 3. Note that all the pretty-printer subfunctions, including the
45 ; the printmacros, return the current column position.
47 ; 4. Miser mode is not fully implemented in this version, mainly because
48 ; lookahead was too slow. The idea is, if the "normal" way of
49 ; printing the current expression would exceed the right margin, then
50 ; use a mode that conserves horizontal space.
52 ; 5. When PP gets to the last 8th of the line and has more to print than
53 ; fits on the line, it starts near the left margin. This is not
54 ; wonderful, but neither are the alternatives. If you have a better
57 ; 6. Storage requirements are about 1450 cells to load.
59 ; 7. I tested this with XLISP 1.7 on an Amiga.
61 ; 8. TAA modified to support prettyprinting arrays. Fixed bug printing
64 ; 9. TAA modified to support prettyprinting of structures, and some code
65 ; cleanup. Also added PP-PAIR-FORM to handle setq like structures
68 ; 10. TAA: It should be noted that you can't pretty print circular lists,
69 ; nor can you successfully read back the following:
70 ; * uninterned symbols, for instance those generated with gensym
71 ; as part of automatically generated code
72 ; * closures, since their environment cannot be reconstructed. These
73 ; are not even expanded.
74 ; * subrs, fsubrs, and streams cannot be represented
76 ; 11. TAA modified so that non-class objects are shown by sending the
77 ; message :storeon (see classes.lsp), printing #. before the expression
78 ; making it an object literal.
80 ; 11. TAA modified so that *print-level* and *print-length* are bound to NIL
81 ; during the course of execution.
84 (unless (find-package "TOOLS")
85 (make-package "TOOLS" :use
'("XLISP")))
89 (export '(tabsize maxsize miser-size pp-file pp-def pp
))
91 ;(DEFUN SYM-FUNCTION (X) ;for Xlisp 1.7
92 ; (CAR (SYMBOL-VALUE X)))
93 (defun sym-function (x) ;for Xlisp 2.0
94 (get-lambda-expression (symbol-function x
)))
96 (defvar tabsize
8) ;set this to NIL for no tabs
98 (defvar maxsize
60) ;for readability, PP tries not to print more
99 ;than this many characters on a line
101 (defvar miser-size
2) ;the indentation in miser mode
103 (defvar min-miser-car
4) ;used for deciding when to use miser mode
105 (defvar max-normal-car
9) ;ditto
107 (defconstant pp-lpar
"(") ; self evident
108 (defconstant pp-rpar
")")
109 (defconstant pp-space
" ")
110 (defconstant pp-immed
"#.")
112 ; The following function prints a file
114 (defun pp-file (filename &optional streamout
)
115 (or streamout
(setq streamout
*standard-output
*))
116 (princ "; Listing of " streamout
)
117 (princ filename streamout
)
120 (do* ((fp (open filename
)) (expr (read fp nil
) (read fp nil
)))
121 ((null expr
) (close fp
))
126 ; Print a lambda or macro form as a DEFUN or DEFMACRO:
128 (defmacro pp-def
(who &optional stream
)
129 `(pp (make-def ,who
) ,stream
))
131 (defmacro make-def
(name &aux expr type
)
132 (setq expr
(sym-function name
))
134 (cadr (assoc (car expr
)
135 '((lambda defun
) (macro defmacro
)))))
137 (append (list type name
) (cdr expr
))))
141 ; The pretty-printer high level function:
144 (defun pp (x &optional stream
)
145 (let (*print-level
* *print-length
*) ; set special vars to NIL
146 (or stream
(setq stream
*standard-output
*))
151 ; print X on STREAM, current cursor is CURPOS, and right margin is RMARGIN
153 (defun pp1 (x stream curpos rmargin
154 &aux
(anarray (arrayp x
))
155 (astruct (typep x
'(and struct
(not random-state
))))
157 (cond (anarray (setq x
(coerce x
'cons
)))
158 ((and (objectp x
) (not (classp x
)))
159 (princ pp-immed stream
) ; immediate execute literal
160 (setq curpos
(+ curpos
2))
161 (setq x
(send x
:storeon
))))
162 (cond (astruct (pp-astruct x stream curpos rmargin
))
163 ((not (consp x
))(prin1 x stream
) (+ curpos
(flatsize x
)))
164 ((printmacrop x stream curpos rmargin
))
165 ((and (> (flatsize x
) (- rmargin curpos
))
166 (< (* 8 (- rmargin curpos
)) rmargin
))
167 (setq size
(+ (/ rmargin
8) (- curpos rmargin
)))
168 (pp-moveto stream curpos size
)
169 (setq position
(pp1 x stream size rmargin
))
170 (pp-moveto stream position size
))
171 (t (when anarray
(princ "#" stream
) (setq curpos
(1+ curpos
)))
172 (princ pp-lpar stream
)
174 (pp1 (car x
) stream
(1+ curpos
) rmargin
))
175 (cond ((and (>= (setq width
(- rmargin position
))
176 (setq size
(flatsize (cdr x
))))
178 (pp-rest-across (cdr x
) stream position rmargin
))
180 (pp-moveto stream position curpos
)
181 (pp-rest (cdr x
) stream curpos rmargin
))
182 ((> (- position curpos
) max-normal-car
)
183 (pp-moveto stream position
(+ curpos miser-size
))
184 (pp-rest (cdr x
) stream
(+ curpos miser-size
) rmargin
))
185 (t (pp-rest (cdr x
) stream position rmargin
))))))
187 ; PP-MOVETO controls indentating and tabbing.
188 ; If CUR > GOAL then goes to new line first.
191 (defun pp-moveto (stream curpos goalpos
&aux i
)
192 (cond ((> curpos goalpos
)
197 ((< (- goalpos curpos
) tabsize
))
199 (setq curpos
(+ curpos tabsize
))))))
200 (dotimes (i (- goalpos curpos
)) (princ pp-space stream
))
203 ; can print the rest of the list without new lines
205 (defun pp-rest-across (x stream curpos rmargin
&aux position
)
206 (setq position curpos
)
209 (cond ((null x
) (princ pp-rpar stream
) (return (1+ position
)))
213 (princ pp-rpar stream
)
214 (return (+ 4 position
(flatsize x
))))
215 (t (princ pp-space stream
)
217 (pp1 (car x
) stream
(1+ position
) rmargin
))
221 ; Can print the rest of the list, but must use new lines for each element
224 (defun pp-rest (x stream curpos rmargin
&aux position pos2
)
225 (setq position curpos
)
228 (cond ((null x
) (princ pp-rpar stream
) (return (1+ position
)))
230 (and (> (flatsize x
) (- (- rmargin position
) 3))
231 (setq position
(pp-moveto stream position curpos
)))
234 (princ pp-rpar stream
)
235 (return (+ position
4 (flatsize x
))))
237 (not (typep (car x
) '(or list array struct
)))
238 (<= (setq pos2
(+ 1 position
(flatsize (car x
))))
240 (<= pos2
(+ curpos maxsize
)))
241 (princ pp-space stream
)
242 (prin1 (car x
) stream
)
243 (setq position pos2
))
244 (t (pp-moveto stream position
(1+ curpos
))
246 (pp1 (car x
) stream
(1+ curpos
) rmargin
))))
247 (cond ((and (consp (car x
)) (cdr x
))
248 (setq position
(pp-moveto stream position curpos
))))
253 ; Handles structures by printing in form:
254 ; #S(structtype :slot val
258 ; code does not check for defaults.
260 (defun pp-astruct (x stream pos rmar
&aux cur snames args
)
262 snames
(mapcar #'car
(get (type-of x
) '*struct-slots
*))
264 (mapcan #'(lambda (p)
268 (strcat (string (type-of x
))
274 (if (and (>= (- rmar pos
) (+ 2 (flatsize x
)))
275 (<= (flatsize x
) maxsize
))
276 (pp1 (cons (type-of x
) args
) stream
(+ 2 pos
) rmar
)
278 (princ pp-lpar stream
)
279 (prin1 (type-of x
) stream
)
280 (princ pp-space stream
)
281 (setq pos
(setq cur
(+ pos
4 (flatsize (type-of x
)))))
283 (prin1 (first args
) stream
)
284 (princ pp-space stream
)
288 (+ pos
1 (flatsize (first args
)))
290 (setq args
(cddr args
))
292 (princ pp-rpar stream
)
293 (return-from pp-astruct
(1+ cur
)))
294 (pp-moveto stream cur pos
)
298 ; PRINTMACROP is the printmacro interface routine. Note that the
299 ; called function has the same argument list as PP1. It may either
300 ; decide not to handle the form, by returning NIL (and not printing)
301 ; or it may print the form and return the resulting position.
303 (defun printmacrop (x stream curpos rmargin
&aux macro
)
304 (and (symbolp (car x
))
305 (car x
) ; must not be NIL (TAA fix)
306 (setq macro
(get (car x
) 'printmacro
))
307 (apply macro
(list x stream curpos rmargin
))))
309 ; The remaining forms define various printmacros.
312 ; Printing format (xxx xxx
316 (defun pp-binding-form (x stream pos rmar
&aux cur
)
318 (cond ((and (>= (- rmar pos
) (flatsize x
))
319 (<= (flatsize x
) maxsize
)) nil
)
321 (princ pp-lpar stream
)
322 (prin1 (car x
) stream
)
323 (princ pp-space stream
)
327 (+ 2 pos
(flatsize (car x
)))
329 (pp-moveto stream cur
(+ pos
1))
330 (pp-rest (cddr x
) stream
(+ pos
1) rmar
))))
332 ; Format (xxxx xxx xxx
336 (defun pp-pair-form (x stream pos rmar
&aux cur
)
338 (cond ((and (>= (- rmar pos
) (flatsize x
))
339 (<= (flatsize x
) maxsize
)) nil
)
341 (princ pp-lpar stream
)
342 (prin1 (first x
) stream
)
343 (princ pp-space stream
)
344 (setq pos
(setq cur
(+ pos
2 (flatsize (first x
)))))
347 (pp-moveto stream cur pos
)
348 (setq cur
(pp1 (first x
) stream pos rmar
))
349 (princ pp-space stream
)
351 (setq cur
(pp1 (first x
) stream
(1+ cur
) rmar
))
352 (when (null (setq x
(rest x
)))
353 (princ pp-rpar stream
)
354 (return-from pp-pair-form
(1+ cur
)))))))
361 (defun pp-do-form (x stream pos rmar
&aux cur pos2
)
363 (cond ((and (>= (- rmar pos
) (flatsize x
))
364 (<= (flatsize x
) maxsize
)) nil
)
366 (princ pp-lpar stream
)
367 (prin1 (car x
) stream
)
368 (princ pp-space stream
)
369 (setq pos2
(+ 2 pos
(flatsize (car x
))))
370 (setq cur
(pp1 (cadr x
) stream pos2 rmar
))
371 (pp-moveto stream cur pos2
)
372 (setq cur
(pp1 (caddr x
) stream pos2 rmar
))
373 (pp-moveto stream cur
(+ pos
1))
374 (pp-rest (cdddr x
) stream
(+ pos
1) rmar
))))
376 ; format (xxx xxx xxx
379 (defun pp-defining-form (x stream pos rmar
&aux cur
)
381 (cond ((and (>= (- rmar pos
) (flatsize x
))
382 (<= (flatsize x
) maxsize
)) nil
)
384 (princ pp-lpar stream
)
385 (prin1 (car x
) stream
)
386 (princ pp-space stream
)
387 (prin1 (cadr x
) stream
)
388 (princ pp-space stream
)
392 (+ 3 pos
(flatsize (car x
)) (flatsize (cadr x
)))
394 (pp-moveto stream cur
(+ 3 pos
))
395 (pp-rest (cdddr x
) stream
(+ 3 pos
) rmar
))))
398 '(lambda (x stream pos rmargin
)
399 (cond ((and (cdr x
) (null (cddr x
)))
401 (pp1 (cadr x
) stream
(1+ pos
) rmargin
))))
405 '(lambda (x stream pos rmargin
)
406 (cond ((and (cdr x
) (null (cddr x
)))
408 (pp1 (cadr x
) stream
(1+ pos
) rmargin
))))
412 '(lambda (x stream pos rmargin
)
413 (cond ((and (cdr x
) (null (cddr x
)))
415 (pp1 (cadr x
) stream
(1+ pos
) rmargin
))))
419 '(lambda (x stream pos rmargin
)
420 (cond ((and (cdr x
) (null (cddr x
)))
422 (pp1 (cadr x
) stream
(+ pos
2) rmargin
))))
426 '(lambda (x stream pos rmargin
)
427 (cond ((and (cdr x
) (null (cddr x
)))
429 (pp1 (cadr x
) stream
(+ pos
2) rmargin
))))
456 (putprop 'do
'pp-do-form
'printmacro
)