source: trunk/abcl/src/org/armedbear/lisp/pprint.lisp

Last change on this file was 15748, checked in by Mark Evenson, 6 months ago

Add gray-streams:stream-line-length extension

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 57.6 KB
Line 
1;;; pprint.lisp
2;;;
3;;; Copyright (C) 2004-2005 Peter Graves
4;;; $Id: pprint.lisp 15748 2023-11-20 16:14:09Z mevenson $
5;;;
6;;; This program is free software; you can redistribute it and/or
7;;; modify it under the terms of the GNU General Public License
8;;; as published by the Free Software Foundation; either version 2
9;;; of the License, or (at your option) any later version.
10;;;
11;;; This program is distributed in the hope that it will be useful,
12;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with this program; if not, write to the Free Software
18;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19;;;
20;;; As a special exception, the copyright holders of this library give you
21;;; permission to link this library with independent modules to produce an
22;;; executable, regardless of the license terms of these independent
23;;; modules, and to copy and distribute the resulting executable under
24;;; terms of your choice, provided that you also meet, for each linked
25;;; independent module, the terms and conditions of the license of that
26;;; module.  An independent module is a module which is not derived from
27;;; or based on this library.  If you modify this library, you may extend
28;;; this exception to your version of the library, but you are not
29;;; obligated to do so.  If you do not wish to do so, delete this
30;;; exception statement from your version.
31
32;;; Adapted from the November, 26 1991 version of Richard C. Waters' XP pretty
33;;; printer.
34
35;------------------------------------------------------------------------
36
37;Copyright Massachusetts Institute of Technology, Cambridge, Massachusetts.
38
39;Permission to use, copy, modify, and distribute this software and its
40;documentation for any purpose and without fee is hereby granted,
41;provided that this copyright and permission notice appear in all
42;copies and supporting documentation, and that the name of M.I.T. not
43;be used in advertising or publicity pertaining to distribution of the
44;software without specific, written prior permission. M.I.T. makes no
45;representations about the suitability of this software for any
46;purpose.  It is provided "as is" without express or implied warranty.
47
48;    M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
49;    ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
50;    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
51;    ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
52;    WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
53;    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
54;    SOFTWARE.
55
56;------------------------------------------------------------------------
57
58(in-package #:xp)
59
60;must do the following in common lisps not supporting *print-shared*
61
62(require "PRINT")
63
64(defvar *print-shared* nil)
65(export '(*print-shared*))
66
67(defvar *default-right-margin* 80
68  "controls default line length; must be a non-negative integer")
69
70(defun ext:line-length (stream)
71  (declare (ignore stream))
72  (max 0 (or *print-right-margin* *default-right-margin* 80)))
73
74(defvar *current-level* 0
75  "current depth in logical blocks.")
76(defvar *abbreviation-happened* nil
77  "t if current thing being printed has been abbreviated.")
78(defvar *result* nil "used to pass back a value")
79
80;default (bad) definitions for the non-portable functions
81
82#-(or :symbolics :lucid :franz-inc :cmu)(eval-when (eval load compile)
83(defun structure-type-p (x) (and (symbolp x) (get x 'structure-printer)))
84(defun output-width     (&optional (s *standard-output*)) (declare (ignore s)) nil))
85
86(defvar *locating-circularities* nil
87  "Integer if making a first pass over things to identify circularities.
88   Integer used as counter for #n= syntax.")
89
90;               ---- XP STRUCTURES, AND THE INTERNAL ALGORITHM ----
91
92(eval-when (eval load compile) ;not used at run time.
93  (defvar block-stack-entry-size 1)
94  (defvar prefix-stack-entry-size 5)
95  (defvar queue-entry-size 7)
96  (defvar buffer-entry-size 1)
97  (defvar prefix-entry-size 1)
98  (defvar suffix-entry-size 1))
99
100(eval-when (eval load compile) ;used at run time
101  (defvar block-stack-min-size #.(* 35. block-stack-entry-size))
102  (defvar prefix-stack-min-size #.(* 30. prefix-stack-entry-size))
103  (defvar queue-min-size #.(* 75. queue-entry-size))
104  (defvar buffer-min-size 256.)
105  (defvar prefix-min-size 256.)
106  (defvar suffix-min-size 256.)
107  )
108
109(defstruct (xp-structure (:conc-name nil) #+nil (:print-function describe-xp))
110  (base-stream nil) ;;The stream io eventually goes to.
111  line-length ;;The line length to use for formatting.
112  line-limit ;;If non-NIL the max number of lines to print.
113  line-no ;;number of next line to be printed.
114  depth-in-blocks
115  ;;Number of logical blocks at QRIGHT that are started but not ended.
116  (block-stack (make-array #.block-stack-min-size)) block-stack-ptr
117  ;;This stack is pushed and popped in accordance with the way blocks are
118  ;;nested at the moment they are entered into the queue.  It contains the
119  ;;following block specific value.
120  ;;SECTION-START total position where the section (see AIM-1102)
121  ;;that is rightmost in the queue started.
122  (buffer (make-array #.buffer-min-size :element-type 'character))
123  charpos buffer-ptr buffer-offset
124  ;;This is a vector of characters (eg a string) that builds up the
125  ;;line images that will be printed out.  BUFFER-PTR is the
126  ;;buffer position where the next character should be inserted in
127  ;;the string.  CHARPOS is the output character position of the
128  ;;first character in the buffer (non-zero only if a partial line
129  ;;has been output).  BUFFER-OFFSET is used in computing total lengths.
130  ;;It is changed to reflect all shifting and insertion of prefixes so that
131  ;;total length computes things as they would be if they were
132  ;;all on one line.  Positions are kept three different ways
133  ;; Buffer position (eg BUFFER-PTR)
134  ;; Line position (eg (+ BUFFER-PTR CHARPOS)).  Indentations are stored in this form.
135  ;; Total position if all on one line (eg (+ BUFFER-PTR BUFFER-OFFSET))
136  ;;  Positions are stored in this form.
137  (queue (make-array #.queue-min-size))
138  qleft
139  qright
140  ;;This holds a queue of action descriptors.  QLEFT and QRIGHT
141  ;;point to the next entry to dequeue and the last entry enqueued
142  ;;respectively.  The queue is empty when
143  ;;(> QLEFT QRIGHT).  The queue entries have several parts:
144  ;;QTYPE one of :NEWLINE/:IND/:START-BLOCK/:END-BLOCK
145  ;;QKIND :LINEAR/:MISER/:FILL/:MANDATORY or :UNCONDITIONAL/:FRESH
146  ;; or :BLOCK/:CURRENT
147  ;;QPOS total position corresponding to this entry
148  ;;QDEPTH depth in blocks of this entry.
149  ;;QEND offset to entry marking end of section this entry starts. (NIL until known.)
150  ;; Only :start-block and non-literal :newline entries can start sections.
151  ;;QOFFSET offset to :END-BLOCK for :START-BLOCK (NIL until known).
152  ;;QARG for :IND indentation delta
153  ;;     for :START-BLOCK suffix in the block if any.
154  ;;                      or if per-line-prefix then cons of suffix and
155  ;;                      per-line-prefix.
156  ;;     for :END-BLOCK suffix for the block if any.
157  (prefix (make-array #.buffer-min-size :element-type 'character))
158  ;;this stores the prefix that should be used at the start of the line
159  (prefix-stack (make-array #.prefix-stack-min-size))
160  prefix-stack-ptr
161  ;;This stack is pushed and popped in accordance with the way blocks
162  ;;are nested at the moment things are taken off the queue and printed.
163  ;;It contains the following block specific values.
164  ;;PREFIX-PTR current length of PREFIX.
165  ;;SUFFIX-PTR current length of pending suffix
166  ;;NON-BLANK-PREFIX-PTR current length of non-blank prefix.
167  ;;INITIAL-PREFIX-PTR prefix-ptr at the start of this block.
168  ;;SECTION-START-LINE line-no value at last non-literal break at this level.
169  (suffix (make-array #.buffer-min-size :element-type 'character))
170  ;;this stores the suffixes that have to be printed to close of the current
171  ;;open blocks.  For convenient in popping, the whole suffix
172  ;;is stored in reverse order.
173)
174
175
176(defun ext:charpos (stream)
177  (cond ((xp-structure-p stream)
178         (charpos stream))
179        ((streamp stream)
180         (sys::stream-charpos stream))))
181
182(defun (setf ext:charpos) (new-value stream)
183  (cond ((xp-structure-p stream)
184         (setf (charpos stream) new-value))
185        ((streamp stream)
186         (sys::stream-%set-charpos stream new-value))))
187
188
189(defmacro LP<-BP (xp &optional (ptr nil))
190  (if (null ptr) (setq ptr `(buffer-ptr ,xp)))
191  `(+ ,ptr (charpos ,xp)))
192(defmacro TP<-BP (xp)
193  `(+ (buffer-ptr ,xp) (buffer-offset ,xp)))
194(defmacro BP<-LP (xp ptr)
195  `(- ,ptr (charpos ,xp)))
196(defmacro BP<-TP (xp ptr)
197  `(- ,ptr (buffer-offset ,xp)))
198;This does not tell you the line position you were at when the TP
199;was set, unless there have been no newlines or indentation output
200;between ptr and the current output point.
201(defmacro LP<-TP (xp ptr)
202  `(LP<-BP ,xp (BP<-TP ,xp ,ptr)))
203
204;We don't use adjustable vectors or any of that, because we seldom have
205;to actually extend and non-adjustable vectors are a lot faster in
206;many Common Lisps.
207
208(defmacro check-size (xp vect ptr)
209  (let* ((min-size
210           (symbol-value
211             (intern (concatenate 'string (string vect) "-MIN-SIZE")
212                     (find-package "XP"))))
213         (entry-size
214           (symbol-value
215             (intern (concatenate 'string (string vect) "-ENTRY-SIZE")
216                     (find-package "XP")))))
217    `(when (and (> ,ptr ,(- min-size entry-size)) ;seldom happens
218                (> ,ptr (- (length (,vect ,xp)) ,entry-size)))
219       (let* ((old (,vect ,xp))
220              (new (make-array (+ ,ptr ,(if (= entry-size 1) 50
221                                            (* 10 entry-size)))
222                               :element-type (array-element-type old))))
223         (replace new old)
224         (setf (,vect ,xp) new)))))
225
226(defmacro section-start (xp) `(aref (block-stack ,xp) (block-stack-ptr ,xp)))
227
228(defun push-block-stack (xp)
229  (incf (block-stack-ptr xp) #.block-stack-entry-size)
230  (check-size xp block-stack (block-stack-ptr xp)))
231
232(defun pop-block-stack (xp)
233  (decf (block-stack-ptr xp) #.block-stack-entry-size))
234
235(defmacro prefix-ptr (xp)
236  `(aref (prefix-stack ,xp) (prefix-stack-ptr ,xp)))
237(defmacro suffix-ptr (xp)
238  `(aref (prefix-stack ,xp) (+ (prefix-stack-ptr ,xp) 1)))
239(defmacro non-blank-prefix-ptr (xp)
240  `(aref (prefix-stack ,xp) (+ (prefix-stack-ptr ,xp) 2)))
241(defmacro initial-prefix-ptr (xp)
242  `(aref (prefix-stack ,xp) (+ (prefix-stack-ptr ,xp) 3)))
243(defmacro section-start-line (xp)
244  `(aref (prefix-stack ,xp) (+ (prefix-stack-ptr ,xp) 4)))
245
246(defun push-prefix-stack (xp)
247  (let ((old-prefix 0)
248        (old-suffix 0)
249        (old-non-blank 0))
250    (when (not (minusp (prefix-stack-ptr xp)))
251      (setq old-prefix (prefix-ptr xp)
252            old-suffix (suffix-ptr xp)
253            old-non-blank (non-blank-prefix-ptr xp)))
254    (incf (prefix-stack-ptr xp) #.prefix-stack-entry-size)
255    (check-size xp prefix-stack (prefix-stack-ptr xp))
256    (setf (prefix-ptr xp) old-prefix)
257    (setf (suffix-ptr xp) old-suffix)
258    (setf (non-blank-prefix-ptr xp) old-non-blank)))
259
260(defun pop-prefix-stack (xp)
261  (decf (prefix-stack-ptr xp) #.prefix-stack-entry-size))
262
263(defmacro Qtype   (xp index) `(aref (queue ,xp) ,index))
264(defmacro Qkind   (xp index) `(aref (queue ,xp) (1+ ,index)))
265(defmacro Qpos    (xp index) `(aref (queue ,xp) (+ ,index 2)))
266(defmacro Qdepth  (xp index) `(aref (queue ,xp) (+ ,index 3)))
267(defmacro Qend    (xp index) `(aref (queue ,xp) (+ ,index 4)))
268(defmacro Qoffset (xp index) `(aref (queue ,xp) (+ ,index 5)))
269(defmacro Qarg    (xp index) `(aref (queue ,xp) (+ ,index 6)))
270
271;we shift the queue over rather than using a circular queue because
272;that works out to be a lot faster in practice.  Note, short printout
273;does not ever cause a shift, and even in long printout, the queue is
274;shifted left for free every time it happens to empty out.
275
276(defun enqueue (xp type kind &optional arg)
277  (incf (Qright xp) #.queue-entry-size)
278  (when (> (Qright xp) #.(- queue-min-size queue-entry-size))
279    (replace (queue xp) (queue xp) :start2 (Qleft xp) :end2 (Qright xp))
280    (setf (Qright xp) (- (Qright xp) (Qleft xp)))
281    (setf (Qleft xp) 0))
282  (check-size xp queue (Qright xp))
283  (setf (Qtype xp (Qright xp)) type)
284  (setf (Qkind xp (Qright xp)) kind)
285  (setf (Qpos xp (Qright xp)) (TP<-BP xp))
286  (setf (Qdepth xp (Qright xp)) (depth-in-blocks xp))
287  (setf (Qend xp (Qright xp)) nil)
288  (setf (Qoffset xp (Qright xp)) nil)
289  (setf (Qarg xp (Qright xp)) arg))
290
291(defmacro Qnext (index) `(+ ,index #.queue-entry-size))
292
293;This is called to initialize things when you start pretty printing.
294
295(defun initialize-xp (xp stream)
296  (setf (base-stream xp) stream)
297  (setf (line-length xp) (ext:line-length stream))
298  (setf (line-limit xp) *print-lines*)
299  (setf (line-no xp) 1)
300  (setf (depth-in-blocks xp) 0)
301  (setf (block-stack-ptr xp) 0)
302  (setf (charpos xp) (cond ((ext:charpos stream)) (t 0)))
303  (setf (section-start xp) 0)
304  (setf (buffer-ptr xp) 0)
305  (setf (buffer-offset xp) (charpos xp))
306  (setf (Qleft xp) 0)
307  (setf (Qright xp) #.(- queue-entry-size))
308  (setf (prefix-stack-ptr xp) #.(- prefix-stack-entry-size))
309  xp)
310
311;This handles the basic outputting of characters.  note + suffix means that
312;the stream is known to be an XP stream, all inputs are mandatory, and no
313;error checking has to be done.  Suffix ++ additionally means that the
314;output is guaranteed not to contain a newline char.
315
316(defun write-char+ (char xp)
317  (if (eql char #\newline) (pprint-newline+ :unconditional xp)
318      (write-char++ char xp)))
319
320(defun write-string+ (string xp start end)
321  (let ((sub-end nil) next-newline)
322    (loop (setq next-newline
323                (position #\newline string :test #'char= :start start :end end))
324          (setq sub-end (if next-newline next-newline end))
325          (write-string++ string xp start sub-end)
326          (when (null next-newline) (return nil))
327          (pprint-newline+ :unconditional xp)
328          (setq start (1+ sub-end)))))
329
330;note this checks (> BUFFER-PTR LINE-LENGTH) instead of (> (LP<-BP) LINE-LENGTH)
331;this is important so that when things are longer than a line they
332;end up getting printed in chunks of size LINE-LENGTH.
333
334(defun write-char++ (char xp)
335  (when (> (buffer-ptr xp) (line-length xp))
336    (force-some-output xp))
337  (let ((new-buffer-end (1+ (buffer-ptr xp))))
338    (check-size xp buffer new-buffer-end)
339    (setf (char (buffer xp) (buffer-ptr xp)) char)
340    (setf (buffer-ptr xp) new-buffer-end)))
341
342(defun force-some-output (xp)
343  (attempt-to-output xp nil nil)
344  (when (> (buffer-ptr xp) (line-length xp)) ;only if printing off end of line
345    (attempt-to-output xp T T)))
346
347(defun write-string++ (string xp start end)
348  (when (> (buffer-ptr xp) (line-length xp))
349    (force-some-output xp))
350  (write-string+++ string xp start end))
351
352;never forces output; therefore safe to call from within output-line.
353
354(defun write-string+++ (string xp start end)
355  (let ((new-buffer-end (+ (buffer-ptr xp) (- end start))))
356    (check-size xp buffer new-buffer-end)
357    (do ((buffer (buffer xp))
358         (i (buffer-ptr xp) (1+ i))
359         (j start (1+ j)))
360        ((= j end))
361      (let ((char (char string j)))
362        (setf (char buffer i) char)))
363    (setf (buffer-ptr xp) new-buffer-end)))
364
365(defun pprint-tab+ (kind colnum colinc xp)
366  (let ((indented? nil) (relative? nil))
367    (case kind
368      (:section (setq indented? t))
369      (:line-relative (setq relative? t))
370      (:section-relative (setq indented? t relative? t)))
371    (let* ((current
372             (if (not indented?) (LP<-BP xp)
373                 (- (TP<-BP xp) (section-start xp))))
374           (new
375             (if (zerop colinc)
376                 (if relative? (+ current colnum) (max colnum current))
377                 (cond (relative?
378                        (* colinc (floor (+ current colnum colinc -1) colinc)))
379                       ((> colnum current) colnum)
380                       (T (+ colnum
381                             (* colinc
382                                (floor (+ current (- colnum) colinc) colinc)))))))
383           (length (- new current)))
384      (when (plusp length)
385        (let ((end (+ (buffer-ptr xp) length)))
386          (check-size xp buffer end)
387          (fill (buffer xp) #\space :start (buffer-ptr xp) :end end)
388          (setf (buffer-ptr xp) end))))))
389
390;note following is smallest number >= x that is a multiple of colinc
391;  (* colinc (floor (+ x (1- colinc)) colinc))
392
393(defun pprint-newline+ (kind xp)
394  (enqueue xp :newline kind)
395  (do ((ptr (Qleft xp) (Qnext ptr)))    ;find sections we are ending
396      ((not (< ptr (Qright xp))))       ;all but last
397    (when (and (null (Qend xp ptr))
398               (not (> (depth-in-blocks xp) (Qdepth xp ptr)))
399               (member (Qtype xp ptr) '(:newline :start-block)))
400      (setf (Qend xp ptr) (- (Qright xp) ptr))))
401  (setf (section-start xp) (TP<-BP xp))
402  (when (member kind '(:fresh :unconditional :mandatory))
403    (attempt-to-output xp T nil)))
404
405(defun start-block (xp prefix on-each-line? suffix)
406  (unless (stringp prefix)
407    (error 'type-error
408           :datum prefix
409           :expected-type 'string))
410  (unless (stringp suffix)
411    (error 'type-error
412           :datum suffix
413           :expected-type 'string))
414  (when prefix
415    (write-string++ prefix xp 0 (length prefix)))
416  (push-block-stack xp)
417  (enqueue xp :start-block nil
418           (if on-each-line? (cons suffix prefix) suffix))
419  (incf (depth-in-blocks xp))         ;must be after enqueue
420  (setf (section-start xp) (TP<-BP xp)))
421
422(defun end-block (xp suffix)
423  (unless (eq *abbreviation-happened* '*print-lines*)
424    (when suffix
425      (write-string+ suffix xp 0 (length suffix)))
426    (decf (depth-in-blocks xp))
427    (enqueue xp :end-block nil suffix)
428    (do ((ptr (Qleft xp) (Qnext ptr))) ;looking for start of block we are ending
429        ((not (< ptr (Qright xp))))    ;all but last
430      (when (and (= (depth-in-blocks xp) (Qdepth xp ptr))
431                 (eq (Qtype xp ptr) :start-block)
432                 (null (Qoffset xp ptr)))
433        (setf (Qoffset xp ptr) (- (Qright xp) ptr))
434        (return nil)))  ;can only be 1
435    (pop-block-stack xp)))
436
437(defun pprint-indent+ (kind n xp)
438  (enqueue xp :ind kind n))
439
440; The next function scans the queue looking for things it can do.
441;it keeps outputting things until the queue is empty, or it finds
442;a place where it cannot make a decision yet.
443
444(defmacro maybe-too-large (xp Qentry)
445  `(let ((limit (line-length ,xp)))
446     (when (eql (line-limit ,xp) (line-no ,xp)) ;prevents suffix overflow
447       (decf limit 2) ;3 for " .." minus 1 for space (heuristic)
448       (when (not (minusp (prefix-stack-ptr ,xp)))
449         (decf limit (suffix-ptr ,xp))))
450     (cond ((Qend ,xp ,Qentry)
451            (> (LP<-TP ,xp (Qpos ,xp (+ ,Qentry (Qend ,xp ,Qentry)))) limit))
452           ((or force-newlines? (> (LP<-BP ,xp) limit)) T)
453           (T (return nil)))))  ;wait until later to decide.
454
455(defmacro misering? (xp)
456  `(and *print-miser-width*
457        (<= (- (line-length ,xp) (initial-prefix-ptr ,xp)) *print-miser-width*)))
458
459;If flush-out? is T and force-newlines? is NIL then the buffer,
460;prefix-stack, and queue will be in an inconsistent state after the call.
461;You better not call it this way except as the last act of outputting.
462
463(defun attempt-to-output (xp force-newlines? flush-out?)
464  (do () ((> (Qleft xp) (Qright xp))
465          (setf (Qleft xp) 0)
466          (setf (Qright xp) #.(- queue-entry-size))) ;saves shifting
467    (case (Qtype xp (Qleft xp))
468      (:ind
469       (unless (misering? xp)
470         (set-indentation-prefix xp
471           (case (Qkind xp (Qleft xp))
472             (:block (+ (initial-prefix-ptr xp) (Qarg xp (Qleft xp))))
473             (T ; :current
474               (+ (LP<-TP xp (Qpos xp (Qleft xp)))
475                  (Qarg xp (Qleft xp)))))))
476       (setf (Qleft xp) (Qnext (Qleft xp))))
477      (:start-block
478       (cond ((maybe-too-large xp (Qleft xp))
479              (push-prefix-stack xp)
480              (setf (initial-prefix-ptr xp) (prefix-ptr xp))
481              (set-indentation-prefix xp (LP<-TP xp (Qpos xp (Qleft xp))))
482              (let ((arg (Qarg xp (Qleft xp))))
483                (when (consp arg) (set-prefix xp (cdr arg)))
484                (setf (initial-prefix-ptr xp) (prefix-ptr xp))
485                (cond ((not (listp arg)) (set-suffix xp arg))
486                      ((car arg) (set-suffix xp (car arg)))))
487              (setf (section-start-line xp) (line-no xp)))
488             (T (incf (Qleft xp) (Qoffset xp (Qleft xp)))))
489       (setf (Qleft xp) (Qnext (Qleft xp))))
490      (:end-block (pop-prefix-stack xp) (setf (Qleft xp) (Qnext (Qleft xp))))
491      (T ; :newline
492       (when (case (Qkind xp (Qleft xp))
493               (:fresh (not (zerop (LP<-BP xp))))
494               (:miser (misering? xp))
495               (:fill (or (misering? xp)
496                          (> (line-no xp) (section-start-line xp))
497                          (maybe-too-large xp (Qleft xp))))
498               (T T)) ;(:linear :unconditional :mandatory)
499         (output-line xp (Qleft xp))
500         (setup-for-next-line xp (Qleft xp)))
501       (setf (Qleft xp) (Qnext (Qleft xp))))))
502  (when flush-out? (flush xp)))
503
504;this can only be called last!
505
506(defun flush (xp)
507  (unless *locating-circularities*
508    (write-string (buffer xp) (base-stream xp) :end (buffer-ptr xp)))
509  (incf (buffer-offset xp) (buffer-ptr xp))
510  (incf (charpos xp) (buffer-ptr xp))
511  (setf (buffer-ptr xp) 0))
512
513;This prints out a line of stuff.
514
515(defun output-line (xp Qentry)
516  (let* ((out-point (BP<-TP xp (Qpos xp Qentry)))
517         (last-non-blank (position #\space (buffer xp) :test-not #'char=
518                                   :from-end T :end out-point))
519         (end (cond ((member (Qkind xp Qentry) '(:fresh :unconditional)) out-point)
520                    (last-non-blank (1+ last-non-blank))
521                    (T 0)))
522         (line-limit-exit (and (line-limit xp)
523                               (not *print-readably*)
524                               (not (> (line-limit xp) (line-no xp))))))
525    (when line-limit-exit
526      (setf (buffer-ptr xp) end)          ;truncate pending output.
527      (write-string+++ " .." xp 0 3)
528      (reverse-string-in-place (suffix xp) 0 (suffix-ptr xp))
529      (write-string+++ (suffix xp) xp 0 (suffix-ptr xp))
530      (setf (Qleft xp) (Qnext (Qright xp)))
531      (setf *abbreviation-happened* '*print-lines*)
532      (throw 'line-limit-abbreviation-exit T))
533    (incf (line-no xp))
534    (unless *locating-circularities*
535      (let ((stream (base-stream xp)))
536        (sys::%write-string (buffer xp) stream 0 end)
537        (sys::%terpri stream)))))
538
539(defun setup-for-next-line (xp Qentry)
540  (let* ((out-point (BP<-TP xp (Qpos xp Qentry)))
541         (prefix-end
542           (cond ((member (Qkind xp Qentry) '(:unconditional :fresh))
543                  (non-blank-prefix-ptr xp))
544                 (T (prefix-ptr xp))))
545         (change (- prefix-end out-point)))
546    (setf (charpos xp) 0)
547    (when (plusp change)                  ;almost never happens
548      (check-size xp buffer (+ (buffer-ptr xp) change)))
549    (replace (buffer xp) (buffer xp) :start1 prefix-end
550             :start2 out-point :end2 (buffer-ptr xp))
551    (replace (buffer xp) (prefix xp) :end2 prefix-end)
552    (incf (buffer-ptr xp) change)
553    (decf (buffer-offset xp) change)
554    (when (not (member (Qkind xp Qentry) '(:unconditional :fresh)))
555      (setf (section-start-line xp) (line-no xp)))))
556
557(defun set-indentation-prefix (xp new-position)
558  (let ((new-ind (max (non-blank-prefix-ptr xp) new-position)))
559    (setf (prefix-ptr xp) (initial-prefix-ptr xp))
560    (check-size xp prefix new-ind)
561    (when (> new-ind (prefix-ptr xp))
562      (fill (prefix xp) #\space :start (prefix-ptr xp) :end new-ind))
563    (setf (prefix-ptr xp) new-ind)))
564
565(defun set-prefix (xp prefix-string)
566  (replace (prefix xp) prefix-string
567           :start1 (- (prefix-ptr xp) (length prefix-string)))
568  (setf (non-blank-prefix-ptr xp) (prefix-ptr xp)))
569
570(defun set-suffix (xp suffix-string)
571  (let* ((end (length suffix-string))
572         (new-end (+ (suffix-ptr xp) end)))
573    (check-size xp suffix new-end)
574    (do ((i (1- new-end) (1- i)) (j 0 (1+ j))) ((= j end))
575      (setf (char (suffix xp) i) (char suffix-string j)))
576    (setf (suffix-ptr xp) new-end)))
577
578(defun reverse-string-in-place (string start end)
579  (do ((i start (1+ i)) (j (1- end) (1- j))) ((not (< i j)) string)
580    (let ((c (char string i)))
581      (setf (char string i) (char string j))
582      (setf (char string j) c))))
583
584;                  ---- BASIC INTERFACE FUNCTIONS ----
585
586;The internal functions in this file, and the (formatter "...") expansions
587;use the '+' forms of these functions directly (which is faster) because,
588;they do not need error checking of fancy stream coercion.  The '++' forms
589;additionally assume the thing being output does not contain a newline.
590
591(defun write (object &key
592                     ((:stream stream) *standard-output*)
593                     ((:escape *print-escape*) *print-escape*)
594                     ((:radix *print-radix*) *print-radix*)
595                     ((:base *print-base*) *print-base*)
596                     ((:circle *print-circle*) *print-circle*)
597                     ((:pretty *print-pretty*) *print-pretty*)
598                     ((:level *print-level*) *print-level*)
599                     ((:length *print-length*) *print-length*)
600                     ((:case *print-case*) *print-case*)
601                     ((:array *print-array*) *print-array*)
602                     ((:gensym *print-gensym*) *print-gensym*)
603                     ((:readably *print-readably*) *print-readably*)
604                     ((:right-margin *print-right-margin*)
605                      *print-right-margin*)
606                     ((:miser-width *print-miser-width*)
607                      *print-miser-width*)
608                     ((:lines *print-lines*) *print-lines*)
609                     ((:pprint-dispatch *print-pprint-dispatch*)
610                      *print-pprint-dispatch*))
611  (sys:output-object object (sys:out-synonym-of stream))
612  object)
613
614(defun maybe-initiate-xp-printing (object fn stream &rest args)
615  (if (xp-structure-p stream)
616      (apply fn stream args)
617      (let ((*abbreviation-happened* nil)
618            (*result* nil))
619        (if (and *print-circle* (null sys::*circularity-hash-table*))
620            (let ((sys::*circularity-hash-table* (make-hash-table :test 'eq)))
621              (setf (gethash object sys::*circularity-hash-table*) t)
622              (xp-print fn (make-broadcast-stream) args)
623              (let ((sys::*circularity-counter* 0))
624                (when (eql 0 (gethash object sys::*circularity-hash-table*))
625                  (setf (gethash object sys::*circularity-hash-table*)
626                        (incf sys::*circularity-counter*))
627                  (sys::print-label (gethash object sys::*circularity-hash-table*)
628                               (sys:out-synonym-of stream)))
629                (xp-print fn (sys:out-synonym-of stream) args)))
630            (xp-print fn (sys:out-synonym-of stream) args))
631        *result*)))
632
633(defun xp-print (fn stream args)
634  (setq *result* (do-xp-printing fn stream args))
635  (when *locating-circularities*
636    (setq *locating-circularities* nil)
637    (setq *abbreviation-happened* nil)
638;;     (setq *parents* nil)
639    (setq *result* (do-xp-printing fn stream args))))
640
641(defun do-xp-printing (fn stream args)
642  (let ((xp (initialize-xp (make-xp-structure) stream))
643        (*current-level* 0)
644        (result nil))
645    (catch 'line-limit-abbreviation-exit
646      (start-block xp "" nil "")
647      (setq result (apply fn xp args))
648      (end-block xp nil))
649    (when (and *locating-circularities*
650               (zerop *locating-circularities*) ;No circularities.
651               (= (line-no xp) 1)               ;Didn't suppress line.
652               (zerop (buffer-offset xp)))      ;Didn't suppress partial line.
653      (setq *locating-circularities* nil))      ;print what you have got.
654    (when (catch 'line-limit-abbreviation-exit
655            (attempt-to-output xp nil t) nil)
656      (attempt-to-output xp t t))
657    result))
658
659(defun write+ (object xp)
660;;   (let ((*parents* *parents*))
661;;     (unless (and *circularity-hash-table*
662;;                  (eq (circularity-process xp object nil) :subsequent))
663;;       (when (and *circularity-hash-table* (consp object))
664;;      ;;avoid possible double check in handle-logical-block.
665;;      (setq object (cons (car object) (cdr object))))
666  (let ((printer (if *print-pretty* (get-printer object *print-pprint-dispatch*) nil))
667        type)
668    (cond (printer (funcall printer xp object))
669          ((maybe-print-fast object xp))
670          ((and *print-pretty*
671                (symbolp (setq type (type-of object)))
672                (setq printer (get type 'structure-printer))
673                (not (eq printer :none)))
674           (funcall printer xp object))
675          ((and *print-pretty* *print-array* (arrayp object)
676                (not (stringp object)) (not (bit-vector-p object))
677                (not (structure-type-p (type-of object))))
678           (pretty-array xp object))
679          (t
680           (let ((stuff (with-output-to-string (s) (non-pretty-print object s))))
681             (write-string+ stuff xp 0 (length stuff)))))))
682
683(defun non-pretty-print (object s)
684;;   (write object
685;;          :level (if *print-level*
686;;                     (- *print-level* *current-level*))
687;;          :pretty nil
688;;          :stream s))
689  (sys::output-ugly-object object s))
690
691;This prints a few very common, simple atoms very fast.
692;Pragmatically, this turns out to be an enormous savings over going to the
693;standard printer all the time.  There would be diminishing returns from making
694;this work with more things, but might be worth it.
695(defun maybe-print-fast (object xp)
696  (cond ((stringp object)
697         (let ((s (sys::%write-to-string object)))
698           (write-string++ s xp 0 (length s))
699           t))
700        ((ext:fixnump object)
701         (print-fixnum xp object)
702         t)
703        ((and (symbolp object)
704              (or (symbol-package object)
705                  (null *print-circle*)))
706         (let ((s (sys::%write-to-string object)))
707           (write-string++ s xp 0 (length s))
708           t)
709         )))
710
711(defun print-fixnum (xp fixnum)
712  (let ((s (sys::%write-to-string fixnum)))
713    (write-string++ s xp 0 (length s))))
714
715(defun print (object &optional (stream *standard-output*))
716  (setf stream (sys:out-synonym-of stream))
717  (terpri stream)
718  (let ((*print-escape* t))
719    (sys:output-object object stream))
720  (write-char #\space stream)
721  object)
722
723(defun prin1 (object &optional (stream *standard-output*))
724  (let ((*print-escape* t))
725    (sys:output-object object (sys:out-synonym-of stream)))
726  object)
727
728(defun princ (object &optional (stream *standard-output*))
729  (let ((*print-escape* nil)
730        (*print-readably* nil))
731    (sys:output-object object (sys:out-synonym-of stream)))
732  object)
733
734(defun pprint (object &optional (stream *standard-output*))
735  (setq stream (sys:out-synonym-of stream))
736  (terpri stream)
737  (let ((*print-escape* T) (*print-pretty* T))
738    (sys:output-object object stream))
739  (values))
740
741(defun write-to-string (object &key
742                               ((:escape *print-escape*) *print-escape*)
743                               ((:radix *print-radix*) *print-radix*)
744                               ((:base *print-base*) *print-base*)
745                               ((:circle *print-circle*) *print-circle*)
746                               ((:pretty *print-pretty*) *print-pretty*)
747                               ((:level *print-level*) *print-level*)
748                               ((:length *print-length*) *print-length*)
749                               ((:case *print-case*) *print-case*)
750                               ((:array *print-array*) *print-array*)
751                               ((:gensym *print-gensym*) *print-gensym*)
752                               ((:readably *print-readably*) *print-readably*)
753                               ((:right-margin *print-right-margin*) *print-right-margin*)
754                               ((:miser-width *print-miser-width*) *print-miser-width*)
755                               ((:lines *print-lines*) *print-lines*)
756                               ((:pprint-dispatch *print-pprint-dispatch*) *print-pprint-dispatch*))
757  (let ((stream (make-string-output-stream)))
758    (sys:output-object object stream)
759    (get-output-stream-string stream)))
760
761(defun prin1-to-string (object)
762  (with-output-to-string (stream)
763    (let ((*print-escape* t))
764      (sys:output-object object stream))))
765
766(defun princ-to-string (object)
767  (with-output-to-string (stream)
768    (let ((*print-escape* nil)
769          (*print-readably* nil))
770      (sys:output-object object stream))))
771
772(defun write-char (char &optional (stream *standard-output*))
773  (setf stream (sys:out-synonym-of stream))
774  (if (xp-structure-p stream)
775      (write-char+ char stream)
776      (sys:%stream-write-char char stream))
777  char)
778
779(defun write-string (string &optional (stream *standard-output*)
780                            &key (start 0) end)
781  (setf stream (sys:out-synonym-of stream))
782  (setf end (or end (length string))) ;; default value for end is NIL
783  (if (xp-structure-p stream)
784      (write-string+ string stream start end)
785      (progn
786        (unless start
787          (setf start 0))
788        (if end
789            (setf end (min end (length string)))
790            (setf end (length string)))
791        (sys::%write-string string stream start end)))
792  string)
793
794(defun write-line (string &optional (stream *standard-output*)
795                   &key (start 0) end)
796  (setf stream (sys:out-synonym-of stream))
797  (setf end (or end (length string)))
798  (cond ((xp-structure-p stream)
799         (write-string+ string stream start end)
800         (pprint-newline+ :unconditional stream))
801        (t (sys::%write-string string stream start end)
802           (sys::%terpri stream)))
803  string)
804
805(defun terpri (&optional (stream *standard-output*))
806  (setf stream (sys:out-synonym-of stream))
807  (if (xp-structure-p stream)
808      (pprint-newline+ :unconditional stream)
809      (sys:%stream-terpri stream))
810  nil)
811
812;This has to violate the XP data abstraction and fool with internal
813;stuff, in order to find out the right info to return as the result.
814
815(defun fresh-line (&optional (stream *standard-output*))
816  (setf stream (sys:out-synonym-of stream))
817  (cond ((xp-structure-p stream)
818         (attempt-to-output stream t t) ;ok because we want newline
819         (when (not (zerop (LP<-BP stream)))
820           (pprint-newline+ :fresh stream)
821           t))
822        (t
823         (sys::%fresh-line stream))))
824
825;Each of these causes the stream to be pessimistic and insert
826;newlines wherever it might have to, when forcing the partial output
827;out.  This is so that things will be in a consistent state if
828;output continues to the stream later.
829
830(defun finish-output (&optional (stream *standard-output*))
831  (setf stream (sys:out-synonym-of stream))
832  (when (xp-structure-p stream)
833    (attempt-to-output stream T T)
834    (setf stream (base-stream stream)))
835  (sys::%finish-output stream)
836  nil)
837
838(defun force-output (&optional (stream *standard-output*))
839  (setf stream (sys:out-synonym-of stream))
840  (when (xp-structure-p stream)
841    (attempt-to-output stream T T)
842    (setf stream (base-stream stream)))
843  (sys::%force-output stream)
844  nil)
845
846(defun clear-output (&optional (stream *standard-output*))
847  (setf stream (sys:out-synonym-of stream))
848  (when (xp-structure-p stream)
849    (let ((*locating-circularities* 0)) ;hack to prevent visible output
850      (attempt-to-output stream T T)
851      (setf stream (base-stream stream))))
852  (sys::%clear-output stream)
853  nil)
854
855;The internal functions in this file, and the (formatter "...") expansions
856;use the '+' forms of these functions directly (which is faster) because,
857;they do not need error checking or fancy stream coercion.  The '++' forms
858;additionally assume the thing being output does not contain a newline.
859
860(defmacro pprint-logical-block ((stream-symbol object
861                                               &key
862                                               (prefix "" prefix-p)
863                                               (per-line-prefix "" per-line-prefix-p)
864                                               (suffix ""))
865                                &body body)
866  (cond ((eq stream-symbol nil)
867         (setf stream-symbol '*standard-output*))
868        ((eq stream-symbol t)
869         (setf stream-symbol '*terminal-io*)))
870  (unless (symbolp stream-symbol)
871    (warn "STREAM-SYMBOL arg ~S to PPRINT-LOGICAL-BLOCK is not a bindable symbol."
872          stream-symbol)
873    (setf stream-symbol '*standard-output*))
874  (when (and prefix-p per-line-prefix-p)
875    (error "Cannot specify values for both PREFIX and PER-LINE-PREFIX."))
876  `(let ((+l ,object))
877     (maybe-initiate-xp-printing
878      +l
879      #'(lambda (,stream-symbol)
880          (let ((+l +l)
881                (+p ,(cond (prefix-p prefix)
882                           (per-line-prefix-p per-line-prefix)
883                           (t "")))
884                (+s ,suffix))
885            (pprint-logical-block+
886             (,stream-symbol +l +p +s ,per-line-prefix-p t nil)
887             ,@ body nil)))
888      (sys:out-synonym-of ,stream-symbol))))
889
890;Assumes var and args must be variables.  Other arguments must be literals or variables.
891
892(defmacro pprint-logical-block+ ((var args prefix suffix per-line? circle-check? atsign?)
893                                 &body body)
894;;    (when (and circle-check? atsign?)
895;;      (setf circle-check? 'not-first-p))
896  (declare (ignore atsign?))
897  `(let ((*current-level* (1+ *current-level*))
898         (sys:*current-print-length* -1)
899;;       ,@(if (and circle-check? atsign?)
900;;                `((not-first-p (plusp sys:*current-print-length*))))
901         )
902     (unless (check-block-abbreviation ,var ,args ,circle-check?)
903       (block logical-block
904         (start-block ,var ,prefix ,per-line? ,suffix)
905         (unwind-protect
906           (macrolet ((pprint-pop () `(pprint-pop+ ,',args ,',var))
907                      (pprint-exit-if-list-exhausted ()
908                        `(if (null ,',args) (return-from logical-block nil))))
909             ,@ body)
910           (end-block ,var ,suffix))))))
911
912;; "If stream is a pretty printing stream and the value of *PRINT-PRETTY* is
913;; true, a line break is inserted in the output when the appropriate condition
914;; below is satisfied; otherwise, PPRINT-NEWLINE has no effect."
915(defun pprint-newline (kind &optional (stream *standard-output*))
916  (sys:require-type kind '(MEMBER :LINEAR :MISER :FILL :MANDATORY))
917  (setq stream (sys:out-synonym-of stream))
918  (when (not (member kind '(:linear :miser :fill :mandatory)))
919    (error 'simple-type-error
920           :format-control "Invalid KIND argument ~A to PPRINT-NEWLINE."
921           :format-arguments (list kind)))
922  (when (and (xp-structure-p stream) *print-pretty*)
923    (pprint-newline+ kind stream))
924  nil)
925
926;; "If stream is a pretty printing stream and the value of *PRINT-PRETTY* is
927;; true, PPRINT-INDENT sets the indentation in the innermost dynamically
928;; enclosing logical block; otherwise, PPRINT-INDENT has no effect."
929(defun pprint-indent (relative-to n &optional (stream *standard-output*))
930  (setq stream (sys:out-synonym-of stream))
931  (when (not (member relative-to '(:block :current)))
932    (error "Invalid KIND argument ~A to PPRINT-INDENT" relative-to))
933  (when (and (xp-structure-p stream) *print-pretty*)
934    (pprint-indent+ relative-to (truncate n) stream))
935  nil)
936
937(defun pprint-tab (kind colnum colinc &optional (stream *standard-output*))
938  (setq stream (sys:out-synonym-of stream))
939  (when (not (member kind '(:line :section :line-relative :section-relative)))
940    (error "Invalid KIND argument ~A to PPRINT-TAB" kind))
941  (when (and (xp-structure-p stream) *print-pretty*)
942    (pprint-tab+ kind colnum colinc stream))
943  nil)
944
945(eval-when (:compile-toplevel :load-toplevel :execute)
946  (defmacro pprint-pop+ (args xp)
947    `(if (pprint-pop-check+ ,args ,xp)
948         (return-from logical-block nil)
949         (pop ,args)))
950
951  (defun pprint-pop-check+ (args xp)
952    (incf sys:*current-print-length*)
953    (cond ((not (listp args))  ;must be first so supersedes length abbrev
954           (write-string++ ". " xp 0 2)
955           (sys:output-object args xp)
956           t)
957          ((and *print-length* ;must supersede circle check
958                (not *print-readably*)
959                (not (< sys:*current-print-length* *print-length*)))
960           (write-string++ "..." xp 0 3)
961;;            (setq *abbreviation-happened* T)
962           t)
963;;           ((and *circularity-hash-table* (not (zerop sys:*current-print-length*)))
964;;            (case (circularity-process xp args T)
965;;              (:first ;; note must inhibit rechecking of circularity for args.
966;;               (write+ (cons (car args) (cdr args)) xp) T)
967;;              (:subsequent t)
968;;              (t nil)))
969
970          ((or (not *print-circle*)
971               (sys::uniquely-identified-by-print-p args))
972           nil)
973
974          ((and (plusp sys:*current-print-length*)
975                (sys::check-for-circularity args))
976           (write-string++ ". " xp 0 2)
977           (sys:output-object args xp)
978           t)
979
980          ))
981
982  (defun check-block-abbreviation (xp args circle-check?)
983    (declare (ignore circle-check?))
984    (cond ((not (listp args))
985           (sys:output-object args xp) T)
986          ((and *print-level*
987                (not *print-readably*)
988                (> *current-level* *print-level*))
989           (write-char++ #\# xp)
990           (setf *abbreviation-happened* t)
991           t)
992;;           ((and *circularity-hash-table*
993;;                 circle-check?
994;;                 (eq (circularity-process xp args nil) :subsequent)) T)
995
996          (t
997           nil)))
998) ;; EVAL-WHEN
999
1000;                ---- PRETTY PRINTING FORMATS ----
1001
1002(defun pretty-array (xp array)
1003  (cond ((vectorp array)
1004         (pretty-vector xp array))
1005        ((zerop (array-rank array))
1006         (when *print-readably*
1007           (unless (eq (array-element-type array) t)
1008             (error 'print-not-readable :object array)))
1009         (write-string++ "#0A" xp 0 3)
1010         (sys:output-object (aref array) xp))
1011        (t
1012         (pretty-non-vector xp array))))
1013
1014(defun pretty-vector (xp v)
1015  (pprint-logical-block (xp nil :prefix "#(" :suffix ")")
1016    (let ((end (length v))
1017          (i 0))
1018      (when (plusp end)
1019        (loop
1020          (pprint-pop)
1021          (sys:output-object (aref v i) xp)
1022          (when (= (incf i) end)
1023            (return nil))
1024          (write-char++ #\space xp)
1025          (pprint-newline+ :fill xp))))))
1026
1027(declaim (special *prefix*))
1028
1029(defun pretty-non-vector (xp array)
1030  (when (and *print-readably*
1031             (not (array-readably-printable-p array)))
1032    (error 'print-not-readable :object array))
1033  (let* ((bottom (1- (array-rank array)))
1034         (indices (make-list (1+ bottom) :initial-element 0))
1035         (dims (array-dimensions array))
1036         (*prefix* (cl:format nil "#~DA(" (1+ bottom))))
1037    (labels ((pretty-slice (slice)
1038               (pprint-logical-block (xp nil :prefix *prefix* :suffix ")")
1039                 (let ((end (nth slice dims))
1040                       (spot (nthcdr slice indices))
1041                       (i 0)
1042                       (*prefix* "("))
1043                   (when (plusp end)
1044                     (loop (pprint-pop)
1045                           (setf (car spot) i)
1046                           (if (= slice bottom)
1047                               (sys:output-object (apply #'aref array indices) xp)
1048                               (pretty-slice (1+ slice)))
1049                           (if (= (incf i) end) (return nil))
1050                           (write-char++ #\space xp)
1051                           (pprint-newline+ (if (= slice bottom) :fill :linear) xp)))))))
1052      (pretty-slice 0))))
1053
1054(defun array-readably-printable-p (array)
1055  (and (eq (array-element-type array) t)
1056       (let ((zero (position 0 (array-dimensions array)))
1057             (number (position 0 (array-dimensions array)
1058                               :test (complement #'eql)
1059                               :from-end t)))
1060         (or (null zero) (null number) (> zero number)))))
1061
1062;Must use pprint-logical-block (no +) in the following three, because they are
1063;exported functions.
1064
1065(defun pprint-linear (s list &optional (colon? T) atsign?)
1066  (declare (ignore atsign?))
1067  (pprint-logical-block (s list :prefix (if colon? "(" "")
1068                                :suffix (if colon? ")" ""))
1069    (pprint-exit-if-list-exhausted)
1070    (loop
1071      (sys:output-object (pprint-pop) s)
1072      (pprint-exit-if-list-exhausted)
1073      (write-char++ #\space s)
1074      (pprint-newline+ :linear s))))
1075
1076(defun pprint-fill (stream object &optional (colon-p t) at-sign-p)
1077  (declare (ignore at-sign-p))
1078  (pprint-logical-block (stream object :prefix (if colon-p "(" "")
1079                                       :suffix (if colon-p ")" ""))
1080    (pprint-exit-if-list-exhausted)
1081    (loop
1082      (sys:output-object (pprint-pop) stream)
1083      (pprint-exit-if-list-exhausted)
1084      (write-char++ #\space stream)
1085      (pprint-newline+ :fill stream))))
1086
1087(defun pprint-tabular (stream list &optional (colon-p T) at-sign-p (tabsize nil))
1088  (declare (ignore at-sign-p))
1089  (when (null tabsize) (setq tabsize 16))
1090  (pprint-logical-block (stream list :prefix (if colon-p "(" "")
1091                                :suffix (if colon-p ")" ""))
1092    (pprint-exit-if-list-exhausted)
1093    (loop
1094      (sys:output-object (pprint-pop) stream)
1095      (pprint-exit-if-list-exhausted)
1096      (write-char++ #\space stream)
1097      (pprint-tab+ :section-relative 0 tabsize stream)
1098      (pprint-newline+ :fill stream))))
1099
1100(defun fn-call (xp list)
1101  (funcall (formatter "~:<~W~^ ~:I~@_~@{~W~^ ~_~}~:>") xp list))
1102
1103;Although idiosyncratic, I have found this very useful to avoid large
1104;indentations when printing out code.
1105
1106(defun alternative-fn-call (xp list)
1107  (if (> (length (symbol-name (car list))) 12)
1108      (funcall (formatter "~:<~1I~@{~W~^ ~_~}~:>") xp list)
1109      (funcall (formatter "~:<~W~^ ~:I~@_~@{~W~^ ~_~}~:>") xp list)))
1110
1111(defun bind-list (xp list &rest args)
1112    (declare (ignore args))
1113  (if (do ((i 50 (1- i))
1114           (ls list (cdr ls))) ((null ls) t)
1115        (when (or (not (consp ls)) (not (symbolp (car ls))) (minusp i))
1116          (return nil)))
1117      (pprint-fill xp list)
1118      (funcall (formatter "~:<~@{~:/xp:pprint-fill/~^ ~_~}~:>") xp list)))
1119
1120(defun block-like (xp list &rest args)
1121  (declare (ignore args))
1122  (funcall (formatter "~:<~1I~^~W~^ ~@_~W~^~@{ ~_~W~^~}~:>") xp list))
1123
1124(defun defun-like (xp list &rest args)
1125  (declare (ignore args))
1126  (funcall (formatter "~:<~1I~W~^ ~@_~W~^ ~@_~:/xp:pprint-fill/~^~@{ ~_~W~^~}~:>")
1127           xp list))
1128
1129(defun print-fancy-fn-call (xp list template)
1130  (let ((i 0) (in-first-section t))
1131    (pprint-logical-block+ (xp list "(" ")" nil t nil)
1132      (sys:output-object (pprint-pop) xp)
1133      (pprint-indent+ :current 1 xp)
1134      (loop
1135        (pprint-exit-if-list-exhausted)
1136        (write-char++ #\space xp)
1137        (when (eq i (car template))
1138          (pprint-indent+ :block (cadr template) xp)
1139          (setq template (cddr template))
1140          (setq in-first-section nil))
1141        (pprint-newline (cond ((and (zerop i) in-first-section) :miser)
1142                              (in-first-section :fill)
1143                              (T :linear))
1144                        xp)
1145        (sys:output-object (pprint-pop) xp)
1146        (incf i)))))
1147
1148;This is an attempt to specify a correct format for every form in the CL book
1149;that does not just get printed out like an ordinary function call
1150;(i.e., most special forms and many macros).  This of course does not
1151;cover anything new you define.
1152
1153(defun let-print (xp obj)
1154  (funcall (formatter "~:<~^~W~^ ~@_~:<~@{~:<~^~W~@{ ~_~W~}~:>~^ ~_~}~:>~1I~:@_~@{~W~^ ~_~}~:>")
1155           xp obj))
1156
1157(defun cond-print (xp obj)
1158  (funcall (formatter "~:<~W~^ ~:I~@_~@{~:/xp:pprint-linear/~^ ~_~}~:>") xp obj))
1159
1160(defun dmm-print (xp list)
1161  (print-fancy-fn-call xp list '(3 1)))
1162
1163(defun defsetf-print (xp list)
1164  (print-fancy-fn-call xp list '(3 1)))
1165
1166(defun do-print (xp obj)
1167  (funcall
1168   (formatter "~:<~W~^ ~:I~@_~/xp:bind-list/~^ ~_~:/xp:pprint-linear/ ~1I~^~@{ ~_~W~^~}~:>")
1169   xp obj))
1170
1171(defun flet-print (xp obj)
1172  (funcall (formatter "~:<~1I~W~^ ~@_~:<~@{~/xp:block-like/~^ ~_~}~:>~^~@{ ~_~W~^~}~:>")
1173           xp obj))
1174
1175(defun function-print (xp list)
1176  (if (and (consp (cdr list)) (null (cddr list)))
1177      (funcall (formatter "#'~W") xp (cadr list))
1178      (fn-call xp list)))
1179
1180(defun mvb-print (xp list)
1181  (print-fancy-fn-call xp list '(1 3 2 1)))
1182
1183;; Used by PROG-PRINT and TAGBODY-PRINT.
1184(defun maybelab (xp item &rest args)
1185  (declare (ignore args) (special need-newline indentation))
1186  (when need-newline (pprint-newline+ :mandatory xp))
1187  (cond ((and item (symbolp item))
1188         (write+ item xp)
1189         (setq need-newline nil))
1190        (t (pprint-tab+ :section indentation 0 xp)
1191           (write+ item xp)
1192           (setq need-newline T))))
1193
1194(defun prog-print (xp list)
1195  (let ((need-newline T) (indentation (1+ (length (symbol-name (car list))))))
1196    (declare (special need-newline indentation))
1197    (funcall (formatter "~:<~W~^ ~:/xp:pprint-fill/~^ ~@{~/xp:maybelab/~^ ~}~:>")
1198             xp list)))
1199
1200(defun tagbody-print (xp list)
1201  (let ((need-newline (and (consp (cdr list))
1202                           (symbolp (cadr list)) (cadr list)))
1203        (indentation (1+ (length (symbol-name (car list))))))
1204    (declare (special need-newline indentation))
1205    (funcall (formatter "~:<~W~^ ~@{~/xp:maybelab/~^ ~}~:>") xp list)))
1206
1207(defun setq-print (xp obj)
1208  (funcall (formatter "~:<~W~^ ~:I~@_~@{~W~^ ~:_~W~^ ~_~}~:>") xp obj))
1209
1210(defun quote-print (xp list)
1211  (if (and (consp (cdr list)) (null (cddr list)))
1212      (funcall (formatter "'~W") xp (cadr list))
1213      (pprint-fill xp list)))
1214
1215(defun up-print (xp list)
1216  (print-fancy-fn-call xp list '(0 3 1 1)))
1217
1218;here is some simple stuff for printing LOOP
1219
1220;The challange here is that we have to effectively parse the clauses of the
1221;loop in order to know how to print things.  Also you want to do this in a
1222;purely incremental way so that all of the abbreviation things work, and
1223;you wont blow up on circular lists or the like.  (More aesthic output could
1224;be produced by really parsing the clauses into nested lists before printing them.)
1225
1226;The following program assumes the following simplified grammar of the loop
1227;clauses that explains how to print them.  Note that it does not bare much
1228;resemblence to the right parsing grammar, however, it produces half decent
1229;output.  The way to make the output better is to make the grammar more
1230;detailed.
1231;
1232;loop == (LOOP {clause}*)      ;one clause on each line.
1233;clause == block | linear | cond | finally
1234;block == block-head {expr}*   ;as many exprs as possible on each line.
1235;linear == linear-head {expr}* ;one expr on each line.
1236;finally == FINALLY [DO | DOING | RETURN] {expr}* ;one expr on each line.
1237;cond == cond-head [expr]
1238;          clause
1239;          {AND clause}*       ;one AND on each line.
1240;        [ELSE
1241;          clause
1242;          {AND clause}*]      ;one AND on each line.
1243;        [END]
1244;block-head == FOR | AS | WITH | AND
1245;              | REPEAT | NAMED | WHILE | UNTIL | ALWAYS | NEVER | THEREIS | RETURN
1246;              | COLLECT | COLLECTING | APPEND | APPENDING | NCONC | NCONCING | COUNT
1247;              | COUNTING | SUM | SUMMING | MAXIMIZE | MAXIMIZING | MINIMIZE | MINIMIZING
1248;linear-head == DO | DOING | INITIALLY
1249;var-head == FOR | AS | WITH
1250;cond-head == IF | WHEN | UNLESS
1251;expr == <anything that is not a head symbol>
1252
1253;Note all the string comparisons below are required to support some
1254;existing implementations of LOOP.
1255
1256(defun token-type (token &aux string)
1257  (cond ((not (symbolp token)) :expr)
1258        ((string= (setq string (string token)) "FINALLY") :finally)
1259        ((member string '("IF" "WHEN" "UNLESS") :test #'string=) :cond-head)
1260        ((member string '("DO" "DOING" "INITIALLY") :test #'string=) :linear-head)
1261        ((member string '("FOR" "AS" "WITH" "AND" "END" "ELSE"
1262                          "REPEAT" "NAMED" "WHILE" "UNTIL" "ALWAYS" "NEVER"
1263                          "THEREIS" "RETURN" "COLLECT" "COLLECTING" "APPEND"
1264                          "APPENDING" "NCONC" "NCONCING" "COUNT" "COUNTING"
1265                          "SUM" "SUMMING" "MAXIMIZE" "MAXIMIZING"
1266                          "MINIMIZE" "MINIMIZING")
1267                 :test #'string=)
1268         :block-head)
1269        (T :expr)))
1270
1271(defun pretty-loop (xp loop)
1272  (if (not (and (consp (cdr loop)) (symbolp (cadr loop)))) ; old-style loop
1273      (fn-call xp loop)
1274      (pprint-logical-block (xp loop :prefix "(" :suffix ")")
1275        (let (token type)
1276          (labels ((next-token ()
1277                     (pprint-exit-if-list-exhausted)
1278                     (setq token (pprint-pop))
1279                     (setq type (token-type token)))
1280                   (print-clause (xp)
1281                     (case type
1282                       (:linear-head (print-exprs xp nil :mandatory))
1283                       (:cond-head (print-cond xp))
1284                       (:finally (print-exprs xp T :mandatory))
1285                       (otherwise (print-exprs xp nil :fill))))
1286                   (print-exprs (xp skip-first-non-expr newline-type)
1287                     (let ((first token))
1288                       (next-token)     ;so always happens no matter what
1289                       (pprint-logical-block (xp nil)
1290                         (write first :stream xp)
1291                         (when (and skip-first-non-expr (not (eq type :expr)))
1292                           (write-char #\space xp)
1293                           (write token :stream xp)
1294                           (next-token))
1295                         (when (eq type :expr)
1296                           (write-char #\space xp)
1297                           (pprint-indent :current 0 xp)
1298                           (loop (write token :stream xp)
1299                                 (next-token)
1300                                 (when (not (eq type :expr)) (return nil))
1301                                 (write-char #\space xp)
1302                                 (pprint-newline newline-type xp))))))
1303                   (print-cond (xp)
1304                     (let ((first token))
1305                       (next-token)     ;so always happens no matter what
1306                       (pprint-logical-block (xp nil)
1307                         (write first :stream xp)
1308                         (when (eq type :expr)
1309                           (write-char #\space xp)
1310                           (write token :stream xp)
1311                           (next-token))
1312                         (write-char #\space xp)
1313                         (pprint-indent :block 2 xp)
1314                         (pprint-newline :linear xp)
1315                         (print-clause xp)
1316                         (print-and-list xp)
1317                         (when (and (symbolp token)
1318                                    (string= (string token) "ELSE"))
1319                           (print-else-or-end xp)
1320                           (write-char #\space xp)
1321                           (pprint-newline :linear xp)
1322                           (print-clause xp)
1323                           (print-and-list xp))
1324                         (when (and (symbolp token)
1325                                    (string= (string token) "END"))
1326                           (print-else-or-end xp)))))
1327                   (print-and-list (xp)
1328                     (loop (when (not (and (symbolp token)
1329                                           (string= (string token) "AND")))
1330                                 (return nil))
1331                           (write-char #\space xp)
1332                           (pprint-newline :mandatory xp)
1333                           (write token :stream xp)
1334                           (next-token)
1335                           (write-char #\space xp)
1336                           (print-clause xp)))
1337                   (print-else-or-end (xp)
1338                     (write-char #\space xp)
1339                     (pprint-indent :block 0 xp)
1340                     (pprint-newline :linear xp)
1341                     (write token :stream xp)
1342                     (next-token)
1343                     (pprint-indent :block 2 xp)))
1344            (pprint-exit-if-list-exhausted)
1345            (write (pprint-pop) :stream xp)
1346            (next-token)
1347            (write-char #\space xp)
1348            (pprint-indent :current 0 xp)
1349            (loop (print-clause xp)
1350                  (write-char #\space xp)
1351                  (pprint-newline :linear xp)))))))
1352
1353;; (defun basic-write (object stream)
1354;;   (cond ((xp-structure-p stream)
1355;;          (write+ object stream))
1356;;      (*print-pretty*
1357;;          (maybe-initiate-xp-printing #'(lambda (s o) (write+ o s))
1358;;                                      stream object))
1359;;      (t
1360;;          (assert nil)
1361;;          (syss:output-object object stream))))
1362
1363(defun output-pretty-object (object stream)
1364;;   (basic-write object stream))
1365  (cond ((xp-structure-p stream)
1366         (write+ object stream))
1367        (*print-pretty*
1368         (maybe-initiate-xp-printing object #'(lambda (s o) (write+ o s))
1369                                     stream object))
1370        (t
1371         (assert nil)
1372         (sys:output-object object stream))))
1373
1374(provide "PPRINT")
1375
1376;------------------------------------------------------------------------
1377
1378;Copyright Massachusetts Institute of Technology, Cambridge, Massachusetts.
1379
1380;Permission to use, copy, modify, and distribute this software and its
1381;documentation for any purpose and without fee is hereby granted,
1382;provided that this copyright and permission notice appear in all
1383;copies and supporting documentation, and that the name of M.I.T. not
1384;be used in advertising or publicity pertaining to distribution of the
1385;software without specific, written prior permission. M.I.T. makes no
1386;representations about the suitability of this software for any
1387;purpose.  It is provided "as is" without express or implied warranty.
1388
1389;    M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
1390;    ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
1391;    M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
1392;    ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
1393;    WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
1394;    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
1395;    SOFTWARE.
1396
1397;------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.