On Tue, Nov 18, 2008 at 11:29 AM, [EMAIL PROTECTED]
<[EMAIL PROTECTED]> wrote:
>
> I looked at Wadler's "A Prettier Printer" paper (http://
> homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf) and did a
> rote translation of it into Clojure. Then I wrote printing routines
> for sequences and maps -- very barebones. They work OK:

I started working on a pretty-printer as well.  I wouldn't show it to
anybody yet, but I also don't want any unnecessary duplicated effort.
So I'll go ahead and show what I've got so far, and then we can decide
how to proceed. I have no interest in pursuing my solution if it's not
a good approach, or if anyone else would rather pursue it.

That is, I want to use a pretty printer, not necessarily write one. :-)

> user> (def something '(a b c d (e f g h i) j k (l m n) (o p q r s t u
> v) w x y (z)))
> #'user/something
> user> (pp something 20)

I don't have nice API yet, so for now I have to use binding:

user=> (binding [*max-width* 20] (pprint something))
(a
  b
  c
  d
  (e f g h i)
  j
  k
  (l m n)
  (o p q r s t u v)
  w
  x
  y
  (z))

> user> (def things {:one "another" :two {:map "inside" :a "map"} :three
> [1 2 3 4 5] :four "still making things up" :five :done})
> #'user/things
> user> (pp things)

user=> (pprint things)
{:five
   :done
 :three
   [1 2 3 4 5]
 :two
   {:a "map", :map "inside"}
 :four
   "still making things up"
 :one
   "another"}

I still need to add commas and collapse key/vals onto a single line
when possible.

> user> (pp (range 1000))
>  [Thrown class java.lang.StackOverflowError]

user=> (pprint (range 5000))
(0
  1
  2
  3
  4
[...manually snipped here...]
  4996
  4997
  4998
  4999)

Not a problem, though I don't have *line-limit* yet.

I allow \newlines to print, which helps with long multi-line strings:

(defn
  gen-and-load-class
  "Generates and immediately loads the bytecode for the specified
  class. Note that a class generated this way can be loaded only once
  - the JVM supports only one class with a given name per
  classloader. Subsequent to generation you can import it into any
  desired namespaces just like any other class. See gen-class for a
  description of the options."
  [name & options]
  (let
    [{:keys [name bytecode]} (apply gen-class (str name) options)]
    (.. clojure.lang.RT ROOT_CLASSLOADER (defineClass (str name) bytecode))))

I'd like to add a *detect-code* option that looks for "well-known"
symbols that are used in code (defn, let, etc.) and can format the
required args differently than the "rest" args. This would allow some
code forms to look more natural.

Anyway, it's definitely a work in progress.  What I've got so far is
attached.  All thoughts and comments are welcome.
--Chouser

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

;   Copyright (c) Chris Houser, Nov 2008. All rights reserved.
;   The use and distribution terms for this software are covered by the
;   Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
;   which can be found in the file CPL.TXT at the root of this distribution.
;   By using this software in any fashion, you are agreeing to be bound by
;   the terms of this license.
;   You must not remove this notice, or any other, from this software.

; Print nicely-indented Clojure data structures, including code forms

;(ns clojure.contrib.pprint)

(def *max-width* 80) ; not a hard limit
(def *vertical-start-column* 40)
(def *spaces-per-indent* 2)
;(def *conserve-lines* false)
;(def *detect-code* true)

(defn- indent [depth]
  (print (apply str \newline (replicate depth \space))))

(defmulti pprint-method (fn [obj depth] (class obj)))

(defn- pprint-seq-vert [begin end o d]
  (print begin)
  (pprint-method (first o) (inc d))
  (let [d (+ d *spaces-per-indent*)]
    (loop [o (rest o)]
      (when o
        (indent d)
        (pprint-method (first o) d)
        (recur (rest o))))
    (print end)))

(defn- pprint-seq [begin end o d]
  (if (< d *vertical-start-column*)
    (let [one-line (pr-str o)] ; XXX rebind limits here
      (if (< (+ d (.length one-line)) *max-width*)
        (print one-line)
        (pprint-seq-vert begin end o d)))
    (pprint-seq-vert begin end o d)))

(defmethod pprint-method :default [o d]
  (pr o))

(defmethod pprint-method clojure.lang.ISeq [o d]
  (pprint-seq "(" ")" o d))

(defmethod pprint-method clojure.lang.IPersistentVector [o d]
  (pprint-seq "[" "]" o d))

(defmethod pprint-method clojure.lang.IPersistentMap [o d]
  (binding [*spaces-per-indent* 1]
    (pprint-seq "{" "}" o d)))

(defmethod pprint-method clojure.lang.AMapEntry [o d]
  (pprint-method (key o) d)
  (let [d2 (+ 1 d *spaces-per-indent*)]
    (indent d2)
    (pprint-method (val o) d2)))

(def multi-line-escape (assoc char-escape-string \newline "\n"))

(defmethod pprint-method String [s d]
  (print \")
  (dotimes [n (count s)]
    (let [c (.charAt s n)]
      (print (or (multi-line-escape c) c))))
  (print \"))

(defn pprint [obj]
  (pprint-method obj 0)
  (print \newline))

Reply via email to