*** empty log message ***
[bpt/emacs.git] / lisp / emacs-lisp / cust-print.el
CommitLineData
fd7fa35a 1;; cust-print.el -- handles print-level and print-circle.
ecb4184d 2
e5167999
ER
3;; Author: Daniel LaLiberte <liberte@cs.uiuc.edu>
4;; Version: 1.0
5;; Last-Modified: 17 Mar 1992
6;; Adapted-By: ESR
fd7fa35a 7;; Keyword: extensions
ecb4184d
ER
8
9;; Copyright (C) 1992 Free Software Foundation, Inc.
10
11;; This file is part of GNU Emacs.
12
13;; GNU Emacs is free software; you can redistribute it and/or modify
14;; it under the terms of the GNU General Public License as published by
e5167999 15;; the Free Software Foundation; either version 2, or (at your option)
ecb4184d
ER
16;; any later version.
17
18;; GNU Emacs is distributed in the hope that it will be useful,
19;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21;; GNU General Public License for more details.
22
23;; You should have received a copy of the GNU General Public License
24;; along with GNU Emacs; see the file COPYING. If not, write to
25;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
26
e5167999
ER
27;;; Commentary:
28
ecb4184d
ER
29;; This package provides a general print handler for prin1 and princ
30;; that supports print-level and print-circle, and by the way,
31;; print-length since the standard routines are being replaced. Also,
32;; to print custom types constructed from lists and vectors, use
33;; custom-print-list and custom-print-vector. See the documentation
34;; strings of these variables for more details.
35
36;; If the results of your expressions contain circular references to
37;; other parts of the same structure, the standard Emacs print
38;; subroutines may fail to print with an untrappable error,
39;; "Apparently circular structure being printed". If you only use cdr
40;; circular lists (where cdrs of lists point back; what is the right
41;; term here?), you can limit the length of printing with
42;; print-length. But car circular lists and circular vectors generate
43;; the above mentioned untrappable error in Emacs version 18. Version
44;; 19 will support print-level, but it is often useful to get a better
45;; print representation of circular structures; the print-circle
46;; option may be used to print more concise representations.
47
48;; There are two main ways to use this package. First, you may
49;; replace prin1, princ, and some subroutines that use them by calling
50;; install-custom-print-funcs so that any use of these functions in
51;; lisp code will be affected. Second, you could call the custom
52;; routines directly, thus only affecting the printing that requires
53;; them.
54
55;; Note that subroutines which call print subroutines directly will not
56;; use the custom print functions. In particular, the evaluation
57;; functions like eval-region call the print subroutines directly.
58;; Therefore, evaluating (aref circ-list 0), which calls error
59;; directly (because circ-list is not an array), will jump to the top
60;; level instead of printing the circular list.
61
62;; Obviously the right way to implement this custom-print facility
63;; is in C. Please volunteer since I don't have the time or need.
64
65;; Implementation design: we want to use the same list and vector
66;; processing algorithm for all versions of prin1 and princ, since how
67;; the processing is done depends on print-length, print-level, and
68;; print-circle. For circle printing, a preprocessing step is
69;; required before the final printing. Thanks to Jamie Zawinski
70;; for motivation and algorithms.
71
72;;=========================================================
73;; export list:
74
75;; print-level
76;; print-circle
77
78;; custom-print-list
79;; custom-print-vector
80;; add-custom-print-list
81;; add-custom-print-vector
82
83;; install-custom-print-funcs
84;; uninstall-custom-print-funcs
85
86;; custom-prin1
87;; custom-princ
88;; custom-prin1-to-string
89;; custom-print
90;; custom-format
91;; custom-message
92;; custom-error
93
e5167999 94;;; Code:
ecb4184d
ER
95
96(provide 'custom-print)
97;; Abbreviated package name: "CP"
98
99;;(defvar print-length nil
100;; "*Controls how many elements of a list, at each level, are printed.
101;;This is defined by emacs.")
102
103(defvar print-level nil
104 "*Controls how many levels deep a nested data object will print.
105
106If nil, printing proceeds recursively and may lead to
107max-lisp-eval-depth being exceeded or an untrappable error may occur:
108\"Apparently circular structure being printed.\" Also see
109print-length and print-circle.
110
111If non-nil, components at levels equal to or greater than print-level
112are printed simply as \"#\". The object to be printed is at level 0,
113and if the object is a list or vector, its top-level components are at
114level 1.")
115
116
117(defvar print-circle nil
118 "*Controls the printing of recursive structures.
119
120If nil, printing proceeds recursively and may lead to
121max-lisp-eval-depth being exceeded or an untrappable error may occur:
122\"Apparently circular structure being printed.\" Also see
123print-length and print-level.
124
125If non-nil, shared substructures anywhere in the structure are printed
126with \"#n=\" before the first occurance (in the order of the print
127representation) and \"#n#\" in place of each subsequent occurance,
128where n is a positive decimal integer.
129
130Currently, there is no way to read this representation in Emacs.")
131
132
133(defconst custom-print-list
134 nil
135 ;; e.g. '((floatp . float-to-string))
136 "If non-nil, an alist for printing of custom list objects.
137Pairs are of the form (pred . converter). If the predicate is true
138for an object, the converter is called with the object and should
139return a string which will be printed with princ.
140Also see custom-print-vector.")
141
142(defconst custom-print-vector
143 nil
144 "If non-nil, an alist for printing of custom vector objects.
145Pairs are of the form (pred . converter). If the predicate is true
146for an object, the converter is called with the object and should
147return a string which will be printed with princ.
148Also see custom-print-list.")
149
150
151(defun add-custom-print-list (pred converter)
152 "Add the pair, a PREDICATE and a CONVERTER, to custom-print-list.
153Any pair that has the same PREDICATE is first removed."
154 (setq custom-print-list (cons (cons pred converter)
155 (delq (assq pred custom-print-list)
156 custom-print-list))))
157;; e.g. (add-custom-print-list 'floatp 'float-to-string)
158
159
160(defun add-custom-print-vector (pred converter)
161 "Add the pair, a PREDICATE and a CONVERTER, to custom-print-vector.
162Any pair that has the same PREDICATE is first removed."
163 (setq custom-print-vector (cons (cons pred converter)
164 (delq (assq pred custom-print-vector)
165 custom-print-vector))))
166
167
168;;====================================================
169;; Saving and restoring internal printing routines.
170
171(defun CP::set-function-cell (symbol-pair)
172 (fset (car symbol-pair)
173 (symbol-function (car (cdr symbol-pair)))))
174
175
176(if (not (fboundp 'CP::internal-prin1))
177 (mapcar 'CP::set-function-cell
178 '((CP::internal-prin1 prin1)
179 (CP::internal-princ princ)
180 (CP::internal-print print)
181 (CP::internal-prin1-to-string prin1-to-string)
182 (CP::internal-format format)
183 (CP::internal-message message)
184 (CP::internal-error error))))
185
186
187(defun install-custom-print-funcs ()
188 "Replace print functions with general, customizable, lisp versions.
189The internal subroutines are saved away and may be recovered with
190uninstall-custom-print-funcs."
191 (interactive)
192 (mapcar 'CP::set-function-cell
193 '((prin1 custom-prin1)
194 (princ custom-princ)
195 (print custom-print)
196 (prin1-to-string custom-prin1-to-string)
197 (format custom-format)
198 (message custom-message)
199 (error custom-error)
200 )))
201
202(defun uninstall-custom-print-funcs ()
203 "Reset print functions to their internal subroutines."
204 (interactive)
205 (mapcar 'CP::set-function-cell
206 '((prin1 CP::internal-prin1)
207 (princ CP::internal-princ)
208 (print CP::internal-print)
209 (prin1-to-string CP::internal-prin1-to-string)
210 (format CP::internal-format)
211 (message CP::internal-message)
212 (error CP::internal-error)
213 )))
214
215
216;;===============================================================
217;; Lisp replacements for prin1 and princ and for subrs that use prin1
218;; (or princ) -- so far only the printing and formatting subrs.
219
220(defun custom-prin1 (object &optional stream)
221 "Replacement for standard prin1 that uses the appropriate
222printer depending on the values of print-level and print-circle (which see).
223
224Output the printed representation of OBJECT, any Lisp object.
225Quoting characters are printed when needed to make output that `read'
226can handle, whenever this is possible.
227Output stream is STREAM, or value of `standard-output' (which see)."
228 (CP::top-level object stream 'CP::internal-prin1))
229
230
231(defun custom-princ (object &optional stream)
232 "Same as custom-prin1 except no quoting."
233 (CP::top-level object stream 'CP::internal-princ))
234
235(defun custom-prin1-to-string-func (c)
236 "Stream function for custom-prin1-to-string."
237 (setq prin1-chars (cons c prin1-chars)))
238
239(defun custom-prin1-to-string (object)
240 "Replacement for standard prin1-to-string."
241 (let ((prin1-chars nil))
242 (custom-prin1 object 'custom-prin1-to-string-func)
243 (concat (nreverse prin1-chars))))
244
245
246(defun custom-print (object &optional stream)
247 "Replacement for standard print."
248 (CP::internal-princ "\n")
249 (custom-prin1 object stream)
250 (CP::internal-princ "\n"))
251
252
253(defun custom-format (fmt &rest args)
254 "Replacement for standard format.
255
256Calls format after first making strings for list or vector args.
257The format specification for such args should be %s in any case, so a
258string argument will also work. The string is generated with
259custom-prin1-to-string, which quotes quotable characters."
260 (apply 'CP::internal-format fmt
261 (mapcar (function (lambda (arg)
262 (if (or (listp arg) (vectorp arg))
263 (custom-prin1-to-string arg)
264 arg)))
265 args)))
266
267
268
269(defun custom-message (fmt &rest args)
270 "Replacement for standard message that works like custom-format."
271 ;; It doesnt work to princ the result of custom-format
272 ;; because the echo area requires special handling
273 ;; to avoid duplicating the output. CP::internal-message does it right.
274 ;; (CP::internal-princ (apply 'custom-format fmt args))
275 (apply 'CP::internal-message fmt
276 (mapcar (function (lambda (arg)
277 (if (or (listp arg) (vectorp arg))
278 (custom-prin1-to-string arg)
279 arg)))
280 args)))
281
282
283(defun custom-error (fmt &rest args)
284 "Replacement for standard error that uses custom-format"
285 (signal 'error (list (apply 'custom-format fmt args))))
286
287
288;;=========================================
289;; Support for custom prin1 and princ
290
291(defun CP::top-level (object stream internal-printer)
292 "Set up for printing."
293 (let ((standard-output (or stream standard-output))
294 (circle-table (and print-circle (CP::preprocess-circle-tree object)))
295 (level (or print-level -1))
296 )
297
298 (fset 'CP::internal-printer internal-printer)
299 (fset 'CP::low-level-prin
300 (cond
301 ((or custom-print-list
302 custom-print-vector
303 print-level ; comment out for version 19
304 )
305 'CP::custom-object)
306 (circle-table
307 'CP::object)
308 (t 'CP::internal-printer)))
309 (fset 'CP::prin (if circle-table 'CP::circular 'CP::low-level-prin))
310
311 (CP::prin object)
312 object))
313
314
315(defun CP::object (object)
316 "Test object type and print accordingly."
317 ;; Could be called as either CP::low-level-prin or CP::prin.
318 (cond
319 ((null object) (CP::internal-printer object))
320 ((consp object) (CP::list object))
321 ((vectorp object) (CP::vector object))
322 ;; All other types, just print.
323 (t (CP::internal-printer object))))
324
325
326(defun CP::custom-object (object)
327 "Test object type and print accordingly."
328 ;; Could be called as either CP::low-level-prin or CP::prin.
329 (cond
330 ((null object) (CP::internal-printer object))
331
332 ((consp object)
333 (or (and custom-print-list
334 (CP::custom-object1 object custom-print-list))
335 (CP::list object)))
336
337 ((vectorp object)
338 (or (and custom-print-vector
339 (CP::custom-object1 object custom-print-vector))
340 (CP::vector object)))
341
342 ;; All other types, just print.
343 (t (CP::internal-printer object))))
344
345
346(defun CP::custom-object1 (object alist)
347 "Helper for CP::custom-object.
348Print the custom OBJECT using the custom type ALIST.
349For the first predicate that matches the object, the corresponding
350converter is evaluated with the object and the string that results is
351printed with princ. Return nil if no predicte matches the object."
352 (while (and alist (not (funcall (car (car alist)) object)))
353 (setq alist (cdr alist)))
354 ;; If alist is not null, then something matched.
355 (if alist
356 (CP::internal-princ
357 (funcall (cdr (car alist)) object) ; returns string
358 )))
359
360
361(defun CP::circular (object)
362 "Printer for prin1 and princ that handles circular structures.
363If OBJECT appears multiply, and has not yet been printed,
364prefix with label; if it has been printed, use #n# instead.
365Otherwise, print normally."
366 (let ((tag (assq object circle-table)))
367 (if tag
368 (let ((id (cdr tag)))
369 (if (> id 0)
370 (progn
371 ;; Already printed, so just print id.
372 (CP::internal-princ "#")
373 (CP::internal-princ id)
374 (CP::internal-princ "#"))
375 ;; Not printed yet, so label with id and print object.
376 (setcdr tag (- id)) ; mark it as printed
377 (CP::internal-princ "#")
378 (CP::internal-princ (- id))
379 (CP::internal-princ "=")
380 (CP::low-level-prin object)
381 ))
382 ;; Not repeated in structure.
383 (CP::low-level-prin object))))
384
385
386;;================================================
387;; List and vector processing for print functions.
388
389(defun CP::list (list)
390 "Print a list using print-length, print-level, and print-circle."
391 (if (= level 0)
392 (CP::internal-princ "#")
393 (let ((level (1- level)))
394 (CP::internal-princ "(")
395 (let ((length (or print-length 0)))
396
397 ;; Print the first element always (even if length = 0).
398 (CP::prin (car list))
399 (setq list (cdr list))
400 (if list (CP::internal-princ " "))
401 (setq length (1- length))
402
403 ;; Print the rest of the elements.
404 (while (and list (/= 0 length))
405 (if (and (listp list)
406 (not (assq list circle-table)))
407 (progn
408 (CP::prin (car list))
409 (setq list (cdr list)))
410
411 ;; cdr is not a list, or it is in circle-table.
412 (CP::internal-princ ". ")
413 (CP::prin list)
414 (setq list nil))
415
416 (setq length (1- length))
417 (if list (CP::internal-princ " ")))
418
419 (if (and list (= length 0)) (CP::internal-princ "..."))
420 (CP::internal-princ ")"))))
421 list)
422
423
424(defun CP::vector (vector)
425 "Print a vector using print-length, print-level, and print-circle."
426 (if (= level 0)
427 (CP::internal-princ "#")
428 (let ((level (1- level))
429 (i 0)
430 (len (length vector)))
431 (CP::internal-princ "[")
432
433 (if print-length
434 (setq len (min print-length len)))
435 ;; Print the elements
436 (while (< i len)
437 (CP::prin (aref vector i))
438 (setq i (1+ i))
439 (if (< i (length vector)) (CP::internal-princ " ")))
440
441 (if (< i (length vector)) (CP::internal-princ "..."))
442 (CP::internal-princ "]")
443 ))
444 vector)
445
446
447;;==================================
448;; Circular structure preprocessing
449
450(defun CP::preprocess-circle-tree (object)
451 ;; Fill up the table.
452 (let (;; Table of tags for each object in an object to be printed.
453 ;; A tag is of the form:
454 ;; ( <object> <nil-t-or-id-number> )
455 ;; The id-number is generated after the entire table has been computed.
456 ;; During walk through, the real circle-table lives in the cdr so we
457 ;; can use setcdr to add new elements instead of having to setq the
458 ;; variable sometimes (poor man's locf).
459 (circle-table (list nil)))
460 (CP::walk-circle-tree object)
461
462 ;; Reverse table so it is in the order that the objects will be printed.
463 ;; This pass could be avoided if we always added to the end of the
464 ;; table with setcdr in walk-circle-tree.
465 (setcdr circle-table (nreverse (cdr circle-table)))
466
467 ;; Walk through the table, assigning id-numbers to those
468 ;; objects which will be printed using #N= syntax. Delete those
469 ;; objects which will be printed only once (to speed up assq later).
470 (let ((rest circle-table)
471 (id -1))
472 (while (cdr rest)
473 (let ((tag (car (cdr rest))))
474 (cond ((cdr tag)
475 (setcdr tag id)
476 (setq id (1- id))
477 (setq rest (cdr rest)))
478 ;; Else delete this object.
479 (t (setcdr rest (cdr (cdr rest))))))
480 ))
481 ;; Drop the car.
482 (cdr circle-table)
483 ))
484
485
486
487(defun CP::walk-circle-tree (object)
488 (let (read-equivalent-p tag)
489 (while object
490 (setq read-equivalent-p (or (numberp object) (symbolp object))
491 tag (and (not read-equivalent-p)
492 (assq object (cdr circle-table))))
493 (cond (tag
494 ;; Seen this object already, so note that.
495 (setcdr tag t))
496
497 ((not read-equivalent-p)
498 ;; Add a tag for this object.
499 (setcdr circle-table
500 (cons (list object)
501 (cdr circle-table)))))
502 (setq object
503 (cond
504 (tag ;; No need to descend since we have already.
505 nil)
506
507 ((consp object)
508 ;; Walk the car of the list recursively.
509 (CP::walk-circle-tree (car object))
510 ;; But walk the cdr with the above while loop
511 ;; to avoid problems with max-lisp-eval-depth.
512 ;; And it should be faster than recursion.
513 (cdr object))
514
515 ((vectorp object)
516 ;; Walk the vector.
517 (let ((i (length object))
518 (j 0))
519 (while (< j i)
520 (CP::walk-circle-tree (aref object j))
521 (setq j (1+ j))))))))))
522
523
524
525;;=======================================
526
527(quote
528 examples
529
530 (progn
531 ;; Create some circular structures.
532 (setq circ-sym (let ((x (make-symbol "FOO"))) (list x x)))
533 (setq circ-list (list 'a 'b (vector 1 2 3 4) 'd 'e 'f))
534 (setcar (nthcdr 3 circ-list) circ-list)
535 (aset (nth 2 circ-list) 2 circ-list)
536 (setq dotted-circ-list (list 'a 'b 'c))
537 (setcdr (cdr (cdr dotted-circ-list)) dotted-circ-list)
538 (setq circ-vector (vector 1 2 3 4 (list 'a 'b 'c 'd) 6 7))
539 (aset circ-vector 5 (make-symbol "-gensym-"))
540 (setcar (cdr (aref circ-vector 4)) (aref circ-vector 5))
541 nil)
542
543 (install-custom-print-funcs)
544 ;; (setq print-circle t)
545
546 (let ((print-circle t))
547 (or (equal (prin1-to-string circ-list) "#1=(a b [1 2 #1# 4] #1# e f)")
548 (error "circular object with array printing")))
549
550 (let ((print-circle t))
551 (or (equal (prin1-to-string dotted-circ-list) "#1=(a b c . #1#)")
552 (error "circular object with array printing")))
553
554 (let* ((print-circle t)
555 (x (list 'p 'q))
556 (y (list (list 'a 'b) x 'foo x)))
557 (setcdr (cdr (cdr (cdr y))) (cdr y))
558 (or (equal (prin1-to-string y) "((a b) . #1=(#2=(p q) foo #2# . #1#))"
559 )
560 (error "circular list example from CL manual")))
561
562 ;; There's no special handling of uninterned symbols in custom-print.
563 (let ((print-circle nil))
564 (or (equal (prin1-to-string circ-sym) "(#:FOO #:FOO)")
565 (error "uninterned symbols in list")))
566 (let ((print-circle t))
567 (or (equal (prin1-to-string circ-sym) "(#1=FOO #1#)")
568 (error "circular uninterned symbols in list")))
569
570 (uninstall-custom-print-funcs)
571 )
572
fd7fa35a 573;;; cust-print.el ends here