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