source: branches/0.22.x/abcl/src/org/armedbear/lisp/pprint-dispatch.lisp

Last change on this file was 11391, checked in by vvoutilainen, 16 years ago

ABCL license is GPL + Classpath exception. This was intended
by Peter Graves, the original author. For reference, see
http://sourceforge.net/mailarchive/forum.php?thread_name=20040721115302.839%40prufrock&forum_name=armedbear-j-announce

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 15.4 KB
Line 
1;;; pprint-dispatch.lisp
2;;;
3;;; Copyright (C) 2004-2005 Peter Graves
4;;; $Id: pprint-dispatch.lisp 11391 2008-11-15 22:38:34Z vvoutilainen $
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(defvar *ipd* nil ;see initialization at end of file.
61  "initial print dispatch table.")
62
63(defstruct (pprint-dispatch-table (:conc-name nil) (:copier nil))
64  (conses-with-cars (make-hash-table :test #'eq) :type hash-table)
65  (structures (make-hash-table :test #'eq) :type hash-table)
66  (others nil :type list))
67
68;The list and the hash-tables contain entries of the
69;following form.  When stored in the hash tables, the test entry is
70;the number of entries in the OTHERS list that have a higher priority.
71
72(defstruct (entry (:conc-name nil))
73  (test nil)        ;predicate function or count of higher priority others.
74  (fn nil)          ;pprint function
75  (full-spec nil))  ;list of priority and type specifier
76
77(defun copy-pprint-dispatch (&optional (table *print-pprint-dispatch*))
78  (unless table
79    (setf table *ipd*))
80  (sys::require-type table 'pprint-dispatch-table)
81  (let* ((new-conses-with-cars
82          (make-hash-table :test #'eq
83                           :size (max (hash-table-count (conses-with-cars table)) 32)))
84   (new-structures
85          (make-hash-table :test #'eq
86                           :size (max (hash-table-count (structures table)) 32))))
87    (maphash #'(lambda (key value)
88                (setf (gethash key new-conses-with-cars) (copy-entry value)))
89       (conses-with-cars table))
90    (maphash #'(lambda (key value)
91                (setf (gethash key new-structures) (copy-entry value)))
92       (structures table))
93    (make-pprint-dispatch-table
94     :conses-with-cars new-conses-with-cars
95     :structures new-structures
96     :others (copy-list (others table)))))
97
98(defun set-pprint-dispatch (type-specifier function
99                                           &optional (priority 0) (table *print-pprint-dispatch*))
100  (when (or (not (numberp priority)) (complexp priority))
101    (error "invalid PRIORITY argument ~A to SET-PPRINT-DISPATCH" priority))
102  (set-pprint-dispatch+ type-specifier function priority table))
103
104(defun set-pprint-dispatch+ (type-specifier function priority table)
105  (let* ((category (specifier-category type-specifier))
106   (pred
107          (if (not (eq category 'other)) nil
108              (let ((pred (specifier-fn type-specifier)))
109                (if (and (consp (caddr pred))
110                         (symbolp (caaddr pred))
111                         (equal (cdaddr pred) '(x)))
112                    (symbol-function (caaddr pred))
113                    ;;                      (compile nil pred)
114                    pred
115                    ))))
116   (entry (if function (make-entry :test pred
117           :fn function
118           :full-spec (list priority type-specifier)))))
119    (case category
120      (cons-with-car
121       (cond ((null entry) (remhash (cadadr type-specifier) (conses-with-cars table)))
122             (T (setf (test entry)
123                      (count-if #'(lambda (e)
124                                   (priority-> (car (full-spec e)) priority))
125                                (others table)))
126                (setf (gethash (cadadr type-specifier) (conses-with-cars table)) entry))))
127      (structure-type
128       (cond ((null entry) (remhash type-specifier (structures table)))
129             (T (setf (test entry)
130                      (count-if #'(lambda (e)
131                                   (priority-> (car (full-spec e)) priority))
132                                (others table)))
133                (setf (gethash type-specifier (structures table)) entry))))
134      (T ;other
135       (let ((old (car (member type-specifier (others table) :test #'equal
136                               :key #'(lambda (e) (cadr (full-spec e)))))))
137         (when old
138           (setf (others table) (delete old (others table)))
139           (adjust-counts table (car (full-spec old)) -1)))
140       (when entry
141         (let ((others (cons nil (others table))))
142           (do ((l others (cdr l)))
143               ((null (cdr l)) (rplacd l (list entry)))
144             (when (priority-> priority (car (full-spec (cadr l))))
145               (rplacd l (cons entry (cdr l)))
146               (return nil)))
147           (setf (others table) (cdr others)))
148         (adjust-counts table priority 1)))))
149  nil)
150
151(defun priority-> (x y)
152  (if (consp x)
153      (if (consp y) (> (car x) (car y)) nil)
154      (if (consp y) T (> x y))))
155
156
157(defun adjust-counts (table priority delta)
158  (maphash #'(lambda (key value)
159              (declare (ignore key))
160              (if (priority-> priority (car (full-spec value)))
161                  (incf (test value) delta)))
162     (conses-with-cars table))
163  (maphash #'(lambda (key value)
164              (declare (ignore key))
165              (if (priority-> priority (car (full-spec value)))
166                  (incf (test value) delta)))
167     (structures table)))
168
169(defun pprint-dispatch (object &optional (table *print-pprint-dispatch*))
170  (unless table
171    (setf table *ipd*))
172  (let ((fn (get-printer object table)))
173    (values (or fn #'non-pretty-print) (not (null fn)))))
174
175(defun get-printer (object table)
176  (let* ((entry (if (consp object)
177        (gethash (car object) (conses-with-cars table))
178        (gethash (type-of object) (structures table)))))
179    (if (not entry)
180  (setq entry (find object (others table) :test #'fits))
181  (do ((i (test entry) (1- i))
182       (l (others table) (cdr l)))
183      ((zerop i))
184    (when (fits object (car l)) (setq entry (car l)) (return nil))))
185    (when entry (fn entry))))
186
187(defun fits (obj entry) (funcall (test entry) obj))
188
189(defun specifier-category (spec)
190  (cond ((and (consp spec)
191        (eq (car spec) 'cons)
192        (consp (cdr spec))
193        (null (cddr spec))
194        (consp (cadr spec))
195        (eq (caadr spec) 'member)
196        (consp (cdadr spec))
197        (null (cddadr spec)))
198   'cons-with-car)
199  ((and (symbolp spec)
200;;               (structure-type-p spec)
201              (get spec 'structure-printer)
202              )
203         'structure-type)
204  (T 'other)))
205
206(defvar *preds-for-specs*
207  '((T always-true) (cons consp) (simple-atom simple-atom-p) (other otherp)
208    (null null) (symbol symbolp) (atom atom) (cons consp)
209    (list listp) (number numberp) (integer integerp)
210    (rational rationalp) (float floatp) (complex complexp)
211    (character characterp) (string stringp) (bit-vector bit-vector-p)
212    (vector vectorp) (simple-vector simple-vector-p)
213    (simple-string simple-string-p) (simple-bit-vector simple-bit-vector-p)
214    (array arrayp) (package packagep) (function functionp)
215    (compiled-function compiled-function-p) (common commonp)))
216
217(defun always-true (x) (declare (ignore x)) T)
218
219(defun specifier-fn (spec)
220  `(lambda (x) ,(convert-body spec)))
221
222(defun convert-body (spec)
223  (cond ((atom spec)
224   (let ((pred (cadr (assoc spec *preds-for-specs*))))
225     (if pred `(,pred x) `(typep x ',spec))))
226  ((member (car spec) '(and or not))
227   (cons (car spec) (mapcar #'convert-body (cdr spec))))
228  ((eq (car spec) 'member)
229   `(member x ',(copy-list (cdr spec))))
230  ((eq (car spec) 'cons)
231   `(and (consp x)
232         ,@(if (cdr spec) `((let ((x (car x)))
233            ,(convert-body (cadr spec)))))
234         ,@(if (cddr spec) `((let ((x (cdr x)))
235             ,(convert-body (caddr spec)))))))
236  ((eq (car spec) 'satisfies)
237   `(funcall (function ,(cadr spec)) x))
238        ((eq (car spec) 'eql)
239         `(eql x ',(cadr spec)))
240  (t
241         `(typep x ',(copy-tree spec)))))
242
243
244
245(defun function-call-p (x)
246  (and (consp x) (symbolp (car x)) (fboundp (car x))))
247
248
249
250(setq *ipd* (make-pprint-dispatch-table))
251
252(set-pprint-dispatch+ '(satisfies function-call-p) 'fn-call '(-5) *ipd*)
253(set-pprint-dispatch+ 'cons 'pprint-fill '(-10) *ipd*)
254
255(set-pprint-dispatch+ '(cons (member block)) 'block-like '(0) *ipd*)
256(set-pprint-dispatch+ '(cons (member case)) 'block-like '(0) *ipd*)
257(set-pprint-dispatch+ '(cons (member catch)) 'block-like '(0) *ipd*)
258(set-pprint-dispatch+ '(cons (member ccase)) 'block-like '(0) *ipd*)
259(set-pprint-dispatch+ '(cons (member compiler-let)) 'let-print '(0) *ipd*)
260(set-pprint-dispatch+ '(cons (member cond)) 'cond-print '(0) *ipd*)
261(set-pprint-dispatch+ '(cons (member ctypecase)) 'block-like '(0) *ipd*)
262(set-pprint-dispatch+ '(cons (member defconstant)) 'defun-like '(0) *ipd*)
263(set-pprint-dispatch+ '(cons (member define-setf-method)) 'defun-like '(0) *ipd*)
264(set-pprint-dispatch+ '(cons (member defmacro)) 'defun-like '(0) *ipd*)
265(set-pprint-dispatch+ '(cons (member define-modify-macro)) 'dmm-print '(0) *ipd*)
266(set-pprint-dispatch+ '(cons (member defparameter)) 'defun-like '(0) *ipd*)
267(set-pprint-dispatch+ '(cons (member defsetf)) 'defsetf-print '(0) *ipd*)
268(set-pprint-dispatch+ '(cons (member define-setf-method)) 'defun-like '(0) *ipd*)
269(set-pprint-dispatch+ '(cons (member defstruct)) 'block-like '(0) *ipd*)
270(set-pprint-dispatch+ '(cons (member deftype)) 'defun-like '(0) *ipd*)
271(set-pprint-dispatch+ '(cons (member defun)) 'defun-like '(0) *ipd*)
272(set-pprint-dispatch+ '(cons (member defvar)) 'defun-like '(0) *ipd*)
273(set-pprint-dispatch+ '(cons (member do)) 'do-print '(0) *ipd*)
274(set-pprint-dispatch+ '(cons (member do*)) 'do-print '(0) *ipd*)
275(set-pprint-dispatch+ '(cons (member do-all-symbols)) 'block-like '(0) *ipd*)
276(set-pprint-dispatch+ '(cons (member do-external-symbols)) 'block-like '(0) *ipd*)
277(set-pprint-dispatch+ '(cons (member do-symbols)) 'block-like '(0) *ipd*)
278(set-pprint-dispatch+ '(cons (member dolist)) 'block-like '(0) *ipd*)
279(set-pprint-dispatch+ '(cons (member dotimes)) 'block-like '(0) *ipd*)
280(set-pprint-dispatch+ '(cons (member ecase)) 'block-like '(0) *ipd*)
281(set-pprint-dispatch+ '(cons (member etypecase)) 'block-like '(0) *ipd*)
282(set-pprint-dispatch+ '(cons (member eval-when)) 'block-like '(0) *ipd*)
283(set-pprint-dispatch+ '(cons (member flet)) 'flet-print '(0) *ipd*)
284(set-pprint-dispatch+ '(cons (member function)) 'function-print '(0) *ipd*)
285(set-pprint-dispatch+ '(cons (member labels)) 'flet-print '(0) *ipd*)
286(set-pprint-dispatch+ '(cons (member lambda)) 'block-like '(0) *ipd*)
287(set-pprint-dispatch+ '(cons (member let)) 'let-print '(0) *ipd*)
288(set-pprint-dispatch+ '(cons (member let*)) 'let-print '(0) *ipd*)
289(set-pprint-dispatch+ '(cons (member locally)) 'block-like '(0) *ipd*)
290(set-pprint-dispatch+ '(cons (member loop)) 'pretty-loop '(0) *ipd*)
291(set-pprint-dispatch+ '(cons (member macrolet)) 'flet-print '(0) *ipd*)
292(set-pprint-dispatch+ '(cons (member multiple-value-bind)) 'mvb-print '(0) *ipd*)
293(set-pprint-dispatch+ '(cons (member multiple-value-setq)) 'block-like '(0) *ipd*)
294(set-pprint-dispatch+ '(cons (member prog)) 'prog-print '(0) *ipd*)
295(set-pprint-dispatch+ '(cons (member prog*)) 'prog-print '(0) *ipd*)
296(set-pprint-dispatch+ '(cons (member progv)) 'defun-like '(0) *ipd*)
297(set-pprint-dispatch+ '(cons (member psetf)) 'setq-print '(0) *ipd*)
298(set-pprint-dispatch+ '(cons (member psetq)) 'setq-print '(0) *ipd*)
299(set-pprint-dispatch+ '(cons (member quote)) 'quote-print '(0) *ipd*)
300(set-pprint-dispatch+ '(cons (member return-from)) 'block-like '(0) *ipd*)
301(set-pprint-dispatch+ '(cons (member setf)) 'setq-print '(0) *ipd*)
302(set-pprint-dispatch+ '(cons (member setq)) 'setq-print '(0) *ipd*)
303(set-pprint-dispatch+ '(cons (member tagbody)) 'tagbody-print '(0) *ipd*)
304(set-pprint-dispatch+ '(cons (member throw)) 'block-like '(0) *ipd*)
305(set-pprint-dispatch+ '(cons (member typecase)) 'block-like '(0) *ipd*)
306(set-pprint-dispatch+ '(cons (member unless)) 'block-like '(0) *ipd*)
307(set-pprint-dispatch+ '(cons (member unwind-protect)) 'up-print '(0) *ipd*)
308(set-pprint-dispatch+ '(cons (member when)) 'block-like '(0) *ipd*)
309(set-pprint-dispatch+ '(cons (member with-input-from-string)) 'block-like '(0) *ipd*)
310(set-pprint-dispatch+ '(cons (member with-open-file)) 'block-like '(0) *ipd*)
311(set-pprint-dispatch+ '(cons (member with-open-stream)) 'block-like '(0) *ipd*)
312(set-pprint-dispatch+ '(cons (member with-output-to-string)) 'block-like '(0) *ipd*)
313
314(defun pprint-dispatch-print (xp table)
315  (let ((stuff (copy-list (others table))))
316    (maphash #'(lambda (key val) (declare (ignore key))
317                (push val stuff))
318       (conses-with-cars table))
319    (maphash #'(lambda (key val) (declare (ignore key))
320                (push val stuff))
321       (structures table))
322    (setq stuff (sort stuff #'priority-> :key #'(lambda (x) (car (full-spec x)))))
323    (pprint-logical-block (xp stuff :prefix "#<" :suffix ">")
324                          (format xp (formatter "pprint dispatch table containing ~A entries: ")
325                                  (length stuff))
326                          (loop (pprint-exit-if-list-exhausted)
327                            (let ((entry (pprint-pop)))
328                              (format xp (formatter "~{~_P=~4D ~W~} F=~W ")
329                                      (full-spec entry) (fn entry)))))))
330
331(setf (get 'pprint-dispatch-table 'structure-printer) #'pprint-dispatch-print)
332
333(set-pprint-dispatch+ 'pprint-dispatch-table #'pprint-dispatch-print '(0) *ipd*)
334
335(setf *print-pprint-dispatch* (copy-pprint-dispatch nil))
Note: See TracBrowser for help on using the repository browser.