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