diff src/clojure/contrib/pprint/cl_format.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/cl_format.clj	Sat Aug 21 06:25:44 2010 -0400
     1.3 @@ -0,0 +1,1844 @@
     1.4 +;;; cl_format.clj -- part of the pretty printer for Clojure
     1.5 +
     1.6 +;; by Tom Faulhaber
     1.7 +;; April 3, 2009
     1.8 +
     1.9 +;   Copyright (c) Tom Faulhaber, Dec 2008. All rights reserved.
    1.10 +;   The use and distribution terms for this software are covered by the
    1.11 +;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
    1.12 +;   which can be found in the file epl-v10.html at the root of this distribution.
    1.13 +;   By using this software in any fashion, you are agreeing to be bound by
    1.14 +;   the terms of this license.
    1.15 +;   You must not remove this notice, or any other, from this software.
    1.16 +
    1.17 +;; This module implements the Common Lisp compatible format function as documented
    1.18 +;; in "Common Lisp the Language, 2nd edition", Chapter 22 (available online at:
    1.19 +;; http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/clm/node200.html#SECTION002633000000000000000)
    1.20 +
    1.21 +(in-ns 'clojure.contrib.pprint)
    1.22 +
    1.23 +;;; Forward references
    1.24 +(declare compile-format)
    1.25 +(declare execute-format)
    1.26 +(declare init-navigator)
    1.27 +;;; End forward references
    1.28 +
    1.29 +(defn cl-format 
    1.30 +  "An implementation of a Common Lisp compatible format function. cl-format formats its
    1.31 +arguments to an output stream or string based on the format control string given. It 
    1.32 +supports sophisticated formatting of structured data.
    1.33 +
    1.34 +Writer is an instance of java.io.Writer, true to output to *out* or nil to output 
    1.35 +to a string, format-in is the format control string and the remaining arguments 
    1.36 +are the data to be formatted.
    1.37 +
    1.38 +The format control string is a string to be output with embedded 'format directives' 
    1.39 +describing how to format the various arguments passed in.
    1.40 +
    1.41 +If writer is nil, cl-format returns the formatted result string. Otherwise, cl-format 
    1.42 +returns nil.
    1.43 +
    1.44 +For example:
    1.45 + (let [results [46 38 22]]
    1.46 +        (cl-format true \"There ~[are~;is~:;are~]~:* ~d result~:p: ~{~d~^, ~}~%\" 
    1.47 +                   (count results) results))
    1.48 +
    1.49 +Prints to *out*:
    1.50 + There are 3 results: 46, 38, 22
    1.51 +
    1.52 +Detailed documentation on format control strings is available in the \"Common Lisp the 
    1.53 +Language, 2nd edition\", Chapter 22 (available online at:
    1.54 +http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/clm/node200.html#SECTION002633000000000000000) 
    1.55 +and in the Common Lisp HyperSpec at 
    1.56 +http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm
    1.57 +"
    1.58 +  {:see-also [["http://www.cs.cmu.edu/afs/cs.cmu.edu/project/ai-repository/ai/html/cltl/clm/node200.html#SECTION002633000000000000000" 
    1.59 +               "Common Lisp the Language"]
    1.60 +              ["http://www.lispworks.com/documentation/HyperSpec/Body/22_c.htm"
    1.61 +               "Common Lisp HyperSpec"]]}
    1.62 +  [writer format-in & args]
    1.63 +  (let [compiled-format (if (string? format-in) (compile-format format-in) format-in)
    1.64 +        navigator (init-navigator args)]
    1.65 +    (execute-format writer compiled-format navigator)))
    1.66 +
    1.67 +(def ^{:private true} *format-str* nil)
    1.68 +
    1.69 +(defn- format-error [message offset] 
    1.70 +  (let [full-message (str message \newline *format-str* \newline 
    1.71 +                           (apply str (repeat offset \space)) "^" \newline)]
    1.72 +    (throw (RuntimeException. full-message))))
    1.73 +
    1.74 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    1.75 +;;; Argument navigators manage the argument list
    1.76 +;;; as the format statement moves through the list
    1.77 +;;; (possibly going forwards and backwards as it does so)
    1.78 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    1.79 +
    1.80 +(defstruct ^{:private true}
    1.81 +  arg-navigator :seq :rest :pos )
    1.82 +
    1.83 +(defn init-navigator 
    1.84 +  "Create a new arg-navigator from the sequence with the position set to 0"
    1.85 +  {:skip-wiki true}
    1.86 +  [s]
    1.87 +  (let [s (seq s)]
    1.88 +    (struct arg-navigator s s 0)))
    1.89 +
    1.90 +;; TODO call format-error with offset
    1.91 +(defn- next-arg [ navigator ]
    1.92 +  (let [ rst (:rest navigator) ]
    1.93 +    (if rst
    1.94 +      [(first rst) (struct arg-navigator (:seq navigator ) (next rst) (inc (:pos navigator)))]
    1.95 +     (throw (new Exception  "Not enough arguments for format definition")))))
    1.96 +
    1.97 +(defn- next-arg-or-nil [navigator]
    1.98 +  (let [rst (:rest navigator)]
    1.99 +    (if rst
   1.100 +      [(first rst) (struct arg-navigator (:seq navigator ) (next rst) (inc (:pos navigator)))]
   1.101 +      [nil navigator])))
   1.102 +
   1.103 +;; Get an argument off the arg list and compile it if it's not already compiled
   1.104 +(defn- get-format-arg [navigator]
   1.105 +  (let [[raw-format navigator] (next-arg navigator)
   1.106 +        compiled-format (if (instance? String raw-format) 
   1.107 +                               (compile-format raw-format)
   1.108 +                               raw-format)]
   1.109 +    [compiled-format navigator]))
   1.110 +
   1.111 +(declare relative-reposition)
   1.112 +
   1.113 +(defn- absolute-reposition [navigator position]
   1.114 +  (if (>= position (:pos navigator))
   1.115 +    (relative-reposition navigator (- (:pos navigator) position))
   1.116 +    (struct arg-navigator (:seq navigator) (drop position (:seq navigator)) position)))
   1.117 +
   1.118 +(defn- relative-reposition [navigator position]
   1.119 +  (let [newpos (+ (:pos navigator) position)]
   1.120 +    (if (neg? position)
   1.121 +      (absolute-reposition navigator newpos)
   1.122 +      (struct arg-navigator (:seq navigator) (drop position (:rest navigator)) newpos))))
   1.123 +
   1.124 +(defstruct ^{:private true}
   1.125 +  compiled-directive :func :def :params :offset)
   1.126 +
   1.127 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.128 +;;; When looking at the parameter list, we may need to manipulate
   1.129 +;;; the argument list as well (for 'V' and '#' parameter types).
   1.130 +;;; We hide all of this behind a function, but clients need to
   1.131 +;;; manage changing arg navigator
   1.132 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.133 +
   1.134 +;; TODO: validate parameters when they come from arg list
   1.135 +(defn- realize-parameter [[param [raw-val offset]] navigator]
   1.136 +  (let [[real-param new-navigator]
   1.137 +        (cond 
   1.138 +         (contains? #{ :at :colon } param) ;pass flags through unchanged - this really isn't necessary
   1.139 +         [raw-val navigator]
   1.140 +
   1.141 +         (= raw-val :parameter-from-args) 
   1.142 +         (next-arg navigator)
   1.143 +
   1.144 +         (= raw-val :remaining-arg-count) 
   1.145 +         [(count (:rest navigator)) navigator]
   1.146 +
   1.147 +         true 
   1.148 +         [raw-val navigator])]
   1.149 +    [[param [real-param offset]] new-navigator]))
   1.150 +         
   1.151 +(defn- realize-parameter-list [parameter-map navigator]
   1.152 +  (let [[pairs new-navigator] 
   1.153 +        (map-passing-context realize-parameter navigator parameter-map)]
   1.154 +    [(into {} pairs) new-navigator]))
   1.155 +
   1.156 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.157 +;;; Functions that support individual directives
   1.158 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.159 +
   1.160 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.161 +;;; Common handling code for ~A and ~S
   1.162 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.163 +
   1.164 +(declare opt-base-str)
   1.165 +
   1.166 +(def ^{:private true}
   1.167 +     special-radix-markers {2 "#b" 8 "#o", 16 "#x"})
   1.168 +
   1.169 +(defn- format-simple-number [n]
   1.170 +  (cond 
   1.171 +    (integer? n) (if (= *print-base* 10)
   1.172 +                   (str n (if *print-radix* "."))
   1.173 +                   (str
   1.174 +                    (if *print-radix* (or (get special-radix-markers *print-base*) (str "#" *print-base* "r")))
   1.175 +                    (opt-base-str *print-base* n)))
   1.176 +    (ratio? n) (str
   1.177 +                (if *print-radix* (or (get special-radix-markers *print-base*) (str "#" *print-base* "r")))
   1.178 +                (opt-base-str *print-base* (.numerator n))
   1.179 +                "/"
   1.180 +                (opt-base-str *print-base* (.denominator n)))
   1.181 +    :else nil))
   1.182 +
   1.183 +(defn- format-ascii [print-func params arg-navigator offsets]
   1.184 +  (let [ [arg arg-navigator] (next-arg arg-navigator) 
   1.185 +         ^String base-output (or (format-simple-number arg) (print-func arg))
   1.186 +         base-width (.length base-output)
   1.187 +         min-width (+ base-width (:minpad params))
   1.188 +         width (if (>= min-width (:mincol params)) 
   1.189 +                 min-width
   1.190 +                 (+ min-width 
   1.191 +                    (* (+ (quot (- (:mincol params) min-width 1) 
   1.192 +                                (:colinc params) )
   1.193 +                          1)
   1.194 +                       (:colinc params))))
   1.195 +         chars (apply str (repeat (- width base-width) (:padchar params)))]
   1.196 +    (if (:at params)
   1.197 +      (print (str chars base-output))
   1.198 +      (print (str base-output chars)))
   1.199 +    arg-navigator))
   1.200 +
   1.201 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.202 +;;; Support for the integer directives ~D, ~X, ~O, ~B and some
   1.203 +;;; of ~R
   1.204 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.205 +
   1.206 +(defn- integral?
   1.207 +  "returns true if a number is actually an integer (that is, has no fractional part)"
   1.208 +  [x]
   1.209 +  (cond
   1.210 +   (integer? x) true
   1.211 +   (decimal? x) (>= (.ulp (.stripTrailingZeros (bigdec 0))) 1) ; true iff no fractional part
   1.212 +   (float? x)   (= x (Math/floor x))
   1.213 +   (ratio? x)   (let [^clojure.lang.Ratio r x]
   1.214 +                  (= 0 (rem (.numerator r) (.denominator r))))
   1.215 +   :else        false))
   1.216 +
   1.217 +(defn- remainders
   1.218 +  "Return the list of remainders (essentially the 'digits') of val in the given base"
   1.219 +  [base val]
   1.220 +  (reverse 
   1.221 +   (first 
   1.222 +    (consume #(if (pos? %) 
   1.223 +                [(rem % base) (quot % base)] 
   1.224 +                [nil nil]) 
   1.225 +             val))))
   1.226 +
   1.227 +;;; TODO: xlated-val does not seem to be used here.
   1.228 +(defn- base-str
   1.229 +  "Return val as a string in the given base"
   1.230 +  [base val]
   1.231 +  (if (zero? val)
   1.232 +    "0"
   1.233 +    (let [xlated-val (cond
   1.234 +                       (float? val) (bigdec val)
   1.235 +                       (ratio? val) (let [^clojure.lang.Ratio r val] 
   1.236 +                                      (/ (.numerator r) (.denominator r)))
   1.237 +                       :else val)] 
   1.238 +      (apply str 
   1.239 +             (map 
   1.240 +              #(if (< % 10) (char (+ (int \0) %)) (char (+ (int \a) (- % 10)))) 
   1.241 +              (remainders base val))))))
   1.242 +
   1.243 +(def ^{:private true}
   1.244 +     java-base-formats {8 "%o", 10 "%d", 16 "%x"})
   1.245 +
   1.246 +(defn- opt-base-str
   1.247 +  "Return val as a string in the given base, using clojure.core/format if supported
   1.248 +for improved performance"
   1.249 +  [base val]
   1.250 +  (let [format-str (get java-base-formats base)]
   1.251 +    (if (and format-str (integer? val) (-> val class .getName (.startsWith "java.")))
   1.252 +      (clojure.core/format format-str val)
   1.253 +      (base-str base val))))
   1.254 +
   1.255 +(defn- group-by* [unit lis]
   1.256 +  (reverse
   1.257 +   (first
   1.258 +    (consume (fn [x] [(seq (reverse (take unit x))) (seq (drop unit x))]) (reverse lis)))))
   1.259 +
   1.260 +(defn- format-integer [base params arg-navigator offsets]
   1.261 +  (let [[arg arg-navigator] (next-arg arg-navigator)]
   1.262 +    (if (integral? arg)
   1.263 +      (let [neg (neg? arg)
   1.264 +            pos-arg (if neg (- arg) arg)
   1.265 +            raw-str (opt-base-str base pos-arg)
   1.266 +            group-str (if (:colon params)
   1.267 +                        (let [groups (map #(apply str %) (group-by* (:commainterval params) raw-str))
   1.268 +                              commas (repeat (count groups) (:commachar params))]
   1.269 +                          (apply str (next (interleave commas groups))))
   1.270 +                        raw-str)
   1.271 +            ^String signed-str (cond
   1.272 +                                  neg (str "-" group-str)
   1.273 +                                  (:at params) (str "+" group-str)
   1.274 +                                  true group-str)
   1.275 +            padded-str (if (< (.length signed-str) (:mincol params))
   1.276 +                         (str (apply str (repeat (- (:mincol params) (.length signed-str)) 
   1.277 +                                                 (:padchar params)))
   1.278 +                              signed-str)
   1.279 +                         signed-str)]
   1.280 +        (print padded-str))
   1.281 +      (format-ascii print-str {:mincol (:mincol params) :colinc 1 :minpad 0 
   1.282 +                               :padchar (:padchar params) :at true} 
   1.283 +                    (init-navigator [arg]) nil))
   1.284 +    arg-navigator))
   1.285 +
   1.286 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.287 +;;; Support for english formats (~R and ~:R)
   1.288 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.289 +
   1.290 +(def ^{:private true}
   1.291 +     english-cardinal-units 
   1.292 +     ["zero" "one" "two" "three" "four" "five" "six" "seven" "eight" "nine"
   1.293 +      "ten" "eleven" "twelve" "thirteen" "fourteen"
   1.294 +      "fifteen" "sixteen" "seventeen" "eighteen" "nineteen"])
   1.295 +
   1.296 +(def ^{:private true}
   1.297 +     english-ordinal-units 
   1.298 +     ["zeroth" "first" "second" "third" "fourth" "fifth" "sixth" "seventh" "eighth" "ninth"
   1.299 +      "tenth" "eleventh" "twelfth" "thirteenth" "fourteenth"
   1.300 +      "fifteenth" "sixteenth" "seventeenth" "eighteenth" "nineteenth"])
   1.301 +
   1.302 +(def ^{:private true}
   1.303 +     english-cardinal-tens
   1.304 +     ["" "" "twenty" "thirty" "forty" "fifty" "sixty" "seventy" "eighty" "ninety"])
   1.305 +
   1.306 +(def ^{:private true}
   1.307 +     english-ordinal-tens
   1.308 +     ["" "" "twentieth" "thirtieth" "fortieth" "fiftieth"
   1.309 +      "sixtieth" "seventieth" "eightieth" "ninetieth"])
   1.310 +
   1.311 +;; We use "short scale" for our units (see http://en.wikipedia.org/wiki/Long_and_short_scales)
   1.312 +;; Number names from http://www.jimloy.com/math/billion.htm
   1.313 +;; We follow the rules for writing numbers from the Blue Book
   1.314 +;; (http://www.grammarbook.com/numbers/numbers.asp)
   1.315 +(def ^{:private true}
   1.316 +     english-scale-numbers 
   1.317 +     ["" "thousand" "million" "billion" "trillion" "quadrillion" "quintillion" 
   1.318 +      "sextillion" "septillion" "octillion" "nonillion" "decillion" 
   1.319 +      "undecillion" "duodecillion" "tredecillion" "quattuordecillion" 
   1.320 +      "quindecillion" "sexdecillion" "septendecillion" 
   1.321 +      "octodecillion" "novemdecillion" "vigintillion"])
   1.322 +
   1.323 +(defn- format-simple-cardinal
   1.324 +  "Convert a number less than 1000 to a cardinal english string"
   1.325 +  [num]
   1.326 +  (let [hundreds (quot num 100)
   1.327 +        tens (rem num 100)]
   1.328 +    (str
   1.329 +     (if (pos? hundreds) (str (nth english-cardinal-units hundreds) " hundred"))
   1.330 +     (if (and (pos? hundreds) (pos? tens)) " ")
   1.331 +     (if (pos? tens) 
   1.332 +       (if (< tens 20) 
   1.333 +         (nth english-cardinal-units tens)
   1.334 +         (let [ten-digit (quot tens 10)
   1.335 +               unit-digit (rem tens 10)]
   1.336 +           (str
   1.337 +            (if (pos? ten-digit) (nth english-cardinal-tens ten-digit))
   1.338 +            (if (and (pos? ten-digit) (pos? unit-digit)) "-")
   1.339 +            (if (pos? unit-digit) (nth english-cardinal-units unit-digit)))))))))
   1.340 +
   1.341 +(defn- add-english-scales
   1.342 +  "Take a sequence of parts, add scale numbers (e.g., million) and combine into a string
   1.343 +offset is a factor of 10^3 to multiply by"
   1.344 +  [parts offset]
   1.345 +  (let [cnt (count parts)]
   1.346 +    (loop [acc []
   1.347 +           pos (dec cnt)
   1.348 +           this (first parts)
   1.349 +           remainder (next parts)]
   1.350 +      (if (nil? remainder)
   1.351 +        (str (apply str (interpose ", " acc))
   1.352 +             (if (and (not (empty? this)) (not (empty? acc))) ", ")
   1.353 +             this
   1.354 +             (if (and (not (empty? this)) (pos? (+ pos offset)))
   1.355 +               (str " " (nth english-scale-numbers (+ pos offset)))))
   1.356 +        (recur 
   1.357 +         (if (empty? this)
   1.358 +           acc
   1.359 +           (conj acc (str this " " (nth english-scale-numbers (+ pos offset)))))
   1.360 +         (dec pos)
   1.361 +         (first remainder)
   1.362 +         (next remainder))))))
   1.363 +
   1.364 +(defn- format-cardinal-english [params navigator offsets]
   1.365 +  (let [[arg navigator] (next-arg navigator)]
   1.366 +    (if (= 0 arg)
   1.367 +      (print "zero")
   1.368 +      (let [abs-arg (if (neg? arg) (- arg) arg) ; some numbers are too big for Math/abs
   1.369 +            parts (remainders 1000 abs-arg)]
   1.370 +        (if (<= (count parts) (count english-scale-numbers))
   1.371 +          (let [parts-strs (map format-simple-cardinal parts)
   1.372 +                full-str (add-english-scales parts-strs 0)]
   1.373 +            (print (str (if (neg? arg) "minus ") full-str)))
   1.374 +          (format-integer ;; for numbers > 10^63, we fall back on ~D
   1.375 +           10
   1.376 +           { :mincol 0, :padchar \space, :commachar \, :commainterval 3, :colon true}
   1.377 +           (init-navigator [arg])
   1.378 +           { :mincol 0, :padchar 0, :commachar 0 :commainterval 0}))))
   1.379 +    navigator))
   1.380 +
   1.381 +(defn- format-simple-ordinal
   1.382 +  "Convert a number less than 1000 to a ordinal english string
   1.383 +Note this should only be used for the last one in the sequence"
   1.384 +  [num]
   1.385 +  (let [hundreds (quot num 100)
   1.386 +        tens (rem num 100)]
   1.387 +    (str
   1.388 +     (if (pos? hundreds) (str (nth english-cardinal-units hundreds) " hundred"))
   1.389 +     (if (and (pos? hundreds) (pos? tens)) " ")
   1.390 +     (if (pos? tens) 
   1.391 +       (if (< tens 20) 
   1.392 +         (nth english-ordinal-units tens)
   1.393 +         (let [ten-digit (quot tens 10)
   1.394 +               unit-digit (rem tens 10)]
   1.395 +           (if (and (pos? ten-digit) (not (pos? unit-digit)))
   1.396 +             (nth english-ordinal-tens ten-digit)
   1.397 +             (str
   1.398 +              (if (pos? ten-digit) (nth english-cardinal-tens ten-digit))
   1.399 +              (if (and (pos? ten-digit) (pos? unit-digit)) "-")
   1.400 +              (if (pos? unit-digit) (nth english-ordinal-units unit-digit))))))
   1.401 +       (if (pos? hundreds) "th")))))
   1.402 +
   1.403 +(defn- format-ordinal-english [params navigator offsets]
   1.404 +  (let [[arg navigator] (next-arg navigator)]
   1.405 +    (if (= 0 arg)
   1.406 +      (print "zeroth")
   1.407 +      (let [abs-arg (if (neg? arg) (- arg) arg) ; some numbers are too big for Math/abs
   1.408 +            parts (remainders 1000 abs-arg)]
   1.409 +        (if (<= (count parts) (count english-scale-numbers))
   1.410 +          (let [parts-strs (map format-simple-cardinal (drop-last parts))
   1.411 +                head-str (add-english-scales parts-strs 1)
   1.412 +                tail-str (format-simple-ordinal (last parts))]
   1.413 +            (print (str (if (neg? arg) "minus ") 
   1.414 +                        (cond 
   1.415 +                         (and (not (empty? head-str)) (not (empty? tail-str))) 
   1.416 +                         (str head-str ", " tail-str)
   1.417 +                         
   1.418 +                         (not (empty? head-str)) (str head-str "th")
   1.419 +                         :else tail-str))))
   1.420 +          (do (format-integer ;; for numbers > 10^63, we fall back on ~D
   1.421 +               10
   1.422 +               { :mincol 0, :padchar \space, :commachar \, :commainterval 3, :colon true}
   1.423 +               (init-navigator [arg])
   1.424 +               { :mincol 0, :padchar 0, :commachar 0 :commainterval 0})
   1.425 +              (let [low-two-digits (rem arg 100)
   1.426 +                    not-teens (or (< 11 low-two-digits) (> 19 low-two-digits))
   1.427 +                    low-digit (rem low-two-digits 10)]
   1.428 +                (print (cond 
   1.429 +                        (and (== low-digit 1) not-teens) "st"
   1.430 +                        (and (== low-digit 2) not-teens) "nd"
   1.431 +                        (and (== low-digit 3) not-teens) "rd"
   1.432 +                        :else "th")))))))
   1.433 +    navigator))
   1.434 +
   1.435 +
   1.436 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.437 +;;; Support for roman numeral formats (~@R and ~@:R)
   1.438 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.439 +
   1.440 +(def ^{:private true}
   1.441 +     old-roman-table
   1.442 +     [[ "I" "II" "III" "IIII" "V" "VI" "VII" "VIII" "VIIII"]
   1.443 +      [ "X" "XX" "XXX" "XXXX" "L" "LX" "LXX" "LXXX" "LXXXX"]
   1.444 +      [ "C" "CC" "CCC" "CCCC" "D" "DC" "DCC" "DCCC" "DCCCC"]
   1.445 +      [ "M" "MM" "MMM"]])
   1.446 +
   1.447 +(def ^{:private true}
   1.448 +     new-roman-table
   1.449 +     [[ "I" "II" "III" "IV" "V" "VI" "VII" "VIII" "IX"]
   1.450 +      [ "X" "XX" "XXX" "XL" "L" "LX" "LXX" "LXXX" "XC"]
   1.451 +      [ "C" "CC" "CCC" "CD" "D" "DC" "DCC" "DCCC" "CM"]
   1.452 +      [ "M" "MM" "MMM"]])
   1.453 +
   1.454 +(defn- format-roman
   1.455 +  "Format a roman numeral using the specified look-up table"
   1.456 +  [table params navigator offsets]
   1.457 +  (let [[arg navigator] (next-arg navigator)]
   1.458 +    (if (and (number? arg) (> arg 0) (< arg 4000))
   1.459 +      (let [digits (remainders 10 arg)]
   1.460 +        (loop [acc []
   1.461 +               pos (dec (count digits))
   1.462 +               digits digits]
   1.463 +          (if (empty? digits)
   1.464 +            (print (apply str acc))
   1.465 +            (let [digit (first digits)]
   1.466 +              (recur (if (= 0 digit) 
   1.467 +                       acc 
   1.468 +                       (conj acc (nth (nth table pos) (dec digit))))
   1.469 +                     (dec pos)
   1.470 +                     (next digits))))))
   1.471 +      (format-integer ;; for anything <= 0 or > 3999, we fall back on ~D
   1.472 +           10
   1.473 +           { :mincol 0, :padchar \space, :commachar \, :commainterval 3, :colon true}
   1.474 +           (init-navigator [arg])
   1.475 +           { :mincol 0, :padchar 0, :commachar 0 :commainterval 0}))
   1.476 +    navigator))
   1.477 +
   1.478 +(defn- format-old-roman [params navigator offsets]
   1.479 +  (format-roman old-roman-table params navigator offsets))
   1.480 +
   1.481 +(defn- format-new-roman [params navigator offsets]
   1.482 +  (format-roman new-roman-table params navigator offsets))
   1.483 +
   1.484 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.485 +;;; Support for character formats (~C)
   1.486 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.487 +
   1.488 +(def ^{:private true} 
   1.489 +     special-chars { 8 "Backspace", 9 "Tab",  10 "Newline", 13 "Return", 32 "Space"})
   1.490 +
   1.491 +(defn- pretty-character [params navigator offsets]
   1.492 +  (let [[c navigator] (next-arg navigator)
   1.493 +        as-int (int c)
   1.494 +        base-char (bit-and as-int 127)
   1.495 +        meta (bit-and as-int 128)
   1.496 +        special (get special-chars base-char)]
   1.497 +    (if (> meta 0) (print "Meta-"))
   1.498 +    (print (cond
   1.499 +            special special
   1.500 +            (< base-char 32) (str "Control-" (char (+ base-char 64)))
   1.501 +            (= base-char 127) "Control-?"
   1.502 +            :else (char base-char)))
   1.503 +    navigator))
   1.504 +
   1.505 +(defn- readable-character [params navigator offsets]
   1.506 +  (let [[c navigator] (next-arg navigator)]
   1.507 +    (condp = (:char-format params)
   1.508 +      \o (cl-format true "\\o~3,'0o" (int c))
   1.509 +      \u (cl-format true "\\u~4,'0x" (int c))
   1.510 +      nil (pr c))
   1.511 +    navigator))
   1.512 +
   1.513 +(defn- plain-character [params navigator offsets]
   1.514 +  (let [[char navigator] (next-arg navigator)]
   1.515 +    (print char)
   1.516 +    navigator))
   1.517 +
   1.518 +;; Check to see if a result is an abort (~^) construct
   1.519 +;; TODO: move these funcs somewhere more appropriate
   1.520 +(defn- abort? [context]
   1.521 +  (let [token (first context)]
   1.522 +    (or (= :up-arrow token) (= :colon-up-arrow token))))
   1.523 +
   1.524 +;; Handle the execution of "sub-clauses" in bracket constructions
   1.525 +(defn- execute-sub-format [format args base-args]
   1.526 +  (second
   1.527 +   (map-passing-context 
   1.528 +    (fn [element context]
   1.529 +      (if (abort? context)
   1.530 +        [nil context] ; just keep passing it along
   1.531 +        (let [[params args] (realize-parameter-list (:params element) context)
   1.532 +              [params offsets] (unzip-map params)
   1.533 +              params (assoc params :base-args base-args)]
   1.534 +          [nil (apply (:func element) [params args offsets])])))
   1.535 +    args
   1.536 +    format)))
   1.537 +
   1.538 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.539 +;;; Support for real number formats
   1.540 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.541 +
   1.542 +;; TODO - return exponent as int to eliminate double conversion
   1.543 +(defn- float-parts-base
   1.544 +  "Produce string parts for the mantissa (normalized 1-9) and exponent"
   1.545 +  [^Object f]
   1.546 +  (let [^String s (.toLowerCase (.toString f))
   1.547 +        exploc (.indexOf s (int \e))]
   1.548 +    (if (neg? exploc)
   1.549 +      (let [dotloc (.indexOf s (int \.))]
   1.550 +        (if (neg? dotloc)
   1.551 +          [s (str (dec (count s)))]
   1.552 +          [(str (subs s 0 dotloc) (subs s (inc dotloc))) (str (dec dotloc))]))
   1.553 +      [(str (subs s 0 1) (subs s 2 exploc)) (subs s (inc exploc))])))
   1.554 +
   1.555 +
   1.556 +(defn- float-parts
   1.557 +  "Take care of leading and trailing zeros in decomposed floats"
   1.558 +  [f]
   1.559 +  (let [[m ^String e] (float-parts-base f)
   1.560 +        m1 (rtrim m \0)
   1.561 +        m2 (ltrim m1 \0)
   1.562 +        delta (- (count m1) (count m2))
   1.563 +        ^String e (if (and (pos? (count e)) (= (nth e 0) \+)) (subs e 1) e)]
   1.564 +    (if (empty? m2)
   1.565 +      ["0" 0]
   1.566 +      [m2 (- (Integer/valueOf e) delta)])))
   1.567 +
   1.568 +(defn- round-str [m e d w]
   1.569 +  (if (or d w)
   1.570 +    (let [len (count m)
   1.571 +          round-pos (if d (+ e d 1))
   1.572 +          round-pos (if (and w (< (inc e) (dec w)) 
   1.573 +                             (or (nil? round-pos) (< (dec w) round-pos)))
   1.574 +                      (dec w)
   1.575 +                      round-pos)
   1.576 +          [m1 e1 round-pos len] (if (= round-pos 0) 
   1.577 +                                  [(str "0" m) (inc e) 1 (inc len)]
   1.578 +                                  [m e round-pos len])]
   1.579 +      (if round-pos
   1.580 +        (if (neg? round-pos)
   1.581 +          ["0" 0 false]
   1.582 +          (if (> len round-pos)
   1.583 +            (let [round-char (nth m1 round-pos)
   1.584 +                  ^String result (subs m1 0 round-pos)]
   1.585 +              (if (>= (int round-char) (int \5))
   1.586 +                (let [result-val (Integer/valueOf result)
   1.587 +                      leading-zeros (subs result 0 (min (prefix-count result \0) (- round-pos 1)))
   1.588 +                      round-up-result (str leading-zeros
   1.589 +                                           (String/valueOf (+ result-val 
   1.590 +                                                              (if (neg? result-val) -1 1))))
   1.591 +                      expanded (> (count round-up-result) (count result))]
   1.592 +                  [round-up-result e1 expanded])
   1.593 +                [result e1 false]))
   1.594 +            [m e false]))
   1.595 +        [m e false]))
   1.596 +    [m e false]))
   1.597 +
   1.598 +(defn- expand-fixed [m e d]
   1.599 +  (let [m1 (if (neg? e) (str (apply str (repeat (dec (- e)) \0)) m) m)
   1.600 +        len (count m1)
   1.601 +        target-len (if d (+ e d 1) (inc e))]
   1.602 +    (if (< len target-len) 
   1.603 +      (str m1 (apply str (repeat (- target-len len) \0))) 
   1.604 +      m1)))
   1.605 +
   1.606 +(defn- insert-decimal
   1.607 +  "Insert the decimal point at the right spot in the number to match an exponent"
   1.608 +  [m e]
   1.609 +  (if (neg? e)
   1.610 +    (str "." m)
   1.611 +    (let [loc (inc e)]
   1.612 +      (str (subs m 0 loc) "." (subs m loc)))))
   1.613 +
   1.614 +(defn- get-fixed [m e d]
   1.615 +  (insert-decimal (expand-fixed m e d) e))
   1.616 +
   1.617 +(defn- insert-scaled-decimal
   1.618 +  "Insert the decimal point at the right spot in the number to match an exponent"
   1.619 +  [m k]
   1.620 +  (if (neg? k)
   1.621 +    (str "." m)
   1.622 +    (str (subs m 0 k) "." (subs m k))))
   1.623 +
   1.624 +;; the function to render ~F directives
   1.625 +;; TODO: support rationals. Back off to ~D/~A is the appropriate cases
   1.626 +(defn- fixed-float [params navigator offsets]
   1.627 +  (let [w (:w params)
   1.628 +        d (:d params)
   1.629 +        [arg navigator] (next-arg navigator)
   1.630 +        [sign abs] (if (neg? arg) ["-" (- arg)] ["+" arg])
   1.631 +        [mantissa exp] (float-parts abs)
   1.632 +        scaled-exp (+ exp (:k params))
   1.633 +        add-sign (or (:at params) (neg? arg))
   1.634 +        append-zero (and (not d) (<= (dec (count mantissa)) scaled-exp))
   1.635 +        [rounded-mantissa scaled-exp expanded] (round-str mantissa scaled-exp 
   1.636 +                                                          d (if w (- w (if add-sign 1 0))))
   1.637 +        fixed-repr (get-fixed rounded-mantissa (if expanded (inc scaled-exp) scaled-exp) d)
   1.638 +        prepend-zero (= (first fixed-repr) \.)]
   1.639 +    (if w
   1.640 +      (let [len (count fixed-repr)
   1.641 +            signed-len (if add-sign (inc len) len)
   1.642 +            prepend-zero (and prepend-zero (not (>= signed-len w)))
   1.643 +            append-zero (and append-zero (not (>= signed-len w)))
   1.644 +            full-len (if (or prepend-zero append-zero)
   1.645 +                       (inc signed-len) 
   1.646 +                       signed-len)]
   1.647 +        (if (and (> full-len w) (:overflowchar params))
   1.648 +          (print (apply str (repeat w (:overflowchar params))))
   1.649 +          (print (str
   1.650 +                  (apply str (repeat (- w full-len) (:padchar params)))
   1.651 +                  (if add-sign sign) 
   1.652 +                  (if prepend-zero "0")
   1.653 +                  fixed-repr
   1.654 +                  (if append-zero "0")))))
   1.655 +      (print (str
   1.656 +              (if add-sign sign) 
   1.657 +              (if prepend-zero "0")
   1.658 +              fixed-repr
   1.659 +              (if append-zero "0"))))
   1.660 +    navigator))
   1.661 +
   1.662 +
   1.663 +;; the function to render ~E directives
   1.664 +;; TODO: support rationals. Back off to ~D/~A is the appropriate cases
   1.665 +;; TODO: define ~E representation for Infinity
   1.666 +(defn- exponential-float [params navigator offsets]
   1.667 +  (let [[arg navigator] (next-arg navigator)]
   1.668 +    (loop [[mantissa exp] (float-parts (if (neg? arg) (- arg) arg))]
   1.669 +      (let [w (:w params)
   1.670 +            d (:d params)
   1.671 +            e (:e params)
   1.672 +            k (:k params)
   1.673 +            expchar (or (:exponentchar params) \E)
   1.674 +            add-sign (or (:at params) (neg? arg))
   1.675 +            prepend-zero (<= k 0)
   1.676 +            ^Integer scaled-exp (- exp (dec k))
   1.677 +            scaled-exp-str (str (Math/abs scaled-exp))
   1.678 +            scaled-exp-str (str expchar (if (neg? scaled-exp) \- \+) 
   1.679 +                                (if e (apply str 
   1.680 +                                             (repeat 
   1.681 +                                              (- e 
   1.682 +                                                 (count scaled-exp-str)) 
   1.683 +                                              \0))) 
   1.684 +                                scaled-exp-str)
   1.685 +            exp-width (count scaled-exp-str)
   1.686 +            base-mantissa-width (count mantissa)
   1.687 +            scaled-mantissa (str (apply str (repeat (- k) \0))
   1.688 +                                 mantissa
   1.689 +                                 (if d 
   1.690 +                                   (apply str 
   1.691 +                                          (repeat 
   1.692 +                                           (- d (dec base-mantissa-width)
   1.693 +                                              (if (neg? k) (- k) 0)) \0))))
   1.694 +            w-mantissa (if w (- w exp-width))
   1.695 +            [rounded-mantissa _ incr-exp] (round-str 
   1.696 +                                           scaled-mantissa 0
   1.697 +                                           (cond
   1.698 +                                            (= k 0) (dec d)
   1.699 +                                            (pos? k) d
   1.700 +                                            (neg? k) (dec d))
   1.701 +                                           (if w-mantissa 
   1.702 +                                             (- w-mantissa (if add-sign 1 0))))
   1.703 +            full-mantissa (insert-scaled-decimal rounded-mantissa k)
   1.704 +            append-zero (and (= k (count rounded-mantissa)) (nil? d))]
   1.705 +        (if (not incr-exp)
   1.706 +          (if w
   1.707 +            (let [len (+ (count full-mantissa) exp-width)
   1.708 +                  signed-len (if add-sign (inc len) len)
   1.709 +                  prepend-zero (and prepend-zero (not (= signed-len w)))
   1.710 +                  full-len (if prepend-zero (inc signed-len) signed-len)
   1.711 +                  append-zero (and append-zero (< full-len w))]
   1.712 +              (if (and (or (> full-len w) (and e (> (- exp-width 2) e)))
   1.713 +                       (:overflowchar params))
   1.714 +                (print (apply str (repeat w (:overflowchar params))))
   1.715 +                (print (str
   1.716 +                        (apply str 
   1.717 +                               (repeat 
   1.718 +                                (- w full-len (if append-zero 1 0) )
   1.719 +                                (:padchar params)))
   1.720 +                        (if add-sign (if (neg? arg) \- \+)) 
   1.721 +                        (if prepend-zero "0")
   1.722 +                        full-mantissa
   1.723 +                        (if append-zero "0")
   1.724 +                        scaled-exp-str))))
   1.725 +            (print (str
   1.726 +                    (if add-sign (if (neg? arg) \- \+)) 
   1.727 +                    (if prepend-zero "0")
   1.728 +                    full-mantissa
   1.729 +                    (if append-zero "0")
   1.730 +                    scaled-exp-str)))
   1.731 +          (recur [rounded-mantissa (inc exp)]))))
   1.732 +    navigator))
   1.733 +
   1.734 +;; the function to render ~G directives
   1.735 +;; This just figures out whether to pass the request off to ~F or ~E based 
   1.736 +;; on the algorithm in CLtL.
   1.737 +;; TODO: support rationals. Back off to ~D/~A is the appropriate cases
   1.738 +;; TODO: refactor so that float-parts isn't called twice
   1.739 +(defn- general-float [params navigator offsets]
   1.740 +  (let [[arg _] (next-arg navigator)
   1.741 +        [mantissa exp] (float-parts (if (neg? arg) (- arg) arg))
   1.742 +        w (:w params)
   1.743 +        d (:d params)
   1.744 +        e (:e params)
   1.745 +        n (if (= arg 0.0) 0 (inc exp))
   1.746 +        ee (if e (+ e 2) 4)
   1.747 +        ww (if w (- w ee))
   1.748 +        d (if d d (max (count mantissa) (min n 7)))
   1.749 +        dd (- d n)]
   1.750 +    (if (<= 0 dd d)
   1.751 +      (let [navigator (fixed-float {:w ww, :d dd, :k 0, 
   1.752 +                                    :overflowchar (:overflowchar params),
   1.753 +                                    :padchar (:padchar params), :at (:at params)} 
   1.754 +                                   navigator offsets)]
   1.755 +        (print (apply str (repeat ee \space)))
   1.756 +        navigator)
   1.757 +      (exponential-float params navigator offsets))))
   1.758 +
   1.759 +;; the function to render ~$ directives
   1.760 +;; TODO: support rationals. Back off to ~D/~A is the appropriate cases
   1.761 +(defn- dollar-float [params navigator offsets]
   1.762 +  (let [[^Double arg navigator] (next-arg navigator)
   1.763 +        [mantissa exp] (float-parts (Math/abs arg))
   1.764 +        d (:d params) ; digits after the decimal
   1.765 +        n (:n params) ; minimum digits before the decimal
   1.766 +        w (:w params) ; minimum field width
   1.767 +        add-sign (or (:at params) (neg? arg))
   1.768 +        [rounded-mantissa scaled-exp expanded] (round-str mantissa exp d nil)
   1.769 +        ^String fixed-repr (get-fixed rounded-mantissa (if expanded (inc scaled-exp) scaled-exp) d)
   1.770 +        full-repr (str (apply str (repeat (- n (.indexOf fixed-repr (int \.))) \0)) fixed-repr)
   1.771 +        full-len (+ (count full-repr) (if add-sign 1 0))]
   1.772 +    (print (str
   1.773 +            (if (and (:colon params) add-sign) (if (neg? arg) \- \+))
   1.774 +            (apply str (repeat (- w full-len) (:padchar params)))
   1.775 +            (if (and (not (:colon params)) add-sign) (if (neg? arg) \- \+))
   1.776 +            full-repr))
   1.777 +    navigator))
   1.778 +        
   1.779 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.780 +;;; Support for the '~[...~]' conditional construct in its
   1.781 +;;; different flavors
   1.782 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.783 +
   1.784 +;; ~[...~] without any modifiers chooses one of the clauses based on the param or 
   1.785 +;; next argument
   1.786 +;; TODO check arg is positive int
   1.787 +(defn- choice-conditional [params arg-navigator offsets]
   1.788 +  (let [arg (:selector params)
   1.789 +        [arg navigator] (if arg [arg arg-navigator] (next-arg arg-navigator))
   1.790 +        clauses (:clauses params)
   1.791 +        clause (if (or (neg? arg) (>= arg (count clauses)))
   1.792 +                 (first (:else params))
   1.793 +                 (nth clauses arg))]
   1.794 +    (if clause
   1.795 +      (execute-sub-format clause navigator (:base-args params))
   1.796 +      navigator)))
   1.797 +
   1.798 +;; ~:[...~] with the colon reads the next argument treating it as a truth value
   1.799 +(defn- boolean-conditional [params arg-navigator offsets]
   1.800 +  (let [[arg navigator] (next-arg arg-navigator)
   1.801 +        clauses (:clauses params)
   1.802 +        clause (if arg
   1.803 +                 (second clauses)
   1.804 +                 (first clauses))]
   1.805 +    (if clause
   1.806 +      (execute-sub-format clause navigator (:base-args params))
   1.807 +      navigator)))
   1.808 +
   1.809 +;; ~@[...~] with the at sign executes the conditional if the next arg is not
   1.810 +;; nil/false without consuming the arg
   1.811 +(defn- check-arg-conditional [params arg-navigator offsets]
   1.812 +  (let [[arg navigator] (next-arg arg-navigator)
   1.813 +        clauses (:clauses params)
   1.814 +        clause (if arg (first clauses))]
   1.815 +    (if arg
   1.816 +      (if clause
   1.817 +        (execute-sub-format clause arg-navigator (:base-args params))
   1.818 +        arg-navigator)
   1.819 +      navigator)))
   1.820 +
   1.821 +
   1.822 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.823 +;;; Support for the '~{...~}' iteration construct in its
   1.824 +;;; different flavors
   1.825 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.826 +
   1.827 +
   1.828 +;; ~{...~} without any modifiers uses the next argument as an argument list that 
   1.829 +;; is consumed by all the iterations
   1.830 +(defn- iterate-sublist [params navigator offsets]
   1.831 +  (let [max-count (:max-iterations params)
   1.832 +        param-clause (first (:clauses params))
   1.833 +        [clause navigator] (if (empty? param-clause) 
   1.834 +                             (get-format-arg navigator)
   1.835 +                             [param-clause navigator]) 
   1.836 +        [arg-list navigator] (next-arg navigator)
   1.837 +        args (init-navigator arg-list)]
   1.838 +    (loop [count 0
   1.839 +           args args
   1.840 +           last-pos (num -1)]
   1.841 +      (if (and (not max-count) (= (:pos args) last-pos) (> count 1))
   1.842 +        ;; TODO get the offset in here and call format exception
   1.843 +        (throw (RuntimeException. "%{ construct not consuming any arguments: Infinite loop!")))
   1.844 +      (if (or (and (empty? (:rest args))
   1.845 +                   (or (not (:colon (:right-params params))) (> count 0)))
   1.846 +              (and max-count (>= count max-count)))
   1.847 +        navigator
   1.848 +        (let [iter-result (execute-sub-format clause args (:base-args params))] 
   1.849 +          (if (= :up-arrow (first iter-result))
   1.850 +            navigator
   1.851 +            (recur (inc count) iter-result (:pos args))))))))
   1.852 +
   1.853 +;; ~:{...~} with the colon treats the next argument as a list of sublists. Each of the
   1.854 +;; sublists is used as the arglist for a single iteration.
   1.855 +(defn- iterate-list-of-sublists [params navigator offsets]
   1.856 +  (let [max-count (:max-iterations params)
   1.857 +        param-clause (first (:clauses params))
   1.858 +        [clause navigator] (if (empty? param-clause) 
   1.859 +                             (get-format-arg navigator)
   1.860 +                             [param-clause navigator]) 
   1.861 +        [arg-list navigator] (next-arg navigator)]
   1.862 +    (loop [count 0
   1.863 +           arg-list arg-list]
   1.864 +      (if (or (and (empty? arg-list)
   1.865 +                   (or (not (:colon (:right-params params))) (> count 0)))
   1.866 +              (and max-count (>= count max-count)))
   1.867 +        navigator
   1.868 +        (let [iter-result (execute-sub-format 
   1.869 +                           clause 
   1.870 +                           (init-navigator (first arg-list))
   1.871 +                           (init-navigator (next arg-list)))]
   1.872 +          (if (= :colon-up-arrow (first iter-result))
   1.873 +            navigator
   1.874 +            (recur (inc count) (next arg-list))))))))
   1.875 +
   1.876 +;; ~@{...~} with the at sign uses the main argument list as the arguments to the iterations
   1.877 +;; is consumed by all the iterations
   1.878 +(defn- iterate-main-list [params navigator offsets]
   1.879 +  (let [max-count (:max-iterations params)
   1.880 +        param-clause (first (:clauses params))
   1.881 +        [clause navigator] (if (empty? param-clause) 
   1.882 +                             (get-format-arg navigator)
   1.883 +                             [param-clause navigator])]
   1.884 +    (loop [count 0
   1.885 +           navigator navigator
   1.886 +           last-pos (num -1)]
   1.887 +      (if (and (not max-count) (= (:pos navigator) last-pos) (> count 1))
   1.888 +        ;; TODO get the offset in here and call format exception
   1.889 +        (throw (RuntimeException. "%@{ construct not consuming any arguments: Infinite loop!")))
   1.890 +      (if (or (and (empty? (:rest navigator))
   1.891 +                   (or (not (:colon (:right-params params))) (> count 0)))
   1.892 +              (and max-count (>= count max-count)))
   1.893 +        navigator
   1.894 +        (let [iter-result (execute-sub-format clause navigator (:base-args params))] 
   1.895 +          (if (= :up-arrow (first iter-result))
   1.896 +            (second iter-result)
   1.897 +            (recur 
   1.898 +             (inc count) iter-result (:pos navigator))))))))
   1.899 +
   1.900 +;; ~@:{...~} with both colon and at sign uses the main argument list as a set of sublists, one
   1.901 +;; of which is consumed with each iteration
   1.902 +(defn- iterate-main-sublists [params navigator offsets]
   1.903 +  (let [max-count (:max-iterations params)
   1.904 +        param-clause (first (:clauses params))
   1.905 +        [clause navigator] (if (empty? param-clause) 
   1.906 +                             (get-format-arg navigator)
   1.907 +                             [param-clause navigator]) 
   1.908 +        ]
   1.909 +    (loop [count 0
   1.910 +           navigator navigator]
   1.911 +      (if (or (and (empty? (:rest navigator))
   1.912 +                   (or (not (:colon (:right-params params))) (> count 0)))
   1.913 +              (and max-count (>= count max-count)))
   1.914 +        navigator
   1.915 +        (let [[sublist navigator] (next-arg-or-nil navigator)
   1.916 +              iter-result (execute-sub-format clause (init-navigator sublist) navigator)]
   1.917 +          (if (= :colon-up-arrow (first iter-result))
   1.918 +            navigator
   1.919 +            (recur (inc count) navigator)))))))
   1.920 +
   1.921 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.922 +;;; The '~< directive has two completely different meanings
   1.923 +;;; in the '~<...~>' form it does justification, but with
   1.924 +;;; ~<...~:>' it represents the logical block operation of the
   1.925 +;;; pretty printer.
   1.926 +;;; 
   1.927 +;;; Unfortunately, the current architecture decides what function
   1.928 +;;; to call at form parsing time before the sub-clauses have been
   1.929 +;;; folded, so it is left to run-time to make the decision.
   1.930 +;;; 
   1.931 +;;; TODO: make it possible to make these decisions at compile-time.
   1.932 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.933 +
   1.934 +(declare format-logical-block)
   1.935 +(declare justify-clauses)
   1.936 +
   1.937 +(defn- logical-block-or-justify [params navigator offsets]
   1.938 +  (if (:colon (:right-params params))
   1.939 +    (format-logical-block params navigator offsets)
   1.940 +    (justify-clauses params navigator offsets)))
   1.941 +
   1.942 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.943 +;;; Support for the '~<...~>' justification directive
   1.944 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
   1.945 +
   1.946 +(defn- render-clauses [clauses navigator base-navigator]
   1.947 +  (loop [clauses clauses
   1.948 +         acc []
   1.949 +         navigator navigator]
   1.950 +    (if (empty? clauses)
   1.951 +      [acc navigator]
   1.952 +      (let [clause (first clauses)
   1.953 +            [iter-result result-str] (binding [*out* (java.io.StringWriter.)]
   1.954 +                                       [(execute-sub-format clause navigator base-navigator) 
   1.955 +                                        (.toString *out*)])]
   1.956 +        (if (= :up-arrow (first iter-result))
   1.957 +          [acc (second iter-result)]
   1.958 +          (recur (next clauses) (conj acc result-str) iter-result))))))
   1.959 +
   1.960 +;; TODO support for ~:; constructions
   1.961 +(defn- justify-clauses [params navigator offsets]
   1.962 +  (let [[[eol-str] new-navigator] (when-let [else (:else params)]
   1.963 +                                    (render-clauses else navigator (:base-args params)))
   1.964 +        navigator (or new-navigator navigator)
   1.965 +        [else-params new-navigator] (when-let [p (:else-params params)]
   1.966 +                                      (realize-parameter-list p navigator))
   1.967 +        navigator (or new-navigator navigator)
   1.968 +        min-remaining (or (first (:min-remaining else-params)) 0)
   1.969 +        max-columns (or (first (:max-columns else-params))
   1.970 +                        (get-max-column *out*))
   1.971 +        clauses (:clauses params)
   1.972 +        [strs navigator] (render-clauses clauses navigator (:base-args params))
   1.973 +        slots (max 1
   1.974 +                   (+ (dec (count strs)) (if (:colon params) 1 0) (if (:at params) 1 0)))
   1.975 +        chars (reduce + (map count strs))
   1.976 +        mincol (:mincol params)
   1.977 +        minpad (:minpad params)
   1.978 +        colinc (:colinc params)
   1.979 +        minout (+ chars (* slots minpad))
   1.980 +        result-columns (if (<= minout mincol) 
   1.981 +                         mincol
   1.982 +                         (+ mincol (* colinc
   1.983 +                                      (+ 1 (quot (- minout mincol 1) colinc)))))
   1.984 +        total-pad (- result-columns chars)
   1.985 +        pad (max minpad (quot total-pad slots))
   1.986 +        extra-pad (- total-pad (* pad slots))
   1.987 +        pad-str (apply str (repeat pad (:padchar params)))]
   1.988 +    (if (and eol-str (> (+ (get-column (:base @@*out*)) min-remaining result-columns) 
   1.989 +                        max-columns))
   1.990 +      (print eol-str))
   1.991 +    (loop [slots slots
   1.992 +           extra-pad extra-pad
   1.993 +           strs strs
   1.994 +           pad-only (or (:colon params)
   1.995 +                        (and (= (count strs) 1) (not (:at params))))]
   1.996 +      (if (seq strs)
   1.997 +        (do
   1.998 +          (print (str (if (not pad-only) (first strs))
   1.999 +                      (if (or pad-only (next strs) (:at params)) pad-str)
  1.1000 +                      (if (pos? extra-pad) (:padchar params))))
  1.1001 +          (recur 
  1.1002 +           (dec slots)
  1.1003 +           (dec extra-pad)
  1.1004 +           (if pad-only strs (next strs))
  1.1005 +           false))))
  1.1006 +    navigator))
  1.1007 +
  1.1008 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1.1009 +;;; Support for case modification with ~(...~).
  1.1010 +;;; We do this by wrapping the underlying writer with
  1.1011 +;;; a special writer to do the appropriate modification. This
  1.1012 +;;; allows us to support arbitrary-sized output and sources
  1.1013 +;;; that may block.
  1.1014 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1.1015 +
  1.1016 +(defn- downcase-writer 
  1.1017 +  "Returns a proxy that wraps writer, converting all characters to lower case"
  1.1018 +  [^java.io.Writer writer]
  1.1019 +  (proxy [java.io.Writer] []
  1.1020 +    (close [] (.close writer))
  1.1021 +    (flush [] (.flush writer))
  1.1022 +    (write ([^chars cbuf ^Integer off ^Integer len] 
  1.1023 +              (.write writer cbuf off len))
  1.1024 +           ([x]
  1.1025 +              (condp = (class x)
  1.1026 +		String 
  1.1027 +		(let [s ^String x]
  1.1028 +		  (.write writer (.toLowerCase s)))
  1.1029 +
  1.1030 +		Integer
  1.1031 +		(let [c ^Character x]
  1.1032 +		  (.write writer (int (Character/toLowerCase (char c))))))))))
  1.1033 +
  1.1034 +(defn- upcase-writer 
  1.1035 +  "Returns a proxy that wraps writer, converting all characters to upper case"
  1.1036 +  [^java.io.Writer writer]
  1.1037 +  (proxy [java.io.Writer] []
  1.1038 +    (close [] (.close writer))
  1.1039 +    (flush [] (.flush writer))
  1.1040 +    (write ([^chars cbuf ^Integer off ^Integer len] 
  1.1041 +              (.write writer cbuf off len))
  1.1042 +           ([x]
  1.1043 +              (condp = (class x)
  1.1044 +		String 
  1.1045 +		(let [s ^String x]
  1.1046 +		  (.write writer (.toUpperCase s)))
  1.1047 +
  1.1048 +		Integer
  1.1049 +		(let [c ^Character x]
  1.1050 +		  (.write writer (int (Character/toUpperCase (char c))))))))))
  1.1051 +
  1.1052 +(defn- capitalize-string
  1.1053 +  "Capitalizes the words in a string. If first? is false, don't capitalize the 
  1.1054 +                                      first character of the string even if it's a letter."
  1.1055 +  [s first?]
  1.1056 +  (let [^Character f (first s) 
  1.1057 +        s (if (and first? f (Character/isLetter f))
  1.1058 +            (str (Character/toUpperCase f) (subs s 1))
  1.1059 +            s)]
  1.1060 +    (apply str 
  1.1061 +           (first
  1.1062 +            (consume
  1.1063 +             (fn [s]
  1.1064 +               (if (empty? s)
  1.1065 +                 [nil nil]
  1.1066 +                 (let [m (re-matcher #"\W\w" s)
  1.1067 +                       match (re-find m)
  1.1068 +                       offset (and match (inc (.start m)))]
  1.1069 +                   (if offset
  1.1070 +                     [(str (subs s 0 offset) 
  1.1071 +                           (Character/toUpperCase ^Character (nth s offset)))
  1.1072 +                      (subs s (inc offset))]
  1.1073 +                     [s nil]))))
  1.1074 +             s)))))
  1.1075 +
  1.1076 +(defn- capitalize-word-writer
  1.1077 +  "Returns a proxy that wraps writer, captializing all words"
  1.1078 +  [^java.io.Writer writer]
  1.1079 +  (let [last-was-whitespace? (ref true)] 
  1.1080 +    (proxy [java.io.Writer] []
  1.1081 +      (close [] (.close writer))
  1.1082 +      (flush [] (.flush writer))
  1.1083 +      (write 
  1.1084 +       ([^chars cbuf ^Integer off ^Integer len] 
  1.1085 +          (.write writer cbuf off len))
  1.1086 +       ([x]
  1.1087 +          (condp = (class x)
  1.1088 +            String 
  1.1089 +            (let [s ^String x]
  1.1090 +              (.write writer 
  1.1091 +                      ^String (capitalize-string (.toLowerCase s) @last-was-whitespace?))
  1.1092 +              (dosync 
  1.1093 +               (ref-set last-was-whitespace? 
  1.1094 +                        (Character/isWhitespace 
  1.1095 +                         ^Character (nth s (dec (count s)))))))
  1.1096 +
  1.1097 +            Integer
  1.1098 +            (let [c (char x)]
  1.1099 +              (let [mod-c (if @last-was-whitespace? (Character/toUpperCase ^Character (char x)) c)] 
  1.1100 +                (.write writer (int mod-c))
  1.1101 +                (dosync (ref-set last-was-whitespace? (Character/isWhitespace ^Character (char x))))))))))))
  1.1102 +
  1.1103 +(defn- init-cap-writer
  1.1104 +  "Returns a proxy that wraps writer, capitalizing the first word"
  1.1105 +  [^java.io.Writer writer]
  1.1106 +  (let [capped (ref false)] 
  1.1107 +    (proxy [java.io.Writer] []
  1.1108 +      (close [] (.close writer))
  1.1109 +      (flush [] (.flush writer))
  1.1110 +      (write ([^chars cbuf ^Integer off ^Integer len] 
  1.1111 +                (.write writer cbuf off len))
  1.1112 +             ([x]
  1.1113 +                (condp = (class x)
  1.1114 +                 String 
  1.1115 +                 (let [s (.toLowerCase ^String x)]
  1.1116 +                   (if (not @capped) 
  1.1117 +                     (let [m (re-matcher #"\S" s)
  1.1118 +                           match (re-find m)
  1.1119 +                           offset (and match (.start m))]
  1.1120 +                       (if offset
  1.1121 +                         (do (.write writer 
  1.1122 +                                   (str (subs s 0 offset) 
  1.1123 +                                        (Character/toUpperCase ^Character (nth s offset))
  1.1124 +                                        (.toLowerCase ^String (subs s (inc offset)))))
  1.1125 +                           (dosync (ref-set capped true)))
  1.1126 +                         (.write writer s))) 
  1.1127 +                     (.write writer (.toLowerCase s))))
  1.1128 +
  1.1129 +                 Integer
  1.1130 +                 (let [c ^Character (char x)]
  1.1131 +                   (if (and (not @capped) (Character/isLetter c))
  1.1132 +                     (do
  1.1133 +                       (dosync (ref-set capped true))
  1.1134 +                       (.write writer (int (Character/toUpperCase c))))
  1.1135 +                     (.write writer (int (Character/toLowerCase c)))))))))))
  1.1136 +
  1.1137 +(defn- modify-case [make-writer params navigator offsets]
  1.1138 +  (let [clause (first (:clauses params))]
  1.1139 +    (binding [*out* (make-writer *out*)] 
  1.1140 +      (execute-sub-format clause navigator (:base-args params)))))
  1.1141 +
  1.1142 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1.1143 +;;; If necessary, wrap the writer in a PrettyWriter object
  1.1144 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1.1145 +
  1.1146 +(defn get-pretty-writer [writer]
  1.1147 +  (if (pretty-writer? writer) 
  1.1148 +    writer
  1.1149 +    (pretty-writer writer *print-right-margin* *print-miser-width*)))
  1.1150 + 
  1.1151 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1.1152 +;;; Support for column-aware operations ~&, ~T
  1.1153 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1.1154 +
  1.1155 +;; TODO: make an automatic newline for non-ColumnWriters
  1.1156 +(defn fresh-line
  1.1157 +  "Make a newline if the Writer is not already at the beginning of the line.
  1.1158 +N.B. Only works on ColumnWriters right now."
  1.1159 +  []
  1.1160 +  (if (not (= 0 (get-column (:base @@*out*))))
  1.1161 +    (prn)))
  1.1162 +
  1.1163 +(defn- absolute-tabulation [params navigator offsets]
  1.1164 +  (let [colnum (:colnum params) 
  1.1165 +        colinc (:colinc params)
  1.1166 +        current (get-column (:base @@*out*))
  1.1167 +        space-count (cond
  1.1168 +                     (< current colnum) (- colnum current)
  1.1169 +                     (= colinc 0) 0
  1.1170 +                     :else (- colinc (rem (- current colnum) colinc)))]
  1.1171 +    (print (apply str (repeat space-count \space))))
  1.1172 +  navigator)
  1.1173 +
  1.1174 +(defn- relative-tabulation [params navigator offsets]
  1.1175 +  (let [colrel (:colnum params) 
  1.1176 +        colinc (:colinc params)
  1.1177 +        start-col (+ colrel (get-column (:base @@*out*)))
  1.1178 +        offset (if (pos? colinc) (rem start-col colinc) 0)
  1.1179 +        space-count (+ colrel (if (= 0 offset) 0 (- colinc offset)))]
  1.1180 +    (print (apply str (repeat space-count \space))))
  1.1181 +  navigator)
  1.1182 +
  1.1183 +
  1.1184 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1.1185 +;;; Support for accessing the pretty printer from a format
  1.1186 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1.1187 +
  1.1188 +;; TODO: support ~@; per-line-prefix separator
  1.1189 +;; TODO: get the whole format wrapped so we can start the lb at any column
  1.1190 +(defn- format-logical-block [params navigator offsets]
  1.1191 +  (let [clauses (:clauses params)
  1.1192 +        clause-count (count clauses)
  1.1193 +        prefix (cond
  1.1194 +                (> clause-count 1) (:string (:params (first (first clauses))))
  1.1195 +                (:colon params) "(")
  1.1196 +        body (nth clauses (if (> clause-count 1) 1 0))
  1.1197 +        suffix (cond
  1.1198 +                (> clause-count 2) (:string (:params (first (nth clauses 2))))
  1.1199 +                (:colon params) ")")
  1.1200 +        [arg navigator] (next-arg navigator)]
  1.1201 +    (pprint-logical-block :prefix prefix :suffix suffix
  1.1202 +      (execute-sub-format 
  1.1203 +       body 
  1.1204 +       (init-navigator arg)
  1.1205 +       (:base-args params)))
  1.1206 +    navigator))
  1.1207 +
  1.1208 +(defn- set-indent [params navigator offsets]
  1.1209 +  (let [relative-to (if (:colon params) :current :block)]
  1.1210 +    (pprint-indent relative-to (:n params))
  1.1211 +    navigator))
  1.1212 +
  1.1213 +;;; TODO: support ~:T section options for ~T
  1.1214 +
  1.1215 +(defn- conditional-newline [params navigator offsets]
  1.1216 +  (let [kind (if (:colon params) 
  1.1217 +               (if (:at params) :mandatory :fill)
  1.1218 +               (if (:at params) :miser :linear))]
  1.1219 +    (pprint-newline kind)
  1.1220 +    navigator))
  1.1221 +
  1.1222 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1.1223 +;;; The table of directives we support, each with its params,
  1.1224 +;;; properties, and the compilation function
  1.1225 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1.1226 +
  1.1227 +;; We start with a couple of helpers
  1.1228 +(defn- process-directive-table-element [ [ char params flags bracket-info & generator-fn ] ]
  1.1229 +  [char, 
  1.1230 +   {:directive char,
  1.1231 +    :params `(array-map ~@params),
  1.1232 +    :flags flags,
  1.1233 +    :bracket-info bracket-info,
  1.1234 +    :generator-fn (concat '(fn [ params offset]) generator-fn) }])
  1.1235 +
  1.1236 +(defmacro ^{:private true}
  1.1237 +  defdirectives 
  1.1238 +  [ & directives ]
  1.1239 +  `(def ^{:private true}
  1.1240 +        directive-table (hash-map ~@(mapcat process-directive-table-element directives))))
  1.1241 +
  1.1242 +(defdirectives 
  1.1243 +  (\A 
  1.1244 +   [ :mincol [0 Integer] :colinc [1 Integer] :minpad [0 Integer] :padchar [\space Character] ] 
  1.1245 +   #{ :at :colon :both} {}
  1.1246 +   #(format-ascii print-str %1 %2 %3))
  1.1247 +
  1.1248 +  (\S 
  1.1249 +   [ :mincol [0 Integer] :colinc [1 Integer] :minpad [0 Integer] :padchar [\space Character] ] 
  1.1250 +   #{ :at :colon :both} {}
  1.1251 +   #(format-ascii pr-str %1 %2 %3))
  1.1252 +
  1.1253 +  (\D
  1.1254 +   [ :mincol [0 Integer] :padchar [\space Character] :commachar [\, Character] 
  1.1255 +    :commainterval [ 3 Integer]]
  1.1256 +   #{ :at :colon :both } {}
  1.1257 +   #(format-integer 10 %1 %2 %3))
  1.1258 +
  1.1259 +  (\B
  1.1260 +   [ :mincol [0 Integer] :padchar [\space Character] :commachar [\, Character] 
  1.1261 +    :commainterval [ 3 Integer]]
  1.1262 +   #{ :at :colon :both } {}
  1.1263 +   #(format-integer 2 %1 %2 %3))
  1.1264 +
  1.1265 +  (\O
  1.1266 +   [ :mincol [0 Integer] :padchar [\space Character] :commachar [\, Character] 
  1.1267 +    :commainterval [ 3 Integer]]
  1.1268 +   #{ :at :colon :both } {}
  1.1269 +   #(format-integer 8 %1 %2 %3))
  1.1270 +
  1.1271 +  (\X
  1.1272 +   [ :mincol [0 Integer] :padchar [\space Character] :commachar [\, Character] 
  1.1273 +    :commainterval [ 3 Integer]]
  1.1274 +   #{ :at :colon :both } {}
  1.1275 +   #(format-integer 16 %1 %2 %3))
  1.1276 +
  1.1277 +  (\R
  1.1278 +   [:base [nil Integer] :mincol [0 Integer] :padchar [\space Character] :commachar [\, Character] 
  1.1279 +    :commainterval [ 3 Integer]]
  1.1280 +   #{ :at :colon :both } {}
  1.1281 +   (do
  1.1282 +     (cond                          ; ~R is overloaded with bizareness
  1.1283 +       (first (:base params))     #(format-integer (:base %1) %1 %2 %3)
  1.1284 +       (and (:at params) (:colon params))   #(format-old-roman %1 %2 %3)
  1.1285 +       (:at params)               #(format-new-roman %1 %2 %3)
  1.1286 +       (:colon params)            #(format-ordinal-english %1 %2 %3)
  1.1287 +       true                       #(format-cardinal-english %1 %2 %3))))
  1.1288 +
  1.1289 +  (\P
  1.1290 +   [ ]
  1.1291 +   #{ :at :colon :both } {}
  1.1292 +   (fn [params navigator offsets]
  1.1293 +     (let [navigator (if (:colon params) (relative-reposition navigator -1) navigator)
  1.1294 +           strs (if (:at params) ["y" "ies"] ["" "s"])
  1.1295 +           [arg navigator] (next-arg navigator)]
  1.1296 +       (print (if (= arg 1) (first strs) (second strs)))
  1.1297 +       navigator)))
  1.1298 +
  1.1299 +  (\C
  1.1300 +   [:char-format [nil Character]]
  1.1301 +   #{ :at :colon :both } {}
  1.1302 +   (cond
  1.1303 +     (:colon params) pretty-character
  1.1304 +     (:at params) readable-character
  1.1305 +     :else plain-character))
  1.1306 +
  1.1307 +  (\F
  1.1308 +   [ :w [nil Integer] :d [nil Integer] :k [0 Integer] :overflowchar [nil Character] 
  1.1309 +    :padchar [\space Character] ]
  1.1310 +   #{ :at } {}
  1.1311 +   fixed-float)
  1.1312 +
  1.1313 +  (\E
  1.1314 +   [ :w [nil Integer] :d [nil Integer] :e [nil Integer] :k [1 Integer] 
  1.1315 +    :overflowchar [nil Character] :padchar [\space Character] 
  1.1316 +    :exponentchar [nil Character] ]
  1.1317 +   #{ :at } {}
  1.1318 +   exponential-float)
  1.1319 +
  1.1320 +  (\G
  1.1321 +   [ :w [nil Integer] :d [nil Integer] :e [nil Integer] :k [1 Integer] 
  1.1322 +    :overflowchar [nil Character] :padchar [\space Character] 
  1.1323 +    :exponentchar [nil Character] ]
  1.1324 +   #{ :at } {}
  1.1325 +   general-float)
  1.1326 +
  1.1327 +  (\$
  1.1328 +   [ :d [2 Integer] :n [1 Integer] :w [0 Integer] :padchar [\space Character]]
  1.1329 +   #{ :at :colon :both} {}
  1.1330 +   dollar-float)
  1.1331 +
  1.1332 +  (\% 
  1.1333 +   [ :count [1 Integer] ] 
  1.1334 +   #{ } {}
  1.1335 +   (fn [params arg-navigator offsets]
  1.1336 +     (dotimes [i (:count params)]
  1.1337 +       (prn))
  1.1338 +     arg-navigator))
  1.1339 +
  1.1340 +  (\&
  1.1341 +   [ :count [1 Integer] ] 
  1.1342 +   #{ :pretty } {}
  1.1343 +   (fn [params arg-navigator offsets]
  1.1344 +     (let [cnt (:count params)]
  1.1345 +       (if (pos? cnt) (fresh-line))
  1.1346 +       (dotimes [i (dec cnt)]
  1.1347 +         (prn)))
  1.1348 +     arg-navigator))
  1.1349 +
  1.1350 +  (\| 
  1.1351 +   [ :count [1 Integer] ] 
  1.1352 +   #{ } {}
  1.1353 +   (fn [params arg-navigator offsets]
  1.1354 +     (dotimes [i (:count params)]
  1.1355 +       (print \formfeed))
  1.1356 +     arg-navigator))
  1.1357 +
  1.1358 +  (\~ 
  1.1359 +   [ :n [1 Integer] ] 
  1.1360 +   #{ } {}
  1.1361 +   (fn [params arg-navigator offsets]
  1.1362 +     (let [n (:n params)]
  1.1363 +       (print (apply str (repeat n \~)))
  1.1364 +       arg-navigator)))
  1.1365 +
  1.1366 +  (\newline ;; Whitespace supression is handled in the compilation loop
  1.1367 +   [ ] 
  1.1368 +   #{:colon :at} {}
  1.1369 +   (fn [params arg-navigator offsets]
  1.1370 +     (if (:at params)
  1.1371 +       (prn))
  1.1372 +     arg-navigator))
  1.1373 +
  1.1374 +  (\T
  1.1375 +   [ :colnum [1 Integer] :colinc [1 Integer] ] 
  1.1376 +   #{ :at :pretty } {}
  1.1377 +   (if (:at params)
  1.1378 +     #(relative-tabulation %1 %2 %3)
  1.1379 +     #(absolute-tabulation %1 %2 %3)))
  1.1380 +
  1.1381 +  (\* 
  1.1382 +   [ :n [1 Integer] ] 
  1.1383 +   #{ :colon :at } {}
  1.1384 +   (fn [params navigator offsets]
  1.1385 +     (let [n (:n params)]
  1.1386 +       (if (:at params)
  1.1387 +         (absolute-reposition navigator n)
  1.1388 +         (relative-reposition navigator (if (:colon params) (- n) n)))
  1.1389 +       )))
  1.1390 +
  1.1391 +  (\? 
  1.1392 +   [ ] 
  1.1393 +   #{ :at } {}
  1.1394 +   (if (:at params)
  1.1395 +     (fn [params navigator offsets]     ; args from main arg list
  1.1396 +       (let [[subformat navigator] (get-format-arg navigator)]
  1.1397 +         (execute-sub-format subformat navigator  (:base-args params))))
  1.1398 +     (fn [params navigator offsets]     ; args from sub-list
  1.1399 +       (let [[subformat navigator] (get-format-arg navigator)
  1.1400 +             [subargs navigator] (next-arg navigator)
  1.1401 +             sub-navigator (init-navigator subargs)]
  1.1402 +         (execute-sub-format subformat sub-navigator (:base-args params))
  1.1403 +         navigator))))
  1.1404 +       
  1.1405 +
  1.1406 +  (\(
  1.1407 +   [ ]
  1.1408 +   #{ :colon :at :both} { :right \), :allows-separator nil, :else nil }
  1.1409 +   (let [mod-case-writer (cond
  1.1410 +                           (and (:at params) (:colon params))
  1.1411 +                           upcase-writer
  1.1412 +
  1.1413 +                           (:colon params)
  1.1414 +                           capitalize-word-writer
  1.1415 +
  1.1416 +                           (:at params)
  1.1417 +                           init-cap-writer
  1.1418 +
  1.1419 +                           :else
  1.1420 +                           downcase-writer)]
  1.1421 +     #(modify-case mod-case-writer %1 %2 %3)))
  1.1422 +
  1.1423 +  (\) [] #{} {} nil) 
  1.1424 +
  1.1425 +  (\[
  1.1426 +   [ :selector [nil Integer] ]
  1.1427 +   #{ :colon :at } { :right \], :allows-separator true, :else :last }
  1.1428 +   (cond
  1.1429 +     (:colon params)
  1.1430 +     boolean-conditional
  1.1431 +
  1.1432 +     (:at params)
  1.1433 +     check-arg-conditional
  1.1434 +
  1.1435 +     true
  1.1436 +     choice-conditional))
  1.1437 +
  1.1438 +  (\; [:min-remaining [nil Integer] :max-columns [nil Integer]] 
  1.1439 +   #{ :colon } { :separator true } nil) 
  1.1440 +   
  1.1441 +  (\] [] #{} {} nil) 
  1.1442 +
  1.1443 +  (\{
  1.1444 +   [ :max-iterations [nil Integer] ]
  1.1445 +   #{ :colon :at :both} { :right \}, :allows-separator false }
  1.1446 +   (cond
  1.1447 +     (and (:at params) (:colon params))
  1.1448 +     iterate-main-sublists
  1.1449 +
  1.1450 +     (:colon params)
  1.1451 +     iterate-list-of-sublists
  1.1452 +
  1.1453 +     (:at params)
  1.1454 +     iterate-main-list
  1.1455 +
  1.1456 +     true
  1.1457 +     iterate-sublist))
  1.1458 +
  1.1459 +   
  1.1460 +  (\} [] #{:colon} {} nil) 
  1.1461 +
  1.1462 +  (\<
  1.1463 +   [:mincol [0 Integer] :colinc [1 Integer] :minpad [0 Integer] :padchar [\space Character]]
  1.1464 +   #{:colon :at :both :pretty} { :right \>, :allows-separator true, :else :first }
  1.1465 +   logical-block-or-justify)
  1.1466 +
  1.1467 +  (\> [] #{:colon} {} nil) 
  1.1468 +
  1.1469 +  ;; TODO: detect errors in cases where colon not allowed
  1.1470 +  (\^ [:arg1 [nil Integer] :arg2 [nil Integer] :arg3 [nil Integer]] 
  1.1471 +   #{:colon} {} 
  1.1472 +   (fn [params navigator offsets]
  1.1473 +     (let [arg1 (:arg1 params)
  1.1474 +           arg2 (:arg2 params)
  1.1475 +           arg3 (:arg3 params)
  1.1476 +           exit (if (:colon params) :colon-up-arrow :up-arrow)]
  1.1477 +       (cond
  1.1478 +         (and arg1 arg2 arg3)
  1.1479 +         (if (<= arg1 arg2 arg3) [exit navigator] navigator)
  1.1480 +
  1.1481 +         (and arg1 arg2)
  1.1482 +         (if (= arg1 arg2) [exit navigator] navigator)
  1.1483 +
  1.1484 +         arg1
  1.1485 +         (if (= arg1 0) [exit navigator] navigator)
  1.1486 +
  1.1487 +         true     ; TODO: handle looking up the arglist stack for info
  1.1488 +         (if (if (:colon params) 
  1.1489 +               (empty? (:rest (:base-args params)))
  1.1490 +               (empty? (:rest navigator)))
  1.1491 +           [exit navigator] navigator))))) 
  1.1492 +
  1.1493 +  (\W 
  1.1494 +   [] 
  1.1495 +   #{:at :colon :both} {}
  1.1496 +   (if (or (:at params) (:colon params))
  1.1497 +     (let [bindings (concat
  1.1498 +                     (if (:at params) [:level nil :length nil] [])
  1.1499 +                     (if (:colon params) [:pretty true] []))]
  1.1500 +       (fn [params navigator offsets]
  1.1501 +         (let [[arg navigator] (next-arg navigator)]
  1.1502 +           (if (apply write arg bindings)
  1.1503 +             [:up-arrow navigator]
  1.1504 +             navigator))))
  1.1505 +     (fn [params navigator offsets]
  1.1506 +       (let [[arg navigator] (next-arg navigator)]
  1.1507 +         (if (write-out arg)
  1.1508 +           [:up-arrow navigator]
  1.1509 +           navigator)))))
  1.1510 +
  1.1511 +  (\_
  1.1512 +   []
  1.1513 +   #{:at :colon :both} {}
  1.1514 +   conditional-newline)
  1.1515 +
  1.1516 +  (\I
  1.1517 +   [:n [0 Integer]]
  1.1518 +   #{:colon} {}
  1.1519 +   set-indent)
  1.1520 +  )
  1.1521 +
  1.1522 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1.1523 +;;; Code to manage the parameters and flags associated with each
  1.1524 +;;; directive in the format string.
  1.1525 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1.1526 +
  1.1527 +(def ^{:private true}
  1.1528 +     param-pattern #"^([vV]|#|('.)|([+-]?\d+)|(?=,))")
  1.1529 +(def ^{:private true}
  1.1530 +     special-params #{ :parameter-from-args :remaining-arg-count })
  1.1531 +
  1.1532 +(defn- extract-param [[s offset saw-comma]]
  1.1533 +  (let [m (re-matcher param-pattern s)
  1.1534 +        param (re-find m)]
  1.1535 +    (if param
  1.1536 +      (let [token-str (first (re-groups m))
  1.1537 +            remainder (subs s (.end m))
  1.1538 +            new-offset (+ offset (.end m))]
  1.1539 +        (if (not (= \, (nth remainder 0)))
  1.1540 +          [ [token-str offset] [remainder new-offset false]]
  1.1541 +          [ [token-str offset] [(subs remainder 1) (inc new-offset) true]]))
  1.1542 +      (if saw-comma 
  1.1543 +        (format-error "Badly formed parameters in format directive" offset)
  1.1544 +        [ nil [s offset]]))))
  1.1545 +
  1.1546 +
  1.1547 +(defn- extract-params [s offset] 
  1.1548 +  (consume extract-param [s offset false]))
  1.1549 +
  1.1550 +(defn- translate-param
  1.1551 +  "Translate the string representation of a param to the internalized
  1.1552 +                                      representation"
  1.1553 +  [[^String p offset]]
  1.1554 +  [(cond 
  1.1555 +    (= (.length p) 0) nil
  1.1556 +    (and (= (.length p) 1) (contains? #{\v \V} (nth p 0))) :parameter-from-args
  1.1557 +    (and (= (.length p) 1) (= \# (nth p 0))) :remaining-arg-count
  1.1558 +    (and (= (.length p) 2) (= \' (nth p 0))) (nth p 1)
  1.1559 +    true (new Integer p))
  1.1560 +   offset])
  1.1561 + 
  1.1562 +(def ^{:private true}
  1.1563 +     flag-defs { \: :colon, \@ :at })
  1.1564 +
  1.1565 +(defn- extract-flags [s offset]
  1.1566 +  (consume
  1.1567 +   (fn [[s offset flags]]
  1.1568 +     (if (empty? s)
  1.1569 +       [nil [s offset flags]]
  1.1570 +       (let [flag (get flag-defs (first s))]
  1.1571 +         (if flag
  1.1572 +           (if (contains? flags flag)
  1.1573 +             (format-error 
  1.1574 +              (str "Flag \"" (first s) "\" appears more than once in a directive")
  1.1575 +              offset)
  1.1576 +             [true [(subs s 1) (inc offset) (assoc flags flag [true offset])]])
  1.1577 +           [nil [s offset flags]]))))
  1.1578 +   [s offset {}]))
  1.1579 +
  1.1580 +(defn- check-flags [def flags]
  1.1581 +  (let [allowed (:flags def)]
  1.1582 +    (if (and (not (:at allowed)) (:at flags))
  1.1583 +      (format-error (str "\"@\" is an illegal flag for format directive \"" (:directive def) "\"")
  1.1584 +                    (nth (:at flags) 1)))
  1.1585 +    (if (and (not (:colon allowed)) (:colon flags))
  1.1586 +      (format-error (str "\":\" is an illegal flag for format directive \"" (:directive def) "\"")
  1.1587 +                    (nth (:colon flags) 1)))
  1.1588 +    (if (and (not (:both allowed)) (:at flags) (:colon flags))
  1.1589 +      (format-error (str "Cannot combine \"@\" and \":\" flags for format directive \"" 
  1.1590 +                         (:directive def) "\"")
  1.1591 +                    (min (nth (:colon flags) 1) (nth (:at flags) 1))))))
  1.1592 +
  1.1593 +(defn- map-params
  1.1594 +  "Takes a directive definition and the list of actual parameters and
  1.1595 +a map of flags and returns a map of the parameters and flags with defaults
  1.1596 +filled in. We check to make sure that there are the right types and number
  1.1597 +of parameters as well."
  1.1598 +  [def params flags offset]
  1.1599 +  (check-flags def flags)
  1.1600 +  (if (> (count params) (count (:params def)))
  1.1601 +    (format-error 
  1.1602 +     (cl-format 
  1.1603 +      nil 
  1.1604 +      "Too many parameters for directive \"~C\": ~D~:* ~[were~;was~:;were~] specified but only ~D~:* ~[are~;is~:;are~] allowed"
  1.1605 +      (:directive def) (count params) (count (:params def)))
  1.1606 +     (second (first params))))
  1.1607 +  (doall
  1.1608 +   (map #(let [val (first %1)]
  1.1609 +           (if (not (or (nil? val) (contains? special-params val) 
  1.1610 +                        (instance? (second (second %2)) val)))
  1.1611 +             (format-error (str "Parameter " (name (first %2))
  1.1612 +                                " has bad type in directive \"" (:directive def) "\": "
  1.1613 +                                (class val))
  1.1614 +                           (second %1))) )
  1.1615 +        params (:params def)))
  1.1616 +     
  1.1617 +  (merge                                ; create the result map
  1.1618 +   (into (array-map) ; start with the default values, make sure the order is right
  1.1619 +         (reverse (for [[name [default]] (:params def)] [name [default offset]])))
  1.1620 +   (reduce #(apply assoc %1 %2) {} (filter #(first (nth % 1)) (zipmap (keys (:params def)) params))) ; add the specified parameters, filtering out nils
  1.1621 +   flags))                                ; and finally add the flags
  1.1622 +
  1.1623 +(defn- compile-directive [s offset]
  1.1624 +  (let [[raw-params [rest offset]] (extract-params s offset)
  1.1625 +        [_ [rest offset flags]] (extract-flags rest offset)
  1.1626 +        directive (first rest)
  1.1627 +        def (get directive-table (Character/toUpperCase ^Character directive))
  1.1628 +        params (if def (map-params def (map translate-param raw-params) flags offset))]
  1.1629 +    (if (not directive)
  1.1630 +      (format-error "Format string ended in the middle of a directive" offset))
  1.1631 +    (if (not def)
  1.1632 +      (format-error (str "Directive \"" directive "\" is undefined") offset))
  1.1633 +    [(struct compiled-directive ((:generator-fn def) params offset) def params offset)
  1.1634 +     (let [remainder (subs rest 1) 
  1.1635 +           offset (inc offset)
  1.1636 +           trim? (and (= \newline (:directive def))
  1.1637 +                      (not (:colon params)))
  1.1638 +           trim-count (if trim? (prefix-count remainder [\space \tab]) 0)
  1.1639 +           remainder (subs remainder trim-count)
  1.1640 +           offset (+ offset trim-count)]
  1.1641 +       [remainder offset])]))
  1.1642 +    
  1.1643 +(defn- compile-raw-string [s offset]
  1.1644 +  (struct compiled-directive (fn [_ a _] (print s) a) nil { :string s } offset))
  1.1645 +
  1.1646 +(defn- right-bracket [this] (:right (:bracket-info (:def this))))
  1.1647 +(defn- separator? [this] (:separator (:bracket-info (:def this))))
  1.1648 +(defn- else-separator? [this] 
  1.1649 +  (and (:separator (:bracket-info (:def this)))
  1.1650 +       (:colon (:params this))))
  1.1651 +  
  1.1652 +
  1.1653 +(declare collect-clauses)
  1.1654 +
  1.1655 +(defn- process-bracket [this remainder]
  1.1656 +  (let [[subex remainder] (collect-clauses (:bracket-info (:def this))
  1.1657 +                                           (:offset this) remainder)]
  1.1658 +    [(struct compiled-directive 
  1.1659 +             (:func this) (:def this) 
  1.1660 +             (merge (:params this) (tuple-map subex (:offset this)))
  1.1661 +             (:offset this))
  1.1662 +     remainder]))
  1.1663 +
  1.1664 +(defn- process-clause [bracket-info offset remainder]
  1.1665 +  (consume 
  1.1666 +   (fn [remainder]
  1.1667 +     (if (empty? remainder)
  1.1668 +       (format-error "No closing bracket found." offset)
  1.1669 +       (let [this (first remainder)
  1.1670 +             remainder (next remainder)]
  1.1671 +         (cond
  1.1672 +          (right-bracket this)
  1.1673 +          (process-bracket this remainder)
  1.1674 +
  1.1675 +          (= (:right bracket-info) (:directive (:def this)))
  1.1676 +          [ nil [:right-bracket (:params this) nil remainder]]
  1.1677 +
  1.1678 +          (else-separator? this)
  1.1679 +          [nil [:else nil (:params this) remainder]]
  1.1680 +
  1.1681 +          (separator? this)
  1.1682 +          [nil [:separator nil nil remainder]] ;; TODO: check to make sure that there are no params on ~;
  1.1683 +
  1.1684 +          true
  1.1685 +          [this remainder]))))
  1.1686 +   remainder))
  1.1687 +
  1.1688 +(defn- collect-clauses [bracket-info offset remainder]
  1.1689 +  (second
  1.1690 +   (consume
  1.1691 +    (fn [[clause-map saw-else remainder]]
  1.1692 +      (let [[clause [type right-params else-params remainder]] 
  1.1693 +            (process-clause bracket-info offset remainder)]
  1.1694 +        (cond
  1.1695 +         (= type :right-bracket)
  1.1696 +         [nil [(merge-with concat clause-map 
  1.1697 +                           {(if saw-else :else :clauses) [clause] 
  1.1698 +                            :right-params right-params})
  1.1699 +               remainder]]
  1.1700 +
  1.1701 +         (= type :else)
  1.1702 +         (cond
  1.1703 +          (:else clause-map)
  1.1704 +          (format-error "Two else clauses (\"~:;\") inside bracket construction." offset)
  1.1705 +         
  1.1706 +          (not (:else bracket-info))
  1.1707 +          (format-error "An else clause (\"~:;\") is in a bracket type that doesn't support it." 
  1.1708 +                        offset)
  1.1709 +
  1.1710 +          (and (= :first (:else bracket-info)) (seq (:clauses clause-map)))
  1.1711 +          (format-error
  1.1712 +           "The else clause (\"~:;\") is only allowed in the first position for this directive." 
  1.1713 +           offset)
  1.1714 +         
  1.1715 +          true         ; if the ~:; is in the last position, the else clause
  1.1716 +                                        ; is next, this was a regular clause
  1.1717 +          (if (= :first (:else bracket-info))
  1.1718 +            [true [(merge-with concat clause-map { :else [clause] :else-params else-params})
  1.1719 +                   false remainder]]
  1.1720 +            [true [(merge-with concat clause-map { :clauses [clause] })
  1.1721 +                   true remainder]]))
  1.1722 +
  1.1723 +         (= type :separator)
  1.1724 +         (cond
  1.1725 +          saw-else
  1.1726 +          (format-error "A plain clause (with \"~;\") follows an else clause (\"~:;\") inside bracket construction." offset)
  1.1727 +         
  1.1728 +          (not (:allows-separator bracket-info))
  1.1729 +          (format-error "A separator (\"~;\") is in a bracket type that doesn't support it." 
  1.1730 +                        offset)
  1.1731 +         
  1.1732 +          true
  1.1733 +          [true [(merge-with concat clause-map { :clauses [clause] })
  1.1734 +                 false remainder]]))))
  1.1735 +    [{ :clauses [] } false remainder])))
  1.1736 +
  1.1737 +(defn- process-nesting
  1.1738 +  "Take a linearly compiled format and process the bracket directives to give it 
  1.1739 +   the appropriate tree structure"
  1.1740 +  [format]
  1.1741 +  (first
  1.1742 +   (consume 
  1.1743 +    (fn [remainder]
  1.1744 +      (let [this (first remainder)
  1.1745 +            remainder (next remainder)
  1.1746 +            bracket (:bracket-info (:def this))]
  1.1747 +        (if (:right bracket)
  1.1748 +          (process-bracket this remainder)
  1.1749 +          [this remainder])))
  1.1750 +    format)))
  1.1751 +
  1.1752 +(defn compile-format 
  1.1753 +  "Compiles format-str into a compiled format which can be used as an argument
  1.1754 +to cl-format just like a plain format string. Use this function for improved 
  1.1755 +performance when you're using the same format string repeatedly"
  1.1756 +  [ format-str ]
  1.1757 +;  (prlabel compiling format-str)
  1.1758 +  (binding [*format-str* format-str]
  1.1759 +    (process-nesting
  1.1760 +     (first 
  1.1761 +      (consume 
  1.1762 +       (fn [[^String s offset]]
  1.1763 +         (if (empty? s)
  1.1764 +           [nil s]
  1.1765 +           (let [tilde (.indexOf s (int \~))]
  1.1766 +             (cond
  1.1767 +              (neg? tilde) [(compile-raw-string s offset) ["" (+ offset (.length s))]]
  1.1768 +              (zero? tilde)  (compile-directive (subs s 1) (inc offset))
  1.1769 +              true 
  1.1770 +              [(compile-raw-string (subs s 0 tilde) offset) [(subs s tilde) (+ tilde offset)]]))))
  1.1771 +       [format-str 0])))))
  1.1772 +
  1.1773 +(defn- needs-pretty 
  1.1774 +  "determine whether a given compiled format has any directives that depend on the
  1.1775 +column number or pretty printing"
  1.1776 +  [format]
  1.1777 +  (loop [format format]
  1.1778 +    (if (empty? format)
  1.1779 +      false
  1.1780 +      (if (or (:pretty (:flags (:def (first format))))
  1.1781 +              (some needs-pretty (first (:clauses (:params (first format)))))
  1.1782 +              (some needs-pretty (first (:else (:params (first format))))))
  1.1783 +        true
  1.1784 +        (recur (next format))))))
  1.1785 +
  1.1786 +(defn execute-format 
  1.1787 +  "Executes the format with the arguments. This should never be used directly, but is public
  1.1788 +because the formatter macro uses it."
  1.1789 +  {:skip-wiki true}
  1.1790 +  ([stream format args]
  1.1791 +     (let [^java.io.Writer real-stream (cond 
  1.1792 +                                         (not stream) (java.io.StringWriter.)
  1.1793 +                                         (true? stream) *out*
  1.1794 +                                         :else stream)
  1.1795 +           ^java.io.Writer wrapped-stream (if (and (needs-pretty format) 
  1.1796 +                                                    (not (pretty-writer? real-stream)))
  1.1797 +                                             (get-pretty-writer real-stream)
  1.1798 +                                             real-stream)]
  1.1799 +       (binding [*out* wrapped-stream]
  1.1800 +         (try
  1.1801 +          (execute-format format args)
  1.1802 +          (finally
  1.1803 +           (if-not (identical? real-stream wrapped-stream)
  1.1804 +             (.flush wrapped-stream))))
  1.1805 +         (if (not stream) (.toString real-stream)))))
  1.1806 +  ([format args]
  1.1807 +     (map-passing-context 
  1.1808 +      (fn [element context]
  1.1809 +        (if (abort? context)
  1.1810 +          [nil context]
  1.1811 +          (let [[params args] (realize-parameter-list 
  1.1812 +                               (:params element) context)
  1.1813 +                [params offsets] (unzip-map params)
  1.1814 +                params (assoc params :base-args args)]
  1.1815 +            [nil (apply (:func element) [params args offsets])])))
  1.1816 +      args
  1.1817 +      format)))
  1.1818 +
  1.1819 +
  1.1820 +(defmacro formatter
  1.1821 +  "Makes a function which can directly run format-in. The function is
  1.1822 +fn [stream & args] ... and returns nil unless the stream is nil (meaning 
  1.1823 +output to a string) in which case it returns the resulting string.
  1.1824 +
  1.1825 +format-in can be either a control string or a previously compiled format."
  1.1826 +  [format-in]
  1.1827 +  (let [cf (gensym "compiled-format")]
  1.1828 +    `(let [format-in# ~format-in]
  1.1829 +       (do (defonce ~cf (if (string? format-in#) (compile-format format-in#) format-in#))
  1.1830 +           (fn [stream# & args#]
  1.1831 +             (let [navigator# (init-navigator args#)]
  1.1832 +               (execute-format stream# ~cf navigator#)))))))
  1.1833 +
  1.1834 +(defmacro formatter-out
  1.1835 +  "Makes a function which can directly run format-in. The function is
  1.1836 +fn [& args] ... and returns nil. This version of the formatter macro is
  1.1837 +designed to be used with *out* set to an appropriate Writer. In particular,
  1.1838 +this is meant to be used as part of a pretty printer dispatch method.
  1.1839 +
  1.1840 +format-in can be either a control string or a previously compiled format."
  1.1841 +  [format-in]
  1.1842 +  (let [cf (gensym "compiled-format")]
  1.1843 +    `(let [format-in# ~format-in]
  1.1844 +       (do (defonce ~cf (if (string? format-in#) (compile-format format-in#) format-in#))
  1.1845 +           (fn [& args#]
  1.1846 +             (let [navigator# (init-navigator args#)]
  1.1847 +               (execute-format ~cf navigator#)))))))