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