Mercurial > lasercutter
comparison src/clojure/contrib/pprint/dispatch.clj @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
9:35cf337adfcf | 10:ef7dbbd6452c |
---|---|
1 ;; dispatch.clj -- part of the pretty printer for Clojure | |
2 | |
3 ;; by Tom Faulhaber | |
4 ;; April 3, 2009 | |
5 | |
6 ; Copyright (c) Tom Faulhaber, Feb 2009. All rights reserved. | |
7 ; The use and distribution terms for this software are covered by the | |
8 ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) | |
9 ; which can be found in the file epl-v10.html at the root of this distribution. | |
10 ; By using this software in any fashion, you are agreeing to be bound by | |
11 ; the terms of this license. | |
12 ; You must not remove this notice, or any other, from this software. | |
13 | |
14 ;; This module implements the default dispatch tables for pretty printing code and | |
15 ;; data. | |
16 | |
17 (in-ns 'clojure.contrib.pprint) | |
18 | |
19 (defn use-method | |
20 "Installs a function as a new method of multimethod associated with dispatch-value. " | |
21 [multifn dispatch-val func] | |
22 (. multifn addMethod dispatch-val func)) | |
23 | |
24 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
25 ;; Implementations of specific dispatch table entries | |
26 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
27 | |
28 ;;; Handle forms that can be "back-translated" to reader macros | |
29 ;;; Not all reader macros can be dealt with this way or at all. | |
30 ;;; Macros that we can't deal with at all are: | |
31 ;;; ; - The comment character is aborbed by the reader and never is part of the form | |
32 ;;; ` - Is fully processed at read time into a lisp expression (which will contain concats | |
33 ;;; and regular quotes). | |
34 ;;; ~@ - Also fully eaten by the processing of ` and can't be used outside. | |
35 ;;; , - is whitespace and is lost (like all other whitespace). Formats can generate commas | |
36 ;;; where they deem them useful to help readability. | |
37 ;;; ^ - Adding metadata completely disappears at read time and the data appears to be | |
38 ;;; completely lost. | |
39 ;;; | |
40 ;;; Most other syntax stuff is dealt with directly by the formats (like (), [], {}, and #{}) | |
41 ;;; or directly by printing the objects using Clojure's built-in print functions (like | |
42 ;;; :keyword, \char, or ""). The notable exception is #() which is special-cased. | |
43 | |
44 (def reader-macros | |
45 {'quote "'", 'clojure.core/deref "@", | |
46 'var "#'", 'clojure.core/unquote "~"}) | |
47 | |
48 (defn pprint-reader-macro [alis] | |
49 (let [^String macro-char (reader-macros (first alis))] | |
50 (when (and macro-char (= 2 (count alis))) | |
51 (.write ^java.io.Writer *out* macro-char) | |
52 (write-out (second alis)) | |
53 true))) | |
54 | |
55 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
56 ;; Dispatch for the basic data types when interpreted | |
57 ;; as data (as opposed to code). | |
58 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
59 | |
60 ;;; TODO: inline these formatter statements into funcs so that we | |
61 ;;; are a little easier on the stack. (Or, do "real" compilation, a | |
62 ;;; la Common Lisp) | |
63 | |
64 ;;; (def pprint-simple-list (formatter-out "~:<~@{~w~^ ~_~}~:>")) | |
65 (defn pprint-simple-list [alis] | |
66 (pprint-logical-block :prefix "(" :suffix ")" | |
67 (loop [alis (seq alis)] | |
68 (when alis | |
69 (write-out (first alis)) | |
70 (when (next alis) | |
71 (.write ^java.io.Writer *out* " ") | |
72 (pprint-newline :linear) | |
73 (recur (next alis))))))) | |
74 | |
75 (defn pprint-list [alis] | |
76 (if-not (pprint-reader-macro alis) | |
77 (pprint-simple-list alis))) | |
78 | |
79 ;;; (def pprint-vector (formatter-out "~<[~;~@{~w~^ ~_~}~;]~:>")) | |
80 (defn pprint-vector [avec] | |
81 (pprint-logical-block :prefix "[" :suffix "]" | |
82 (loop [aseq (seq avec)] | |
83 (when aseq | |
84 (write-out (first aseq)) | |
85 (when (next aseq) | |
86 (.write ^java.io.Writer *out* " ") | |
87 (pprint-newline :linear) | |
88 (recur (next aseq))))))) | |
89 | |
90 (def pprint-array (formatter-out "~<[~;~@{~w~^, ~:_~}~;]~:>")) | |
91 | |
92 ;;; (def pprint-map (formatter-out "~<{~;~@{~<~w~^ ~_~w~:>~^, ~_~}~;}~:>")) | |
93 (defn pprint-map [amap] | |
94 (pprint-logical-block :prefix "{" :suffix "}" | |
95 (loop [aseq (seq amap)] | |
96 (when aseq | |
97 (pprint-logical-block | |
98 (write-out (ffirst aseq)) | |
99 (.write ^java.io.Writer *out* " ") | |
100 (pprint-newline :linear) | |
101 (write-out (fnext (first aseq)))) | |
102 (when (next aseq) | |
103 (.write ^java.io.Writer *out* ", ") | |
104 (pprint-newline :linear) | |
105 (recur (next aseq))))))) | |
106 | |
107 (def pprint-set (formatter-out "~<#{~;~@{~w~^ ~:_~}~;}~:>")) | |
108 (defn pprint-ref [ref] | |
109 (pprint-logical-block :prefix "#<Ref " :suffix ">" | |
110 (write-out @ref))) | |
111 (defn pprint-atom [ref] | |
112 (pprint-logical-block :prefix "#<Atom " :suffix ">" | |
113 (write-out @ref))) | |
114 (defn pprint-agent [ref] | |
115 (pprint-logical-block :prefix "#<Agent " :suffix ">" | |
116 (write-out @ref))) | |
117 | |
118 (defn pprint-simple-default [obj] | |
119 (cond | |
120 (.isArray (class obj)) (pprint-array obj) | |
121 (and *print-suppress-namespaces* (symbol? obj)) (print (name obj)) | |
122 :else (pr obj))) | |
123 | |
124 | |
125 (defmulti | |
126 *simple-dispatch* | |
127 "The pretty print dispatch function for simple data structure format." | |
128 {:arglists '[[object]]} | |
129 class) | |
130 | |
131 (use-method *simple-dispatch* clojure.lang.ISeq pprint-list) | |
132 (use-method *simple-dispatch* clojure.lang.IPersistentVector pprint-vector) | |
133 (use-method *simple-dispatch* clojure.lang.IPersistentMap pprint-map) | |
134 (use-method *simple-dispatch* clojure.lang.IPersistentSet pprint-set) | |
135 (use-method *simple-dispatch* clojure.lang.Ref pprint-ref) | |
136 (use-method *simple-dispatch* clojure.lang.Atom pprint-atom) | |
137 (use-method *simple-dispatch* clojure.lang.Agent pprint-agent) | |
138 (use-method *simple-dispatch* nil pr) | |
139 (use-method *simple-dispatch* :default pprint-simple-default) | |
140 | |
141 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
142 ;;; Dispatch for the code table | |
143 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
144 | |
145 (declare pprint-simple-code-list) | |
146 | |
147 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
148 ;;; Format something that looks like a simple def (sans metadata, since the reader | |
149 ;;; won't give it to us now). | |
150 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
151 | |
152 (def pprint-hold-first (formatter-out "~:<~w~^ ~@_~w~^ ~_~@{~w~^ ~_~}~:>")) | |
153 | |
154 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
155 ;;; Format something that looks like a defn or defmacro | |
156 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
157 | |
158 ;;; Format the params and body of a defn with a single arity | |
159 (defn- single-defn [alis has-doc-str?] | |
160 (if (seq alis) | |
161 (do | |
162 (if has-doc-str? | |
163 ((formatter-out " ~_")) | |
164 ((formatter-out " ~@_"))) | |
165 ((formatter-out "~{~w~^ ~_~}") alis)))) | |
166 | |
167 ;;; Format the param and body sublists of a defn with multiple arities | |
168 (defn- multi-defn [alis has-doc-str?] | |
169 (if (seq alis) | |
170 ((formatter-out " ~_~{~w~^ ~_~}") alis))) | |
171 | |
172 ;;; TODO: figure out how to support capturing metadata in defns (we might need a | |
173 ;;; special reader) | |
174 (defn pprint-defn [alis] | |
175 (if (next alis) | |
176 (let [[defn-sym defn-name & stuff] alis | |
177 [doc-str stuff] (if (string? (first stuff)) | |
178 [(first stuff) (next stuff)] | |
179 [nil stuff]) | |
180 [attr-map stuff] (if (map? (first stuff)) | |
181 [(first stuff) (next stuff)] | |
182 [nil stuff])] | |
183 (pprint-logical-block :prefix "(" :suffix ")" | |
184 ((formatter-out "~w ~1I~@_~w") defn-sym defn-name) | |
185 (if doc-str | |
186 ((formatter-out " ~_~w") doc-str)) | |
187 (if attr-map | |
188 ((formatter-out " ~_~w") attr-map)) | |
189 ;; Note: the multi-defn case will work OK for malformed defns too | |
190 (cond | |
191 (vector? (first stuff)) (single-defn stuff (or doc-str attr-map)) | |
192 :else (multi-defn stuff (or doc-str attr-map))))) | |
193 (pprint-simple-code-list alis))) | |
194 | |
195 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
196 ;;; Format something with a binding form | |
197 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
198 | |
199 (defn pprint-binding-form [binding-vec] | |
200 (pprint-logical-block :prefix "[" :suffix "]" | |
201 (loop [binding binding-vec] | |
202 (when (seq binding) | |
203 (pprint-logical-block binding | |
204 (write-out (first binding)) | |
205 (when (next binding) | |
206 (.write ^java.io.Writer *out* " ") | |
207 (pprint-newline :miser) | |
208 (write-out (second binding)))) | |
209 (when (next (rest binding)) | |
210 (.write ^java.io.Writer *out* " ") | |
211 (pprint-newline :linear) | |
212 (recur (next (rest binding)))))))) | |
213 | |
214 (defn pprint-let [alis] | |
215 (let [base-sym (first alis)] | |
216 (pprint-logical-block :prefix "(" :suffix ")" | |
217 (if (and (next alis) (vector? (second alis))) | |
218 (do | |
219 ((formatter-out "~w ~1I~@_") base-sym) | |
220 (pprint-binding-form (second alis)) | |
221 ((formatter-out " ~_~{~w~^ ~_~}") (next (rest alis)))) | |
222 (pprint-simple-code-list alis))))) | |
223 | |
224 | |
225 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
226 ;;; Format something that looks like "if" | |
227 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
228 | |
229 (def pprint-if (formatter-out "~:<~1I~w~^ ~@_~w~@{ ~_~w~}~:>")) | |
230 | |
231 (defn pprint-cond [alis] | |
232 (pprint-logical-block :prefix "(" :suffix ")" | |
233 (pprint-indent :block 1) | |
234 (write-out (first alis)) | |
235 (when (next alis) | |
236 (.write ^java.io.Writer *out* " ") | |
237 (pprint-newline :linear) | |
238 (loop [alis (next alis)] | |
239 (when alis | |
240 (pprint-logical-block alis | |
241 (write-out (first alis)) | |
242 (when (next alis) | |
243 (.write ^java.io.Writer *out* " ") | |
244 (pprint-newline :miser) | |
245 (write-out (second alis)))) | |
246 (when (next (rest alis)) | |
247 (.write ^java.io.Writer *out* " ") | |
248 (pprint-newline :linear) | |
249 (recur (next (rest alis))))))))) | |
250 | |
251 (defn pprint-condp [alis] | |
252 (if (> (count alis) 3) | |
253 (pprint-logical-block :prefix "(" :suffix ")" | |
254 (pprint-indent :block 1) | |
255 (apply (formatter-out "~w ~@_~w ~@_~w ~_") alis) | |
256 (loop [alis (seq (drop 3 alis))] | |
257 (when alis | |
258 (pprint-logical-block alis | |
259 (write-out (first alis)) | |
260 (when (next alis) | |
261 (.write ^java.io.Writer *out* " ") | |
262 (pprint-newline :miser) | |
263 (write-out (second alis)))) | |
264 (when (next (rest alis)) | |
265 (.write ^java.io.Writer *out* " ") | |
266 (pprint-newline :linear) | |
267 (recur (next (rest alis))))))) | |
268 (pprint-simple-code-list alis))) | |
269 | |
270 ;;; The map of symbols that are defined in an enclosing #() anonymous function | |
271 (def *symbol-map* {}) | |
272 | |
273 (defn pprint-anon-func [alis] | |
274 (let [args (second alis) | |
275 nlis (first (rest (rest alis)))] | |
276 (if (vector? args) | |
277 (binding [*symbol-map* (if (= 1 (count args)) | |
278 {(first args) "%"} | |
279 (into {} | |
280 (map | |
281 #(vector %1 (str \% %2)) | |
282 args | |
283 (range 1 (inc (count args))))))] | |
284 ((formatter-out "~<#(~;~@{~w~^ ~_~}~;)~:>") nlis)) | |
285 (pprint-simple-code-list alis)))) | |
286 | |
287 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
288 ;;; The master definitions for formatting lists in code (that is, (fn args...) or | |
289 ;;; special forms). | |
290 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
291 | |
292 ;;; This is the equivalent of (formatter-out "~:<~1I~@{~w~^ ~_~}~:>"), but is | |
293 ;;; easier on the stack. | |
294 | |
295 (defn pprint-simple-code-list [alis] | |
296 (pprint-logical-block :prefix "(" :suffix ")" | |
297 (pprint-indent :block 1) | |
298 (loop [alis (seq alis)] | |
299 (when alis | |
300 (write-out (first alis)) | |
301 (when (next alis) | |
302 (.write ^java.io.Writer *out* " ") | |
303 (pprint-newline :linear) | |
304 (recur (next alis))))))) | |
305 | |
306 ;;; Take a map with symbols as keys and add versions with no namespace. | |
307 ;;; That is, if ns/sym->val is in the map, add sym->val to the result. | |
308 (defn two-forms [amap] | |
309 (into {} | |
310 (mapcat | |
311 identity | |
312 (for [x amap] | |
313 [x [(symbol (name (first x))) (second x)]])))) | |
314 | |
315 (defn add-core-ns [amap] | |
316 (let [core "clojure.core"] | |
317 (into {} | |
318 (map #(let [[s f] %] | |
319 (if (not (or (namespace s) (special-symbol? s))) | |
320 [(symbol core (name s)) f] | |
321 %)) | |
322 amap)))) | |
323 | |
324 (def *code-table* | |
325 (two-forms | |
326 (add-core-ns | |
327 {'def pprint-hold-first, 'defonce pprint-hold-first, | |
328 'defn pprint-defn, 'defn- pprint-defn, 'defmacro pprint-defn, 'fn pprint-defn, | |
329 'let pprint-let, 'loop pprint-let, 'binding pprint-let, | |
330 'with-local-vars pprint-let, 'with-open pprint-let, 'when-let pprint-let, | |
331 'if-let pprint-let, 'doseq pprint-let, 'dotimes pprint-let, | |
332 'when-first pprint-let, | |
333 'if pprint-if, 'if-not pprint-if, 'when pprint-if, 'when-not pprint-if, | |
334 'cond pprint-cond, 'condp pprint-condp, | |
335 'fn* pprint-anon-func, | |
336 '. pprint-hold-first, '.. pprint-hold-first, '-> pprint-hold-first, | |
337 'locking pprint-hold-first, 'struct pprint-hold-first, | |
338 'struct-map pprint-hold-first, | |
339 }))) | |
340 | |
341 (defn pprint-code-list [alis] | |
342 (if-not (pprint-reader-macro alis) | |
343 (if-let [special-form (*code-table* (first alis))] | |
344 (special-form alis) | |
345 (pprint-simple-code-list alis)))) | |
346 | |
347 (defn pprint-code-symbol [sym] | |
348 (if-let [arg-num (sym *symbol-map*)] | |
349 (print arg-num) | |
350 (if *print-suppress-namespaces* | |
351 (print (name sym)) | |
352 (pr sym)))) | |
353 | |
354 (defmulti | |
355 *code-dispatch* | |
356 "The pretty print dispatch function for pretty printing Clojure code." | |
357 {:arglists '[[object]]} | |
358 class) | |
359 | |
360 (use-method *code-dispatch* clojure.lang.ISeq pprint-code-list) | |
361 (use-method *code-dispatch* clojure.lang.Symbol pprint-code-symbol) | |
362 | |
363 ;; The following are all exact copies of *simple-dispatch* | |
364 (use-method *code-dispatch* clojure.lang.IPersistentVector pprint-vector) | |
365 (use-method *code-dispatch* clojure.lang.IPersistentMap pprint-map) | |
366 (use-method *code-dispatch* clojure.lang.IPersistentSet pprint-set) | |
367 (use-method *code-dispatch* clojure.lang.Ref pprint-ref) | |
368 (use-method *code-dispatch* clojure.lang.Atom pprint-atom) | |
369 (use-method *code-dispatch* clojure.lang.Agent pprint-agent) | |
370 (use-method *code-dispatch* nil pr) | |
371 (use-method *code-dispatch* :default pprint-simple-default) | |
372 | |
373 (set-pprint-dispatch *simple-dispatch*) | |
374 | |
375 | |
376 ;;; For testing | |
377 (comment | |
378 | |
379 (with-pprint-dispatch *code-dispatch* | |
380 (pprint | |
381 '(defn cl-format | |
382 "An implementation of a Common Lisp compatible format function" | |
383 [stream format-in & args] | |
384 (let [compiled-format (if (string? format-in) (compile-format format-in) format-in) | |
385 navigator (init-navigator args)] | |
386 (execute-format stream compiled-format navigator))))) | |
387 | |
388 (with-pprint-dispatch *code-dispatch* | |
389 (pprint | |
390 '(defn cl-format | |
391 [stream format-in & args] | |
392 (let [compiled-format (if (string? format-in) (compile-format format-in) format-in) | |
393 navigator (init-navigator args)] | |
394 (execute-format stream compiled-format navigator))))) | |
395 | |
396 (with-pprint-dispatch *code-dispatch* | |
397 (pprint | |
398 '(defn- -write | |
399 ([this x] | |
400 (condp = (class x) | |
401 String | |
402 (let [s0 (write-initial-lines this x) | |
403 s (.replaceFirst s0 "\\s+$" "") | |
404 white-space (.substring s0 (count s)) | |
405 mode (getf :mode)] | |
406 (if (= mode :writing) | |
407 (dosync | |
408 (write-white-space this) | |
409 (.col_write this s) | |
410 (setf :trailing-white-space white-space)) | |
411 (add-to-buffer this (make-buffer-blob s white-space)))) | |
412 | |
413 Integer | |
414 (let [c ^Character x] | |
415 (if (= (getf :mode) :writing) | |
416 (do | |
417 (write-white-space this) | |
418 (.col_write this x)) | |
419 (if (= c (int \newline)) | |
420 (write-initial-lines this "\n") | |
421 (add-to-buffer this (make-buffer-blob (str (char c)) nil)))))))))) | |
422 | |
423 (with-pprint-dispatch *code-dispatch* | |
424 (pprint | |
425 '(defn pprint-defn [writer alis] | |
426 (if (next alis) | |
427 (let [[defn-sym defn-name & stuff] alis | |
428 [doc-str stuff] (if (string? (first stuff)) | |
429 [(first stuff) (next stuff)] | |
430 [nil stuff]) | |
431 [attr-map stuff] (if (map? (first stuff)) | |
432 [(first stuff) (next stuff)] | |
433 [nil stuff])] | |
434 (pprint-logical-block writer :prefix "(" :suffix ")" | |
435 (cl-format true "~w ~1I~@_~w" defn-sym defn-name) | |
436 (if doc-str | |
437 (cl-format true " ~_~w" doc-str)) | |
438 (if attr-map | |
439 (cl-format true " ~_~w" attr-map)) | |
440 ;; Note: the multi-defn case will work OK for malformed defns too | |
441 (cond | |
442 (vector? (first stuff)) (single-defn stuff (or doc-str attr-map)) | |
443 :else (multi-defn stuff (or doc-str attr-map))))) | |
444 (pprint-simple-code-list writer alis))))) | |
445 ) | |
446 nil | |
447 |