Initial commit, 3-52-19 alpha
[cls.git] / xlisponly / lsp / pp.lsp
blob9b7aa4916f95717ce162fe3695d88d370bb50dca
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:
13 ; (PP OBJECT STREAM)
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
55 ; idea, go for it.
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
62 ; (NIL ...).
64 ; 9. TAA modified to support prettyprinting of structures, and some code
65 ; cleanup. Also added PP-PAIR-FORM to handle setq like structures
66 ; more nicely.
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.
83 #+:packages
84 (unless (find-package "TOOLS")
85 (make-package "TOOLS" :use '("XLISP")))
87 (in-package "TOOLS")
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)
118 (terpri streamout)
119 (terpri streamout)
120 (do* ((fp (open filename)) (expr (read fp nil) (read fp nil)))
121 ((null expr) (close fp))
122 (pp expr streamout)
123 (terpri streamout)))
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))
133 (setq type
134 (cadr (assoc (car expr)
135 '((lambda defun) (macro defmacro)))))
136 (list 'quote
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*))
147 (pp1 x stream 1 80)
148 (terpri stream)
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))))
156 size position width)
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)
173 (setq position
174 (pp1 (car x) stream (1+ curpos) rmargin))
175 (cond ((and (>= (setq width (- rmargin position))
176 (setq size (flatsize (cdr x))))
177 (<= size maxsize))
178 (pp-rest-across (cdr x) stream position rmargin))
179 ((consp (car x))
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.
189 ; will space to GOAL
191 (defun pp-moveto (stream curpos goalpos &aux i)
192 (cond ((> curpos goalpos)
193 (terpri stream)
194 (setq curpos 1)
195 (if tabsize
196 (do nil
197 ((< (- goalpos curpos) tabsize))
198 (princ "\t" stream)
199 (setq curpos (+ curpos tabsize))))))
200 (dotimes (i (- goalpos curpos)) (princ pp-space stream))
201 goalpos)
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)
207 (prog nil
209 (cond ((null x) (princ pp-rpar stream) (return (1+ position)))
210 ((not (consp x))
211 (princ " . " stream)
212 (prin1 x stream)
213 (princ pp-rpar stream)
214 (return (+ 4 position (flatsize x))))
215 (t (princ pp-space stream)
216 (setq position
217 (pp1 (car x) stream (1+ position) rmargin))
218 (setq x (cdr x))
219 (go lp)))))
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)
226 (prog nil
228 (cond ((null x) (princ pp-rpar stream) (return (1+ position)))
229 ((not (consp x))
230 (and (> (flatsize x) (- (- rmargin position) 3))
231 (setq position (pp-moveto stream position curpos)))
232 (princ " . " stream)
233 (prin1 x stream)
234 (princ pp-rpar stream)
235 (return (+ position 4 (flatsize x))))
236 ((and
237 (not (typep (car x) '(or list array struct)))
238 (<= (setq pos2 (+ 1 position (flatsize (car x))))
239 rmargin)
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))
245 (setq position
246 (pp1 (car x) stream (1+ curpos) rmargin))))
247 (cond ((and (consp (car x)) (cdr x))
248 (setq position (pp-moveto stream position curpos))))
249 (setq x (cdr x))
250 (go lp)))
253 ; Handles structures by printing in form:
254 ; #S(structtype :slot val
255 ; ...
256 ; :slot val)
258 ; code does not check for defaults.
260 (defun pp-astruct (x stream pos rmar &aux cur snames args)
261 (setq cur pos
262 snames (mapcar #'car (get (type-of x) '*struct-slots*))
263 args
264 (mapcan #'(lambda (p)
265 (list p
266 (apply
267 (intern
268 (strcat (string (type-of x))
269 "-"
270 (string p)))
271 (list x))))
272 snames))
273 (princ "#s" stream)
274 (if (and (>= (- rmar pos) (+ 2 (flatsize x)))
275 (<= (flatsize x) maxsize))
276 (pp1 (cons (type-of x) args) stream (+ 2 pos) rmar)
277 (prog ()
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)
285 (setq cur
286 (pp1 (second args)
287 stream
288 (+ pos 1 (flatsize (first args)))
289 rmar))
290 (setq args (cddr args))
291 (when (null args)
292 (princ pp-rpar stream)
293 (return-from pp-astruct (1+ cur)))
294 (pp-moveto stream cur pos)
295 (go lp))))
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
313 ; <pp-rest>)
316 (defun pp-binding-form (x stream pos rmar &aux cur)
317 (setq cur pos)
318 (cond ((and (>= (- rmar pos) (flatsize x))
319 (<= (flatsize x) maxsize)) nil)
320 ((> (length x) 2)
321 (princ pp-lpar stream)
322 (prin1 (car x) stream)
323 (princ pp-space stream)
324 (setq cur
325 (pp1 (cadr x)
326 stream
327 (+ 2 pos (flatsize (car x)))
328 rmar))
329 (pp-moveto stream cur (+ pos 1))
330 (pp-rest (cddr x) stream (+ pos 1) rmar))))
332 ; Format (xxxx xxx xxx
333 ;...
334 ; xxx xxx)
336 (defun pp-pair-form (x stream pos rmar &aux cur)
337 (setq cur pos)
338 (cond ((and (>= (- rmar pos) (flatsize x))
339 (<= (flatsize x) maxsize)) nil)
340 ((> (length x) 1)
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)))))
345 (setq x (rest x))
346 (loop
347 (pp-moveto stream cur pos)
348 (setq cur (pp1 (first x) stream pos rmar))
349 (princ pp-space stream)
350 (setq x (rest x))
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)))))))
356 ; format (xxx xxx
357 ; xxx
358 ; <pprest>)
361 (defun pp-do-form (x stream pos rmar &aux cur pos2)
362 (setq cur pos)
363 (cond ((and (>= (- rmar pos) (flatsize x))
364 (<= (flatsize x) maxsize)) nil)
365 ((> (length x) 2)
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
377 ; <pprest>)
379 (defun pp-defining-form (x stream pos rmar &aux cur)
380 (setq cur pos)
381 (cond ((and (>= (- rmar pos) (flatsize x))
382 (<= (flatsize x) maxsize)) nil)
383 ((> (length x) 3)
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)
389 (setq cur
390 (pp1 (caddr x)
391 stream
392 (+ 3 pos (flatsize (car x)) (flatsize (cadr x)))
393 rmar))
394 (pp-moveto stream cur (+ 3 pos))
395 (pp-rest (cdddr x) stream (+ 3 pos) rmar))))
397 (putprop 'quote
398 '(lambda (x stream pos rmargin)
399 (cond ((and (cdr x) (null (cddr x)))
400 (princ "'" stream)
401 (pp1 (cadr x) stream (1+ pos) rmargin))))
402 'printmacro)
404 (putprop 'backquote
405 '(lambda (x stream pos rmargin)
406 (cond ((and (cdr x) (null (cddr x)))
407 (princ "`" stream)
408 (pp1 (cadr x) stream (1+ pos) rmargin))))
409 'printmacro)
411 (putprop 'comma
412 '(lambda (x stream pos rmargin)
413 (cond ((and (cdr x) (null (cddr x)))
414 (princ "," stream)
415 (pp1 (cadr x) stream (1+ pos) rmargin))))
416 'printmacro)
418 (putprop 'comma-at
419 '(lambda (x stream pos rmargin)
420 (cond ((and (cdr x) (null (cddr x)))
421 (princ ",@" stream)
422 (pp1 (cadr x) stream (+ pos 2) rmargin))))
423 'printmacro)
425 (putprop 'function
426 '(lambda (x stream pos rmargin)
427 (cond ((and (cdr x) (null (cddr x)))
428 (princ "#'" stream)
429 (pp1 (cadr x) stream (+ pos 2) rmargin))))
430 'printmacro)
432 (putprop 'prog
433 'pp-binding-form
434 'printmacro)
436 (putprop 'prog*
437 'pp-binding-form
438 'printmacro)
440 (putprop 'let
441 'pp-binding-form
442 'printmacro)
444 (putprop 'let*
445 'pp-binding-form
446 'printmacro)
448 (putprop 'lambda
449 'pp-binding-form
450 'printmacro)
452 (putprop 'macro
453 'pp-binding-form
454 'printmacro)
456 (putprop 'do 'pp-do-form 'printmacro)
458 (putprop 'do*
459 'pp-do-form
460 'printmacro)
462 (putprop 'defun
463 'pp-defining-form
464 'printmacro)
466 (putprop 'defmacro
467 'pp-defining-form
468 'printmacro)
471 (putprop 'setq
472 'pp-pair-form
473 'printmacro)
475 (putprop 'setf
476 'pp-pair-form
477 'printmacro)
479 (putprop 'psetq
480 'pp-pair-form
481 'printmacro)
484 (putprop 'send
485 'pp-defining-form
486 'printmacro)