Mercurial > lasercutter
view src/clojure/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 |
line wrap: on
line source
1 ;; dispatch.clj -- part of the pretty printer for Clojure3 ; Copyright (c) Rich Hickey. All rights reserved.4 ; The use and distribution terms for this software are covered by the5 ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)6 ; which can be found in the file epl-v10.html at the root of this distribution.7 ; By using this software in any fashion, you are agreeing to be bound by8 ; the terms of this license.9 ; You must not remove this notice, or any other, from this software.11 ;; Author: Tom Faulhaber12 ;; April 3, 200915 ;; This module implements the default dispatch tables for pretty printing code and16 ;; data.18 (in-ns 'clojure.pprint)20 (defn- use-method21 "Installs a function as a new method of multimethod associated with dispatch-value. "22 [multifn dispatch-val func]23 (. multifn addMethod dispatch-val func))25 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;26 ;; Implementations of specific dispatch table entries27 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;29 ;;; Handle forms that can be "back-translated" to reader macros30 ;;; Not all reader macros can be dealt with this way or at all.31 ;;; Macros that we can't deal with at all are:32 ;;; ; - The comment character is aborbed by the reader and never is part of the form33 ;;; ` - Is fully processed at read time into a lisp expression (which will contain concats34 ;;; and regular quotes).35 ;;; ~@ - Also fully eaten by the processing of ` and can't be used outside.36 ;;; , - is whitespace and is lost (like all other whitespace). Formats can generate commas37 ;;; where they deem them useful to help readability.38 ;;; ^ - Adding metadata completely disappears at read time and the data appears to be39 ;;; completely lost.40 ;;;41 ;;; Most other syntax stuff is dealt with directly by the formats (like (), [], {}, and #{})42 ;;; or directly by printing the objects using Clojure's built-in print functions (like43 ;;; :keyword, \char, or ""). The notable exception is #() which is special-cased.45 (def ^{:private true} reader-macros46 {'quote "'", 'clojure.core/deref "@",47 'var "#'", 'clojure.core/unquote "~"})49 (defn- pprint-reader-macro [alis]50 (let [^String macro-char (reader-macros (first alis))]51 (when (and macro-char (= 2 (count alis)))52 (.write ^java.io.Writer *out* macro-char)53 (write-out (second alis))54 true)))56 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;57 ;; Dispatch for the basic data types when interpreted58 ;; as data (as opposed to code).59 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;61 ;;; TODO: inline these formatter statements into funcs so that we62 ;;; are a little easier on the stack. (Or, do "real" compilation, a63 ;;; la Common Lisp)65 ;;; (def pprint-simple-list (formatter-out "~:<~@{~w~^ ~_~}~:>"))66 (defn- pprint-simple-list [alis]67 (pprint-logical-block :prefix "(" :suffix ")"68 (loop [alis (seq alis)]69 (when alis70 (write-out (first alis))71 (when (next alis)72 (.write ^java.io.Writer *out* " ")73 (pprint-newline :linear)74 (recur (next alis)))))))76 (defn- pprint-list [alis]77 (if-not (pprint-reader-macro alis)78 (pprint-simple-list alis)))80 ;;; (def pprint-vector (formatter-out "~<[~;~@{~w~^ ~_~}~;]~:>"))81 (defn- pprint-vector [avec]82 (pprint-logical-block :prefix "[" :suffix "]"83 (loop [aseq (seq avec)]84 (when aseq85 (write-out (first aseq))86 (when (next aseq)87 (.write ^java.io.Writer *out* " ")88 (pprint-newline :linear)89 (recur (next aseq)))))))91 (def ^{:private true} pprint-array (formatter-out "~<[~;~@{~w~^, ~:_~}~;]~:>"))93 ;;; (def pprint-map (formatter-out "~<{~;~@{~<~w~^ ~_~w~:>~^, ~_~}~;}~:>"))94 (defn- pprint-map [amap]95 (pprint-logical-block :prefix "{" :suffix "}"96 (loop [aseq (seq amap)]97 (when aseq98 (pprint-logical-block99 (write-out (ffirst aseq))100 (.write ^java.io.Writer *out* " ")101 (pprint-newline :linear)102 (write-out (fnext (first aseq))))103 (when (next aseq)104 (.write ^java.io.Writer *out* ", ")105 (pprint-newline :linear)106 (recur (next aseq)))))))108 (def ^{:private true} pprint-set (formatter-out "~<#{~;~@{~w~^ ~:_~}~;}~:>"))110 ;;; TODO: don't block on promise (currently impossible)112 (def ^{:private true}113 type-map {"core$future_call" "Future",114 "core$promise" "Promise"})116 (defn- map-ref-type117 "Map ugly type names to something simpler"118 [name]119 (or (when-let [match (re-find #"^[^$]+\$[^$]+" name)]120 (type-map match))121 name))123 (defn- pprint-ideref [o]124 (let [prefix (format "#<%s@%x%s: "125 (map-ref-type (.getSimpleName (class o)))126 (System/identityHashCode o)127 (if (and (instance? clojure.lang.Agent o)128 (agent-error o))129 " FAILED"130 ""))]131 (pprint-logical-block :prefix prefix :suffix ">"132 (pprint-indent :block (-> (count prefix) (- 2) -))133 (pprint-newline :linear)134 (write-out (cond135 (and (future? o) (not (future-done? o))) :pending136 :else @o)))))138 (def ^{:private true} pprint-pqueue (formatter-out "~<<-(~;~@{~w~^ ~_~}~;)-<~:>"))140 (defn- pprint-simple-default [obj]141 (cond142 (.isArray (class obj)) (pprint-array obj)143 (and *print-suppress-namespaces* (symbol? obj)) (print (name obj))144 :else (pr obj)))147 (defmulti148 simple-dispatch149 "The pretty print dispatch function for simple data structure format."150 {:added "1.2" :arglists '[[object]]}151 class)153 (use-method simple-dispatch clojure.lang.ISeq pprint-list)154 (use-method simple-dispatch clojure.lang.IPersistentVector pprint-vector)155 (use-method simple-dispatch clojure.lang.IPersistentMap pprint-map)156 (use-method simple-dispatch clojure.lang.IPersistentSet pprint-set)157 (use-method simple-dispatch clojure.lang.PersistentQueue pprint-pqueue)158 (use-method simple-dispatch clojure.lang.IDeref pprint-ideref)159 (use-method simple-dispatch nil pr)160 (use-method simple-dispatch :default pprint-simple-default)162 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;163 ;;; Dispatch for the code table164 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;166 (declare pprint-simple-code-list)168 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;169 ;;; Format something that looks like a simple def (sans metadata, since the reader170 ;;; won't give it to us now).171 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;173 (def ^{:private true} pprint-hold-first (formatter-out "~:<~w~^ ~@_~w~^ ~_~@{~w~^ ~_~}~:>"))175 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;176 ;;; Format something that looks like a defn or defmacro177 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;179 ;;; Format the params and body of a defn with a single arity180 (defn- single-defn [alis has-doc-str?]181 (if (seq alis)182 (do183 (if has-doc-str?184 ((formatter-out " ~_"))185 ((formatter-out " ~@_")))186 ((formatter-out "~{~w~^ ~_~}") alis))))188 ;;; Format the param and body sublists of a defn with multiple arities189 (defn- multi-defn [alis has-doc-str?]190 (if (seq alis)191 ((formatter-out " ~_~{~w~^ ~_~}") alis)))193 ;;; TODO: figure out how to support capturing metadata in defns (we might need a194 ;;; special reader)195 (defn- pprint-defn [alis]196 (if (next alis)197 (let [[defn-sym defn-name & stuff] alis198 [doc-str stuff] (if (string? (first stuff))199 [(first stuff) (next stuff)]200 [nil stuff])201 [attr-map stuff] (if (map? (first stuff))202 [(first stuff) (next stuff)]203 [nil stuff])]204 (pprint-logical-block :prefix "(" :suffix ")"205 ((formatter-out "~w ~1I~@_~w") defn-sym defn-name)206 (if doc-str207 ((formatter-out " ~_~w") doc-str))208 (if attr-map209 ((formatter-out " ~_~w") attr-map))210 ;; Note: the multi-defn case will work OK for malformed defns too211 (cond212 (vector? (first stuff)) (single-defn stuff (or doc-str attr-map))213 :else (multi-defn stuff (or doc-str attr-map)))))214 (pprint-simple-code-list alis)))216 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;217 ;;; Format something with a binding form218 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;220 (defn- pprint-binding-form [binding-vec]221 (pprint-logical-block :prefix "[" :suffix "]"222 (loop [binding binding-vec]223 (when (seq binding)224 (pprint-logical-block binding225 (write-out (first binding))226 (when (next binding)227 (.write ^java.io.Writer *out* " ")228 (pprint-newline :miser)229 (write-out (second binding))))230 (when (next (rest binding))231 (.write ^java.io.Writer *out* " ")232 (pprint-newline :linear)233 (recur (next (rest binding))))))))235 (defn- pprint-let [alis]236 (let [base-sym (first alis)]237 (pprint-logical-block :prefix "(" :suffix ")"238 (if (and (next alis) (vector? (second alis)))239 (do240 ((formatter-out "~w ~1I~@_") base-sym)241 (pprint-binding-form (second alis))242 ((formatter-out " ~_~{~w~^ ~_~}") (next (rest alis))))243 (pprint-simple-code-list alis)))))246 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;247 ;;; Format something that looks like "if"248 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;250 (def ^{:private true} pprint-if (formatter-out "~:<~1I~w~^ ~@_~w~@{ ~_~w~}~:>"))252 (defn- pprint-cond [alis]253 (pprint-logical-block :prefix "(" :suffix ")"254 (pprint-indent :block 1)255 (write-out (first alis))256 (when (next alis)257 (.write ^java.io.Writer *out* " ")258 (pprint-newline :linear)259 (loop [alis (next alis)]260 (when alis261 (pprint-logical-block alis262 (write-out (first alis))263 (when (next alis)264 (.write ^java.io.Writer *out* " ")265 (pprint-newline :miser)266 (write-out (second alis))))267 (when (next (rest alis))268 (.write ^java.io.Writer *out* " ")269 (pprint-newline :linear)270 (recur (next (rest alis)))))))))272 (defn- pprint-condp [alis]273 (if (> (count alis) 3)274 (pprint-logical-block :prefix "(" :suffix ")"275 (pprint-indent :block 1)276 (apply (formatter-out "~w ~@_~w ~@_~w ~_") alis)277 (loop [alis (seq (drop 3 alis))]278 (when alis279 (pprint-logical-block alis280 (write-out (first alis))281 (when (next alis)282 (.write ^java.io.Writer *out* " ")283 (pprint-newline :miser)284 (write-out (second alis))))285 (when (next (rest alis))286 (.write ^java.io.Writer *out* " ")287 (pprint-newline :linear)288 (recur (next (rest alis)))))))289 (pprint-simple-code-list alis)))291 ;;; The map of symbols that are defined in an enclosing #() anonymous function292 (def ^{:private true} *symbol-map* {})294 (defn- pprint-anon-func [alis]295 (let [args (second alis)296 nlis (first (rest (rest alis)))]297 (if (vector? args)298 (binding [*symbol-map* (if (= 1 (count args))299 {(first args) "%"}300 (into {}301 (map302 #(vector %1 (str \% %2))303 args304 (range 1 (inc (count args))))))]305 ((formatter-out "~<#(~;~@{~w~^ ~_~}~;)~:>") nlis))306 (pprint-simple-code-list alis))))308 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;309 ;;; The master definitions for formatting lists in code (that is, (fn args...) or310 ;;; special forms).311 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;313 ;;; This is the equivalent of (formatter-out "~:<~1I~@{~w~^ ~_~}~:>"), but is314 ;;; easier on the stack.316 (defn- pprint-simple-code-list [alis]317 (pprint-logical-block :prefix "(" :suffix ")"318 (pprint-indent :block 1)319 (loop [alis (seq alis)]320 (when alis321 (write-out (first alis))322 (when (next alis)323 (.write ^java.io.Writer *out* " ")324 (pprint-newline :linear)325 (recur (next alis)))))))327 ;;; Take a map with symbols as keys and add versions with no namespace.328 ;;; That is, if ns/sym->val is in the map, add sym->val to the result.329 (defn- two-forms [amap]330 (into {}331 (mapcat332 identity333 (for [x amap]334 [x [(symbol (name (first x))) (second x)]]))))336 (defn- add-core-ns [amap]337 (let [core "clojure.core"]338 (into {}339 (map #(let [[s f] %]340 (if (not (or (namespace s) (special-symbol? s)))341 [(symbol core (name s)) f]342 %))343 amap))))345 (def ^{:private true} *code-table*346 (two-forms347 (add-core-ns348 {'def pprint-hold-first, 'defonce pprint-hold-first,349 'defn pprint-defn, 'defn- pprint-defn, 'defmacro pprint-defn, 'fn pprint-defn,350 'let pprint-let, 'loop pprint-let, 'binding pprint-let,351 'with-local-vars pprint-let, 'with-open pprint-let, 'when-let pprint-let,352 'if-let pprint-let, 'doseq pprint-let, 'dotimes pprint-let,353 'when-first pprint-let,354 'if pprint-if, 'if-not pprint-if, 'when pprint-if, 'when-not pprint-if,355 'cond pprint-cond, 'condp pprint-condp,356 'fn* pprint-anon-func,357 '. pprint-hold-first, '.. pprint-hold-first, '-> pprint-hold-first,358 'locking pprint-hold-first, 'struct pprint-hold-first,359 'struct-map pprint-hold-first,360 })))362 (defn- pprint-code-list [alis]363 (if-not (pprint-reader-macro alis)364 (if-let [special-form (*code-table* (first alis))]365 (special-form alis)366 (pprint-simple-code-list alis))))368 (defn- pprint-code-symbol [sym]369 (if-let [arg-num (sym *symbol-map*)]370 (print arg-num)371 (if *print-suppress-namespaces*372 (print (name sym))373 (pr sym))))375 (defmulti376 code-dispatch377 "The pretty print dispatch function for pretty printing Clojure code."378 {:added "1.2" :arglists '[[object]]}379 class)381 (use-method code-dispatch clojure.lang.ISeq pprint-code-list)382 (use-method code-dispatch clojure.lang.Symbol pprint-code-symbol)384 ;; The following are all exact copies of simple-dispatch385 (use-method code-dispatch clojure.lang.IPersistentVector pprint-vector)386 (use-method code-dispatch clojure.lang.IPersistentMap pprint-map)387 (use-method code-dispatch clojure.lang.IPersistentSet pprint-set)388 (use-method code-dispatch clojure.lang.PersistentQueue pprint-pqueue)389 (use-method code-dispatch clojure.lang.IDeref pprint-ideref)390 (use-method code-dispatch nil pr)391 (use-method code-dispatch :default pprint-simple-default)393 (set-pprint-dispatch simple-dispatch)396 ;;; For testing397 (comment399 (with-pprint-dispatch code-dispatch400 (pprint401 '(defn cl-format402 "An implementation of a Common Lisp compatible format function"403 [stream format-in & args]404 (let [compiled-format (if (string? format-in) (compile-format format-in) format-in)405 navigator (init-navigator args)]406 (execute-format stream compiled-format navigator)))))408 (with-pprint-dispatch code-dispatch409 (pprint410 '(defn cl-format411 [stream format-in & args]412 (let [compiled-format (if (string? format-in) (compile-format format-in) format-in)413 navigator (init-navigator args)]414 (execute-format stream compiled-format navigator)))))416 (with-pprint-dispatch code-dispatch417 (pprint418 '(defn- -write419 ([this x]420 (condp = (class x)421 String422 (let [s0 (write-initial-lines this x)423 s (.replaceFirst s0 "\\s+$" "")424 white-space (.substring s0 (count s))425 mode (getf :mode)]426 (if (= mode :writing)427 (dosync428 (write-white-space this)429 (.col_write this s)430 (setf :trailing-white-space white-space))431 (add-to-buffer this (make-buffer-blob s white-space))))433 Integer434 (let [c ^Character x]435 (if (= (getf :mode) :writing)436 (do437 (write-white-space this)438 (.col_write this x))439 (if (= c (int \newline))440 (write-initial-lines this "\n")441 (add-to-buffer this (make-buffer-blob (str (char c)) nil))))))))))443 (with-pprint-dispatch code-dispatch444 (pprint445 '(defn pprint-defn [writer alis]446 (if (next alis)447 (let [[defn-sym defn-name & stuff] alis448 [doc-str stuff] (if (string? (first stuff))449 [(first stuff) (next stuff)]450 [nil stuff])451 [attr-map stuff] (if (map? (first stuff))452 [(first stuff) (next stuff)]453 [nil stuff])]454 (pprint-logical-block writer :prefix "(" :suffix ")"455 (cl-format true "~w ~1I~@_~w" defn-sym defn-name)456 (if doc-str457 (cl-format true " ~_~w" doc-str))458 (if attr-map459 (cl-format true " ~_~w" attr-map))460 ;; Note: the multi-defn case will work OK for malformed defns too461 (cond462 (vector? (first stuff)) (single-defn stuff (or doc-str attr-map))463 :else (multi-defn stuff (or doc-str attr-map)))))464 (pprint-simple-code-list writer alis)))))465 )466 nil