Mercurial > lasercutter
diff src/clojure/pprint/pretty_writer.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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/clojure/pprint/pretty_writer.clj Sat Aug 21 06:25:44 2010 -0400 1.3 @@ -0,0 +1,483 @@ 1.4 +;;; pretty_writer.clj -- part of the pretty printer for Clojure 1.5 + 1.6 +; Copyright (c) Rich Hickey. All rights reserved. 1.7 +; The use and distribution terms for this software are covered by the 1.8 +; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) 1.9 +; which can be found in the file epl-v10.html at the root of this distribution. 1.10 +; By using this software in any fashion, you are agreeing to be bound by 1.11 +; the terms of this license. 1.12 +; You must not remove this notice, or any other, from this software. 1.13 + 1.14 +;; Author: Tom Faulhaber 1.15 +;; April 3, 2009 1.16 +;; Revised to use proxy instead of gen-class April 2010 1.17 + 1.18 +;; This module implements a wrapper around a java.io.Writer which implements the 1.19 +;; core of the XP algorithm. 1.20 + 1.21 +(in-ns 'clojure.pprint) 1.22 + 1.23 +(import [clojure.lang IDeref] 1.24 + [java.io Writer]) 1.25 + 1.26 +;; TODO: Support for tab directives 1.27 + 1.28 + 1.29 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.30 +;;; Forward declarations 1.31 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.32 + 1.33 +(declare get-miser-width) 1.34 + 1.35 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.36 +;;; Macros to simplify dealing with types and classes. These are 1.37 +;;; really utilities, but I'm experimenting with them here. 1.38 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.39 + 1.40 +(defmacro ^{:private true} 1.41 + getf 1.42 + "Get the value of the field a named by the argument (which should be a keyword)." 1.43 + [sym] 1.44 + `(~sym @@~'this)) 1.45 + 1.46 +(defmacro ^{:private true} 1.47 + setf [sym new-val] 1.48 + "Set the value of the field SYM to NEW-VAL" 1.49 + `(alter @~'this assoc ~sym ~new-val)) 1.50 + 1.51 +(defmacro ^{:private true} 1.52 + deftype [type-name & fields] 1.53 + (let [name-str (name type-name)] 1.54 + `(do 1.55 + (defstruct ~type-name :type-tag ~@fields) 1.56 + (alter-meta! #'~type-name assoc :private true) 1.57 + (defn- ~(symbol (str "make-" name-str)) 1.58 + [& vals#] (apply struct ~type-name ~(keyword name-str) vals#)) 1.59 + (defn- ~(symbol (str name-str "?")) [x#] (= (:type-tag x#) ~(keyword name-str)))))) 1.60 + 1.61 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.62 +;;; The data structures used by pretty-writer 1.63 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.64 + 1.65 +(defstruct ^{:private true} logical-block 1.66 + :parent :section :start-col :indent 1.67 + :done-nl :intra-block-nl 1.68 + :prefix :per-line-prefix :suffix 1.69 + :logical-block-callback) 1.70 + 1.71 +(defn- ancestor? [parent child] 1.72 + (loop [child (:parent child)] 1.73 + (cond 1.74 + (nil? child) false 1.75 + (identical? parent child) true 1.76 + :else (recur (:parent child))))) 1.77 + 1.78 +(defstruct ^{:private true} section :parent) 1.79 + 1.80 +(defn- buffer-length [l] 1.81 + (let [l (seq l)] 1.82 + (if l 1.83 + (- (:end-pos (last l)) (:start-pos (first l))) 1.84 + 0))) 1.85 + 1.86 +; A blob of characters (aka a string) 1.87 +(deftype buffer-blob :data :trailing-white-space :start-pos :end-pos) 1.88 + 1.89 +; A newline 1.90 +(deftype nl-t :type :logical-block :start-pos :end-pos) 1.91 + 1.92 +(deftype start-block-t :logical-block :start-pos :end-pos) 1.93 + 1.94 +(deftype end-block-t :logical-block :start-pos :end-pos) 1.95 + 1.96 +(deftype indent-t :logical-block :relative-to :offset :start-pos :end-pos) 1.97 + 1.98 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.99 +;;; Functions to write tokens in the output buffer 1.100 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.101 + 1.102 +(declare emit-nl) 1.103 + 1.104 +(defmulti ^{:private true} write-token #(:type-tag %2)) 1.105 +(defmethod write-token :start-block-t [^Writer this token] 1.106 + (when-let [cb (getf :logical-block-callback)] (cb :start)) 1.107 + (let [lb (:logical-block token)] 1.108 + (dosync 1.109 + (when-let [^String prefix (:prefix lb)] 1.110 + (.write (getf :base) prefix)) 1.111 + (let [col (get-column (getf :base))] 1.112 + (ref-set (:start-col lb) col) 1.113 + (ref-set (:indent lb) col))))) 1.114 + 1.115 +(defmethod write-token :end-block-t [^Writer this token] 1.116 + (when-let [cb (getf :logical-block-callback)] (cb :end)) 1.117 + (when-let [^String suffix (:suffix (:logical-block token))] 1.118 + (.write (getf :base) suffix))) 1.119 + 1.120 +(defmethod write-token :indent-t [^Writer this token] 1.121 + (let [lb (:logical-block token)] 1.122 + (ref-set (:indent lb) 1.123 + (+ (:offset token) 1.124 + (condp = (:relative-to token) 1.125 + :block @(:start-col lb) 1.126 + :current (get-column (getf :base))))))) 1.127 + 1.128 +(defmethod write-token :buffer-blob [^Writer this token] 1.129 + (.write (getf :base) ^String (:data token))) 1.130 + 1.131 +(defmethod write-token :nl-t [^Writer this token] 1.132 +; (prlabel wt @(:done-nl (:logical-block token))) 1.133 +; (prlabel wt (:type token) (= (:type token) :mandatory)) 1.134 + (if (or (= (:type token) :mandatory) 1.135 + (and (not (= (:type token) :fill)) 1.136 + @(:done-nl (:logical-block token)))) 1.137 + (emit-nl this token) 1.138 + (if-let [^String tws (getf :trailing-white-space)] 1.139 + (.write (getf :base) tws))) 1.140 + (dosync (setf :trailing-white-space nil))) 1.141 + 1.142 +(defn- write-tokens [^Writer this tokens force-trailing-whitespace] 1.143 + (doseq [token tokens] 1.144 + (if-not (= (:type-tag token) :nl-t) 1.145 + (if-let [^String tws (getf :trailing-white-space)] 1.146 + (.write (getf :base) tws))) 1.147 + (write-token this token) 1.148 + (setf :trailing-white-space (:trailing-white-space token))) 1.149 + (let [^String tws (getf :trailing-white-space)] 1.150 + (when (and force-trailing-whitespace tws) 1.151 + (.write (getf :base) tws) 1.152 + (setf :trailing-white-space nil)))) 1.153 + 1.154 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.155 +;;; emit-nl? method defs for each type of new line. This makes 1.156 +;;; the decision about whether to print this type of new line. 1.157 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.158 + 1.159 + 1.160 +(defn- tokens-fit? [^Writer this tokens] 1.161 +;;; (prlabel tf? (get-column (getf :base) (buffer-length tokens)) 1.162 + (let [maxcol (get-max-column (getf :base))] 1.163 + (or 1.164 + (nil? maxcol) 1.165 + (< (+ (get-column (getf :base)) (buffer-length tokens)) maxcol)))) 1.166 + 1.167 +(defn- linear-nl? [this lb section] 1.168 +; (prlabel lnl? @(:done-nl lb) (tokens-fit? this section)) 1.169 + (or @(:done-nl lb) 1.170 + (not (tokens-fit? this section)))) 1.171 + 1.172 +(defn- miser-nl? [^Writer this lb section] 1.173 + (let [miser-width (get-miser-width this) 1.174 + maxcol (get-max-column (getf :base))] 1.175 + (and miser-width maxcol 1.176 + (>= @(:start-col lb) (- maxcol miser-width)) 1.177 + (linear-nl? this lb section)))) 1.178 + 1.179 +(defmulti ^{:private true} emit-nl? (fn [t _ _ _] (:type t))) 1.180 + 1.181 +(defmethod emit-nl? :linear [newl this section _] 1.182 + (let [lb (:logical-block newl)] 1.183 + (linear-nl? this lb section))) 1.184 + 1.185 +(defmethod emit-nl? :miser [newl this section _] 1.186 + (let [lb (:logical-block newl)] 1.187 + (miser-nl? this lb section))) 1.188 + 1.189 +(defmethod emit-nl? :fill [newl this section subsection] 1.190 + (let [lb (:logical-block newl)] 1.191 + (or @(:intra-block-nl lb) 1.192 + (not (tokens-fit? this subsection)) 1.193 + (miser-nl? this lb section)))) 1.194 + 1.195 +(defmethod emit-nl? :mandatory [_ _ _ _] 1.196 + true) 1.197 + 1.198 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.199 +;;; Various support functions 1.200 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.201 + 1.202 + 1.203 +(defn- get-section [buffer] 1.204 + (let [nl (first buffer) 1.205 + lb (:logical-block nl) 1.206 + section (seq (take-while #(not (and (nl-t? %) (ancestor? (:logical-block %) lb))) 1.207 + (next buffer)))] 1.208 + [section (seq (drop (inc (count section)) buffer))])) 1.209 + 1.210 +(defn- get-sub-section [buffer] 1.211 + (let [nl (first buffer) 1.212 + lb (:logical-block nl) 1.213 + section (seq (take-while #(let [nl-lb (:logical-block %)] 1.214 + (not (and (nl-t? %) (or (= nl-lb lb) (ancestor? nl-lb lb))))) 1.215 + (next buffer)))] 1.216 + section)) 1.217 + 1.218 +(defn- update-nl-state [lb] 1.219 + (dosync 1.220 + (ref-set (:intra-block-nl lb) false) 1.221 + (ref-set (:done-nl lb) true) 1.222 + (loop [lb (:parent lb)] 1.223 + (if lb 1.224 + (do (ref-set (:done-nl lb) true) 1.225 + (ref-set (:intra-block-nl lb) true) 1.226 + (recur (:parent lb))))))) 1.227 + 1.228 +(defn- emit-nl [^Writer this nl] 1.229 + (.write (getf :base) (int \newline)) 1.230 + (dosync (setf :trailing-white-space nil)) 1.231 + (let [lb (:logical-block nl) 1.232 + ^String prefix (:per-line-prefix lb)] 1.233 + (if prefix 1.234 + (.write (getf :base) prefix)) 1.235 + (let [^String istr (apply str (repeat (- @(:indent lb) (count prefix)) 1.236 + \space))] 1.237 + (.write (getf :base) istr)) 1.238 + (update-nl-state lb))) 1.239 + 1.240 +(defn- split-at-newline [tokens] 1.241 + (let [pre (seq (take-while #(not (nl-t? %)) tokens))] 1.242 + [pre (seq (drop (count pre) tokens))])) 1.243 + 1.244 +;;; Methods for showing token strings for debugging 1.245 + 1.246 +(defmulti ^{:private true} tok :type-tag) 1.247 +(defmethod tok :nl-t [token] 1.248 + (:type token)) 1.249 +(defmethod tok :buffer-blob [token] 1.250 + (str \" (:data token) (:trailing-white-space token) \")) 1.251 +(defmethod tok :default [token] 1.252 + (:type-tag token)) 1.253 +(defn- toks [toks] (map tok toks)) 1.254 + 1.255 +;;; write-token-string is called when the set of tokens in the buffer 1.256 +;;; is longer than the available space on the line 1.257 + 1.258 +(defn- write-token-string [this tokens] 1.259 + (let [[a b] (split-at-newline tokens)] 1.260 +;; (prlabel wts (toks a) (toks b)) 1.261 + (if a (write-tokens this a false)) 1.262 + (if b 1.263 + (let [[section remainder] (get-section b) 1.264 + newl (first b)] 1.265 +;; (prlabel wts (toks section)) (prlabel wts (:type newl)) (prlabel wts (toks remainder)) 1.266 + (let [do-nl (emit-nl? newl this section (get-sub-section b)) 1.267 + result (if do-nl 1.268 + (do 1.269 +;; (prlabel emit-nl (:type newl)) 1.270 + (emit-nl this newl) 1.271 + (next b)) 1.272 + b) 1.273 + long-section (not (tokens-fit? this result)) 1.274 + result (if long-section 1.275 + (let [rem2 (write-token-string this section)] 1.276 +;;; (prlabel recurse (toks rem2)) 1.277 + (if (= rem2 section) 1.278 + (do ; If that didn't produce any output, it has no nls 1.279 + ; so we'll force it 1.280 + (write-tokens this section false) 1.281 + remainder) 1.282 + (into [] (concat rem2 remainder)))) 1.283 + result) 1.284 +;; ff (prlabel wts (toks result)) 1.285 + ] 1.286 + result))))) 1.287 + 1.288 +(defn- write-line [^Writer this] 1.289 + (dosync 1.290 + (loop [buffer (getf :buffer)] 1.291 +;; (prlabel wl1 (toks buffer)) 1.292 + (setf :buffer (into [] buffer)) 1.293 + (if (not (tokens-fit? this buffer)) 1.294 + (let [new-buffer (write-token-string this buffer)] 1.295 +;; (prlabel wl new-buffer) 1.296 + (if-not (identical? buffer new-buffer) 1.297 + (recur new-buffer))))))) 1.298 + 1.299 +;;; Add a buffer token to the buffer and see if it's time to start 1.300 +;;; writing 1.301 +(defn- add-to-buffer [^Writer this token] 1.302 +; (prlabel a2b token) 1.303 + (dosync 1.304 + (setf :buffer (conj (getf :buffer) token)) 1.305 + (if (not (tokens-fit? this (getf :buffer))) 1.306 + (write-line this)))) 1.307 + 1.308 +;;; Write all the tokens that have been buffered 1.309 +(defn- write-buffered-output [^Writer this] 1.310 + (write-line this) 1.311 + (if-let [buf (getf :buffer)] 1.312 + (do 1.313 + (write-tokens this buf true) 1.314 + (setf :buffer [])))) 1.315 + 1.316 +;;; If there are newlines in the string, print the lines up until the last newline, 1.317 +;;; making the appropriate adjustments. Return the remainder of the string 1.318 +(defn- write-initial-lines 1.319 + [^Writer this ^String s] 1.320 + (let [lines (.split s "\n" -1)] 1.321 + (if (= (count lines) 1) 1.322 + s 1.323 + (dosync 1.324 + (let [^String prefix (:per-line-prefix (first (getf :logical-blocks))) 1.325 + ^String l (first lines)] 1.326 + (if (= :buffering (getf :mode)) 1.327 + (let [oldpos (getf :pos) 1.328 + newpos (+ oldpos (count l))] 1.329 + (setf :pos newpos) 1.330 + (add-to-buffer this (make-buffer-blob l nil oldpos newpos)) 1.331 + (write-buffered-output this)) 1.332 + (.write (getf :base) l)) 1.333 + (.write (getf :base) (int \newline)) 1.334 + (doseq [^String l (next (butlast lines))] 1.335 + (.write (getf :base) l) 1.336 + (.write (getf :base) (int \newline)) 1.337 + (if prefix 1.338 + (.write (getf :base) prefix))) 1.339 + (setf :buffering :writing) 1.340 + (last lines)))))) 1.341 + 1.342 + 1.343 +(defn- write-white-space [^Writer this] 1.344 + (if-let [^String tws (getf :trailing-white-space)] 1.345 + (dosync 1.346 + (.write (getf :base) tws) 1.347 + (setf :trailing-white-space nil)))) 1.348 + 1.349 +(defn- p-write-char [^Writer this ^Integer c] 1.350 + (if (= (getf :mode) :writing) 1.351 + (do 1.352 + (write-white-space this) 1.353 + (.write (getf :base) c)) 1.354 + (if (= c \newline) 1.355 + (write-initial-lines this "\n") 1.356 + (let [oldpos (getf :pos) 1.357 + newpos (inc oldpos)] 1.358 + (dosync 1.359 + (setf :pos newpos) 1.360 + (add-to-buffer this (make-buffer-blob (str (char c)) nil oldpos newpos))))))) 1.361 + 1.362 + 1.363 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.364 +;;; Initialize the pretty-writer instance 1.365 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.366 + 1.367 + 1.368 +(defn- pretty-writer [writer max-columns miser-width] 1.369 + (let [lb (struct logical-block nil nil (ref 0) (ref 0) (ref false) (ref false)) 1.370 + fields (ref {:pretty-writer true 1.371 + :base (column-writer writer max-columns) 1.372 + :logical-blocks lb 1.373 + :sections nil 1.374 + :mode :writing 1.375 + :buffer [] 1.376 + :buffer-block lb 1.377 + :buffer-level 1 1.378 + :miser-width miser-width 1.379 + :trailing-white-space nil 1.380 + :pos 0})] 1.381 + (proxy [Writer IDeref] [] 1.382 + (deref [] fields) 1.383 + 1.384 + (write 1.385 + ([x] 1.386 + ;; (prlabel write x (getf :mode)) 1.387 + (condp = (class x) 1.388 + String 1.389 + (let [^String s0 (write-initial-lines this x) 1.390 + ^String s (.replaceFirst s0 "\\s+$" "") 1.391 + white-space (.substring s0 (count s)) 1.392 + mode (getf :mode)] 1.393 + (dosync 1.394 + (if (= mode :writing) 1.395 + (do 1.396 + (write-white-space this) 1.397 + (.write (getf :base) s) 1.398 + (setf :trailing-white-space white-space)) 1.399 + (let [oldpos (getf :pos) 1.400 + newpos (+ oldpos (count s0))] 1.401 + (setf :pos newpos) 1.402 + (add-to-buffer this (make-buffer-blob s white-space oldpos newpos)))))) 1.403 + 1.404 + Integer 1.405 + (p-write-char this x)))) 1.406 + 1.407 + (flush [] 1.408 + (if (= (getf :mode) :buffering) 1.409 + (dosync 1.410 + (write-tokens this (getf :buffer) true) 1.411 + (setf :buffer [])) 1.412 + (write-white-space this))) 1.413 + 1.414 + (close [] 1.415 + (.flush this))))) 1.416 + 1.417 + 1.418 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.419 +;;; Methods for pretty-writer 1.420 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 1.421 + 1.422 +(defn- start-block 1.423 + [^Writer this 1.424 + ^String prefix ^String per-line-prefix ^String suffix] 1.425 + (dosync 1.426 + (let [lb (struct logical-block (getf :logical-blocks) nil (ref 0) (ref 0) 1.427 + (ref false) (ref false) 1.428 + prefix per-line-prefix suffix)] 1.429 + (setf :logical-blocks lb) 1.430 + (if (= (getf :mode) :writing) 1.431 + (do 1.432 + (write-white-space this) 1.433 + (when-let [cb (getf :logical-block-callback)] (cb :start)) 1.434 + (if prefix 1.435 + (.write (getf :base) prefix)) 1.436 + (let [col (get-column (getf :base))] 1.437 + (ref-set (:start-col lb) col) 1.438 + (ref-set (:indent lb) col))) 1.439 + (let [oldpos (getf :pos) 1.440 + newpos (+ oldpos (if prefix (count prefix) 0))] 1.441 + (setf :pos newpos) 1.442 + (add-to-buffer this (make-start-block-t lb oldpos newpos))))))) 1.443 + 1.444 +(defn- end-block [^Writer this] 1.445 + (dosync 1.446 + (let [lb (getf :logical-blocks) 1.447 + ^String suffix (:suffix lb)] 1.448 + (if (= (getf :mode) :writing) 1.449 + (do 1.450 + (write-white-space this) 1.451 + (if suffix 1.452 + (.write (getf :base) suffix)) 1.453 + (when-let [cb (getf :logical-block-callback)] (cb :end))) 1.454 + (let [oldpos (getf :pos) 1.455 + newpos (+ oldpos (if suffix (count suffix) 0))] 1.456 + (setf :pos newpos) 1.457 + (add-to-buffer this (make-end-block-t lb oldpos newpos)))) 1.458 + (setf :logical-blocks (:parent lb))))) 1.459 + 1.460 +(defn- nl [^Writer this type] 1.461 + (dosync 1.462 + (setf :mode :buffering) 1.463 + (let [pos (getf :pos)] 1.464 + (add-to-buffer this (make-nl-t type (getf :logical-blocks) pos pos))))) 1.465 + 1.466 +(defn- indent [^Writer this relative-to offset] 1.467 + (dosync 1.468 + (let [lb (getf :logical-blocks)] 1.469 + (if (= (getf :mode) :writing) 1.470 + (do 1.471 + (write-white-space this) 1.472 + (ref-set (:indent lb) 1.473 + (+ offset (condp = relative-to 1.474 + :block @(:start-col lb) 1.475 + :current (get-column (getf :base)))))) 1.476 + (let [pos (getf :pos)] 1.477 + (add-to-buffer this (make-indent-t lb relative-to offset pos pos))))))) 1.478 + 1.479 +(defn- get-miser-width [^Writer this] 1.480 + (getf :miser-width)) 1.481 + 1.482 +(defn- set-miser-width [^Writer this new-miser-width] 1.483 + (dosync (setf :miser-width new-miser-width))) 1.484 + 1.485 +(defn- set-logical-block-callback [^Writer this f] 1.486 + (dosync (setf :logical-block-callback f)))