|
1 |
| -;;; pprint.clj -- Pretty printer and Common Lisp compatible format function (cl-format) for Clojure |
| 1 | +(ns cljs.pprint |
| 2 | + (:refer-clojure :exclude [deftype]) |
| 3 | + (:require [clojure.walk :as walk])) |
2 | 4 |
|
3 |
| -; Copyright (c) Rich Hickey. All rights reserved. |
4 |
| -; The use and distribution terms for this software are covered by the |
5 |
| -; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) |
6 |
| -; which can be found in the file epl-v10.html at the root of this distribution. |
7 |
| -; By using this software in any fashion, you are agreeing to be bound by |
8 |
| -; the terms of this license. |
9 |
| -; You must not remove this notice, or any other, from this software. |
10 | 5 |
|
11 |
| -;; Author: Tom Faulhaber |
12 |
| -;; April 3, 2009 |
| 6 | +;; required the following changes: |
| 7 | +;; replace .ppflush with -ppflush to switch from Interface to Protocol |
13 | 8 |
|
14 |
| -(ns cljs.pprint) |
| 9 | +(defmacro with-pretty-writer [base-writer & body] |
| 10 | + `(let [base-writer# ~base-writer |
| 11 | + new-writer# (not (pretty-writer? base-writer#))] |
| 12 | + (cljs.core/binding [~'*out* (if new-writer# |
| 13 | + (make-pretty-writer base-writer# *print-right-margin* *print-miser-width*) |
| 14 | + base-writer#)] |
| 15 | + ~@body |
| 16 | + (-ppflush ~'*out*)))) |
| 17 | + |
| 18 | + |
| 19 | +(defmacro getf |
| 20 | + "Get the value of the field a named by the argument (which should be a keyword)." |
| 21 | + [sym] |
| 22 | + `(~sym @@~'this)) |
| 23 | + |
| 24 | +;; change alter to swap! |
| 25 | + |
| 26 | +(defmacro setf |
| 27 | + "Set the value of the field SYM to NEW-VAL" |
| 28 | + [sym new-val] |
| 29 | + `(swap! @~'this assoc ~sym ~new-val)) |
| 30 | + |
| 31 | +(defmacro deftype |
| 32 | + [type-name & fields] |
| 33 | + (let [name-str (name type-name) |
| 34 | + fields (map (comp symbol name) fields)] |
| 35 | + `(do |
| 36 | + (defrecord ~type-name [~'type-tag ~@fields]) |
| 37 | + (defn- ~(symbol (str "make-" name-str)) |
| 38 | + ~(vec fields) |
| 39 | + (~(symbol (str type-name ".")) ~(keyword name-str) ~@fields)) |
| 40 | + (defn- ~(symbol (str name-str "?")) [x#] (= (:type-tag x#) ~(keyword name-str)))))) |
| 41 | + |
| 42 | +(defn- parse-lb-options [opts body] |
| 43 | + (loop [body body |
| 44 | + acc []] |
| 45 | + (if (opts (first body)) |
| 46 | + (recur (drop 2 body) (concat acc (take 2 body))) |
| 47 | + [(apply hash-map acc) body]))) |
| 48 | + |
| 49 | +(defmacro pprint-logical-block |
| 50 | + "Execute the body as a pretty printing logical block with output to *out* which |
| 51 | + must be a pretty printing writer. When used from pprint or cl-format, this can be |
| 52 | + assumed. |
| 53 | +
|
| 54 | + This function is intended for use when writing custom dispatch functions. |
| 55 | +
|
| 56 | + Before the body, the caller can optionally specify options: :prefix, :per-line-prefix |
| 57 | + and :suffix." |
| 58 | + [& args] |
| 59 | + (let [[options body] (parse-lb-options #{:prefix :per-line-prefix :suffix} args)] |
| 60 | + `(do (if (cljs.pprint/level-exceeded) |
| 61 | + (~'-write cljs.pprint/*out* "#") |
| 62 | + (do |
| 63 | + (cljs.core/binding [cljs.pprint/*current-level* (inc cljs.pprint/*current-level*) |
| 64 | + cljs.pprint/*current-length* 0] |
| 65 | + (cljs.pprint/start-block cljs.pprint/*out* |
| 66 | + ~(:prefix options) |
| 67 | + ~(:per-line-prefix options) |
| 68 | + ~(:suffix options)) |
| 69 | + ~@body |
| 70 | + (cljs.pprint/end-block cljs.pprint/*out*)))) |
| 71 | + nil))) |
| 72 | + |
| 73 | +(defn- pll-mod-body [var-sym body] |
| 74 | + (letfn [(inner [form] |
| 75 | + (if (seq? form) |
| 76 | + (let [form (macroexpand form)] |
| 77 | + (condp = (first form) |
| 78 | + 'loop* form |
| 79 | + 'recur (concat `(recur (inc ~var-sym)) (rest form)) |
| 80 | + (walk/walk inner identity form))) |
| 81 | + form))] |
| 82 | + (walk/walk inner identity body))) |
| 83 | + |
| 84 | +(defmacro print-length-loop |
| 85 | + "A version of loop that iterates at most *print-length* times. This is designed |
| 86 | + for use in pretty-printer dispatch functions." |
| 87 | + [bindings & body] |
| 88 | + (let [count-var (gensym "length-count") |
| 89 | + mod-body (pll-mod-body count-var body)] |
| 90 | + `(loop ~(apply vector count-var 0 bindings) |
| 91 | + (if (or (not cljs.core/*print-length*) (< ~count-var cljs.core/*print-length*)) |
| 92 | + (do ~@mod-body) |
| 93 | + (~'-write cljs.pprint/*out* "..."))))) |
| 94 | + |
| 95 | +(defn- process-directive-table-element [[char params flags bracket-info & generator-fn]] |
| 96 | + [char, |
| 97 | + {:directive char, |
| 98 | + :params `(array-map ~@params), |
| 99 | + :flags flags, |
| 100 | + :bracket-info bracket-info, |
| 101 | + :generator-fn (concat '(fn [params offset]) generator-fn)}]) |
| 102 | + |
| 103 | +(defmacro ^{:private true} |
| 104 | + defdirectives |
| 105 | + [& directives] |
| 106 | + `(def ^{:private true} |
| 107 | + ~'directive-table (hash-map ~@(mapcat process-directive-table-element directives)))) |
| 108 | + |
| 109 | +(defmacro formatter |
| 110 | + "Makes a function which can directly run format-in. The function is |
| 111 | +fn [stream & args] ... and returns nil unless the stream is nil (meaning |
| 112 | +output to a string) in which case it returns the resulting string. |
| 113 | +
|
| 114 | +format-in can be either a control string or a previously compiled format." |
| 115 | + [format-in] |
| 116 | + `(let [format-in# ~format-in |
| 117 | + my-c-c# cljs.pprint/cached-compile |
| 118 | + my-e-f# cljs.pprint/execute-format |
| 119 | + my-i-n# cljs.pprint/init-navigator |
| 120 | + cf# (if (string? format-in#) (my-c-c# format-in#) format-in#)] |
| 121 | + (fn [stream# & args#] |
| 122 | + (let [navigator# (my-i-n# args#)] |
| 123 | + (my-e-f# stream# cf# navigator#))))) |
| 124 | + |
| 125 | +(defmacro formatter-out |
| 126 | + "Makes a function which can directly run format-in. The function is |
| 127 | +fn [& args] ... and returns nil. This version of the formatter macro is |
| 128 | +designed to be used with *out* set to an appropriate Writer. In particular, |
| 129 | +this is meant to be used as part of a pretty printer dispatch method. |
| 130 | +
|
| 131 | +format-in can be either a control string or a previously compiled format." |
| 132 | + [format-in] |
| 133 | + `(let [format-in# ~format-in |
| 134 | + cf# (if (string? format-in#) (cljs.pprint/cached-compile format-in#) format-in#)] |
| 135 | + (fn [& args#] |
| 136 | + (let [navigator# (cljs.pprint/init-navigator args#)] |
| 137 | + (cljs.pprint/execute-format cf# navigator#))))) |
| 138 | + |
| 139 | +(defmacro with-pprint-dispatch |
| 140 | + "Execute body with the pretty print dispatch function bound to function." |
| 141 | + [function & body] |
| 142 | + `(cljs.core/binding [cljs.pprint/*print-pprint-dispatch* ~function] |
| 143 | + ~@body)) |
15 | 144 |
|
16 |
| -(defmacro ^{:private true} prlabel [prefix arg & more-args] |
17 |
| - "Print args to *err* in name = value format" |
18 |
| - `(prerr ~@(cons (list 'quote prefix) (mapcat #(list (list 'quote %) "=" %) |
19 |
| - (cons arg (seq more-args)))))) |
|
0 commit comments