summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--etc/todo/scanners/clojure-libs.in.clj6820
1 files changed, 6820 insertions, 0 deletions
diff --git a/etc/todo/scanners/clojure-libs.in.clj b/etc/todo/scanners/clojure-libs.in.clj
new file mode 100644
index 0000000..f8a0044
--- /dev/null
+++ b/etc/todo/scanners/clojure-libs.in.clj
@@ -0,0 +1,6820 @@
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html 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.
+
+(ns clojure.core)
+
+(def unquote)
+(def unquote-splicing)
+
+(def
+ #^{:arglists '([& items])
+ :doc "Creates a new list containing the items."}
+ list (. clojure.lang.PersistentList creator))
+
+(def
+ #^{:arglists '([x seq])
+ :doc "Returns a new seq where x is the first element and seq is
+ the rest."}
+
+ cons (fn* cons [x seq] (. clojure.lang.RT (cons x seq))))
+
+;during bootstrap we don't have destructuring let, loop or fn, will redefine later
+(def
+ #^{:macro true}
+ let (fn* let [& decl] (cons 'let* decl)))
+
+(def
+ #^{:macro true}
+ loop (fn* loop [& decl] (cons 'loop* decl)))
+
+(def
+ #^{:macro true}
+ fn (fn* fn [& decl] (cons 'fn* decl)))
+
+(def
+ #^{:arglists '([coll])
+ :doc "Returns the first item in the collection. Calls seq on its
+ argument. If coll is nil, returns nil."}
+ first (fn first [coll] (. clojure.lang.RT (first coll))))
+
+(def
+ #^{:arglists '([coll])
+ :tag clojure.lang.ISeq
+ :doc "Returns a seq of the items after the first. Calls seq on its
+ argument. If there are no more items, returns nil."}
+ next (fn next [x] (. clojure.lang.RT (next x))))
+
+(def
+ #^{:arglists '([coll])
+ :tag clojure.lang.ISeq
+ :doc "Returns a possibly empty seq of the items after the first. Calls seq on its
+ argument."}
+ rest (fn rest [x] (. clojure.lang.RT (more x))))
+
+(def
+ #^{:arglists '([coll x] [coll x & xs])
+ :doc "conj[oin]. Returns a new collection with the xs
+ 'added'. (conj nil item) returns (item). The 'addition' may
+ happen at different 'places' depending on the concrete type."}
+ conj (fn conj
+ ([coll x] (. clojure.lang.RT (conj coll x)))
+ ([coll x & xs]
+ (if xs
+ (recur (conj coll x) (first xs) (next xs))
+ (conj coll x)))))
+
+(def
+ #^{:doc "Same as (first (next x))"
+ :arglists '([x])}
+ second (fn second [x] (first (next x))))
+
+(def
+ #^{:doc "Same as (first (first x))"
+ :arglists '([x])}
+ ffirst (fn ffirst [x] (first (first x))))
+
+(def
+ #^{:doc "Same as (next (first x))"
+ :arglists '([x])}
+ nfirst (fn nfirst [x] (next (first x))))
+
+(def
+ #^{:doc "Same as (first (next x))"
+ :arglists '([x])}
+ fnext (fn fnext [x] (first (next x))))
+
+(def
+ #^{:doc "Same as (next (next x))"
+ :arglists '([x])}
+ nnext (fn nnext [x] (next (next x))))
+
+(def
+ #^{:arglists '([coll])
+ :doc "Returns a seq on the collection. If the collection is
+ empty, returns nil. (seq nil) returns nil. seq also works on
+ Strings, native Java arrays (of reference types) and any objects
+ that implement Iterable."
+ :tag clojure.lang.ISeq}
+ seq (fn seq [coll] (. clojure.lang.RT (seq coll))))
+
+(def
+ #^{:arglists '([#^Class c x])
+ :doc "Evaluates x and tests if it is an instance of the class
+ c. Returns true or false"}
+ instance? (fn instance? [#^Class c x] (. c (isInstance x))))
+
+(def
+ #^{:arglists '([x])
+ :doc "Return true if x implements ISeq"}
+ seq? (fn seq? [x] (instance? clojure.lang.ISeq x)))
+
+(def
+ #^{:arglists '([x])
+ :doc "Return true if x is a String"}
+ string? (fn string? [x] (instance? String x)))
+
+(def
+ #^{:arglists '([x])
+ :doc "Return true if x implements IPersistentMap"}
+ map? (fn map? [x] (instance? clojure.lang.IPersistentMap x)))
+
+(def
+ #^{:arglists '([x])
+ :doc "Return true if x implements IPersistentVector "}
+ vector? (fn vector? [x] (instance? clojure.lang.IPersistentVector x)))
+
+(def
+ #^{:private true}
+ sigs
+ (fn [fdecl]
+ (if (seq? (first fdecl))
+ (loop [ret [] fdecl fdecl]
+ (if fdecl
+ (recur (conj ret (first (first fdecl))) (next fdecl))
+ (seq ret)))
+ (list (first fdecl)))))
+
+(def
+ #^{:arglists '([map key val] [map key val & kvs])
+ :doc "assoc[iate]. When applied to a map, returns a new map of the
+ same (hashed/sorted) type, that contains the mapping of key(s) to
+ val(s). When applied to a vector, returns a new vector that
+ contains val at index. Note - index must be <= (count vector)."}
+ assoc
+ (fn assoc
+ ([map key val] (. clojure.lang.RT (assoc map key val)))
+ ([map key val & kvs]
+ (let [ret (assoc map key val)]
+ (if kvs
+ (recur ret (first kvs) (second kvs) (nnext kvs))
+ ret)))))
+
+;;;;;;;;;;;;;;;;; metadata ;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(def
+ #^{:arglists '([obj])
+ :doc "Returns the metadata of obj, returns nil if there is no metadata."}
+ meta (fn meta [x]
+ (if (instance? clojure.lang.IMeta x)
+ (. #^clojure.lang.IMeta x (meta)))))
+
+(def
+ #^{:arglists '([#^clojure.lang.IObj obj m])
+ :doc "Returns an object of the same type and value as obj, with
+ map m as its metadata."}
+ with-meta (fn with-meta [#^clojure.lang.IObj x m]
+ (. x (withMeta m))))
+
+(def
+ #^{:arglists '([coll])
+ :doc "Return the last item in coll, in linear time"}
+ last (fn last [s]
+ (if (next s)
+ (recur (next s))
+ (first s))))
+
+(def
+ #^{:arglists '([coll])
+ :doc "Return a seq of all but the last item in coll, in linear time"}
+ butlast (fn butlast [s]
+ (loop [ret [] s s]
+ (if (next s)
+ (recur (conj ret (first s)) (next s))
+ (seq ret)))))
+
+(def
+
+ #^{:doc "Same as (def name (fn [params* ] exprs*)) or (def
+ name (fn ([params* ] exprs*)+)) with any doc-string or attrs added
+ to the var metadata"
+ :arglists '([name doc-string? attr-map? [params*] body]
+ [name doc-string? attr-map? ([params*] body)+ attr-map?])}
+ defn (fn defn [name & fdecl]
+ (let [m (if (string? (first fdecl))
+ {:doc (first fdecl)}
+ {})
+ fdecl (if (string? (first fdecl))
+ (next fdecl)
+ fdecl)
+ m (if (map? (first fdecl))
+ (conj m (first fdecl))
+ m)
+ fdecl (if (map? (first fdecl))
+ (next fdecl)
+ fdecl)
+ fdecl (if (vector? (first fdecl))
+ (list fdecl)
+ fdecl)
+ m (if (map? (last fdecl))
+ (conj m (last fdecl))
+ m)
+ fdecl (if (map? (last fdecl))
+ (butlast fdecl)
+ fdecl)
+ m (conj {:arglists (list 'quote (sigs fdecl))} m)]
+ (list 'def (with-meta name (conj (if (meta name) (meta name) {}) m))
+ (cons `fn fdecl)))))
+
+(. (var defn) (setMacro))
+
+(defn cast
+ "Throws a ClassCastException if x is not a c, else returns x."
+ [#^Class c x]
+ (. c (cast x)))
+
+(defn to-array
+ "Returns an array of Objects containing the contents of coll, which
+ can be any Collection. Maps to java.util.Collection.toArray()."
+ {:tag "[Ljava.lang.Object;"}
+ [coll] (. clojure.lang.RT (toArray coll)))
+
+(defn vector
+ "Creates a new vector containing the args."
+ ([] [])
+ ([& args]
+ (. clojure.lang.LazilyPersistentVector (create args))))
+
+(defn vec
+ "Creates a new vector containing the contents of coll."
+ ([coll]
+ (. clojure.lang.LazilyPersistentVector (createOwning (to-array coll)))))
+
+(defn hash-map
+ "keyval => key val
+ Returns a new hash map with supplied mappings."
+ ([] {})
+ ([& keyvals]
+ (. clojure.lang.PersistentHashMap (create keyvals))))
+
+(defn hash-set
+ "Returns a new hash set with supplied keys."
+ ([] #{})
+ ([& keys]
+ (. clojure.lang.PersistentHashSet (create keys))))
+
+(defn sorted-map
+ "keyval => key val
+ Returns a new sorted map with supplied mappings."
+ ([& keyvals]
+ (. clojure.lang.PersistentTreeMap (create keyvals))))
+
+(defn sorted-set
+ "Returns a new sorted set with supplied keys."
+ ([& keys]
+ (. clojure.lang.PersistentTreeSet (create keys))))
+
+(defn sorted-map-by
+ "keyval => key val
+ Returns a new sorted map with supplied mappings, using the supplied comparator."
+ ([comparator & keyvals]
+ (. clojure.lang.PersistentTreeMap (create comparator keyvals))))
+
+;;;;;;;;;;;;;;;;;;;;
+(def
+
+ #^{:doc "Like defn, but the resulting function name is declared as a
+ macro and will be used as a macro by the compiler when it is
+ called."
+ :arglists '([name doc-string? attr-map? [params*] body]
+ [name doc-string? attr-map? ([params*] body)+ attr-map?])}
+ defmacro (fn [name & args]
+ (list 'do
+ (cons `defn (cons name args))
+ (list '. (list 'var name) '(setMacro))
+ (list 'var name))))
+
+(. (var defmacro) (setMacro))
+
+(defmacro when
+ "Evaluates test. If logical true, evaluates body in an implicit do."
+ [test & body]
+ (list 'if test (cons 'do body)))
+
+(defmacro when-not
+ "Evaluates test. If logical false, evaluates body in an implicit do."
+ [test & body]
+ (list 'if test nil (cons 'do body)))
+
+(defn nil?
+ "Returns true if x is nil, false otherwise."
+ {:tag Boolean}
+ [x] (identical? x nil))
+
+(defn false?
+ "Returns true if x is the value false, false otherwise."
+ {:tag Boolean}
+ [x] (identical? x false))
+
+(defn true?
+ "Returns true if x is the value true, false otherwise."
+ {:tag Boolean}
+ [x] (identical? x true))
+
+(defn not
+ "Returns true if x is logical false, false otherwise."
+ {:tag Boolean}
+ [x] (if x false true))
+
+(defn str
+ "With no args, returns the empty string. With one arg x, returns
+ x.toString(). (str nil) returns the empty string. With more than
+ one arg, returns the concatenation of the str values of the args."
+ {:tag String}
+ ([] "")
+ ([#^Object x]
+ (if (nil? x) "" (. x (toString))))
+ ([x & ys]
+ ((fn [#^StringBuilder sb more]
+ (if more
+ (recur (. sb (append (str (first more)))) (next more))
+ (str sb)))
+ (new StringBuilder #^String (str x)) ys)))
+
+
+(defn symbol?
+ "Return true if x is a Symbol"
+ [x] (instance? clojure.lang.Symbol x))
+
+(defn keyword?
+ "Return true if x is a Keyword"
+ [x] (instance? clojure.lang.Keyword x))
+
+(defn symbol
+ "Returns a Symbol with the given namespace and name."
+ ([name] (if (symbol? name) name (. clojure.lang.Symbol (intern name))))
+ ([ns name] (. clojure.lang.Symbol (intern ns name))))
+
+(defn keyword
+ "Returns a Keyword with the given namespace and name. Do not use :
+ in the keyword strings, it will be added automatically."
+ ([name] (if (keyword? name) name (. clojure.lang.Keyword (intern nil name))))
+ ([ns name] (. clojure.lang.Keyword (intern ns name))))
+
+(defn gensym
+ "Returns a new symbol with a unique name. If a prefix string is
+ supplied, the name is prefix# where # is some unique number. If
+ prefix is not supplied, the prefix is 'G__'."
+ ([] (gensym "G__"))
+ ([prefix-string] (. clojure.lang.Symbol (intern (str prefix-string (str (. clojure.lang.RT (nextID))))))))
+
+(defmacro cond
+ "Takes a set of test/expr pairs. It evaluates each test one at a
+ time. If a test returns logical true, cond evaluates and returns
+ the value of the corresponding expr and doesn't evaluate any of the
+ other tests or exprs. (cond) returns nil."
+ [& clauses]
+ (when clauses
+ (list 'if (first clauses)
+ (if (next clauses)
+ (second clauses)
+ (throw (IllegalArgumentException.
+ "cond requires an even number of forms")))
+ (cons 'clojure.core/cond (next (next clauses))))))
+
+(defn spread
+ {:private true}
+ [arglist]
+ (cond
+ (nil? arglist) nil
+ (nil? (next arglist)) (seq (first arglist))
+ :else (cons (first arglist) (spread (next arglist)))))
+
+(defn apply
+ "Applies fn f to the argument list formed by prepending args to argseq."
+ {:arglists '([f args* argseq])}
+ [#^clojure.lang.IFn f & args]
+ (. f (applyTo (spread args))))
+
+(defn vary-meta
+ "Returns an object of the same type and value as obj, with
+ (apply f (meta obj) args) as its metadata."
+ [obj f & args]
+ (with-meta obj (apply f (meta obj) args)))
+
+(defn list*
+ "Creates a new list containing the item prepended to more."
+ [item & more]
+ (spread (cons item more)))
+
+(defmacro lazy-seq
+ "Takes a body of expressions that returns an ISeq or nil, and yields
+ a Seqable object that will invoke the body only the first time seq
+ is called, and will cache the result and return it on all subsequent
+ seq calls."
+ [& body]
+ (list 'new 'clojure.lang.LazySeq (list* '#^{:once true} fn* [] body)))
+
+(defn concat
+ "Returns a lazy seq representing the concatenation of the elements in the supplied colls."
+ ([] (lazy-seq nil))
+ ([x] (lazy-seq x))
+ ([x y]
+ (lazy-seq
+ (let [s (seq x)]
+ (if s
+ (cons (first s) (concat (rest s) y))
+ y))))
+ ([x y & zs]
+ (let [cat (fn cat [xys zs]
+ (lazy-seq
+ (let [xys (seq xys)]
+ (if xys
+ (cons (first xys) (cat (rest xys) zs))
+ (when zs
+ (cat (first zs) (next zs)))))))]
+ (cat (concat x y) zs))))
+
+;;;;;;;;;;;;;;;;at this point all the support for syntax-quote exists;;;;;;;;;;;;;;;;;;;;;;
+
+
+(defmacro delay
+ "Takes a body of expressions and yields a Delay object that will
+ invoke the body only the first time it is forced (with force), and
+ will cache the result and return it on all subsequent force
+ calls."
+ [& body]
+ (list 'new 'clojure.lang.Delay (list* `#^{:once true} fn* [] body)))
+
+(defn delay?
+ "returns true if x is a Delay created with delay"
+ [x] (instance? clojure.lang.Delay x))
+
+(defn force
+ "If x is a Delay, returns the (possibly cached) value of its expression, else returns x"
+ [x] (. clojure.lang.Delay (force x)))
+
+(defmacro if-not
+ "Evaluates test. If logical false, evaluates and returns then expr, otherwise else expr, if supplied, else nil."
+ ([test then] `(if-not ~test ~then nil))
+ ([test then else]
+ `(if (not ~test) ~then ~else)))
+
+(defn =
+ "Equality. Returns true if x equals y, false if not. Same as
+ Java x.equals(y) except it also works for nil, and compares
+ numbers and collections in a type-independent manner. Clojure's immutable data
+ structures define equals() (and thus =) as a value, not an identity,
+ comparison."
+ {:tag Boolean
+ :inline (fn [x y] `(. clojure.lang.Util equiv ~x ~y))
+ :inline-arities #{2}}
+ ([x] true)
+ ([x y] (clojure.lang.Util/equiv x y))
+ ([x y & more]
+ (if (= x y)
+ (if (next more)
+ (recur y (first more) (next more))
+ (= y (first more)))
+ false)))
+
+(defn not=
+ "Same as (not (= obj1 obj2))"
+ {:tag Boolean}
+ ([x] false)
+ ([x y] (not (= x y)))
+ ([x y & more]
+ (not (apply = x y more))))
+
+
+
+(defn compare
+ "Comparator. Returns 0 if x equals y, -1 if x is logically 'less
+ than' y, else 1. Same as Java x.compareTo(y) except it also works
+ for nil, and compares numbers and collections in a type-independent
+ manner. x must implement Comparable"
+ {:tag Integer
+ :inline (fn [x y] `(. clojure.lang.Util compare ~x ~y))}
+ [x y] (. clojure.lang.Util (compare x y)))
+
+(defmacro and
+ "Evaluates exprs one at a time, from left to right. If a form
+ returns logical false (nil or false), and returns that value and
+ doesn't evaluate any of the other expressions, otherwise it returns
+ the value of the last expr. (and) returns true."
+ ([] true)
+ ([x] x)
+ ([x & next]
+ `(let [and# ~x]
+ (if and# (and ~@next) and#))))
+
+(defmacro or
+ "Evaluates exprs one at a time, from left to right. If a form
+ returns a logical true value, or returns that value and doesn't
+ evaluate any of the other expressions, otherwise it returns the
+ value of the last expression. (or) returns nil."
+ ([] nil)
+ ([x] x)
+ ([x & next]
+ `(let [or# ~x]
+ (if or# or# (or ~@next)))))
+
+;;;;;;;;;;;;;;;;;;; sequence fns ;;;;;;;;;;;;;;;;;;;;;;;
+(defn reduce
+ "f should be a function of 2 arguments. If val is not supplied,
+ returns the result of applying f to the first 2 items in coll, then
+ applying f to that result and the 3rd item, etc. If coll contains no
+ items, f must accept no arguments as well, and reduce returns the
+ result of calling f with no arguments. If coll has only 1 item, it
+ is returned and f is not called. If val is supplied, returns the
+ result of applying f to val and the first item in coll, then
+ applying f to that result and the 2nd item, etc. If coll contains no
+ items, returns val and f is not called."
+ ([f coll]
+ (let [s (seq coll)]
+ (if s
+ (if (instance? clojure.lang.IReduce s)
+ (. #^clojure.lang.IReduce s (reduce f))
+ (reduce f (first s) (next s)))
+ (f))))
+ ([f val coll]
+ (let [s (seq coll)]
+ (if (instance? clojure.lang.IReduce s)
+ (. #^clojure.lang.IReduce s (reduce f val))
+ ((fn [f val s]
+ (if s
+ (recur f (f val (first s)) (next s))
+ val))
+ f val s)))))
+
+(defn reverse
+ "Returns a seq of the items in coll in reverse order. Not lazy."
+ [coll]
+ (reduce conj () coll))
+
+;;math stuff
+(defn +
+ "Returns the sum of nums. (+) returns 0."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (add ~x ~y)))
+ :inline-arities #{2}}
+ ([] 0)
+ ([x] (cast Number x))
+ ([x y] (. clojure.lang.Numbers (add x y)))
+ ([x y & more]
+ (reduce + (+ x y) more)))
+
+(defn *
+ "Returns the product of nums. (*) returns 1."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (multiply ~x ~y)))
+ :inline-arities #{2}}
+ ([] 1)
+ ([x] (cast Number x))
+ ([x y] (. clojure.lang.Numbers (multiply x y)))
+ ([x y & more]
+ (reduce * (* x y) more)))
+
+(defn /
+ "If no denominators are supplied, returns 1/numerator,
+ else returns numerator divided by all of the denominators."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (divide ~x ~y)))
+ :inline-arities #{2}}
+ ([x] (/ 1 x))
+ ([x y] (. clojure.lang.Numbers (divide x y)))
+ ([x y & more]
+ (reduce / (/ x y) more)))
+
+(defn -
+ "If no ys are supplied, returns the negation of x, else subtracts
+ the ys from x and returns the result."
+ {:inline (fn [& args] `(. clojure.lang.Numbers (minus ~@args)))
+ :inline-arities #{1 2}}
+ ([x] (. clojure.lang.Numbers (minus x)))
+ ([x y] (. clojure.lang.Numbers (minus x y)))
+ ([x y & more]
+ (reduce - (- x y) more)))
+
+(defn <
+ "Returns non-nil if nums are in monotonically increasing order,
+ otherwise false."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (lt ~x ~y)))
+ :inline-arities #{2}}
+ ([x] true)
+ ([x y] (. clojure.lang.Numbers (lt x y)))
+ ([x y & more]
+ (if (< x y)
+ (if (next more)
+ (recur y (first more) (next more))
+ (< y (first more)))
+ false)))
+
+(defn <=
+ "Returns non-nil if nums are in monotonically non-decreasing order,
+ otherwise false."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (lte ~x ~y)))
+ :inline-arities #{2}}
+ ([x] true)
+ ([x y] (. clojure.lang.Numbers (lte x y)))
+ ([x y & more]
+ (if (<= x y)
+ (if (next more)
+ (recur y (first more) (next more))
+ (<= y (first more)))
+ false)))
+
+(defn >
+ "Returns non-nil if nums are in monotonically decreasing order,
+ otherwise false."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (gt ~x ~y)))
+ :inline-arities #{2}}
+ ([x] true)
+ ([x y] (. clojure.lang.Numbers (gt x y)))
+ ([x y & more]
+ (if (> x y)
+ (if (next more)
+ (recur y (first more) (next more))
+ (> y (first more)))
+ false)))
+
+(defn >=
+ "Returns non-nil if nums are in monotonically non-increasing order,
+ otherwise false."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (gte ~x ~y)))
+ :inline-arities #{2}}
+ ([x] true)
+ ([x y] (. clojure.lang.Numbers (gte x y)))
+ ([x y & more]
+ (if (>= x y)
+ (if (next more)
+ (recur y (first more) (next more))
+ (>= y (first more)))
+ false)))
+
+(defn ==
+ "Returns non-nil if nums all have the same value, otherwise false"
+ {:inline (fn [x y] `(. clojure.lang.Numbers (equiv ~x ~y)))
+ :inline-arities #{2}}
+ ([x] true)
+ ([x y] (. clojure.lang.Numbers (equiv x y)))
+ ([x y & more]
+ (if (== x y)
+ (if (next more)
+ (recur y (first more) (next more))
+ (== y (first more)))
+ false)))
+
+(defn max
+ "Returns the greatest of the nums."
+ ([x] x)
+ ([x y] (if (> x y) x y))
+ ([x y & more]
+ (reduce max (max x y) more)))
+
+(defn min
+ "Returns the least of the nums."
+ ([x] x)
+ ([x y] (if (< x y) x y))
+ ([x y & more]
+ (reduce min (min x y) more)))
+
+(defn inc
+ "Returns a number one greater than num."
+ {:inline (fn [x] `(. clojure.lang.Numbers (inc ~x)))}
+ [x] (. clojure.lang.Numbers (inc x)))
+
+(defn dec
+ "Returns a number one less than num."
+ {:inline (fn [x] `(. clojure.lang.Numbers (dec ~x)))}
+ [x] (. clojure.lang.Numbers (dec x)))
+
+(defn unchecked-inc
+ "Returns a number one greater than x, an int or long.
+ Note - uses a primitive operator subject to overflow."
+ {:inline (fn [x] `(. clojure.lang.Numbers (unchecked_inc ~x)))}
+ [x] (. clojure.lang.Numbers (unchecked_inc x)))
+
+(defn unchecked-dec
+ "Returns a number one less than x, an int or long.
+ Note - uses a primitive operator subject to overflow."
+ {:inline (fn [x] `(. clojure.lang.Numbers (unchecked_dec ~x)))}
+ [x] (. clojure.lang.Numbers (unchecked_dec x)))
+
+(defn unchecked-negate
+ "Returns the negation of x, an int or long.
+ Note - uses a primitive operator subject to overflow."
+ {:inline (fn [x] `(. clojure.lang.Numbers (unchecked_negate ~x)))}
+ [x] (. clojure.lang.Numbers (unchecked_negate x)))
+
+(defn unchecked-add
+ "Returns the sum of x and y, both int or long.
+ Note - uses a primitive operator subject to overflow."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_add ~x ~y)))}
+ [x y] (. clojure.lang.Numbers (unchecked_add x y)))
+
+(defn unchecked-subtract
+ "Returns the difference of x and y, both int or long.
+ Note - uses a primitive operator subject to overflow."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_subtract ~x ~y)))}
+ [x y] (. clojure.lang.Numbers (unchecked_subtract x y)))
+
+(defn unchecked-multiply
+ "Returns the product of x and y, both int or long.
+ Note - uses a primitive operator subject to overflow."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_multiply ~x ~y)))}
+ [x y] (. clojure.lang.Numbers (unchecked_multiply x y)))
+
+(defn unchecked-divide
+ "Returns the division of x by y, both int or long.
+ Note - uses a primitive operator subject to truncation."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_divide ~x ~y)))}
+ [x y] (. clojure.lang.Numbers (unchecked_divide x y)))
+
+(defn unchecked-remainder
+ "Returns the remainder of division of x by y, both int or long.
+ Note - uses a primitive operator subject to truncation."
+ {:inline (fn [x y] `(. clojure.lang.Numbers (unchecked_remainder ~x ~y)))}
+ [x y] (. clojure.lang.Numbers (unchecked_remainder x y)))
+
+(defn pos?
+ "Returns true if num is greater than zero, else false"
+ {:tag Boolean
+ :inline (fn [x] `(. clojure.lang.Numbers (isPos ~x)))}
+ [x] (. clojure.lang.Numbers (isPos x)))
+
+(defn neg?
+ "Returns true if num is less than zero, else false"
+ {:tag Boolean
+ :inline (fn [x] `(. clojure.lang.Numbers (isNeg ~x)))}
+ [x] (. clojure.lang.Numbers (isNeg x)))
+
+(defn zero?
+ "Returns true if num is zero, else false"
+ {:tag Boolean
+ :inline (fn [x] `(. clojure.lang.Numbers (isZero ~x)))}
+ [x] (. clojure.lang.Numbers (isZero x)))
+
+(defn quot
+ "quot[ient] of dividing numerator by denominator."
+ [num div]
+ (. clojure.lang.Numbers (quotient num div)))
+
+(defn rem
+ "remainder of dividing numerator by denominator."
+ [num div]
+ (. clojure.lang.Numbers (remainder num div)))
+
+(defn rationalize
+ "returns the rational value of num"
+ [num]
+ (. clojure.lang.Numbers (rationalize num)))
+
+;;Bit ops
+
+(defn bit-not
+ "Bitwise complement"
+ {:inline (fn [x] `(. clojure.lang.Numbers (not ~x)))}
+ [x] (. clojure.lang.Numbers not x))
+
+
+(defn bit-and
+ "Bitwise and"
+ {:inline (fn [x y] `(. clojure.lang.Numbers (and ~x ~y)))}
+ [x y] (. clojure.lang.Numbers and x y))
+
+(defn bit-or
+ "Bitwise or"
+ {:inline (fn [x y] `(. clojure.lang.Numbers (or ~x ~y)))}
+ [x y] (. clojure.lang.Numbers or x y))
+
+(defn bit-xor
+ "Bitwise exclusive or"
+ {:inline (fn [x y] `(. clojure.lang.Numbers (xor ~x ~y)))}
+ [x y] (. clojure.lang.Numbers xor x y))
+
+(defn bit-and-not
+ "Bitwise and with complement"
+ [x y] (. clojure.lang.Numbers andNot x y))
+
+
+(defn bit-clear
+ "Clear bit at index n"
+ [x n] (. clojure.lang.Numbers clearBit x n))
+
+(defn bit-set
+ "Set bit at index n"
+ [x n] (. clojure.lang.Numbers setBit x n))
+
+(defn bit-flip
+ "Flip bit at index n"
+ [x n] (. clojure.lang.Numbers flipBit x n))
+
+(defn bit-test
+ "Test bit at index n"
+ [x n] (. clojure.lang.Numbers testBit x n))
+
+
+(defn bit-shift-left
+ "Bitwise shift left"
+ [x n] (. clojure.lang.Numbers shiftLeft x n))
+
+(defn bit-shift-right
+ "Bitwise shift right"
+ [x n] (. clojure.lang.Numbers shiftRight x n))
+
+(defn even?
+ "Returns true if n is even, throws an exception if n is not an integer"
+ [n] (zero? (bit-and n 1)))
+
+(defn odd?
+ "Returns true if n is odd, throws an exception if n is not an integer"
+ [n] (not (even? n)))
+
+
+;;
+
+(defn complement
+ "Takes a fn f and returns a fn that takes the same arguments as f,
+ has the same effects, if any, and returns the opposite truth value."
+ [f]
+ (fn
+ ([] (not (f)))
+ ([x] (not (f x)))
+ ([x y] (not (f x y)))
+ ([x y & zs] (not (apply f x y zs)))))
+
+(defn constantly
+ "Returns a function that takes any number of arguments and returns x."
+ [x] (fn [& args] x))
+
+(defn identity
+ "Returns its argument."
+ [x] x)
+
+;;Collection stuff
+
+
+
+(defn count
+ "Returns the number of items in the collection. (count nil) returns
+ 0. Also works on strings, arrays, and Java Collections and Maps"
+ [coll] (. clojure.lang.RT (count coll)))
+
+;;list stuff
+(defn peek
+ "For a list or queue, same as first, for a vector, same as, but much
+ more efficient than, last. If the collection is empty, returns nil."
+ [coll] (. clojure.lang.RT (peek coll)))
+
+(defn pop
+ "For a list or queue, returns a new list/queue without the first
+ item, for a vector, returns a new vector without the last item. If
+ the collection is empty, throws an exception. Note - not the same
+ as next/butlast."
+ [coll] (. clojure.lang.RT (pop coll)))
+
+(defn nth
+ "Returns the value at the index. get returns nil if index out of
+ bounds, nth throws an exception unless not-found is supplied. nth
+ also works for strings, Java arrays, regex Matchers and Lists, and,
+ in O(n) time, for sequences."
+ ([coll index] (. clojure.lang.RT (nth coll index)))
+ ([coll index not-found] (. clojure.lang.RT (nth coll index not-found))))
+
+;;map stuff
+
+(defn contains?
+ "Returns true if key is present in the given collection, otherwise
+ returns false. Note that for numerically indexed collections like
+ vectors and Java arrays, this tests if the numeric key is within the
+ range of indexes. 'contains?' operates constant or logarithmic time;
+ it will not perform a linear search for a value. See also 'some'."
+ [coll key] (. clojure.lang.RT (contains coll key)))
+
+(defn get
+ "Returns the value mapped to key, not-found or nil if key not present."
+ ([map key]
+ (. clojure.lang.RT (get map key)))
+ ([map key not-found]
+ (. clojure.lang.RT (get map key not-found))))
+
+(defn dissoc
+ "dissoc[iate]. Returns a new map of the same (hashed/sorted) type,
+ that does not contain a mapping for key(s)."
+ ([map] map)
+ ([map key]
+ (. clojure.lang.RT (dissoc map key)))
+ ([map key & ks]
+ (let [ret (dissoc map key)]
+ (if ks
+ (recur ret (first ks) (next ks))
+ ret))))
+
+(defn disj
+ "disj[oin]. Returns a new set of the same (hashed/sorted) type, that
+ does not contain key(s)."
+ ([set] set)
+ ([#^clojure.lang.IPersistentSet set key]
+ (. set (disjoin key)))
+ ([set key & ks]
+ (let [ret (disj set key)]
+ (if ks
+ (recur ret (first ks) (next ks))
+ ret))))
+
+(defn find
+ "Returns the map entry for key, or nil if key not present."
+ [map key] (. clojure.lang.RT (find map key)))
+
+(defn select-keys
+ "Returns a map containing only those entries in map whose key is in keys"
+ [map keyseq]
+ (loop [ret {} keys (seq keyseq)]
+ (if keys
+ (let [entry (. clojure.lang.RT (find map (first keys)))]
+ (recur
+ (if entry
+ (conj ret entry)
+ ret)
+ (next keys)))
+ ret)))
+
+(defn keys
+ "Returns a sequence of the map's keys."
+ [map] (. clojure.lang.RT (keys map)))
+
+(defn vals
+ "Returns a sequence of the map's values."
+ [map] (. clojure.lang.RT (vals map)))
+
+(defn key
+ "Returns the key of the map entry."
+ [#^java.util.Map$Entry e]
+ (. e (getKey)))
+
+(defn val
+ "Returns the value in the map entry."
+ [#^java.util.Map$Entry e]
+ (. e (getValue)))
+
+(defn rseq
+ "Returns, in constant time, a seq of the items in rev (which
+ can be a vector or sorted-map), in reverse order. If rev is empty returns nil"
+ [#^clojure.lang.Reversible rev]
+ (. rev (rseq)))
+
+(defn name
+ "Returns the name String of a symbol or keyword."
+ {:tag String}
+ [#^clojure.lang.Named x]
+ (. x (getName)))
+
+(defn namespace
+ "Returns the namespace String of a symbol or keyword, or nil if not present."
+ {:tag String}
+ [#^clojure.lang.Named x]
+ (. x (getNamespace)))
+
+(defmacro locking
+ "Executes exprs in an implicit do, while holding the monitor of x.
+ Will release the monitor of x in all circumstances."
+ [x & body]
+ `(let [lockee# ~x]
+ (try
+ (monitor-enter lockee#)
+ ~@body
+ (finally
+ (monitor-exit lockee#)))))
+
+(defmacro ..
+ "form => fieldName-symbol or (instanceMethodName-symbol args*)
+
+ Expands into a member access (.) of the first member on the first
+ argument, followed by the next member on the result, etc. For
+ instance:
+
+ (.. System (getProperties) (get \"os.name\"))
+
+ expands to:
+
+ (. (. System (getProperties)) (get \"os.name\"))
+
+ but is easier to write, read, and understand."
+ ([x form] `(. ~x ~form))
+ ([x form & more] `(.. (. ~x ~form) ~@more)))
+
+(defmacro ->
+ "Threads the expr through the forms. Inserts x as the
+ second item in the first form, making a list of it if it is not a
+ list already. If there are more forms, inserts the first form as the
+ second item in second form, etc."
+ ([x form] (if (seq? form)
+ `(~(first form) ~x ~@(next form))
+ (list form x)))
+ ([x form & more] `(-> (-> ~x ~form) ~@more)))
+
+;;multimethods
+(def global-hierarchy)
+
+(defmacro defmulti
+ "Creates a new multimethod with the associated dispatch function.
+ The docstring and attribute-map are optional.
+
+ Options are key-value pairs and may be one of:
+ :default the default dispatch value, defaults to :default
+ :hierarchy the isa? hierarchy to use for dispatching
+ defaults to the global hierarchy"
+ {:arglists '([name docstring? attr-map? dispatch-fn & options])}
+ [mm-name & options]
+ (let [docstring (if (string? (first options))
+ (first options)
+ nil)
+ options (if (string? (first options))
+ (next options)
+ options)
+ m (if (map? (first options))
+ (first options)
+ {})
+ options (if (map? (first options))
+ (next options)
+ options)
+ dispatch-fn (first options)
+ options (next options)
+ m (assoc m :tag 'clojure.lang.MultiFn)
+ m (if docstring
+ (assoc m :doc docstring)
+ m)
+ m (if (meta mm-name)
+ (conj (meta mm-name) m)
+ m)]
+ (when (= (count options) 1)
+ (throw (Exception. "The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value)")))
+ (let [options (apply hash-map options)
+ default (get options :default :default)
+ hierarchy (get options :hierarchy #'global-hierarchy)]
+ `(def ~(with-meta mm-name m)
+ (new clojure.lang.MultiFn ~(name mm-name) ~dispatch-fn ~default ~hierarchy)))))
+
+(defmacro defmethod
+ "Creates and installs a new method of multimethod associated with dispatch-value. "
+ [multifn dispatch-val & fn-tail]
+ `(. ~multifn addMethod ~dispatch-val (fn ~@fn-tail)))
+
+(defn remove-method
+ "Removes the method of multimethod associated with dispatch-value."
+ [#^clojure.lang.MultiFn multifn dispatch-val]
+ (. multifn removeMethod dispatch-val))
+
+(defn prefer-method
+ "Causes the multimethod to prefer matches of dispatch-val-x over dispatch-val-y when there is a conflict"
+ [#^clojure.lang.MultiFn multifn dispatch-val-x dispatch-val-y]
+ (. multifn preferMethod dispatch-val-x dispatch-val-y))
+
+(defn methods
+ "Given a multimethod, returns a map of dispatch values -> dispatch fns"
+ [#^clojure.lang.MultiFn multifn] (.getMethodTable multifn))
+
+(defn get-method
+ "Given a multimethod and a dispatch value, returns the dispatch fn
+ that would apply to that value, or nil if none apply and no default"
+ [#^clojure.lang.MultiFn multifn dispatch-val] (.getMethod multifn dispatch-val))
+
+(defn prefers
+ "Given a multimethod, returns a map of preferred value -> set of other values"
+ [#^clojure.lang.MultiFn multifn] (.getPreferTable multifn))
+
+;;;;;;;;; var stuff
+
+(defmacro #^{:private true} assert-args [fnname & pairs]
+ `(do (when-not ~(first pairs)
+ (throw (IllegalArgumentException.
+ ~(str fnname " requires " (second pairs)))))
+ ~(let [more (nnext pairs)]
+ (when more
+ (list* `assert-args fnname more)))))
+
+(defmacro if-let
+ "bindings => binding-form test
+
+ If test is true, evaluates then with binding-form bound to the value of test, if not, yields else"
+ ([bindings then]
+ `(if-let ~bindings ~then nil))
+ ([bindings then else & oldform]
+ (assert-args if-let
+ (and (vector? bindings) (nil? oldform)) "a vector for its binding"
+ (= 2 (count bindings)) "exactly 2 forms in binding vector")
+ (let [form (bindings 0) tst (bindings 1)]
+ `(let [temp# ~tst]
+ (if temp#
+ (let [~form temp#]
+ ~then)
+ ~else)))))
+
+(defmacro when-let
+ "bindings => binding-form test
+
+ When test is true, evaluates body with binding-form bound to the value of test"
+ [bindings & body]
+ (assert-args when-let
+ (vector? bindings) "a vector for its binding"
+ (= 2 (count bindings)) "exactly 2 forms in binding vector")
+ (let [form (bindings 0) tst (bindings 1)]
+ `(let [temp# ~tst]
+ (when temp#
+ (let [~form temp#]
+ ~@body)))))
+
+(defmacro binding
+ "binding => var-symbol init-expr
+
+ Creates new bindings for the (already-existing) vars, with the
+ supplied initial values, executes the exprs in an implicit do, then
+ re-establishes the bindings that existed before."
+ [bindings & body]
+ (assert-args binding
+ (vector? bindings) "a vector for its binding"
+ (even? (count bindings)) "an even number of forms in binding vector")
+ (let [var-ize (fn [var-vals]
+ (loop [ret [] vvs (seq var-vals)]
+ (if vvs
+ (recur (conj (conj ret `(var ~(first vvs))) (second vvs))
+ (next (next vvs)))
+ (seq ret))))]
+ `(do
+ (. clojure.lang.Var (pushThreadBindings (hash-map ~@(var-ize bindings))))
+ (try
+ ~@body
+ (finally
+ (. clojure.lang.Var (popThreadBindings)))))))
+
+(defn find-var
+ "Returns the global var named by the namespace-qualified symbol, or
+ nil if no var with that name."
+ [sym] (. clojure.lang.Var (find sym)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Refs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defn #^{:private true}
+ setup-reference [#^clojure.lang.ARef r options]
+ (let [opts (apply hash-map options)]
+ (when (:meta opts)
+ (.resetMeta r (:meta opts)))
+ (when (:validator opts)
+ (.setValidator r (:validator opts)))
+ r))
+
+(defn agent
+ "Creates and returns an agent with an initial value of state and
+ zero or more options (in any order):
+
+ :meta metadata-map
+
+ :validator validate-fn
+
+ If metadata-map is supplied, it will be come the metadata on the
+ agent. validate-fn must be nil or a side-effect-free fn of one
+ argument, which will be passed the intended new state on any state
+ change. If the new state is unacceptable, the validate-fn should
+ return false or throw an exception."
+ ([state] (new clojure.lang.Agent state))
+ ([state & options]
+ (setup-reference (agent state) options)))
+
+(defn send
+ "Dispatch an action to an agent. Returns the agent immediately.
+ Subsequently, in a thread from a thread pool, the state of the agent
+ will be set to the value of:
+
+ (apply action-fn state-of-agent args)"
+ [#^clojure.lang.Agent a f & args]
+ (. a (dispatch f args false)))
+
+(defn send-off
+ "Dispatch a potentially blocking action to an agent. Returns the
+ agent immediately. Subsequently, in a separate thread, the state of
+ the agent will be set to the value of:
+
+ (apply action-fn state-of-agent args)"
+ [#^clojure.lang.Agent a f & args]
+ (. a (dispatch f args true)))
+
+(defn release-pending-sends
+ "Normally, actions sent directly or indirectly during another action
+ are held until the action completes (changes the agent's
+ state). This function can be used to dispatch any pending sent
+ actions immediately. This has no impact on actions sent during a
+ transaction, which are still held until commit. If no action is
+ occurring, does nothing. Returns the number of actions dispatched."
+ [] (clojure.lang.Agent/releasePendingSends))
+
+(defn add-watch
+ "Experimental.
+ Adds a watch function to an agent/atom/var/ref reference. The watch
+ fn must be a fn of 4 args: a key, the reference, its old-state, its
+ new-state. Whenever the reference's state might have been changed,
+ any registered watches will have their functions called. The watch fn
+ will be called synchronously, on the agent's thread if an agent,
+ before any pending sends if agent or ref. Note that an atom's or
+ ref's state may have changed again prior to the fn call, so use
+ old/new-state rather than derefing the reference. Note also that watch
+ fns may be called from multiple threads simultaneously. Var watchers
+ are triggered only by root binding changes, not thread-local
+ set!s. Keys must be unique per reference, and can be used to remove
+ the watch with remove-watch, but are otherwise considered opaque by
+ the watch mechanism."
+ [#^clojure.lang.IRef reference key fn] (.addWatch reference key fn))
+
+(defn remove-watch
+ "Experimental.
+ Removes a watch (set by add-watch) from a reference"
+ [#^clojure.lang.IRef reference key]
+ (.removeWatch reference key))
+
+(defn add-watcher
+ "Experimental.
+ Adds a watcher to an agent/atom/var/ref reference. The watcher must
+ be an Agent, and the action a function of the agent's state and one
+ additional arg, the reference. Whenever the reference's state
+ changes, any registered watchers will have their actions
+ sent. send-type must be one of :send or :send-off. The actions will
+ be sent after the reference's state is changed. Var watchers are
+ triggered only by root binding changes, not thread-local set!s"
+ [#^clojure.lang.IRef reference send-type watcher-agent action-fn]
+ (add-watch reference watcher-agent
+ (fn [watcher-agent reference old-state new-state]
+ (when-not (identical? old-state new-state)
+ ((if (= send-type :send-off) send-off send)
+ watcher-agent action-fn reference)))))
+
+(defn remove-watcher
+ "Experimental.
+ Removes a watcher (set by add-watcher) from a reference"
+ [reference watcher-agent]
+ (remove-watch reference watcher-agent))
+
+(defn agent-errors
+ "Returns a sequence of the exceptions thrown during asynchronous
+ actions of the agent."
+ [#^clojure.lang.Agent a] (. a (getErrors)))
+
+(defn clear-agent-errors
+ "Clears any exceptions thrown during asynchronous actions of the
+ agent, allowing subsequent actions to occur."
+ [#^clojure.lang.Agent a] (. a (clearErrors)))
+
+(defn shutdown-agents
+ "Initiates a shutdown of the thread pools that back the agent
+ system. Running actions will complete, but no new actions will be
+ accepted"
+ [] (. clojure.lang.Agent shutdown))
+
+(defn ref
+ "Creates and returns a Ref with an initial value of x and zero or
+ more options (in any order):
+
+ :meta metadata-map
+
+ :validator validate-fn
+
+ If metadata-map is supplied, it will be come the metadata on the
+ ref. validate-fn must be nil or a side-effect-free fn of one
+ argument, which will be passed the intended new state on any state
+ change. If the new state is unacceptable, the validate-fn should
+ return false or throw an exception. validate-fn will be called on
+ transaction commit, when all refs have their final values."
+ ([x] (new clojure.lang.Ref x))
+ ([x & options] (setup-reference (ref x) options)))
+
+(defn deref
+ "Also reader macro: @ref/@agent/@var/@atom/@delay/@future. Within a transaction,
+ returns the in-transaction-value of ref, else returns the
+ most-recently-committed value of ref. When applied to a var, agent
+ or atom, returns its current state. When applied to a delay, forces
+ it if not already forced. When applied to a future, will block if
+ computation not complete"
+ [#^clojure.lang.IDeref ref] (.deref ref))
+
+(defn atom
+ "Creates and returns an Atom with an initial value of x and zero or
+ more options (in any order):
+
+ :meta metadata-map
+
+ :validator validate-fn
+
+ If metadata-map is supplied, it will be come the metadata on the
+ atom. validate-fn must be nil or a side-effect-free fn of one
+ argument, which will be passed the intended new state on any state
+ change. If the new state is unacceptable, the validate-fn should
+ return false or throw an exception."
+ ([x] (new clojure.lang.Atom x))
+ ([x & options] (setup-reference (atom x) options)))
+
+(defn swap!
+ "Atomically swaps the value of atom to be:
+ (apply f current-value-of-atom args). Note that f may be called
+ multiple times, and thus should be free of side effects. Returns
+ the value that was swapped in."
+ ([#^clojure.lang.Atom atom f] (.swap atom f))
+ ([#^clojure.lang.Atom atom f x] (.swap atom f x))
+ ([#^clojure.lang.Atom atom f x y] (.swap atom f x y))
+ ([#^clojure.lang.Atom atom f x y & args] (.swap atom f x y args)))
+
+(defn compare-and-set!
+ "Atomically sets the value of atom to newval if and only if the
+ current value of the atom is identical to oldval. Returns true if
+ set happened, else false"
+ [#^clojure.lang.Atom atom oldval newval] (.compareAndSet atom oldval newval))
+
+(defn reset!
+ "Sets the value of atom to newval without regard for the
+ current value. Returns newval."
+ [#^clojure.lang.Atom atom newval] (.reset atom newval))
+
+(defn set-validator!
+ "Sets the validator-fn for a var/ref/agent/atom. validator-fn must be nil or a
+ side-effect-free fn of one argument, which will be passed the intended
+ new state on any state change. If the new state is unacceptable, the
+ validator-fn should return false or throw an exception. If the current state (root
+ value if var) is not acceptable to the new validator, an exception
+ will be thrown and the validator will not be changed."
+ [#^clojure.lang.IRef iref validator-fn] (. iref (setValidator validator-fn)))
+
+(defn get-validator
+ "Gets the validator-fn for a var/ref/agent/atom."
+ [#^clojure.lang.IRef iref] (. iref (getValidator)))
+
+(defn alter-meta!
+ "Atomically sets the metadata for a namespace/var/ref/agent/atom to be:
+
+ (apply f its-current-meta args)
+
+ f must be free of side-effects"
+ [#^clojure.lang.IReference iref f & args] (.alterMeta iref f args))
+
+(defn reset-meta!
+ "Atomically resets the metadata for a namespace/var/ref/agent/atom"
+ [#^clojure.lang.IReference iref metadata-map] (.resetMeta iref metadata-map))
+
+(defn commute
+ "Must be called in a transaction. Sets the in-transaction-value of
+ ref to:
+
+ (apply fun in-transaction-value-of-ref args)
+
+ and returns the in-transaction-value of ref.
+
+ At the commit point of the transaction, sets the value of ref to be:
+
+ (apply fun most-recently-committed-value-of-ref args)
+
+ Thus fun should be commutative, or, failing that, you must accept
+ last-one-in-wins behavior. commute allows for more concurrency than
+ ref-set."
+
+ [#^clojure.lang.Ref ref fun & args]
+ (. ref (commute fun args)))
+
+(defn alter
+ "Must be called in a transaction. Sets the in-transaction-value of
+ ref to:
+
+ (apply fun in-transaction-value-of-ref args)
+
+ and returns the in-transaction-value of ref."
+ [#^clojure.lang.Ref ref fun & args]
+ (. ref (alter fun args)))
+
+(defn ref-set
+ "Must be called in a transaction. Sets the value of ref.
+ Returns val."
+ [#^clojure.lang.Ref ref val]
+ (. ref (set val)))
+
+(defn ensure
+ "Must be called in a transaction. Protects the ref from modification
+ by other transactions. Returns the in-transaction-value of
+ ref. Allows for more concurrency than (ref-set ref @ref)"
+ [#^clojure.lang.Ref ref]
+ (. ref (touch))
+ (. ref (deref)))
+
+(defmacro sync
+ "transaction-flags => TBD, pass nil for now
+
+ Runs the exprs (in an implicit do) in a transaction that encompasses
+ exprs and any nested calls. Starts a transaction if none is already
+ running on this thread. Any uncaught exception will abort the
+ transaction and flow out of sync. The exprs may be run more than
+ once, but any effects on Refs will be atomic."
+ [flags-ignored-for-now & body]
+ `(. clojure.lang.LockingTransaction
+ (runInTransaction (fn [] ~@body))))
+
+
+(defmacro io!
+ "If an io! block occurs in a transaction, throws an
+ IllegalStateException, else runs body in an implicit do. If the
+ first expression in body is a literal string, will use that as the
+ exception message."
+ [& body]
+ (let [message (when (string? (first body)) (first body))
+ body (if message (next body) body)]
+ `(if (clojure.lang.LockingTransaction/isRunning)
+ (throw (new IllegalStateException ~(or message "I/O in transaction")))
+ (do ~@body))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; fn stuff ;;;;;;;;;;;;;;;;
+
+
+(defn comp
+ "Takes a set of functions and returns a fn that is the composition
+ of those fns. The returned fn takes a variable number of args,
+ applies the rightmost of fns to the args, the next
+ fn (right-to-left) to the result, etc."
+ [& fs]
+ (let [fs (reverse fs)]
+ (fn [& args]
+ (loop [ret (apply (first fs) args) fs (next fs)]
+ (if fs
+ (recur ((first fs) ret) (next fs))
+ ret)))))
+
+(defn partial
+ "Takes a function f and fewer than the normal arguments to f, and
+ returns a fn that takes a variable number of additional args. When
+ called, the returned function calls f with args + additional args."
+ ([f arg1]
+ (fn [& args] (apply f arg1 args)))
+ ([f arg1 arg2]
+ (fn [& args] (apply f arg1 arg2 args)))
+ ([f arg1 arg2 arg3]
+ (fn [& args] (apply f arg1 arg2 arg3 args)))
+ ([f arg1 arg2 arg3 & more]
+ (fn [& args] (apply f arg1 arg2 arg3 (concat more args)))))
+
+;;;;;;;;;;;;;;;;;;; sequence fns ;;;;;;;;;;;;;;;;;;;;;;;
+(defn stream?
+ "Returns true if x is an instance of Stream"
+ [x] (instance? clojure.lang.Stream x))
+
+
+(defn sequence
+ "Coerces coll to a (possibly empty) sequence, if it is not already
+ one. Will not force a lazy seq. (sequence nil) yields ()"
+ [coll]
+ (cond
+ (seq? coll) coll
+ (stream? coll) (.sequence #^clojure.lang.Stream coll)
+ :else (or (seq coll) ())))
+
+(defn every?
+ "Returns true if (pred x) is logical true for every x in coll, else
+ false."
+ {:tag Boolean}
+ [pred coll]
+ (if (seq coll)
+ (and (pred (first coll))
+ (recur pred (next coll)))
+ true))
+
+(def
+ #^{:tag Boolean
+ :doc "Returns false if (pred x) is logical true for every x in
+ coll, else true."
+ :arglists '([pred coll])}
+ not-every? (comp not every?))
+
+(defn some
+ "Returns the first logical true value of (pred x) for any x in coll,
+ else nil. One common idiom is to use a set as pred, for example
+ this will return true if :fred is in the sequence, otherwise nil:
+ (some #{:fred} coll)"
+ [pred coll]
+ (when (seq coll)
+ (or (pred (first coll)) (recur pred (next coll)))))
+
+(def
+ #^{:tag Boolean
+ :doc "Returns false if (pred x) is logical true for any x in coll,
+ else true."
+ :arglists '([pred coll])}
+ not-any? (comp not some))
+
+(defn map
+ "Returns a lazy sequence consisting of the result of applying f to the
+ set of first items of each coll, followed by applying f to the set
+ of second items in each coll, until any one of the colls is
+ exhausted. Any remaining items in other colls are ignored. Function
+ f should accept number-of-colls arguments."
+ ([f coll]
+ (lazy-seq
+ (when-let [s (seq coll)]
+ (cons (f (first s)) (map f (rest s))))))
+ ([f c1 c2]
+ (lazy-seq
+ (let [s1 (seq c1) s2 (seq c2)]
+ (when (and s1 s2)
+ (cons (f (first s1) (first s2))
+ (map f (rest s1) (rest s2)))))))
+ ([f c1 c2 c3]
+ (lazy-seq
+ (let [s1 (seq c1) s2 (seq c2) s3 (seq c3)]
+ (when (and s1 s2 s3)
+ (cons (f (first s1) (first s2) (first s3))
+ (map f (rest s1) (rest s2) (rest s3)))))))
+ ([f c1 c2 c3 & colls]
+ (let [step (fn step [cs]
+ (lazy-seq
+ (let [ss (map seq cs)]
+ (when (every? identity ss)
+ (cons (map first ss) (step (map rest ss)))))))]
+ (map #(apply f %) (step (conj colls c3 c2 c1))))))
+
+(defn mapcat
+ "Returns the result of applying concat to the result of applying map
+ to f and colls. Thus function f should return a collection."
+ [f & colls]
+ (apply concat (apply map f colls)))
+
+(defn filter
+ "Returns a lazy sequence of the items in coll for which
+ (pred item) returns true. pred must be free of side-effects."
+ [pred coll]
+ (let [step (fn [p c]
+ (when-let [s (seq c)]
+ (if (p (first s))
+ (cons (first s) (filter p (rest s)))
+ (recur p (rest s)))))]
+ (lazy-seq (step pred coll))))
+
+
+(defn remove
+ "Returns a lazy sequence of the items in coll for which
+ (pred item) returns false. pred must be free of side-effects."
+ [pred coll]
+ (filter (complement pred) coll))
+
+(defn take
+ "Returns a lazy sequence of the first n items in coll, or all items if
+ there are fewer than n."
+ [n coll]
+ (lazy-seq
+ (when (pos? n)
+ (when-let [s (seq coll)]
+ (cons (first s) (take (dec n) (rest s)))))))
+
+(defn take-while
+ "Returns a lazy sequence of successive items from coll while
+ (pred item) returns true. pred must be free of side-effects."
+ [pred coll]
+ (lazy-seq
+ (when-let [s (seq coll)]
+ (when (pred (first s))
+ (cons (first s) (take-while pred (rest s)))))))
+
+(defn drop
+ "Returns a lazy sequence of all but the first n items in coll."
+ [n coll]
+ (let [step (fn [n coll]
+ (let [s (seq coll)]
+ (if (and (pos? n) s)
+ (recur (dec n) (rest s))
+ s)))]
+ (lazy-seq (step n coll))))
+
+(defn drop-last
+ "Return a lazy sequence of all but the last n (default 1) items in coll"
+ ([s] (drop-last 1 s))
+ ([n s] (map (fn [x _] x) s (drop n s))))
+
+(defn drop-while
+ "Returns a lazy sequence of the items in coll starting from the first
+ item for which (pred item) returns nil."
+ [pred coll]
+ (let [step (fn [pred coll]
+ (let [s (seq coll)]
+ (if (and s (pred (first s)))
+ (recur pred (rest s))
+ s)))]
+ (lazy-seq (step pred coll))))
+
+(defn cycle
+ "Returns a lazy (infinite!) sequence of repetitions of the items in coll."
+ [coll] (lazy-seq
+ (when-let [s (seq coll)]
+ (concat s (cycle s)))))
+
+(defn split-at
+ "Returns a vector of [(take n coll) (drop n coll)]"
+ [n coll]
+ [(take n coll) (drop n coll)])
+
+(defn split-with
+ "Returns a vector of [(take-while pred coll) (drop-while pred coll)]"
+ [pred coll]
+ [(take-while pred coll) (drop-while pred coll)])
+
+(defn repeat
+ "Returns a lazy (infinite!, or length n if supplied) sequence of xs."
+ ([x] (lazy-seq (cons x (repeat x))))
+ ([n x] (take n (repeat x))))
+
+(defn replicate
+ "Returns a lazy seq of n xs."
+ [n x] (take n (repeat x)))
+
+(defn iterate
+ "Returns a lazy sequence of x, (f x), (f (f x)) etc. f must be free of side-effects"
+ [f x] (cons x (lazy-seq (iterate f (f x)))))
+
+(defn range
+ "Returns a lazy seq of nums from start (inclusive) to end
+ (exclusive), by step, where start defaults to 0 and step to 1."
+ ([end] (if (and (> end 0) (<= end (. Integer MAX_VALUE)))
+ (new clojure.lang.Range 0 end)
+ (take end (iterate inc 0))))
+ ([start end] (if (and (< start end)
+ (>= start (. Integer MIN_VALUE))
+ (<= end (. Integer MAX_VALUE)))
+ (new clojure.lang.Range start end)
+ (take (- end start) (iterate inc start))))
+ ([start end step]
+ (take-while (partial (if (pos? step) > <) end) (iterate (partial + step) start))))
+
+(defn merge
+ "Returns a map that consists of the rest of the maps conj-ed onto
+ the first. If a key occurs in more than one map, the mapping from
+ the latter (left-to-right) will be the mapping in the result."
+ [& maps]
+ (when (some identity maps)
+ (reduce #(conj (or %1 {}) %2) maps)))
+
+(defn merge-with
+ "Returns a map that consists of the rest of the maps conj-ed onto
+ the first. If a key occurs in more than one map, the mapping(s)
+ from the latter (left-to-right) will be combined with the mapping in
+ the result by calling (f val-in-result val-in-latter)."
+ [f & maps]
+ (when (some identity maps)
+ (let [merge-entry (fn [m e]
+ (let [k (key e) v (val e)]
+ (if (contains? m k)
+ (assoc m k (f (m k) v))
+ (assoc m k v))))
+ merge2 (fn [m1 m2]
+ (reduce merge-entry (or m1 {}) (seq m2)))]
+ (reduce merge2 maps))))
+
+
+
+(defn zipmap
+ "Returns a map with the keys mapped to the corresponding vals."
+ [keys vals]
+ (loop [map {}
+ ks (seq keys)
+ vs (seq vals)]
+ (if (and ks vs)
+ (recur (assoc map (first ks) (first vs))
+ (next ks)
+ (next vs))
+ map)))
+
+(defn line-seq
+ "Returns the lines of text from rdr as a lazy sequence of strings.
+ rdr must implement java.io.BufferedReader."
+ [#^java.io.BufferedReader rdr]
+ (lazy-seq
+ (let [line (. rdr (readLine))]
+ (when line
+ (cons line (line-seq rdr))))))
+
+(defn comparator
+ "Returns an implementation of java.util.Comparator based upon pred."
+ [pred]
+ (fn [x y]
+ (cond (pred x y) -1 (pred y x) 1 :else 0)))
+
+(defn sort
+ "Returns a sorted sequence of the items in coll. If no comparator is
+ supplied, uses compare. comparator must
+ implement java.util.Comparator."
+ ([coll]
+ (sort compare coll))
+ ([#^java.util.Comparator comp coll]
+ (if (seq coll)
+ (let [a (to-array coll)]
+ (. java.util.Arrays (sort a comp))
+ (seq a))
+ ())))
+
+(defn sort-by
+ "Returns a sorted sequence of the items in coll, where the sort
+ order is determined by comparing (keyfn item). If no comparator is
+ supplied, uses compare. comparator must
+ implement java.util.Comparator."
+ ([keyfn coll]
+ (sort-by keyfn compare coll))
+ ([keyfn #^java.util.Comparator comp coll]
+ (sort (fn [x y] (. comp (compare (keyfn x) (keyfn y)))) coll)))
+
+(defn partition
+ "Returns a lazy sequence of lists of n items each, at offsets step
+ apart. If step is not supplied, defaults to n, i.e. the partitions
+ do not overlap."
+ ([n coll]
+ (partition n n coll))
+ ([n step coll]
+ (lazy-seq
+ (when-let [s (seq coll)]
+ (let [p (take n s)]
+ (when (= n (count p))
+ (cons p (partition n step (drop step s)))))))))
+
+;; evaluation
+
+(defn eval
+ "Evaluates the form data structure (not text!) and returns the result."
+ [form] (. clojure.lang.Compiler (eval form)))
+
+(defmacro doseq
+ "Repeatedly executes body (presumably for side-effects) with
+ bindings and filtering as provided by \"for\". Does not retain
+ the head of the sequence. Returns nil."
+ [seq-exprs & body]
+ (assert-args doseq
+ (vector? seq-exprs) "a vector for its binding"
+ (even? (count seq-exprs)) "an even number of forms in binding vector")
+ (let [step (fn step [recform exprs]
+ (if-not exprs
+ [true `(do ~@body)]
+ (let [k (first exprs)
+ v (second exprs)
+ seqsym (when-not (keyword? k) (gensym))
+ recform (if (keyword? k) recform `(recur (next ~seqsym)))
+ steppair (step recform (nnext exprs))
+ needrec (steppair 0)
+ subform (steppair 1)]
+ (cond
+ (= k :let) [needrec `(let ~v ~subform)]
+ (= k :while) [false `(when ~v
+ ~subform
+ ~@(when needrec [recform]))]
+ (= k :when) [false `(if ~v
+ (do
+ ~subform
+ ~@(when needrec [recform]))
+ ~recform)]
+ :else [true `(loop [~seqsym (seq ~v)]
+ (when ~seqsym
+ (let [~k (first ~seqsym)]
+ ~subform
+ ~@(when needrec [recform]))))]))))]
+ (nth (step nil (seq seq-exprs)) 1)))
+
+(defn dorun
+ "When lazy sequences are produced via functions that have side
+ effects, any effects other than those needed to produce the first
+ element in the seq do not occur until the seq is consumed. dorun can
+ be used to force any effects. Walks through the successive nexts of
+ the seq, does not retain the head and returns nil."
+ ([coll]
+ (when (seq coll)
+ (recur (next coll))))
+ ([n coll]
+ (when (and (seq coll) (pos? n))
+ (recur (dec n) (next coll)))))
+
+(defn doall
+ "When lazy sequences are produced via functions that have side
+ effects, any effects other than those needed to produce the first
+ element in the seq do not occur until the seq is consumed. doall can
+ be used to force any effects. Walks through the successive nexts of
+ the seq, retains the head and returns it, thus causing the entire
+ seq to reside in memory at one time."
+ ([coll]
+ (dorun coll)
+ coll)
+ ([n coll]
+ (dorun n coll)
+ coll))
+
+(defn await
+ "Blocks the current thread (indefinitely!) until all actions
+ dispatched thus far, from this thread or agent, to the agent(s) have
+ occurred."
+ [& agents]
+ (io! "await in transaction"
+ (when *agent*
+ (throw (new Exception "Can't await in agent action")))
+ (let [latch (new java.util.concurrent.CountDownLatch (count agents))
+ count-down (fn [agent] (. latch (countDown)) agent)]
+ (doseq [agent agents]
+ (send agent count-down))
+ (. latch (await)))))
+
+(defn await1 [#^clojure.lang.Agent a]
+ (when (pos? (.getQueueCount a))
+ (await a))
+ a)
+
+(defn await-for
+ "Blocks the current thread until all actions dispatched thus
+ far (from this thread or agent) to the agents have occurred, or the
+ timeout (in milliseconds) has elapsed. Returns nil if returning due
+ to timeout, non-nil otherwise."
+ [timeout-ms & agents]
+ (io! "await-for in transaction"
+ (when *agent*
+ (throw (new Exception "Can't await in agent action")))
+ (let [latch (new java.util.concurrent.CountDownLatch (count agents))
+ count-down (fn [agent] (. latch (countDown)) agent)]
+ (doseq [agent agents]
+ (send agent count-down))
+ (. latch (await timeout-ms (. java.util.concurrent.TimeUnit MILLISECONDS))))))
+
+(defmacro dotimes
+ "bindings => name n
+
+ Repeatedly executes body (presumably for side-effects) with name
+ bound to integers from 0 through n-1."
+ [bindings & body]
+ (assert-args dotimes
+ (vector? bindings) "a vector for its binding"
+ (= 2 (count bindings)) "exactly 2 forms in binding vector")
+ (let [i (first bindings)
+ n (second bindings)]
+ `(let [n# (int ~n)]
+ (loop [~i (int 0)]
+ (when (< ~i n#)
+ ~@body
+ (recur (unchecked-inc ~i)))))))
+
+(defn import
+ "import-list => (package-symbol class-name-symbols*)
+
+ For each name in class-name-symbols, adds a mapping from name to the
+ class named by package.name to the current namespace. Use :import in the ns
+ macro in preference to calling this directly."
+ [& import-symbols-or-lists]
+ (let [#^clojure.lang.Namespace ns *ns*]
+ (doseq [spec import-symbols-or-lists]
+ (if (symbol? spec)
+ (let [n (name spec)
+ dot (.lastIndexOf n (. clojure.lang.RT (intCast \.)))
+ c (symbol (.substring n (inc dot)))]
+ (. ns (importClass c (. clojure.lang.RT (classForName (name spec))))))
+ (let [pkg (first spec)
+ classes (next spec)]
+ (doseq [c classes]
+ (. ns (importClass c (. clojure.lang.RT (classForName (str pkg "." c)))))))))))
+
+
+(defn into-array
+ "Returns an array with components set to the values in aseq. The array's
+ component type is type if provided, or the type of the first value in
+ aseq if present, or Object. All values in aseq must be compatible with
+ the component type. Class objects for the primitive types can be obtained
+ using, e.g., Integer/TYPE."
+ ([aseq]
+ (clojure.lang.RT/seqToTypedArray (seq aseq)))
+ ([type aseq]
+ (clojure.lang.RT/seqToTypedArray type (seq aseq))))
+
+(defn into
+ "Returns a new coll consisting of to-coll with all of the items of
+ from-coll conjoined."
+ [to from]
+ (let [ret to items (seq from)]
+ (if items
+ (recur (conj ret (first items)) (next items))
+ ret)))
+
+(defn #^{:private true}
+ array [& items]
+ (into-array items))
+
+(defn #^Class class
+ "Returns the Class of x"
+ [#^Object x] (if (nil? x) x (. x (getClass))))
+
+(defn type
+ "Returns the :type metadata of x, or its Class if none"
+ [x]
+ (or (:type (meta x)) (class x)))
+
+(defn num
+ "Coerce to Number"
+ {:tag Number
+ :inline (fn [x] `(. clojure.lang.Numbers (num ~x)))}
+ [x] (. clojure.lang.Numbers (num x)))
+
+(defn int
+ "Coerce to int"
+ {:tag Integer
+ :inline (fn [x] `(. clojure.lang.RT (intCast ~x)))}
+ [x] (. clojure.lang.RT (intCast x)))
+
+(defn long
+ "Coerce to long"
+ {:tag Long
+ :inline (fn [x] `(. clojure.lang.RT (longCast ~x)))}
+ [#^Number x] (. x (longValue)))
+
+(defn float
+ "Coerce to float"
+ {:tag Float
+ :inline (fn [x] `(. clojure.lang.RT (floatCast ~x)))}
+ [#^Number x] (. x (floatValue)))
+
+(defn double
+ "Coerce to double"
+ {:tag Double
+ :inline (fn [x] `(. clojure.lang.RT (doubleCast ~x)))}
+ [#^Number x] (. x (doubleValue)))
+
+(defn short
+ "Coerce to short"
+ {:tag Short
+ :inline (fn [x] `(. clojure.lang.RT (shortCast ~x)))}
+ [#^Number x] (. x (shortValue)))
+
+(defn byte
+ "Coerce to byte"
+ {:tag Byte
+ :inline (fn [x] `(. clojure.lang.RT (byteCast ~x)))}
+ [#^Number x] (. x (byteValue)))
+
+(defn char
+ "Coerce to char"
+ {:tag Character
+ :inline (fn [x] `(. clojure.lang.RT (charCast ~x)))}
+ [x] (. clojure.lang.RT (charCast x)))
+
+(defn boolean
+ "Coerce to boolean"
+ {:tag Boolean
+ :inline (fn [x] `(. clojure.lang.RT (booleanCast ~x)))}
+ [x] (if x true false))
+
+(defn number?
+ "Returns true if x is a Number"
+ [x]
+ (instance? Number x))
+
+(defn integer?
+ "Returns true if n is an integer"
+ [n]
+ (or (instance? Integer n)
+ (instance? Long n)
+ (instance? BigInteger n)
+ (instance? Short n)
+ (instance? Byte n)))
+
+(defn mod
+ "Modulus of num and div. Truncates toward negative infinity."
+ [num div]
+ (let [m (rem num div)]
+ (if (or (zero? m) (pos? (* num div)))
+ m
+ (+ m div))))
+
+(defn ratio?
+ "Returns true if n is a Ratio"
+ [n] (instance? clojure.lang.Ratio n))
+
+(defn decimal?
+ "Returns true if n is a BigDecimal"
+ [n] (instance? BigDecimal n))
+
+(defn float?
+ "Returns true if n is a floating point number"
+ [n]
+ (or (instance? Double n)
+ (instance? Float n)))
+
+(defn rational? [n]
+ "Returns true if n is a rational number"
+ (or (integer? n) (ratio? n) (decimal? n)))
+
+(defn bigint
+ "Coerce to BigInteger"
+ {:tag BigInteger}
+ [x] (cond
+ (instance? BigInteger x) x
+ (decimal? x) (.toBigInteger #^BigDecimal x)
+ (number? x) (BigInteger/valueOf (long x))
+ :else (BigInteger. x)))
+
+(defn bigdec
+ "Coerce to BigDecimal"
+ {:tag BigDecimal}
+ [x] (cond
+ (decimal? x) x
+ (float? x) (. BigDecimal valueOf (double x))
+ (ratio? x) (/ (BigDecimal. (.numerator x)) (.denominator x))
+ (instance? BigInteger x) (BigDecimal. #^BigInteger x)
+ (number? x) (BigDecimal/valueOf (long x))
+ :else (BigDecimal. x)))
+
+(def #^{:private true} print-initialized false)
+
+(defmulti print-method (fn [x writer] (type x)))
+(defmulti print-dup (fn [x writer] (class x)))
+
+(defn pr-on
+ {:private true}
+ [x w]
+ (if *print-dup*
+ (print-dup x w)
+ (print-method x w))
+ nil)
+
+(defn pr
+ "Prints the object(s) to the output stream that is the current value
+ of *out*. Prints the object(s), separated by spaces if there is
+ more than one. By default, pr and prn print in a way that objects
+ can be read by the reader"
+ ([] nil)
+ ([x]
+ (pr-on x *out*))
+ ([x & more]
+ (pr x)
+ (. *out* (append \space))
+ (apply pr more)))
+
+(defn newline
+ "Writes a newline to the output stream that is the current value of
+ *out*"
+ []
+ (. *out* (append \newline))
+ nil)
+
+(defn flush
+ "Flushes the output stream that is the current value of
+ *out*"
+ []
+ (. *out* (flush))
+ nil)
+
+(defn prn
+ "Same as pr followed by (newline). Observes *flush-on-newline*"
+ [& more]
+ (apply pr more)
+ (newline)
+ (when *flush-on-newline*
+ (flush)))
+
+(defn print
+ "Prints the object(s) to the output stream that is the current value
+ of *out*. print and println produce output for human consumption."
+ [& more]
+ (binding [*print-readably* nil]
+ (apply pr more)))
+
+(defn println
+ "Same as print followed by (newline)"
+ [& more]
+ (binding [*print-readably* nil]
+ (apply prn more)))
+
+
+(defn read
+ "Reads the next object from stream, which must be an instance of
+ java.io.PushbackReader or some derivee. stream defaults to the
+ current value of *in* ."
+ ([]
+ (read *in*))
+ ([stream]
+ (read stream true nil))
+ ([stream eof-error? eof-value]
+ (read stream eof-error? eof-value false))
+ ([stream eof-error? eof-value recursive?]
+ (. clojure.lang.LispReader (read stream (boolean eof-error?) eof-value recursive?))))
+
+(defn read-line
+ "Reads the next line from stream that is the current value of *in* ."
+ []
+ (if (instance? clojure.lang.LineNumberingPushbackReader *in*)
+ (.readLine #^clojure.lang.LineNumberingPushbackReader *in*)
+ (.readLine #^java.io.BufferedReader *in*)))
+
+(defn read-string
+ "Reads one object from the string s"
+ [s] (clojure.lang.RT/readString s))
+
+(defn subvec
+ "Returns a persistent vector of the items in vector from
+ start (inclusive) to end (exclusive). If end is not supplied,
+ defaults to (count vector). This operation is O(1) and very fast, as
+ the resulting vector shares structure with the original and no
+ trimming is done."
+ ([v start]
+ (subvec v start (count v)))
+ ([v start end]
+ (. clojure.lang.RT (subvec v start end))))
+
+(defmacro with-open
+ "bindings => [name init ...]
+
+ Evaluates body in a try expression with names bound to the values
+ of the inits, and a finally clause that calls (.close name) on each
+ name in reverse order."
+ [bindings & body]
+ (assert-args with-open
+ (vector? bindings) "a vector for its binding"
+ (even? (count bindings)) "an even number of forms in binding vector")
+ (cond
+ (= (count bindings) 0) `(do ~@body)
+ (symbol? (bindings 0)) `(let ~(subvec bindings 0 2)
+ (try
+ (with-open ~(subvec bindings 2) ~@body)
+ (finally
+ (. ~(bindings 0) close))))
+ :else (throw (IllegalArgumentException.
+ "with-open only allows Symbols in bindings"))))
+
+(defmacro doto
+ "Evaluates x then calls all of the methods and functions with the
+ value of x supplied at the from of the given arguments. The forms
+ are evaluated in order. Returns x.
+
+ (doto (new java.util.HashMap) (.put \"a\" 1) (.put \"b\" 2))"
+ [x & forms]
+ (let [gx (gensym)]
+ `(let [~gx ~x]
+ ~@(map (fn [f]
+ (if (seq? f)
+ `(~(first f) ~gx ~@(next f))
+ `(~f ~gx)))
+ forms)
+ ~gx)))
+
+(defmacro memfn
+ "Expands into code that creates a fn that expects to be passed an
+ object and any args and calls the named instance method on the
+ object passing the args. Use when you want to treat a Java method as
+ a first-class fn."
+ [name & args]
+ `(fn [target# ~@args]
+ (. target# (~name ~@args))))
+
+(defmacro time
+ "Evaluates expr and prints the time it took. Returns the value of
+ expr."
+ [expr]
+ `(let [start# (. System (nanoTime))
+ ret# ~expr]
+ (prn (str "Elapsed time: " (/ (double (- (. System (nanoTime)) start#)) 1000000.0) " msecs"))
+ ret#))
+
+
+
+(import '(java.lang.reflect Array))
+
+(defn alength
+ "Returns the length of the Java array. Works on arrays of all
+ types."
+ {:inline (fn [a] `(. clojure.lang.RT (alength ~a)))}
+ [array] (. clojure.lang.RT (alength array)))
+
+(defn aclone
+ "Returns a clone of the Java array. Works on arrays of known
+ types."
+ {:inline (fn [a] `(. clojure.lang.RT (aclone ~a)))}
+ [array] (. clojure.lang.RT (aclone array)))
+
+(defn aget
+ "Returns the value at the index/indices. Works on Java arrays of all
+ types."
+ {:inline (fn [a i] `(. clojure.lang.RT (aget ~a ~i)))
+ :inline-arities #{2}}
+ ([array idx]
+ (clojure.lang.Reflector/prepRet (. Array (get array idx))))
+ ([array idx & idxs]
+ (apply aget (aget array idx) idxs)))
+
+(defn aset
+ "Sets the value at the index/indices. Works on Java arrays of
+ reference types. Returns val."
+ {:inline (fn [a i v] `(. clojure.lang.RT (aset ~a ~i ~v)))
+ :inline-arities #{3}}
+ ([array idx val]
+ (. Array (set array idx val))
+ val)
+ ([array idx idx2 & idxv]
+ (apply aset (aget array idx) idx2 idxv)))
+
+(defmacro
+ #^{:private true}
+ def-aset [name method coerce]
+ `(defn ~name
+ {:arglists '([~'array ~'idx ~'val] [~'array ~'idx ~'idx2 & ~'idxv])}
+ ([array# idx# val#]
+ (. Array (~method array# idx# (~coerce val#)))
+ val#)
+ ([array# idx# idx2# & idxv#]
+ (apply ~name (aget array# idx#) idx2# idxv#))))
+
+(def-aset
+ #^{:doc "Sets the value at the index/indices. Works on arrays of int. Returns val."}
+ aset-int setInt int)
+
+(def-aset
+ #^{:doc "Sets the value at the index/indices. Works on arrays of long. Returns val."}
+ aset-long setLong long)
+
+(def-aset
+ #^{:doc "Sets the value at the index/indices. Works on arrays of boolean. Returns val."}
+ aset-boolean setBoolean boolean)
+
+(def-aset
+ #^{:doc "Sets the value at the index/indices. Works on arrays of float. Returns val."}
+ aset-float setFloat float)
+
+(def-aset
+ #^{:doc "Sets the value at the index/indices. Works on arrays of double. Returns val."}
+ aset-double setDouble double)
+
+(def-aset
+ #^{:doc "Sets the value at the index/indices. Works on arrays of short. Returns val."}
+ aset-short setShort short)
+
+(def-aset
+ #^{:doc "Sets the value at the index/indices. Works on arrays of byte. Returns val."}
+ aset-byte setByte byte)
+
+(def-aset
+ #^{:doc "Sets the value at the index/indices. Works on arrays of char. Returns val."}
+ aset-char setChar char)
+
+(defn make-array
+ "Creates and returns an array of instances of the specified class of
+ the specified dimension(s). Note that a class object is required.
+ Class objects can be obtained by using their imported or
+ fully-qualified name. Class objects for the primitive types can be
+ obtained using, e.g., Integer/TYPE."
+ ([#^Class type len]
+ (. Array (newInstance type (int len))))
+ ([#^Class type dim & more-dims]
+ (let [dims (cons dim more-dims)
+ #^"[I" dimarray (make-array (. Integer TYPE) (count dims))]
+ (dotimes [i (alength dimarray)]
+ (aset-int dimarray i (nth dims i)))
+ (. Array (newInstance type dimarray)))))
+
+(defn to-array-2d
+ "Returns a (potentially-ragged) 2-dimensional array of Objects
+ containing the contents of coll, which can be any Collection of any
+ Collection."
+ {:tag "[[Ljava.lang.Object;"}
+ [#^java.util.Collection coll]
+ (let [ret (make-array (. Class (forName "[Ljava.lang.Object;")) (. coll (size)))]
+ (loop [i 0 xs (seq coll)]
+ (when xs
+ (aset ret i (to-array (first xs)))
+ (recur (inc i) (next xs))))
+ ret))
+
+(defn macroexpand-1
+ "If form represents a macro form, returns its expansion,
+ else returns form."
+ [form]
+ (. clojure.lang.Compiler (macroexpand1 form)))
+
+(defn macroexpand
+ "Repeatedly calls macroexpand-1 on form until it no longer
+ represents a macro form, then returns it. Note neither
+ macroexpand-1 nor macroexpand expand macros in subforms."
+ [form]
+ (let [ex (macroexpand-1 form)]
+ (if (identical? ex form)
+ form
+ (macroexpand ex))))
+
+(defn create-struct
+ "Returns a structure basis object."
+ [& keys]
+ (. clojure.lang.PersistentStructMap (createSlotMap keys)))
+
+(defmacro defstruct
+ "Same as (def name (create-struct keys...))"
+ [name & keys]
+ `(def ~name (create-struct ~@keys)))
+
+(defn struct-map
+ "Returns a new structmap instance with the keys of the
+ structure-basis. keyvals may contain all, some or none of the basis
+ keys - where values are not supplied they will default to nil.
+ keyvals can also contain keys not in the basis."
+ [s & inits]
+ (. clojure.lang.PersistentStructMap (create s inits)))
+
+(defn struct
+ "Returns a new structmap instance with the keys of the
+ structure-basis. vals must be supplied for basis keys in order -
+ where values are not supplied they will default to nil."
+ [s & vals]
+ (. clojure.lang.PersistentStructMap (construct s vals)))
+
+(defn accessor
+ "Returns a fn that, given an instance of a structmap with the basis,
+ returns the value at the key. The key must be in the basis. The
+ returned function should be (slightly) more efficient than using
+ get, but such use of accessors should be limited to known
+ performance-critical areas."
+ [s key]
+ (. clojure.lang.PersistentStructMap (getAccessor s key)))
+
+(defn load-reader
+ "Sequentially read and evaluate the set of forms contained in the
+ stream/file"
+ [rdr] (. clojure.lang.Compiler (load rdr)))
+
+(defn load-string
+ "Sequentially read and evaluate the set of forms contained in the
+ string"
+ [s]
+ (let [rdr (-> (java.io.StringReader. s)
+ (clojure.lang.LineNumberingPushbackReader.))]
+ (load-reader rdr)))
+
+(defn set
+ "Returns a set of the distinct elements of coll."
+ [coll] (apply hash-set coll))
+
+(defn #^{:private true}
+ filter-key [keyfn pred amap]
+ (loop [ret {} es (seq amap)]
+ (if es
+ (if (pred (keyfn (first es)))
+ (recur (assoc ret (key (first es)) (val (first es))) (next es))
+ (recur ret (next es)))
+ ret)))
+
+(defn find-ns
+ "Returns the namespace named by the symbol or nil if it doesn't exist."
+ [sym] (clojure.lang.Namespace/find sym))
+
+(defn create-ns
+ "Create a new namespace named by the symbol if one doesn't already
+ exist, returns it or the already-existing namespace of the same
+ name."
+ [sym] (clojure.lang.Namespace/findOrCreate sym))
+
+(defn remove-ns
+ "Removes the namespace named by the symbol. Use with caution.
+ Cannot be used to remove the clojure namespace."
+ [sym] (clojure.lang.Namespace/remove sym))
+
+(defn all-ns
+ "Returns a sequence of all namespaces."
+ [] (clojure.lang.Namespace/all))
+
+(defn #^clojure.lang.Namespace the-ns
+ "If passed a namespace, returns it. Else, when passed a symbol,
+ returns the namespace named by it, throwing an exception if not
+ found."
+ [x]
+ (if (instance? clojure.lang.Namespace x)
+ x
+ (or (find-ns x) (throw (Exception. (str "No namespace: " x " found"))))))
+
+(defn ns-name
+ "Returns the name of the namespace, a symbol."
+ [ns]
+ (.getName (the-ns ns)))
+
+(defn ns-map
+ "Returns a map of all the mappings for the namespace."
+ [ns]
+ (.getMappings (the-ns ns)))
+
+(defn ns-unmap
+ "Removes the mappings for the symbol from the namespace."
+ [ns sym]
+ (.unmap (the-ns ns) sym))
+
+;(defn export [syms]
+; (doseq [sym syms]
+; (.. *ns* (intern sym) (setExported true))))
+
+(defn ns-publics
+ "Returns a map of the public intern mappings for the namespace."
+ [ns]
+ (let [ns (the-ns ns)]
+ (filter-key val (fn [#^clojure.lang.Var v] (and (instance? clojure.lang.Var v)
+ (= ns (.ns v))
+ (.isPublic v)))
+ (ns-map ns))))
+
+(defn ns-imports
+ "Returns a map of the import mappings for the namespace."
+ [ns]
+ (filter-key val (partial instance? Class) (ns-map ns)))
+
+(defn refer
+ "refers to all public vars of ns, subject to filters.
+ filters can include at most one each of:
+
+ :exclude list-of-symbols
+ :only list-of-symbols
+ :rename map-of-fromsymbol-tosymbol
+
+ For each public interned var in the namespace named by the symbol,
+ adds a mapping from the name of the var to the var to the current
+ namespace. Throws an exception if name is already mapped to
+ something else in the current namespace. Filters can be used to
+ select a subset, via inclusion or exclusion, or to provide a mapping
+ to a symbol different from the var's name, in order to prevent
+ clashes. Use :use in the ns macro in preference to calling this directly."
+ [ns-sym & filters]
+ (let [ns (or (find-ns ns-sym) (throw (new Exception (str "No namespace: " ns-sym))))
+ fs (apply hash-map filters)
+ nspublics (ns-publics ns)
+ rename (or (:rename fs) {})
+ exclude (set (:exclude fs))
+ to-do (or (:only fs) (keys nspublics))]
+ (doseq [sym to-do]
+ (when-not (exclude sym)
+ (let [v (nspublics sym)]
+ (when-not v
+ (throw (new java.lang.IllegalAccessError (str sym " is not public"))))
+ (. *ns* (refer (or (rename sym) sym) v)))))))
+
+(defn ns-refers
+ "Returns a map of the refer mappings for the namespace."
+ [ns]
+ (let [ns (the-ns ns)]
+ (filter-key val (fn [#^clojure.lang.Var v] (and (instance? clojure.lang.Var v)
+ (not= ns (.ns v))))
+ (ns-map ns))))
+
+(defn ns-interns
+ "Returns a map of the intern mappings for the namespace."
+ [ns]
+ (let [ns (the-ns ns)]
+ (filter-key val (fn [#^clojure.lang.Var v] (and (instance? clojure.lang.Var v)
+ (= ns (.ns v))))
+ (ns-map ns))))
+
+(defn alias
+ "Add an alias in the current namespace to another
+ namespace. Arguments are two symbols: the alias to be used, and
+ the symbolic name of the target namespace. Use :as in the ns macro in preference
+ to calling this directly."
+ [alias namespace-sym]
+ (.addAlias *ns* alias (find-ns namespace-sym)))
+
+(defn ns-aliases
+ "Returns a map of the aliases for the namespace."
+ [ns]
+ (.getAliases (the-ns ns)))
+
+(defn ns-unalias
+ "Removes the alias for the symbol from the namespace."
+ [ns sym]
+ (.removeAlias (the-ns ns) sym))
+
+(defn take-nth
+ "Returns a lazy seq of every nth item in coll."
+ [n coll]
+ (lazy-seq
+ (when-let [s (seq coll)]
+ (cons (first s) (take-nth n (drop n s))))))
+
+(defn interleave
+ "Returns a lazy seq of the first item in each coll, then the second
+ etc."
+ [& colls]
+ (apply concat (apply map list colls)))
+
+(defn var-get
+ "Gets the value in the var object"
+ [#^clojure.lang.Var x] (. x (get)))
+
+(defn var-set
+ "Sets the value in the var object to val. The var must be
+ thread-locally bound."
+ [#^clojure.lang.Var x val] (. x (set val)))
+
+(defmacro with-local-vars
+ "varbinding=> symbol init-expr
+
+ Executes the exprs in a context in which the symbols are bound to
+ vars with per-thread bindings to the init-exprs. The symbols refer
+ to the var objects themselves, and must be accessed with var-get and
+ var-set"
+ [name-vals-vec & body]
+ (assert-args with-local-vars
+ (vector? name-vals-vec) "a vector for its binding"
+ (even? (count name-vals-vec)) "an even number of forms in binding vector")
+ `(let [~@(interleave (take-nth 2 name-vals-vec)
+ (repeat '(. clojure.lang.Var (create))))]
+ (. clojure.lang.Var (pushThreadBindings (hash-map ~@name-vals-vec)))
+ (try
+ ~@body
+ (finally (. clojure.lang.Var (popThreadBindings))))))
+
+(defn ns-resolve
+ "Returns the var or Class to which a symbol will be resolved in the
+ namespace, else nil. Note that if the symbol is fully qualified,
+ the var/Class to which it resolves need not be present in the
+ namespace."
+ [ns sym]
+ (clojure.lang.Compiler/maybeResolveIn (the-ns ns) sym))
+
+(defn resolve
+ "same as (ns-resolve *ns* symbol)"
+ [sym] (ns-resolve *ns* sym))
+
+(defn array-map
+ "Constructs an array-map."
+ ([] (. clojure.lang.PersistentArrayMap EMPTY))
+ ([& keyvals] (new clojure.lang.PersistentArrayMap (to-array keyvals))))
+
+(defn nthnext
+ "Returns the nth next of coll, (seq coll) when n is 0."
+ [coll n]
+ (loop [n n xs (seq coll)]
+ (if (and xs (pos? n))
+ (recur (dec n) (next xs))
+ xs)))
+
+
+;redefine let and loop with destructuring
+(defn destructure [bindings]
+ (let [bmap (apply array-map bindings)
+ pb (fn pb [bvec b v]
+ (let [pvec
+ (fn [bvec b val]
+ (let [gvec (gensym "vec__")]
+ (loop [ret (-> bvec (conj gvec) (conj val))
+ n 0
+ bs b
+ seen-rest? false]
+ (if (seq bs)
+ (let [firstb (first bs)]
+ (cond
+ (= firstb '&) (recur (pb ret (second bs) (list `nthnext gvec n))
+ n
+ (nnext bs)
+ true)
+ (= firstb :as) (pb ret (second bs) gvec)
+ :else (if seen-rest?
+ (throw (new Exception "Unsupported binding form, only :as can follow & parameter"))
+ (recur (pb ret firstb (list `nth gvec n nil))
+ (inc n)
+ (next bs)
+ seen-rest?))))
+ ret))))
+ pmap
+ (fn [bvec b v]
+ (let [gmap (or (:as b) (gensym "map__"))
+ defaults (:or b)]
+ (loop [ret (-> bvec (conj gmap) (conj v))
+ bes (reduce
+ (fn [bes entry]
+ (reduce #(assoc %1 %2 ((val entry) %2))
+ (dissoc bes (key entry))
+ ((key entry) bes)))
+ (dissoc b :as :or)
+ {:keys #(keyword (str %)), :strs str, :syms #(list `quote %)})]
+ (if (seq bes)
+ (let [bb (key (first bes))
+ bk (val (first bes))
+ has-default (contains? defaults bb)]
+ (recur (pb ret bb (if has-default
+ (list `get gmap bk (defaults bb))
+ (list `get gmap bk)))
+ (next bes)))
+ ret))))]
+ (cond
+ (symbol? b) (-> bvec (conj b) (conj v))
+ (vector? b) (pvec bvec b v)
+ (map? b) (pmap bvec b v)
+ :else (throw (new Exception (str "Unsupported binding form: " b))))))
+ process-entry (fn [bvec b] (pb bvec (key b) (val b)))]
+ (if (every? symbol? (keys bmap))
+ bindings
+ (reduce process-entry [] bmap))))
+
+(defmacro let
+ "Evaluates the exprs in a lexical context in which the symbols in
+ the binding-forms are bound to their respective init-exprs or parts
+ therein."
+ [bindings & body]
+ (assert-args let
+ (vector? bindings) "a vector for its binding"
+ (even? (count bindings)) "an even number of forms in binding vector")
+ `(let* ~(destructure bindings) ~@body))
+
+;redefine fn with destructuring
+(defmacro fn
+ "(fn name? [params* ] exprs*)
+ (fn name? ([params* ] exprs*)+)
+
+ params => positional-params* , or positional-params* & next-param
+ positional-param => binding-form
+ next-param => binding-form
+ name => symbol
+
+ Defines a function"
+ [& sigs]
+ (let [name (if (symbol? (first sigs)) (first sigs) nil)
+ sigs (if name (next sigs) sigs)
+ sigs (if (vector? (first sigs)) (list sigs) sigs)
+ psig (fn [sig]
+ (let [[params & body] sig]
+ (if (every? symbol? params)
+ sig
+ (loop [params params
+ new-params []
+ lets []]
+ (if params
+ (if (symbol? (first params))
+ (recur (next params) (conj new-params (first params)) lets)
+ (let [gparam (gensym "p__")]
+ (recur (next params) (conj new-params gparam)
+ (-> lets (conj (first params)) (conj gparam)))))
+ `(~new-params
+ (let ~lets
+ ~@body)))))))
+ new-sigs (map psig sigs)]
+ (with-meta
+ (if name
+ (list* 'fn* name new-sigs)
+ (cons 'fn* new-sigs))
+ *macro-meta*)))
+
+(defmacro loop
+ "Evaluates the exprs in a lexical context in which the symbols in
+ the binding-forms are bound to their respective init-exprs or parts
+ therein. Acts as a recur target."
+ [bindings & body]
+ (assert-args loop
+ (vector? bindings) "a vector for its binding"
+ (even? (count bindings)) "an even number of forms in binding vector")
+ (let [db (destructure bindings)]
+ (if (= db bindings)
+ `(loop* ~bindings ~@body)
+ (let [vs (take-nth 2 (drop 1 bindings))
+ bs (take-nth 2 bindings)
+ gs (map (fn [b] (if (symbol? b) b (gensym))) bs)
+ bfs (reduce (fn [ret [b v g]]
+ (if (symbol? b)
+ (conj ret g v)
+ (conj ret g v b g)))
+ [] (map vector bs vs gs))]
+ `(let ~bfs
+ (loop* ~(vec (interleave gs gs))
+ (let ~(vec (interleave bs gs))
+ ~@body)))))))
+
+(defmacro when-first
+ "bindings => x xs
+
+ Same as (when (seq xs) (let [x (first xs)] body))"
+ [bindings & body]
+ (assert-args when-first
+ (vector? bindings) "a vector for its binding"
+ (= 2 (count bindings)) "exactly 2 forms in binding vector")
+ (let [[x xs] bindings]
+ `(when (seq ~xs)
+ (let [~x (first ~xs)]
+ ~@body))))
+
+(defmacro lazy-cat
+ "Expands to code which yields a lazy sequence of the concatenation
+ of the supplied colls. Each coll expr is not evaluated until it is
+ needed.
+
+ (lazy-cat xs ys zs) === (concat (lazy-seq xs) (lazy-seq ys) (lazy-seq zs))"
+ [& colls]
+ `(concat ~@(map #(list `lazy-seq %) colls)))
+
+(defmacro for
+ "List comprehension. Takes a vector of one or more
+ binding-form/collection-expr pairs, each followed by zero or more
+ modifiers, and yields a lazy sequence of evaluations of expr.
+ Collections are iterated in a nested fashion, rightmost fastest,
+ and nested coll-exprs can refer to bindings created in prior
+ binding-forms. Supported modifiers are: :let [binding-form expr ...],
+ :while test, :when test.
+
+ (take 100 (for [x (range 100000000) y (range 1000000) :while (< y x)] [x y]))"
+ [seq-exprs body-expr]
+ (assert-args for
+ (vector? seq-exprs) "a vector for its binding"
+ (even? (count seq-exprs)) "an even number of forms in binding vector")
+ (let [to-groups (fn [seq-exprs]
+ (reduce (fn [groups [k v]]
+ (if (keyword? k)
+ (conj (pop groups) (conj (peek groups) [k v]))
+ (conj groups [k v])))
+ [] (partition 2 seq-exprs)))
+ err (fn [& msg] (throw (IllegalArgumentException. (apply str msg))))
+ emit-bind (fn emit-bind [[[bind expr & mod-pairs]
+ & [[_ next-expr] :as next-groups]]]
+ (let [giter (gensym "iter__")
+ gxs (gensym "s__")
+ do-mod (fn do-mod [[[k v :as pair] & etc]]
+ (cond
+ (= k :let) `(let ~v ~(do-mod etc))
+ (= k :while) `(when ~v ~(do-mod etc))
+ (= k :when) `(if ~v
+ ~(do-mod etc)
+ (recur (rest ~gxs)))
+ (keyword? k) (err "Invalid 'for' keyword " k)
+ next-groups
+ `(let [iterys# ~(emit-bind next-groups)
+ fs# (seq (iterys# ~next-expr))]
+ (if fs#
+ (concat fs# (~giter (rest ~gxs)))
+ (recur (rest ~gxs))))
+ :else `(cons ~body-expr
+ (~giter (rest ~gxs)))))]
+ `(fn ~giter [~gxs]
+ (lazy-seq
+ (loop [~gxs ~gxs]
+ (when-first [~bind ~gxs]
+ ~(do-mod mod-pairs)))))))]
+ `(let [iter# ~(emit-bind (to-groups seq-exprs))]
+ (iter# ~(second seq-exprs)))))
+
+(defmacro comment
+ "Ignores body, yields nil"
+ [& body])
+
+(defmacro with-out-str
+ "Evaluates exprs in a context in which *out* is bound to a fresh
+ StringWriter. Returns the string created by any nested printing
+ calls."
+ [& body]
+ `(let [s# (new java.io.StringWriter)]
+ (binding [*out* s#]
+ ~@body
+ (str s#))))
+
+(defmacro with-in-str
+ "Evaluates body in a context in which *in* is bound to a fresh
+ StringReader initialized with the string s."
+ [s & body]
+ `(with-open [s# (-> (java.io.StringReader. ~s) clojure.lang.LineNumberingPushbackReader.)]
+ (binding [*in* s#]
+ ~@body)))
+
+(defn pr-str
+ "pr to a string, returning it"
+ {:tag String}
+ [& xs]
+ (with-out-str
+ (apply pr xs)))
+
+(defn prn-str
+ "prn to a string, returning it"
+ {:tag String}
+ [& xs]
+ (with-out-str
+ (apply prn xs)))
+
+(defn print-str
+ "print to a string, returning it"
+ {:tag String}
+ [& xs]
+ (with-out-str
+ (apply print xs)))
+
+(defn println-str
+ "println to a string, returning it"
+ {:tag String}
+ [& xs]
+ (with-out-str
+ (apply println xs)))
+
+(defmacro assert
+ "Evaluates expr and throws an exception if it does not evaluate to
+ logical true."
+ [x]
+ `(when-not ~x
+ (throw (new Exception (str "Assert failed: " (pr-str '~x))))))
+
+(defn test
+ "test [v] finds fn at key :test in var metadata and calls it,
+ presuming failure will throw exception"
+ [v]
+ (let [f (:test ^v)]
+ (if f
+ (do (f) :ok)
+ :no-test)))
+
+(defn re-pattern
+ "Returns an instance of java.util.regex.Pattern, for use, e.g. in
+ re-matcher."
+ {:tag java.util.regex.Pattern}
+ [s] (if (instance? java.util.regex.Pattern s)
+ s
+ (. java.util.regex.Pattern (compile s))))
+
+(defn re-matcher
+ "Returns an instance of java.util.regex.Matcher, for use, e.g. in
+ re-find."
+ {:tag java.util.regex.Matcher}
+ [#^java.util.regex.Pattern re s]
+ (. re (matcher s)))
+
+(defn re-groups
+ "Returns the groups from the most recent match/find. If there are no
+ nested groups, returns a string of the entire match. If there are
+ nested groups, returns a vector of the groups, the first element
+ being the entire match."
+ [#^java.util.regex.Matcher m]
+ (let [gc (. m (groupCount))]
+ (if (zero? gc)
+ (. m (group))
+ (loop [ret [] c 0]
+ (if (<= c gc)
+ (recur (conj ret (. m (group c))) (inc c))
+ ret)))))
+
+(defn re-seq
+ "Returns a lazy sequence of successive matches of pattern in string,
+ using java.util.regex.Matcher.find(), each such match processed with
+ re-groups."
+ [#^java.util.regex.Pattern re s]
+ (let [m (re-matcher re s)]
+ ((fn step []
+ (lazy-seq
+ (when (. m (find))
+ (cons (re-groups m) (step))))))))
+
+(defn re-matches
+ "Returns the match, if any, of string to pattern, using
+ java.util.regex.Matcher.matches(). Uses re-groups to return the
+ groups."
+ [#^java.util.regex.Pattern re s]
+ (let [m (re-matcher re s)]
+ (when (. m (matches))
+ (re-groups m))))
+
+
+(defn re-find
+ "Returns the next regex match, if any, of string to pattern, using
+ java.util.regex.Matcher.find(). Uses re-groups to return the
+ groups."
+ ([#^java.util.regex.Matcher m]
+ (when (. m (find))
+ (re-groups m)))
+ ([#^java.util.regex.Pattern re s]
+ (let [m (re-matcher re s)]
+ (re-find m))))
+
+(defn rand
+ "Returns a random floating point number between 0 (inclusive) and
+ n (default 1) (exclusive)."
+ ([] (. Math (random)))
+ ([n] (* n (rand))))
+
+(defn rand-int
+ "Returns a random integer between 0 (inclusive) and n (exclusive)."
+ [n] (int (rand n)))
+
+(defmacro defn-
+ "same as defn, yielding non-public def"
+ [name & decls]
+ (list* `defn (with-meta name (assoc (meta name) :private true)) decls))
+
+(defn print-doc [v]
+ (println "-------------------------")
+ (println (str (ns-name (:ns ^v)) "/" (:name ^v)))
+ (prn (:arglists ^v))
+ (when (:macro ^v)
+ (println "Macro"))
+ (println " " (:doc ^v)))
+
+(defn find-doc
+ "Prints documentation for any var whose documentation or name
+ contains a match for re-string-or-pattern"
+ [re-string-or-pattern]
+ (let [re (re-pattern re-string-or-pattern)]
+ (doseq [ns (all-ns)
+ v (sort-by (comp :name meta) (vals (ns-interns ns)))
+ :when (and (:doc ^v)
+ (or (re-find (re-matcher re (:doc ^v)))
+ (re-find (re-matcher re (str (:name ^v))))))]
+ (print-doc v))))
+
+(defn special-form-anchor
+ "Returns the anchor tag on http://clojure.org/special_forms for the
+ special form x, or nil"
+ [x]
+ (#{'. 'def 'do 'fn 'if 'let 'loop 'monitor-enter 'monitor-exit 'new
+ 'quote 'recur 'set! 'throw 'try 'var} x))
+
+(defn syntax-symbol-anchor
+ "Returns the anchor tag on http://clojure.org/special_forms for the
+ special form that uses syntax symbol x, or nil"
+ [x]
+ ({'& 'fn 'catch 'try 'finally 'try} x))
+
+(defn print-special-doc
+ [name type anchor]
+ (println "-------------------------")
+ (println name)
+ (println type)
+ (println (str " Please see http://clojure.org/special_forms#" anchor)))
+
+(defn print-namespace-doc
+ "Print the documentation string of a Namespace."
+ [nspace]
+ (println "-------------------------")
+ (println (str (ns-name nspace)))
+ (println " " (:doc ^nspace)))
+
+(defmacro doc
+ "Prints documentation for a var or special form given its name"
+ [name]
+ (cond
+ (special-form-anchor `~name)
+ `(print-special-doc '~name "Special Form" (special-form-anchor '~name))
+ (syntax-symbol-anchor `~name)
+ `(print-special-doc '~name "Syntax Symbol" (syntax-symbol-anchor '~name))
+ :else
+ (let [nspace (find-ns name)]
+ (if nspace
+ `(print-namespace-doc ~nspace)
+ `(print-doc (var ~name))))))
+
+ (defn tree-seq
+ "Returns a lazy sequence of the nodes in a tree, via a depth-first walk.
+ branch? must be a fn of one arg that returns true if passed a node
+ that can have children (but may not). children must be a fn of one
+ arg that returns a sequence of the children. Will only be called on
+ nodes for which branch? returns true. Root is the root node of the
+ tree."
+ [branch? children root]
+ (let [walk (fn walk [node]
+ (lazy-seq
+ (cons node
+ (when (branch? node)
+ (mapcat walk (children node))))))]
+ (walk root)))
+
+(defn file-seq
+ "A tree seq on java.io.Files"
+ [dir]
+ (tree-seq
+ (fn [#^java.io.File f] (. f (isDirectory)))
+ (fn [#^java.io.File d] (seq (. d (listFiles))))
+ dir))
+
+(defn xml-seq
+ "A tree seq on the xml elements as per xml/parse"
+ [root]
+ (tree-seq
+ (complement string?)
+ (comp seq :content)
+ root))
+
+(defn special-symbol?
+ "Returns true if s names a special form"
+ [s]
+ (contains? (. clojure.lang.Compiler specials) s))
+
+(defn var?
+ "Returns true if v is of type clojure.lang.Var"
+ [v] (instance? clojure.lang.Var v))
+
+(defn slurp
+ "Reads the file named by f into a string and returns it."
+ [#^String f]
+ (with-open [r (new java.io.BufferedReader (new java.io.FileReader f))]
+ (let [sb (new StringBuilder)]
+ (loop [c (. r (read))]
+ (if (neg? c)
+ (str sb)
+ (do
+ (. sb (append (char c)))
+ (recur (. r (read)))))))))
+
+(defn subs
+ "Returns the substring of s beginning at start inclusive, and ending
+ at end (defaults to length of string), exclusive."
+ ([#^String s start] (. s (substring start)))
+ ([#^String s start end] (. s (substring start end))))
+
+(defn max-key
+ "Returns the x for which (k x), a number, is greatest."
+ ([k x] x)
+ ([k x y] (if (> (k x) (k y)) x y))
+ ([k x y & more]
+ (reduce #(max-key k %1 %2) (max-key k x y) more)))
+
+(defn min-key
+ "Returns the x for which (k x), a number, is least."
+ ([k x] x)
+ ([k x y] (if (< (k x) (k y)) x y))
+ ([k x y & more]
+ (reduce #(min-key k %1 %2) (min-key k x y) more)))
+
+(defn distinct
+ "Returns a lazy sequence of the elements of coll with duplicates removed"
+ [coll]
+ (let [step (fn step [xs seen]
+ (lazy-seq
+ ((fn [[f :as xs] seen]
+ (when-let [s (seq xs)]
+ (if (contains? seen f)
+ (recur (rest s) seen)
+ (cons f (step (rest s) (conj seen f))))))
+ xs seen)))]
+ (step coll #{})))
+
+
+
+(defn replace
+ "Given a map of replacement pairs and a vector/collection, returns a
+ vector/seq with any elements = a key in smap replaced with the
+ corresponding val in smap"
+ [smap coll]
+ (if (vector? coll)
+ (reduce (fn [v i]
+ (if-let [e (find smap (nth v i))]
+ (assoc v i (val e))
+ v))
+ coll (range (count coll)))
+ (map #(if-let [e (find smap %)] (val e) %) coll)))
+
+(defmacro dosync
+ "Runs the exprs (in an implicit do) in a transaction that encompasses
+ exprs and any nested calls. Starts a transaction if none is already
+ running on this thread. Any uncaught exception will abort the
+ transaction and flow out of dosync. The exprs may be run more than
+ once, but any effects on Refs will be atomic."
+ [& exprs]
+ `(sync nil ~@exprs))
+
+(defmacro with-precision
+ "Sets the precision and rounding mode to be used for BigDecimal operations.
+
+ Usage: (with-precision 10 (/ 1M 3))
+ or: (with-precision 10 :rounding HALF_DOWN (/ 1M 3))
+
+ The rounding mode is one of CEILING, FLOOR, HALF_UP, HALF_DOWN,
+ HALF_EVEN, UP, DOWN and UNNECESSARY; it defaults to HALF_UP."
+ [precision & exprs]
+ (let [[body rm] (if (= (first exprs) :rounding)
+ [(next (next exprs))
+ `((. java.math.RoundingMode ~(second exprs)))]
+ [exprs nil])]
+ `(binding [*math-context* (java.math.MathContext. ~precision ~@rm)]
+ ~@body)))
+
+(defn bound-fn
+ {:private true}
+ [#^clojure.lang.Sorted sc test key]
+ (fn [e]
+ (test (.. sc comparator (compare (. sc entryKey e) key)) 0)))
+
+(defn subseq
+ "sc must be a sorted collection, test(s) one of <, <=, > or
+ >=. Returns a seq of those entries with keys ek for
+ which (test (.. sc comparator (compare ek key)) 0) is true"
+ ([#^clojure.lang.Sorted sc test key]
+ (let [include (bound-fn sc test key)]
+ (if (#{> >=} test)
+ (when-let [[e :as s] (. sc seqFrom key true)]
+ (if (include e) s (next s)))
+ (take-while include (. sc seq true)))))
+ ([#^clojure.lang.Sorted sc start-test start-key end-test end-key]
+ (when-let [[e :as s] (. sc seqFrom start-key true)]
+ (take-while (bound-fn sc end-test end-key)
+ (if ((bound-fn sc start-test start-key) e) s (next s))))))
+
+(defn rsubseq
+ "sc must be a sorted collection, test(s) one of <, <=, > or
+ >=. Returns a reverse seq of those entries with keys ek for
+ which (test (.. sc comparator (compare ek key)) 0) is true"
+ ([#^clojure.lang.Sorted sc test key]
+ (let [include (bound-fn sc test key)]
+ (if (#{< <=} test)
+ (when-let [[e :as s] (. sc seqFrom key false)]
+ (if (include e) s (next s)))
+ (take-while include (. sc seq false)))))
+ ([#^clojure.lang.Sorted sc start-test start-key end-test end-key]
+ (when-let [[e :as s] (. sc seqFrom end-key false)]
+ (take-while (bound-fn sc start-test start-key)
+ (if ((bound-fn sc end-test end-key) e) s (next s))))))
+
+(defn repeatedly
+ "Takes a function of no args, presumably with side effects, and returns an infinite
+ lazy sequence of calls to it"
+ [f] (lazy-seq (cons (f) (repeatedly f))))
+
+(defn add-classpath
+ "Adds the url (String or URL object) to the classpath per URLClassLoader.addURL"
+ [url] (. clojure.lang.RT addURL url))
+
+
+
+(defn hash
+ "Returns the hash code of its argument"
+ [x] (. clojure.lang.Util (hash x)))
+
+(defn interpose
+ "Returns a lazy seq of the elements of coll separated by sep"
+ [sep coll] (drop 1 (interleave (repeat sep) coll)))
+
+(defmacro definline
+ "Experimental - like defmacro, except defines a named function whose
+ body is the expansion, calls to which may be expanded inline as if
+ it were a macro. Cannot be used with variadic (&) args."
+ [name & decl]
+ (let [[pre-args [args expr]] (split-with (comp not vector?) decl)]
+ `(do
+ (defn ~name ~@pre-args ~args ~(apply (eval (list `fn args expr)) args))
+ (alter-meta! (var ~name) assoc :inline (fn ~args ~expr))
+ (var ~name))))
+
+(defn empty
+ "Returns an empty collection of the same category as coll, or nil"
+ [coll]
+ (when (instance? clojure.lang.IPersistentCollection coll)
+ (.empty #^clojure.lang.IPersistentCollection coll)))
+
+(defmacro amap
+ "Maps an expression across an array a, using an index named idx, and
+ return value named ret, initialized to a clone of a, then setting each element of
+ ret to the evaluation of expr, returning the new array ret."
+ [a idx ret expr]
+ `(let [a# ~a
+ ~ret (aclone a#)]
+ (loop [~idx (int 0)]
+ (if (< ~idx (alength a#))
+ (do
+ (aset ~ret ~idx ~expr)
+ (recur (unchecked-inc ~idx)))
+ ~ret))))
+
+(defmacro areduce
+ "Reduces an expression across an array a, using an index named idx,
+ and return value named ret, initialized to init, setting ret to the evaluation of expr at
+ each step, returning ret."
+ [a idx ret init expr]
+ `(let [a# ~a]
+ (loop [~idx (int 0) ~ret ~init]
+ (if (< ~idx (alength a#))
+ (recur (unchecked-inc ~idx) ~expr)
+ ~ret))))
+
+(defn float-array
+ "Creates an array of floats"
+ {:inline (fn [& args] `(. clojure.lang.Numbers float_array ~@args))
+ :inline-arities #{1 2}}
+ ([size-or-seq] (. clojure.lang.Numbers float_array size-or-seq))
+ ([size init-val-or-seq] (. clojure.lang.Numbers float_array size init-val-or-seq)))
+
+(defn double-array
+ "Creates an array of doubles"
+ {:inline (fn [& args] `(. clojure.lang.Numbers double_array ~@args))
+ :inline-arities #{1 2}}
+ ([size-or-seq] (. clojure.lang.Numbers double_array size-or-seq))
+ ([size init-val-or-seq] (. clojure.lang.Numbers double_array size init-val-or-seq)))
+
+(defn int-array
+ "Creates an array of ints"
+ {:inline (fn [& args] `(. clojure.lang.Numbers int_array ~@args))
+ :inline-arities #{1 2}}
+ ([size-or-seq] (. clojure.lang.Numbers int_array size-or-seq))
+ ([size init-val-or-seq] (. clojure.lang.Numbers int_array size init-val-or-seq)))
+
+(defn long-array
+ "Creates an array of ints"
+ {:inline (fn [& args] `(. clojure.lang.Numbers long_array ~@args))
+ :inline-arities #{1 2}}
+ ([size-or-seq] (. clojure.lang.Numbers long_array size-or-seq))
+ ([size init-val-or-seq] (. clojure.lang.Numbers long_array size init-val-or-seq)))
+
+(definline floats
+ "Casts to float[]"
+ [xs] `(. clojure.lang.Numbers floats ~xs))
+
+(definline ints
+ "Casts to int[]"
+ [xs] `(. clojure.lang.Numbers ints ~xs))
+
+(definline doubles
+ "Casts to double[]"
+ [xs] `(. clojure.lang.Numbers doubles ~xs))
+
+(definline longs
+ "Casts to long[]"
+ [xs] `(. clojure.lang.Numbers longs ~xs))
+
+(import '(java.util.concurrent BlockingQueue LinkedBlockingQueue))
+
+(defn seque
+ "Creates a queued seq on another (presumably lazy) seq s. The queued
+ seq will produce a concrete seq in the background, and can get up to
+ n items ahead of the consumer. n-or-q can be an integer n buffer
+ size, or an instance of java.util.concurrent BlockingQueue. Note
+ that reading from a seque can block if the reader gets ahead of the
+ producer."
+ ([s] (seque 100 s))
+ ([n-or-q s]
+ (let [#^BlockingQueue q (if (instance? BlockingQueue n-or-q)
+ n-or-q
+ (LinkedBlockingQueue. (int n-or-q)))
+ NIL (Object.) ;nil sentinel since LBQ doesn't support nils
+ agt (agent (seq s))
+ fill (fn [s]
+ (try
+ (loop [[x & xs :as s] s]
+ (if s
+ (if (.offer q (if (nil? x) NIL x))
+ (recur xs)
+ s)
+ (.put q q))) ; q itself is eos sentinel
+ (catch Exception e
+ (.put q q)
+ (throw e))))
+ drain (fn drain []
+ (lazy-seq
+ (let [x (.take q)]
+ (if (identical? x q) ;q itself is eos sentinel
+ (do @agt nil) ;touch agent just to propagate errors
+ (do
+ (send-off agt fill)
+ (cons (if (identical? x NIL) nil x) (drain)))))))]
+ (send-off agt fill)
+ (drain))))
+
+(defn class?
+ "Returns true if x is an instance of Class"
+ [x] (instance? Class x))
+
+(defn alter-var-root
+ "Atomically alters the root binding of var v by applying f to its
+ current value plus any args"
+ [#^clojure.lang.Var v f & args] (.alterRoot v f args))
+
+(defn make-hierarchy
+ "Creates a hierarchy object for use with derive, isa? etc."
+ [] {:parents {} :descendants {} :ancestors {}})
+
+(def #^{:private true}
+ global-hierarchy (make-hierarchy))
+
+(defn not-empty
+ "If coll is empty, returns nil, else coll"
+ [coll] (when (seq coll) coll))
+
+(defn bases
+ "Returns the immediate superclass and direct interfaces of c, if any"
+ [#^Class c]
+ (let [i (.getInterfaces c)
+ s (.getSuperclass c)]
+ (not-empty
+ (if s (cons s i) i))))
+
+(defn supers
+ "Returns the immediate and indirect superclasses and interfaces of c, if any"
+ [#^Class class]
+ (loop [ret (set (bases class)) cs ret]
+ (if (seq cs)
+ (let [c (first cs) bs (bases c)]
+ (recur (into ret bs) (into (disj cs c) bs)))
+ (not-empty ret))))
+
+(defn isa?
+ "Returns true if (= child parent), or child is directly or indirectly derived from
+ parent, either via a Java type inheritance relationship or a
+ relationship established via derive. h must be a hierarchy obtained
+ from make-hierarchy, if not supplied defaults to the global
+ hierarchy"
+ ([child parent] (isa? global-hierarchy child parent))
+ ([h child parent]
+ (or (= child parent)
+ (and (class? parent) (class? child)
+ (. #^Class parent isAssignableFrom child))
+ (contains? ((:ancestors h) child) parent)
+ (and (class? child) (some #(contains? ((:ancestors h) %) parent) (supers child)))
+ (and (vector? parent) (vector? child)
+ (= (count parent) (count child))
+ (loop [ret true i 0]
+ (if (or (not ret) (= i (count parent)))
+ ret
+ (recur (isa? h (child i) (parent i)) (inc i))))))))
+
+(defn parents
+ "Returns the immediate parents of tag, either via a Java type
+ inheritance relationship or a relationship established via derive. h
+ must be a hierarchy obtained from make-hierarchy, if not supplied
+ defaults to the global hierarchy"
+ ([tag] (parents global-hierarchy tag))
+ ([h tag] (not-empty
+ (let [tp (get (:parents h) tag)]
+ (if (class? tag)
+ (into (set (bases tag)) tp)
+ tp)))))
+
+(defn ancestors
+ "Returns the immediate and indirect parents of tag, either via a Java type
+ inheritance relationship or a relationship established via derive. h
+ must be a hierarchy obtained from make-hierarchy, if not supplied
+ defaults to the global hierarchy"
+ ([tag] (ancestors global-hierarchy tag))
+ ([h tag] (not-empty
+ (let [ta (get (:ancestors h) tag)]
+ (if (class? tag)
+ (let [superclasses (set (supers tag))]
+ (reduce into superclasses
+ (cons ta
+ (map #(get (:ancestors h) %) superclasses))))
+ ta)))))
+
+(defn descendants
+ "Returns the immediate and indirect children of tag, through a
+ relationship established via derive. h must be a hierarchy obtained
+ from make-hierarchy, if not supplied defaults to the global
+ hierarchy. Note: does not work on Java type inheritance
+ relationships."
+ ([tag] (descendants global-hierarchy tag))
+ ([h tag] (if (class? tag)
+ (throw (java.lang.UnsupportedOperationException. "Can't get descendants of classes"))
+ (not-empty (get (:descendants h) tag)))))
+
+(defn derive
+ "Establishes a parent/child relationship between parent and
+ tag. Parent must be a namespace-qualified symbol or keyword and
+ child can be either a namespace-qualified symbol or keyword or a
+ class. h must be a hierarchy obtained from make-hierarchy, if not
+ supplied defaults to, and modifies, the global hierarchy."
+ ([tag parent]
+ (assert (namespace parent))
+ (assert (or (class? tag) (and (instance? clojure.lang.Named tag) (namespace tag))))
+
+ (alter-var-root #'global-hierarchy derive tag parent) nil)
+ ([h tag parent]
+ (assert (not= tag parent))
+ (assert (or (class? tag) (instance? clojure.lang.Named tag)))
+ (assert (instance? clojure.lang.Named parent))
+
+ (let [tp (:parents h)
+ td (:descendants h)
+ ta (:ancestors h)
+ tf (fn [m source sources target targets]
+ (reduce (fn [ret k]
+ (assoc ret k
+ (reduce conj (get targets k #{}) (cons target (targets target)))))
+ m (cons source (sources source))))]
+ (or
+ (when-not (contains? (tp tag) parent)
+ (when (contains? (ta tag) parent)
+ (throw (Exception. (print-str tag "already has" parent "as ancestor"))))
+ (when (contains? (ta parent) tag)
+ (throw (Exception. (print-str "Cyclic derivation:" parent "has" tag "as ancestor"))))
+ {:parents (assoc (:parents h) tag (conj (get tp tag #{}) parent))
+ :ancestors (tf (:ancestors h) tag td parent ta)
+ :descendants (tf (:descendants h) parent ta tag td)})
+ h))))
+
+(defn underive
+ "Removes a parent/child relationship between parent and
+ tag. h must be a hierarchy obtained from make-hierarchy, if not
+ supplied defaults to, and modifies, the global hierarchy."
+ ([tag parent] (alter-var-root #'global-hierarchy underive tag parent) nil)
+ ([h tag parent]
+ (let [tp (:parents h)
+ td (:descendants h)
+ ta (:ancestors h)
+ tf (fn [m source sources target targets]
+ (reduce
+ (fn [ret k]
+ (assoc ret k
+ (reduce disj (get targets k) (cons target (targets target)))))
+ m (cons source (sources source))))]
+ (if (contains? (tp tag) parent)
+ {:parent (assoc (:parents h) tag (disj (get tp tag) parent))
+ :ancestors (tf (:ancestors h) tag td parent ta)
+ :descendants (tf (:descendants h) parent ta tag td)}
+ h))))
+
+
+(defn distinct?
+ "Returns true if no two of the arguments are ="
+ {:tag Boolean}
+ ([x] true)
+ ([x y] (not (= x y)))
+ ([x y & more]
+ (if (not= x y)
+ (loop [s #{x y} [x & etc :as xs] more]
+ (if xs
+ (if (contains? s x)
+ false
+ (recur (conj s x) etc))
+ true))
+ false)))
+
+(defn resultset-seq
+ "Creates and returns a lazy sequence of structmaps corresponding to
+ the rows in the java.sql.ResultSet rs"
+ [#^java.sql.ResultSet rs]
+ (let [rsmeta (. rs (getMetaData))
+ idxs (range 1 (inc (. rsmeta (getColumnCount))))
+ keys (map (comp keyword #(.toLowerCase #^String %))
+ (map (fn [i] (. rsmeta (getColumnLabel i))) idxs))
+ check-keys
+ (or (apply distinct? keys)
+ (throw (Exception. "ResultSet must have unique column labels")))
+ row-struct (apply create-struct keys)
+ row-values (fn [] (map (fn [#^Integer i] (. rs (getObject i))) idxs))
+ rows (fn thisfn []
+ (lazy-seq
+ (when (. rs (next))
+ (cons (apply struct row-struct (row-values)) (thisfn)))))]
+ (rows)))
+
+(defn iterator-seq
+ "Returns a seq on a java.util.Iterator. Note that most collections
+ providing iterators implement Iterable and thus support seq directly."
+ [iter]
+ (clojure.lang.IteratorSeq/create iter))
+
+(defn enumeration-seq
+ "Returns a seq on a java.util.Enumeration"
+ [e]
+ (clojure.lang.EnumerationSeq/create e))
+
+(defn format
+ "Formats a string using java.lang.String.format, see java.util.Formatter for format
+ string syntax"
+ {:tag String}
+ [fmt & args]
+ (String/format fmt (to-array args)))
+
+(defn printf
+ "Prints formatted output, as per format"
+ [fmt & args]
+ (print (apply format fmt args)))
+
+(def gen-class)
+
+(defmacro ns
+ "Sets *ns* to the namespace named by name (unevaluated), creating it
+ if needed. references can be zero or more of: (:refer-clojure ...)
+ (:require ...) (:use ...) (:import ...) (:load ...) (:gen-class)
+ with the syntax of refer-clojure/require/use/import/load/gen-class
+ respectively, except the arguments are unevaluated and need not be
+ quoted. (:gen-class ...), when supplied, defaults to :name
+ corresponding to the ns name, :main true, :impl-ns same as ns, and
+ :init-impl-ns true. All options of gen-class are
+ supported. The :gen-class directive is ignored when not
+ compiling. If :gen-class is not supplied, when compiled only an
+ nsname__init.class will be generated. If :refer-clojure is not used, a
+ default (refer 'clojure) is used. Use of ns is preferred to
+ individual calls to in-ns/require/use/import:
+
+ (ns foo.bar
+ (:refer-clojure :exclude [ancestors printf])
+ (:require (clojure.contrib sql sql.tests))
+ (:use (my.lib this that))
+ (:import (java.util Date Timer Random)
+ (java.sql Connection Statement)))"
+
+ [name & references]
+ (let [process-reference
+ (fn [[kname & args]]
+ `(~(symbol "clojure.core" (clojure.core/name kname))
+ ~@(map #(list 'quote %) args)))
+ docstring (when (string? (first references)) (first references))
+ references (if docstring (next references) references)
+ name (if docstring
+ (with-meta name (assoc (meta name)
+ :doc docstring))
+ name)
+ gen-class-clause (first (filter #(= :gen-class (first %)) references))
+ gen-class-call
+ (when gen-class-clause
+ (list* `gen-class :name (.replace (str name) \- \_) :impl-ns name :main true (next gen-class-clause)))
+ references (remove #(= :gen-class (first %)) references)]
+ `(do
+ (clojure.core/in-ns '~name)
+ ~@(when gen-class-call (list gen-class-call))
+ ~@(when (and (not= name 'clojure.core) (not-any? #(= :refer-clojure (first %)) references))
+ `((clojure.core/refer '~'clojure.core)))
+ ~@(map process-reference references))))
+
+(defmacro refer-clojure
+ "Same as (refer 'clojure.core <filters>)"
+ [& filters]
+ `(clojure.core/refer '~'clojure.core ~@filters))
+
+(defmacro defonce
+ "defs name to have the root value of the expr iff the named var has no root value,
+ else expr is unevaluated"
+ [name expr]
+ `(let [v# (def ~name)]
+ (when-not (.hasRoot v#)
+ (def ~name ~expr))))
+
+;;;;;;;;;;; require/use/load, contributed by Stephen C. Gilardi ;;;;;;;;;;;;;;;;;;
+
+(defonce
+ #^{:private true
+ :doc "A ref to a sorted set of symbols representing loaded libs"}
+ *loaded-libs* (ref (sorted-set)))
+
+(defonce
+ #^{:private true
+ :doc "the set of paths currently being loaded by this thread"}
+ *pending-paths* #{})
+
+(defonce
+ #^{:private true :doc
+ "True while a verbose load is pending"}
+ *loading-verbosely* false)
+
+(defn- throw-if
+ "Throws an exception with a message if pred is true"
+ [pred fmt & args]
+ (when pred
+ (let [#^String message (apply format fmt args)
+ exception (Exception. message)
+ raw-trace (.getStackTrace exception)
+ boring? #(not= (.getMethodName #^StackTraceElement %) "doInvoke")
+ trace (into-array (drop 2 (drop-while boring? raw-trace)))]
+ (.setStackTrace exception trace)
+ (throw exception))))
+
+(defn- libspec?
+ "Returns true if x is a libspec"
+ [x]
+ (or (symbol? x)
+ (and (vector? x)
+ (or
+ (nil? (second x))
+ (keyword? (second x))))))
+
+(defn- prependss
+ "Prepends a symbol or a seq to coll"
+ [x coll]
+ (if (symbol? x)
+ (cons x coll)
+ (concat x coll)))
+
+(defn- root-resource
+ "Returns the root directory path for a lib"
+ {:tag String}
+ [lib]
+ (str \/
+ (.. (name lib)
+ (replace \- \_)
+ (replace \. \/))))
+
+(defn- root-directory
+ "Returns the root resource path for a lib"
+ [lib]
+ (let [d (root-resource lib)]
+ (subs d 0 (.lastIndexOf d "/"))))
+
+(def load)
+
+(defn- load-one
+ "Loads a lib given its name. If need-ns, ensures that the associated
+ namespace exists after loading. If require, records the load so any
+ duplicate loads can be skipped."
+ [lib need-ns require]
+ (load (root-resource lib))
+ (throw-if (and need-ns (not (find-ns lib)))
+ "namespace '%s' not found after loading '%s'"
+ lib (root-resource lib))
+ (when require
+ (dosync
+ (commute *loaded-libs* conj lib))))
+
+(defn- load-all
+ "Loads a lib given its name and forces a load of any libs it directly or
+ indirectly loads. If need-ns, ensures that the associated namespace
+ exists after loading. If require, records the load so any duplicate loads
+ can be skipped."
+ [lib need-ns require]
+ (dosync
+ (commute *loaded-libs* #(reduce conj %1 %2)
+ (binding [*loaded-libs* (ref (sorted-set))]
+ (load-one lib need-ns require)
+ @*loaded-libs*))))
+
+(defn- load-lib
+ "Loads a lib with options"
+ [prefix lib & options]
+ (throw-if (and prefix (pos? (.indexOf (name lib) (int \.))))
+ "lib names inside prefix lists must not contain periods")
+ (let [lib (if prefix (symbol (str prefix \. lib)) lib)
+ opts (apply hash-map options)
+ {:keys [as reload reload-all require use verbose]} opts
+ loaded (contains? @*loaded-libs* lib)
+ load (cond reload-all
+ load-all
+ (or reload (not require) (not loaded))
+ load-one)
+ need-ns (or as use)
+ filter-opts (select-keys opts '(:exclude :only :rename))]
+ (binding [*loading-verbosely* (or *loading-verbosely* verbose)]
+ (if load
+ (load lib need-ns require)
+ (throw-if (and need-ns (not (find-ns lib)))
+ "namespace '%s' not found" lib))
+ (when (and need-ns *loading-verbosely*)
+ (printf "(clojure.core/in-ns '%s)\n" (ns-name *ns*)))
+ (when as
+ (when *loading-verbosely*
+ (printf "(clojure.core/alias '%s '%s)\n" as lib))
+ (alias as lib))
+ (when use
+ (when *loading-verbosely*
+ (printf "(clojure.core/refer '%s" lib)
+ (doseq [opt filter-opts]
+ (printf " %s '%s" (key opt) (print-str (val opt))))
+ (printf ")\n"))
+ (apply refer lib (mapcat seq filter-opts))))))
+
+(defn- load-libs
+ "Loads libs, interpreting libspecs, prefix lists, and flags for
+ forwarding to load-lib"
+ [& args]
+ (let [flags (filter keyword? args)
+ opts (interleave flags (repeat true))
+ args (filter (complement keyword?) args)]
+ (doseq [arg args]
+ (if (libspec? arg)
+ (apply load-lib nil (prependss arg opts))
+ (let [[prefix & args] arg]
+ (throw-if (nil? prefix) "prefix cannot be nil")
+ (doseq [arg args]
+ (apply load-lib prefix (prependss arg opts))))))))
+
+;; Public
+
+(defn require
+ "Loads libs, skipping any that are already loaded. Each argument is
+ either a libspec that identifies a lib, a prefix list that identifies
+ multiple libs whose names share a common prefix, or a flag that modifies
+ how all the identified libs are loaded. Use :require in the ns macro
+ in preference to calling this directly.
+
+ Libs
+
+ A 'lib' is a named set of resources in classpath whose contents define a
+ library of Clojure code. Lib names are symbols and each lib is associated
+ with a Clojure namespace and a Java package that share its name. A lib's
+ name also locates its root directory within classpath using Java's
+ package name to classpath-relative path mapping. All resources in a lib
+ should be contained in the directory structure under its root directory.
+ All definitions a lib makes should be in its associated namespace.
+
+ 'require loads a lib by loading its root resource. The root resource path
+ is derived from the root directory path by repeating its last component
+ and appending '.clj'. For example, the lib 'x.y.z has root directory
+ <classpath>/x/y/z; root resource <classpath>/x/y/z/z.clj. The root
+ resource should contain code to create the lib's namespace and load any
+ additional lib resources.
+
+ Libspecs
+
+ A libspec is a lib name or a vector containing a lib name followed by
+ options expressed as sequential keywords and arguments.
+
+ Recognized options: :as
+ :as takes a symbol as its argument and makes that symbol an alias to the
+ lib's namespace in the current namespace.
+
+ Prefix Lists
+
+ It's common for Clojure code to depend on several libs whose names have
+ the same prefix. When specifying libs, prefix lists can be used to reduce
+ repetition. A prefix list contains the shared prefix followed by libspecs
+ with the shared prefix removed from the lib names. After removing the
+ prefix, the names that remain must not contain any periods.
+
+ Flags
+
+ A flag is a keyword.
+ Recognized flags: :reload, :reload-all, :verbose
+ :reload forces loading of all the identified libs even if they are
+ already loaded
+ :reload-all implies :reload and also forces loading of all libs that the
+ identified libs directly or indirectly load via require or use
+ :verbose triggers printing information about each load, alias, and refer"
+
+ [& args]
+ (apply load-libs :require args))
+
+(defn use
+ "Like 'require, but also refers to each lib's namespace using
+ clojure.core/refer. Use :use in the ns macro in preference to calling
+ this directly.
+
+ 'use accepts additional options in libspecs: :exclude, :only, :rename.
+ The arguments and semantics for :exclude, :only, and :rename are the same
+ as those documented for clojure.core/refer."
+ [& args] (apply load-libs :require :use args))
+
+(defn loaded-libs
+ "Returns a sorted set of symbols naming the currently loaded libs"
+ [] @*loaded-libs*)
+
+(defn load
+ "Loads Clojure code from resources in classpath. A path is interpreted as
+ classpath-relative if it begins with a slash or relative to the root
+ directory for the current namespace otherwise."
+ [& paths]
+ (doseq [#^String path paths]
+ (let [#^String path (if (.startsWith path "/")
+ path
+ (str (root-directory (ns-name *ns*)) \/ path))]
+ (when *loading-verbosely*
+ (printf "(clojure.core/load \"%s\")\n" path)
+ (flush))
+; (throw-if (*pending-paths* path)
+; "cannot load '%s' again while it is loading"
+; path)
+ (when-not (*pending-paths* path)
+ (binding [*pending-paths* (conj *pending-paths* path)]
+ (clojure.lang.RT/load (.substring path 1)))))))
+
+(defn compile
+ "Compiles the namespace named by the symbol lib into a set of
+ classfiles. The source for the lib must be in a proper
+ classpath-relative directory. The output files will go into the
+ directory specified by *compile-path*, and that directory too must
+ be in the classpath."
+ [lib]
+ (binding [*compile-files* true]
+ (load-one lib true true))
+ lib)
+
+;;;;;;;;;;;;; nested associative ops ;;;;;;;;;;;
+
+(defn get-in
+ "returns the value in a nested associative structure, where ks is a sequence of keys"
+ [m ks]
+ (reduce get m ks))
+
+(defn assoc-in
+ "Associates a value in a nested associative structure, where ks is a
+ sequence of keys and v is the new value and returns a new nested structure.
+ If any levels do not exist, hash-maps will be created."
+ [m [k & ks] v]
+ (if ks
+ (assoc m k (assoc-in (get m k) ks v))
+ (assoc m k v)))
+
+(defn update-in
+ "'Updates' a value in a nested associative structure, where ks is a
+ sequence of keys and f is a function that will take the old value
+ and any supplied args and return the new value, and returns a new
+ nested structure. If any levels do not exist, hash-maps will be
+ created."
+ ([m [k & ks] f & args]
+ (if ks
+ (assoc m k (apply update-in (get m k) ks f args))
+ (assoc m k (apply f (get m k) args)))))
+
+
+(defn empty?
+ "Returns true if coll has no items - same as (not (seq coll)).
+ Please use the idiom (seq x) rather than (not (empty? x))"
+ [coll] (not (seq coll)))
+
+(defn coll?
+ "Returns true if x implements IPersistentCollection"
+ [x] (instance? clojure.lang.IPersistentCollection x))
+
+(defn list?
+ "Returns true if x implements IPersistentList"
+ [x] (instance? clojure.lang.IPersistentList x))
+
+(defn set?
+ "Returns true if x implements IPersistentSet"
+ [x] (instance? clojure.lang.IPersistentSet x))
+
+(defn ifn?
+ "Returns true if x implements IFn. Note that many data structures
+ (e.g. sets and maps) implement IFn"
+ [x] (instance? clojure.lang.IFn x))
+
+(defn fn?
+ "Returns true if x implements Fn, i.e. is an object created via fn."
+ [x] (instance? clojure.lang.Fn x))
+
+
+(defn associative?
+ "Returns true if coll implements Associative"
+ [coll] (instance? clojure.lang.Associative coll))
+
+(defn sequential?
+ "Returns true if coll implements Sequential"
+ [coll] (instance? clojure.lang.Sequential coll))
+
+(defn sorted?
+ "Returns true if coll implements Sorted"
+ [coll] (instance? clojure.lang.Sorted coll))
+
+(defn counted?
+ "Returns true if coll implements count in constant time"
+ [coll] (instance? clojure.lang.Counted coll))
+
+(defn reversible?
+ "Returns true if coll implements Reversible"
+ [coll] (instance? clojure.lang.Reversible coll))
+
+(def
+ #^{:doc "bound in a repl thread to the most recent value printed"}
+ *1)
+
+(def
+ #^{:doc "bound in a repl thread to the second most recent value printed"}
+ *2)
+
+(def
+ #^{:doc "bound in a repl thread to the third most recent value printed"}
+ *3)
+
+(def
+ #^{:doc "bound in a repl thread to the most recent exception caught by the repl"}
+ *e)
+
+(defmacro declare
+ "defs the supplied var names with no bindings, useful for making forward declarations."
+ [& names] `(do ~@(map #(list 'def %) names)))
+
+(defn trampoline
+ "trampoline can be used to convert algorithms requiring mutual
+ recursion without stack consumption. Calls f with supplied args, if
+ any. If f returns a fn, calls that fn with no arguments, and
+ continues to repeat, until the return value is not a fn, then
+ returns that non-fn value. Note that if you want to return a fn as a
+ final value, you must wrap it in some data structure and unpack it
+ after trampoline returns."
+ ([f]
+ (let [ret (f)]
+ (if (fn? ret)
+ (recur ret)
+ ret)))
+ ([f & args]
+ (trampoline #(apply f args))))
+
+(defn intern
+ "Finds or creates a var named by the symbol name in the namespace
+ ns (which can be a symbol or a namespace), setting its root binding
+ to val if supplied. The namespace must exist. The var will adopt any
+ metadata from the name symbol. Returns the var."
+ ([ns #^clojure.lang.Symbol name]
+ (let [v (clojure.lang.Var/intern (the-ns ns) name)]
+ (when ^name (.setMeta v ^name))
+ v))
+ ([ns name val]
+ (let [v (clojure.lang.Var/intern (the-ns ns) name val)]
+ (when ^name (.setMeta v ^name))
+ v)))
+
+(defmacro while
+ "Repeatedly executes body while test expression is true. Presumes
+ some side-effect will cause test to become false/nil. Returns nil"
+ [test & body]
+ `(loop []
+ (when ~test
+ ~@body
+ (recur))))
+
+(defn memoize
+ "Returns a memoized version of a referentially transparent function. The
+ memoized version of the function keeps a cache of the mapping from arguments
+ to results and, when calls with the same arguments are repeated often, has
+ higher performance at the expense of higher memory use."
+ [f]
+ (let [mem (atom {})]
+ (fn [& args]
+ (if-let [e (find @mem args)]
+ (val e)
+ (let [ret (apply f args)]
+ (swap! mem assoc args ret)
+ ret)))))
+
+(defmacro condp
+ "Takes a binary predicate, an expression, and a set of clauses.
+ Each clause can take the form of either:
+
+ test-expr result-expr
+
+ test-expr :>> result-fn
+
+ Note :>> is an ordinary keyword.
+
+ For each clause, (pred test-expr expr) is evaluated. If it returns
+ logical true, the clause is a match. If a binary clause matches, the
+ result-expr is returned, if a ternary clause matches, its result-fn,
+ which must be a unary function, is called with the result of the
+ predicate as its argument, the result of that call being the return
+ value of condp. A single default expression can follow the clauses,
+ and its value will be returned if no clause matches. If no default
+ expression is provided and no clause matches, an
+ IllegalArgumentException is thrown."
+
+ [pred expr & clauses]
+ (let [gpred (gensym "pred__")
+ gexpr (gensym "expr__")
+ emit (fn emit [pred expr args]
+ (let [[[a b c :as clause] more]
+ (split-at (if (= :>> (second args)) 3 2) args)
+ n (count clause)]
+ (cond
+ (= 0 n) `(throw (IllegalArgumentException. (str "No matching clause: " ~expr)))
+ (= 1 n) a
+ (= 2 n) `(if (~pred ~a ~expr)
+ ~b
+ ~(emit pred expr more))
+ :else `(if-let [p# (~pred ~a ~expr)]
+ (~c p#)
+ ~(emit pred expr more)))))
+ gres (gensym "res__")]
+ `(let [~gpred ~pred
+ ~gexpr ~expr]
+ ~(emit gpred gexpr clauses))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; var documentation ;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defmacro add-doc {:private true} [name docstring]
+ `(alter-meta! (var ~name) assoc :doc ~docstring))
+
+(add-doc *file*
+ "The path of the file being evaluated, as a String.
+
+ Evaluates to nil when there is no file, eg. in the REPL.")
+
+(add-doc *command-line-args*
+ "A sequence of the supplied command line arguments, or nil if
+ none were supplied")
+
+(add-doc *warn-on-reflection*
+ "When set to true, the compiler will emit warnings when reflection is
+ needed to resolve Java method calls or field accesses.
+
+ Defaults to false.")
+
+(add-doc *compile-path*
+ "Specifies the directory where 'compile' will write out .class
+ files. This directory must be in the classpath for 'compile' to
+ work.
+
+ Defaults to \"classes\"")
+
+(add-doc *compile-files*
+ "Set to true when compiling files, false otherwise.")
+
+(add-doc *ns*
+ "A clojure.lang.Namespace object representing the current namespace.")
+
+(add-doc *in*
+ "A java.io.Reader object representing standard input for read operations.
+
+ Defaults to System/in, wrapped in a LineNumberingPushbackReader")
+
+(add-doc *out*
+ "A java.io.Writer object representing standard output for print operations.
+
+ Defaults to System/out")
+
+(add-doc *err*
+ "A java.io.Writer object representing standard error for print operations.
+
+ Defaults to System/err, wrapped in a PrintWriter")
+
+(add-doc *flush-on-newline*
+ "When set to true, output will be flushed whenever a newline is printed.
+
+ Defaults to true.")
+
+(add-doc *print-meta*
+ "If set to logical true, when printing an object, its metadata will also
+ be printed in a form that can be read back by the reader.
+
+ Defaults to false.")
+
+(add-doc *print-dup*
+ "When set to logical true, objects will be printed in a way that preserves
+ their type when read in later.
+
+ Defaults to false.")
+
+(add-doc *print-readably*
+ "When set to logical false, strings and characters will be printed with
+ non-alphanumeric characters converted to the appropriate escape sequences.
+
+ Defaults to true")
+
+(add-doc *read-eval*
+ "When set to logical false, the EvalReader (#=(...)) is disabled in the
+ read/load in the thread-local binding.
+ Example: (binding [*read-eval* false] (read-string \"#=(eval (def x 3))\"))
+
+ Defaults to true")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; helper files ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(alter-meta! (find-ns 'clojure.core) assoc :doc "Fundamental library of the Clojure language")
+(load "core_proxy")
+(load "core_print")
+(load "genclass")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; futures (needs proxy);;;;;;;;;;;;;;;;;;
+(defn future-call
+ "Takes a function of no args and yields a future object that will
+ invoke the function in another thread, and will cache the result and
+ return it on all subsequent calls to deref/@. If the computation has
+ not yet finished, calls to deref/@ will block."
+ [#^Callable f]
+ (let [fut (.submit clojure.lang.Agent/soloExecutor f)]
+ (proxy [clojure.lang.IDeref java.util.concurrent.Future] []
+ (deref [] (.get fut))
+ (get ([] (.get fut))
+ ([timeout unit] (.get fut timeout unit)))
+ (isCancelled [] (.isCancelled fut))
+ (isDone [] (.isDone fut))
+ (cancel [interrupt?] (.cancel fut interrupt?)))))
+
+(defmacro future
+ "Takes a body of expressions and yields a future object that will
+ invoke the body in another thread, and will cache the result and
+ return it on all subsequent calls to deref/@. If the computation has
+ not yet finished, calls to deref/@ will block."
+ [& body] `(future-call (fn [] ~@body)))
+
+(defn pmap
+ "Like map, except f is applied in parallel. Semi-lazy in that the
+ parallel computation stays ahead of the consumption, but doesn't
+ realize the entire result unless required. Only useful for
+ computationally intensive functions where the time of f dominates
+ the coordination overhead."
+ ([f coll]
+ (let [n (+ 2 (.. Runtime getRuntime availableProcessors))
+ rets (map #(future (f %)) coll)
+ step (fn step [[x & xs :as vs] fs]
+ (lazy-seq
+ (if-let [s (seq fs)]
+ (cons (deref x) (step xs (rest s)))
+ (map deref vs))))]
+ (step rets (drop n rets))))
+ ([f coll & colls]
+ (let [step (fn step [cs]
+ (lazy-seq
+ (let [ss (map seq cs)]
+ (when (every? identity ss)
+ (cons (map first ss) (step (map rest ss)))))))]
+ (pmap #(apply f %) (step (cons coll colls))))))
+
+(defn pcalls
+ "Executes the no-arg fns in parallel, returning a lazy sequence of
+ their values"
+ [& fns] (pmap #(%) fns))
+
+(defmacro pvalues
+ "Returns a lazy sequence of the values of the exprs, which are
+ evaluated in parallel"
+ [& exprs]
+ `(pcalls ~@(map #(list `fn [] %) exprs)))
+
+(defmacro letfn
+ "Takes a vector of function specs and a body, and generates a set of
+ bindings of functions to their names. All of the names are available
+ in all of the definitions of the functions, as well as the body.
+
+ fnspec ==> (fname [params*] exprs) or (fname ([params*] exprs)+)"
+ [fnspecs & body]
+ `(letfn* ~(vec (interleave (map first fnspecs)
+ (map #(cons `fn %) fnspecs)))
+ ~@body))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; clojure version number ;;;;;;;;;;;;;;;;;;;;;;
+
+(let [version-stream (.getResourceAsStream (clojure.lang.RT/baseLoader)
+ "clojure/version.properties")
+ properties (doto (new java.util.Properties) (.load version-stream))
+ prop (fn [k] (.getProperty properties (str "clojure.version." k)))
+ clojure-version {:major (Integer/valueOf (prop "major"))
+ :minor (Integer/valueOf (prop "minor"))
+ :incremental (Integer/valueOf (prop "incremental"))
+ :qualifier (prop "qualifier")}]
+ (def *clojure-version*
+ (if (not (= (prop "interim") "false"))
+ (clojure.lang.RT/assoc clojure-version :interim true)
+ clojure-version)))
+
+(add-doc *clojure-version*
+ "The version info for Clojure core, as a map containing :major :minor
+ :incremental and :qualifier keys. Feature releases may increment
+ :minor and/or :major, bugfix releases will increment :incremental.
+ Possible values of :qualifier include \"GA\", \"SNAPSHOT\", \"RC-x\" \"BETA-x\"")
+
+(defn
+ clojure-version
+ "Returns clojure version as a printable string."
+ []
+ (str (:major *clojure-version*)
+ "."
+ (:minor *clojure-version*)
+ (when-let [i (:incremental *clojure-version*)]
+ (str "." i))
+ (when-let [q (:qualifier *clojure-version*)]
+ (str "-" q))
+ (when (:interim *clojure-version*)
+ "-SNAPSHOT")))
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html 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.
+
+(in-ns 'clojure.core)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; printing ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(import '(java.io Writer))
+
+(def
+ #^{:doc "*print-length* controls how many items of each collection the
+ printer will print. If it is bound to logical false, there is no
+ limit. Otherwise, it must be bound to an integer indicating the maximum
+ number of items of each collection to print. If a collection contains
+ more items, the printer will print items up to the limit followed by
+ '...' to represent the remaining items. The root binding is nil
+ indicating no limit."}
+ *print-length* nil)
+
+(def
+ #^{:doc "*print-level* controls how many levels deep the printer will
+ print nested objects. If it is bound to logical false, there is no
+ limit. Otherwise, it must be bound to an integer indicating the maximum
+ level to print. Each argument to print is at level 0; if an argument is a
+ collection, its items are at level 1; and so on. If an object is a
+ collection and is at a level greater than or equal to the value bound to
+ *print-level*, the printer prints '#' to represent it. The root binding
+ is nil indicating no limit."}
+*print-level* nil)
+
+(defn- print-sequential [#^String begin, print-one, #^String sep, #^String end, sequence, #^Writer w]
+ (binding [*print-level* (and (not *print-dup*) *print-level* (dec *print-level*))]
+ (if (and *print-level* (neg? *print-level*))
+ (.write w "#")
+ (do
+ (.write w begin)
+ (when-let [xs (seq sequence)]
+ (if (and (not *print-dup*) *print-length*)
+ (loop [[x & xs] xs
+ print-length *print-length*]
+ (if (zero? print-length)
+ (.write w "...")
+ (do
+ (print-one x w)
+ (when xs
+ (.write w sep)
+ (recur xs (dec print-length))))))
+ (loop [[x & xs] xs]
+ (print-one x w)
+ (when xs
+ (.write w sep)
+ (recur xs)))))
+ (.write w end)))))
+
+(defn- print-meta [o, #^Writer w]
+ (when-let [m (meta o)]
+ (when (and (pos? (count m))
+ (or *print-dup*
+ (and *print-meta* *print-readably*)))
+ (.write w "#^")
+ (if (and (= (count m) 1) (:tag m))
+ (pr-on (:tag m) w)
+ (pr-on m w))
+ (.write w " "))))
+
+(defmethod print-method :default [o, #^Writer w]
+ (print-method (vary-meta o #(dissoc % :type)) w))
+
+(defmethod print-method nil [o, #^Writer w]
+ (.write w "nil"))
+
+(defmethod print-dup nil [o w] (print-method o w))
+
+(defn print-ctor [o print-args #^Writer w]
+ (.write w "#=(")
+ (.write w (.getName #^Class (class o)))
+ (.write w ". ")
+ (print-args o w)
+ (.write w ")"))
+
+(defmethod print-method Object [o, #^Writer w]
+ (.write w "#<")
+ (.write w (.getSimpleName (class o)))
+ (.write w " ")
+ (.write w (str o))
+ (.write w ">"))
+
+(defmethod print-method clojure.lang.Keyword [o, #^Writer w]
+ (.write w (str o)))
+
+(defmethod print-dup clojure.lang.Keyword [o w] (print-method o w))
+
+(defmethod print-method Number [o, #^Writer w]
+ (.write w (str o)))
+
+(defmethod print-dup Number [o, #^Writer w]
+ (print-ctor o
+ (fn [o w]
+ (print-dup (str o) w))
+ w))
+
+(defmethod print-dup clojure.lang.Fn [o, #^Writer w]
+ (print-ctor o (fn [o w]) w))
+
+(prefer-method print-dup clojure.lang.IPersistentCollection clojure.lang.Fn)
+(prefer-method print-dup java.util.Map clojure.lang.Fn)
+(prefer-method print-dup java.util.Collection clojure.lang.Fn)
+
+(defmethod print-method Boolean [o, #^Writer w]
+ (.write w (str o)))
+
+(defmethod print-dup Boolean [o w] (print-method o w))
+
+(defn print-simple [o, #^Writer w]
+ (print-meta o w)
+ (.write w (str o)))
+
+(defmethod print-method clojure.lang.Symbol [o, #^Writer w]
+ (print-simple o w))
+
+(defmethod print-dup clojure.lang.Symbol [o w] (print-method o w))
+
+(defmethod print-method clojure.lang.Var [o, #^Writer w]
+ (print-simple o w))
+
+(defmethod print-dup clojure.lang.Var [#^clojure.lang.Var o, #^Writer w]
+ (.write w (str "#=(var " (.name (.ns o)) "/" (.sym o) ")")))
+
+(defmethod print-method clojure.lang.ISeq [o, #^Writer w]
+ (print-meta o w)
+ (print-sequential "(" pr-on " " ")" o w))
+
+(defmethod print-dup clojure.lang.ISeq [o w] (print-method o w))
+(defmethod print-dup clojure.lang.IPersistentList [o w] (print-method o w))
+(prefer-method print-method clojure.lang.IPersistentList clojure.lang.ISeq)
+(prefer-method print-dup clojure.lang.IPersistentList clojure.lang.ISeq)
+(prefer-method print-method clojure.lang.ISeq clojure.lang.IPersistentCollection)
+(prefer-method print-dup clojure.lang.ISeq clojure.lang.IPersistentCollection)
+(prefer-method print-method clojure.lang.ISeq java.util.Collection)
+(prefer-method print-dup clojure.lang.ISeq java.util.Collection)
+
+(defmethod print-method clojure.lang.IPersistentList [o, #^Writer w]
+ (print-meta o w)
+ (print-sequential "(" print-method " " ")" o w))
+
+
+(defmethod print-dup java.util.Collection [o, #^Writer w]
+ (print-ctor o #(print-sequential "[" print-dup " " "]" %1 %2) w))
+
+(defmethod print-dup clojure.lang.IPersistentCollection [o, #^Writer w]
+ (print-meta o w)
+ (.write w "#=(")
+ (.write w (.getName #^Class (class o)))
+ (.write w "/create ")
+ (print-sequential "[" print-dup " " "]" o w)
+ (.write w ")"))
+
+(prefer-method print-dup clojure.lang.IPersistentCollection java.util.Collection)
+
+(def #^{:tag String
+ :doc "Returns escape string for char or nil if none"}
+ char-escape-string
+ {\newline "\\n"
+ \tab "\\t"
+ \return "\\r"
+ \" "\\\""
+ \\ "\\\\"
+ \formfeed "\\f"
+ \backspace "\\b"})
+
+(defmethod print-method String [#^String s, #^Writer w]
+ (if (or *print-dup* *print-readably*)
+ (do (.append w \")
+ (dotimes [n (count s)]
+ (let [c (.charAt s n)
+ e (char-escape-string c)]
+ (if e (.write w e) (.append w c))))
+ (.append w \"))
+ (.write w s))
+ nil)
+
+(defmethod print-dup String [s w] (print-method s w))
+
+(defmethod print-method clojure.lang.IPersistentVector [v, #^Writer w]
+ (print-meta v w)
+ (print-sequential "[" pr-on " " "]" v w))
+
+(defn- print-map [m print-one w]
+ (print-sequential
+ "{"
+ (fn [e #^Writer w]
+ (do (print-one (key e) w) (.append w \space) (print-one (val e) w)))
+ ", "
+ "}"
+ (seq m) w))
+
+(defmethod print-method clojure.lang.IPersistentMap [m, #^Writer w]
+ (print-meta m w)
+ (print-map m pr-on w))
+
+(defmethod print-dup java.util.Map [m, #^Writer w]
+ (print-ctor m #(print-map (seq %1) print-dup %2) w))
+
+(defmethod print-dup clojure.lang.IPersistentMap [m, #^Writer w]
+ (print-meta m w)
+ (.write w "#=(")
+ (.write w (.getName (class m)))
+ (.write w "/create ")
+ (print-map m print-dup w)
+ (.write w ")"))
+
+(prefer-method print-dup clojure.lang.IPersistentCollection java.util.Map)
+
+(defmethod print-method clojure.lang.IPersistentSet [s, #^Writer w]
+ (print-meta s w)
+ (print-sequential "#{" pr-on " " "}" (seq s) w))
+
+(def #^{:tag String
+ :doc "Returns name string for char or nil if none"}
+ char-name-string
+ {\newline "newline"
+ \tab "tab"
+ \space "space"
+ \backspace "backspace"
+ \formfeed "formfeed"
+ \return "return"})
+
+(defmethod print-method java.lang.Character [#^Character c, #^Writer w]
+ (if (or *print-dup* *print-readably*)
+ (do (.append w \\)
+ (let [n (char-name-string c)]
+ (if n (.write w n) (.append w c))))
+ (.append w c))
+ nil)
+
+(defmethod print-dup java.lang.Character [c w] (print-method c w))
+(defmethod print-dup java.lang.Integer [o w] (print-method o w))
+(defmethod print-dup java.lang.Double [o w] (print-method o w))
+(defmethod print-dup clojure.lang.Ratio [o w] (print-method o w))
+(defmethod print-dup java.math.BigDecimal [o w] (print-method o w))
+(defmethod print-dup clojure.lang.PersistentHashMap [o w] (print-method o w))
+(defmethod print-dup clojure.lang.PersistentHashSet [o w] (print-method o w))
+(defmethod print-dup clojure.lang.PersistentVector [o w] (print-method o w))
+(defmethod print-dup clojure.lang.LazilyPersistentVector [o w] (print-method o w))
+
+(def primitives-classnames
+ {Float/TYPE "Float/TYPE"
+ Integer/TYPE "Integer/TYPE"
+ Long/TYPE "Long/TYPE"
+ Boolean/TYPE "Boolean/TYPE"
+ Character/TYPE "Character/TYPE"
+ Double/TYPE "Double/TYPE"
+ Byte/TYPE "Byte/TYPE"
+ Short/TYPE "Short/TYPE"})
+
+(defmethod print-method Class [#^Class c, #^Writer w]
+ (.write w (.getName c)))
+
+(defmethod print-dup Class [#^Class c, #^Writer w]
+ (cond
+ (.isPrimitive c) (do
+ (.write w "#=(identity ")
+ (.write w #^String (primitives-classnames c))
+ (.write w ")"))
+ (.isArray c) (do
+ (.write w "#=(java.lang.Class/forName \"")
+ (.write w (.getName c))
+ (.write w "\")"))
+ :else (do
+ (.write w "#=")
+ (.write w (.getName c)))))
+
+(defmethod print-method java.math.BigDecimal [b, #^Writer w]
+ (.write w (str b))
+ (.write w "M"))
+
+(defmethod print-method java.util.regex.Pattern [p #^Writer w]
+ (.write w "#\"")
+ (loop [[#^Character c & r :as s] (seq (.pattern #^java.util.regex.Pattern p))
+ qmode false]
+ (when s
+ (cond
+ (= c \\) (let [[#^Character c2 & r2] r]
+ (.append w \\)
+ (.append w c2)
+ (if qmode
+ (recur r2 (not= c2 \E))
+ (recur r2 (= c2 \Q))))
+ (= c \") (do
+ (if qmode
+ (.write w "\\E\\\"\\Q")
+ (.write w "\\\""))
+ (recur r qmode))
+ :else (do
+ (.append w c)
+ (recur r qmode)))))
+ (.append w \"))
+
+(defmethod print-dup java.util.regex.Pattern [p #^Writer w] (print-method p w))
+
+(defmethod print-dup clojure.lang.Namespace [#^clojure.lang.Namespace n #^Writer w]
+ (.write w "#=(find-ns ")
+ (print-dup (.name n) w)
+ (.write w ")"))
+
+(defmethod print-method clojure.lang.IDeref [o #^Writer w]
+ (print-sequential (format "#<%s@%x: "
+ (.getSimpleName (class o))
+ (System/identityHashCode o))
+ pr-on, "", ">", (list @o), w))
+
+(def #^{:private true} print-initialized true)
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html 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.
+
+(in-ns 'clojure.core)
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;; proxy ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(import
+ '(clojure.asm ClassWriter ClassVisitor Opcodes Type)
+ '(java.lang.reflect Modifier Constructor)
+ '(clojure.asm.commons Method GeneratorAdapter)
+ '(clojure.lang IProxy Reflector DynamicClassLoader IPersistentMap PersistentHashMap RT))
+
+(defn method-sig [#^java.lang.reflect.Method meth]
+ [(. meth (getName)) (seq (. meth (getParameterTypes))) (. meth getReturnType)])
+
+(defn- most-specific [rtypes]
+ (or (some (fn [t] (when (every? #(isa? t %) rtypes) t)) rtypes)
+ (throw (Exception. "Incompatible return types"))))
+
+(defn- group-by-sig [coll]
+ "takes a collection of [msig meth] and returns a seq of maps from return-types to meths."
+ (vals (reduce (fn [m [msig meth]]
+ (let [rtype (peek msig)
+ argsig (pop msig)]
+ (assoc m argsig (assoc (m argsig {}) rtype meth))))
+ {} coll)))
+
+(defn proxy-name
+ {:tag String}
+ [#^Class super interfaces]
+ (apply str "clojure.proxy."
+ (.getName super)
+ (interleave (repeat "$")
+ (sort (map #(.getSimpleName #^Class %) interfaces)))))
+
+(defn- generate-proxy [#^Class super interfaces]
+ (let [cv (new ClassWriter (. ClassWriter COMPUTE_MAXS))
+ cname (.replace (proxy-name super interfaces) \. \/) ;(str "clojure/lang/" (gensym "Proxy__"))
+ ctype (. Type (getObjectType cname))
+ iname (fn [#^Class c] (.. Type (getType c) (getInternalName)))
+ fmap "__clojureFnMap"
+ totype (fn [#^Class c] (. Type (getType c)))
+ to-types (fn [cs] (if (pos? (count cs))
+ (into-array (map totype cs))
+ (make-array Type 0)))
+ super-type #^Type (totype super)
+ imap-type #^Type (totype IPersistentMap)
+ ifn-type (totype clojure.lang.IFn)
+ obj-type (totype Object)
+ sym-type (totype clojure.lang.Symbol)
+ rt-type (totype clojure.lang.RT)
+ ex-type (totype java.lang.UnsupportedOperationException)
+ gen-bridge
+ (fn [#^java.lang.reflect.Method meth #^java.lang.reflect.Method dest]
+ (let [pclasses (. meth (getParameterTypes))
+ ptypes (to-types pclasses)
+ rtype #^Type (totype (. meth (getReturnType)))
+ m (new Method (. meth (getName)) rtype ptypes)
+ dtype (totype (.getDeclaringClass dest))
+ dm (new Method (. dest (getName)) (totype (. dest (getReturnType))) (to-types (. dest (getParameterTypes))))
+ gen (new GeneratorAdapter (bit-or (. Opcodes ACC_PUBLIC) (. Opcodes ACC_BRIDGE)) m nil nil cv)]
+ (. gen (visitCode))
+ (. gen (loadThis))
+ (dotimes [i (count ptypes)]
+ (. gen (loadArg i)))
+ (if (-> dest .getDeclaringClass .isInterface)
+ (. gen (invokeInterface dtype dm))
+ (. gen (invokeVirtual dtype dm)))
+ (. gen (returnValue))
+ (. gen (endMethod))))
+ gen-method
+ (fn [#^java.lang.reflect.Method meth else-gen]
+ (let [pclasses (. meth (getParameterTypes))
+ ptypes (to-types pclasses)
+ rtype #^Type (totype (. meth (getReturnType)))
+ m (new Method (. meth (getName)) rtype ptypes)
+ gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)
+ else-label (. gen (newLabel))
+ end-label (. gen (newLabel))
+ decl-type (. Type (getType (. meth (getDeclaringClass))))]
+ (. gen (visitCode))
+ (if (> (count pclasses) 18)
+ (else-gen gen m)
+ (do
+ (. gen (loadThis))
+ (. gen (getField ctype fmap imap-type))
+
+ (. gen (push (. meth (getName))))
+ ;lookup fn in map
+ (. gen (invokeStatic rt-type (. Method (getMethod "Object get(Object, Object)"))))
+ (. gen (dup))
+ (. gen (ifNull else-label))
+ ;if found
+ (.checkCast gen ifn-type)
+ (. gen (loadThis))
+ ;box args
+ (dotimes [i (count ptypes)]
+ (. gen (loadArg i))
+ (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i))))
+ ;call fn
+ (. gen (invokeInterface ifn-type (new Method "invoke" obj-type
+ (into-array (cons obj-type
+ (replicate (count ptypes) obj-type))))))
+ ;unbox return
+ (. gen (unbox rtype))
+ (when (= (. rtype (getSort)) (. Type VOID))
+ (. gen (pop)))
+ (. gen (goTo end-label))
+
+ ;else call supplied alternative generator
+ (. gen (mark else-label))
+ (. gen (pop))
+
+ (else-gen gen m)
+
+ (. gen (mark end-label))))
+ (. gen (returnValue))
+ (. gen (endMethod))))]
+
+ ;start class definition
+ (. cv (visit (. Opcodes V1_5) (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_SUPER))
+ cname nil (iname super)
+ (into-array (map iname (cons IProxy interfaces)))))
+ ;add field for fn mappings
+ (. cv (visitField (+ (. Opcodes ACC_PRIVATE) (. Opcodes ACC_VOLATILE))
+ fmap (. imap-type (getDescriptor)) nil nil))
+ ;add ctors matching/calling super's
+ (doseq [#^Constructor ctor (. super (getDeclaredConstructors))]
+ (when-not (. Modifier (isPrivate (. ctor (getModifiers))))
+ (let [ptypes (to-types (. ctor (getParameterTypes)))
+ m (new Method "<init>" (. Type VOID_TYPE) ptypes)
+ gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)]
+ (. gen (visitCode))
+ ;call super ctor
+ (. gen (loadThis))
+ (. gen (dup))
+ (. gen (loadArgs))
+ (. gen (invokeConstructor super-type m))
+
+ (. gen (returnValue))
+ (. gen (endMethod)))))
+ ;add IProxy methods
+ (let [m (. Method (getMethod "void __initClojureFnMappings(clojure.lang.IPersistentMap)"))
+ gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)]
+ (. gen (visitCode))
+ (. gen (loadThis))
+ (. gen (loadArgs))
+ (. gen (putField ctype fmap imap-type))
+
+ (. gen (returnValue))
+ (. gen (endMethod)))
+ (let [m (. Method (getMethod "void __updateClojureFnMappings(clojure.lang.IPersistentMap)"))
+ gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)]
+ (. gen (visitCode))
+ (. gen (loadThis))
+ (. gen (dup))
+ (. gen (getField ctype fmap imap-type))
+ (.checkCast gen (totype clojure.lang.IPersistentCollection))
+ (. gen (loadArgs))
+ (. gen (invokeInterface (totype clojure.lang.IPersistentCollection)
+ (. Method (getMethod "clojure.lang.IPersistentCollection cons(Object)"))))
+ (. gen (checkCast imap-type))
+ (. gen (putField ctype fmap imap-type))
+
+ (. gen (returnValue))
+ (. gen (endMethod)))
+ (let [m (. Method (getMethod "clojure.lang.IPersistentMap __getClojureFnMappings()"))
+ gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)]
+ (. gen (visitCode))
+ (. gen (loadThis))
+ (. gen (getField ctype fmap imap-type))
+ (. gen (returnValue))
+ (. gen (endMethod)))
+
+ ;calc set of supers' non-private instance methods
+ (let [[mm considered]
+ (loop [mm {} considered #{} c super]
+ (if c
+ (let [[mm considered]
+ (loop [mm mm
+ considered considered
+ meths (concat
+ (seq (. c (getDeclaredMethods)))
+ (seq (. c (getMethods))))]
+ (if (seq meths)
+ (let [#^java.lang.reflect.Method meth (first meths)
+ mods (. meth (getModifiers))
+ mk (method-sig meth)]
+ (if (or (considered mk)
+ (not (or (Modifier/isPublic mods) (Modifier/isProtected mods)))
+ ;(. Modifier (isPrivate mods))
+ (. Modifier (isStatic mods))
+ (. Modifier (isFinal mods))
+ (= "finalize" (.getName meth)))
+ (recur mm (conj considered mk) (next meths))
+ (recur (assoc mm mk meth) (conj considered mk) (next meths))))
+ [mm considered]))]
+ (recur mm considered (. c (getSuperclass))))
+ [mm considered]))
+ ifaces-meths (into {}
+ (for [#^Class iface interfaces meth (. iface (getMethods))
+ :let [msig (method-sig meth)] :when (not (considered msig))]
+ {msig meth}))
+ mgroups (group-by-sig (concat mm ifaces-meths))
+ rtypes (map #(most-specific (keys %)) mgroups)
+ mb (map #(vector (%1 %2) (vals (dissoc %1 %2))) mgroups rtypes)
+ bridge? (reduce into #{} (map second mb))
+ ifaces-meths (remove bridge? (vals ifaces-meths))
+ mm (remove bridge? (vals mm))]
+ ;add methods matching supers', if no mapping -> call super
+ (doseq [[#^java.lang.reflect.Method dest bridges] mb
+ #^java.lang.reflect.Method meth bridges]
+ (gen-bridge meth dest))
+ (doseq [#^java.lang.reflect.Method meth mm]
+ (gen-method meth
+ (fn [#^GeneratorAdapter gen #^Method m]
+ (. gen (loadThis))
+ ;push args
+ (. gen (loadArgs))
+ ;call super
+ (. gen (visitMethodInsn (. Opcodes INVOKESPECIAL)
+ (. super-type (getInternalName))
+ (. m (getName))
+ (. m (getDescriptor)))))))
+
+ ;add methods matching interfaces', if no mapping -> throw
+ (doseq [#^java.lang.reflect.Method meth ifaces-meths]
+ (gen-method meth
+ (fn [#^GeneratorAdapter gen #^Method m]
+ (. gen (throwException ex-type (. m (getName))))))))
+
+ ;finish class def
+ (. cv (visitEnd))
+ [cname (. cv toByteArray)]))
+
+(defn- get-super-and-interfaces [bases]
+ (if (. #^Class (first bases) (isInterface))
+ [Object bases]
+ [(first bases) (next bases)]))
+
+(defn get-proxy-class
+ "Takes an optional single class followed by zero or more
+ interfaces. If not supplied class defaults to Object. Creates an
+ returns an instance of a proxy class derived from the supplied
+ classes. The resulting value is cached and used for any subsequent
+ requests for the same class set. Returns a Class object."
+ [& bases]
+ (let [[super interfaces] (get-super-and-interfaces bases)
+ pname (proxy-name super interfaces)]
+ (or (RT/loadClassForName pname)
+ (let [[cname bytecode] (generate-proxy super interfaces)]
+ (. (RT/getRootClassLoader) (defineClass pname bytecode))))))
+
+(defn construct-proxy
+ "Takes a proxy class and any arguments for its superclass ctor and
+ creates and returns an instance of the proxy."
+ [c & ctor-args]
+ (. Reflector (invokeConstructor c (to-array ctor-args))))
+
+(defn init-proxy
+ "Takes a proxy instance and a map of strings (which must
+ correspond to methods of the proxy superclass/superinterfaces) to
+ fns (which must take arguments matching the corresponding method,
+ plus an additional (explicit) first arg corresponding to this, and
+ sets the proxy's fn map."
+ [#^IProxy proxy mappings]
+ (. proxy (__initClojureFnMappings mappings)))
+
+(defn update-proxy
+ "Takes a proxy instance and a map of strings (which must
+ correspond to methods of the proxy superclass/superinterfaces) to
+ fns (which must take arguments matching the corresponding method,
+ plus an additional (explicit) first arg corresponding to this, and
+ updates (via assoc) the proxy's fn map. nil can be passed instead of
+ a fn, in which case the corresponding method will revert to the
+ default behavior. Note that this function can be used to update the
+ behavior of an existing instance without changing its identity."
+ [#^IProxy proxy mappings]
+ (. proxy (__updateClojureFnMappings mappings)))
+
+(defn proxy-mappings
+ "Takes a proxy instance and returns the proxy's fn map."
+ [#^IProxy proxy]
+ (. proxy (__getClojureFnMappings)))
+
+(defmacro proxy
+ "class-and-interfaces - a vector of class names
+
+ args - a (possibly empty) vector of arguments to the superclass
+ constructor.
+
+ f => (name [params*] body) or
+ (name ([params*] body) ([params+] body) ...)
+
+ Expands to code which creates a instance of a proxy class that
+ implements the named class/interface(s) by calling the supplied
+ fns. A single class, if provided, must be first. If not provided it
+ defaults to Object.
+
+ The interfaces names must be valid interface types. If a method fn
+ is not provided for a class method, the superclass methd will be
+ called. If a method fn is not provided for an interface method, an
+ UnsupportedOperationException will be thrown should it be
+ called. Method fns are closures and can capture the environment in
+ which proxy is called. Each method fn takes an additional implicit
+ first arg, which is bound to 'this. Note that while method fns can
+ be provided to override protected methods, they have no other access
+ to protected members, nor to super, as these capabilities cannot be
+ proxied."
+ [class-and-interfaces args & fs]
+ (let [bases (map #(or (resolve %) (throw (Exception. (str "Can't resolve: " %))))
+ class-and-interfaces)
+ [super interfaces] (get-super-and-interfaces bases)
+ compile-effect (when *compile-files*
+ (let [[cname bytecode] (generate-proxy super interfaces)]
+ (clojure.lang.Compiler/writeClassFile cname bytecode)))
+ pc-effect (apply get-proxy-class bases)
+ pname (proxy-name super interfaces)]
+ `(let [;pc# (get-proxy-class ~@class-and-interfaces)
+ p# (new ~(symbol pname) ~@args)] ;(construct-proxy pc# ~@args)]
+ (init-proxy p#
+ ~(loop [fmap {} fs fs]
+ (if fs
+ (let [[sym & meths] (first fs)
+ meths (if (vector? (first meths))
+ (list meths)
+ meths)
+ meths (map (fn [[params & body]]
+ (cons (apply vector 'this params) body))
+ meths)]
+ (if-not (contains? fmap (name sym))
+ (recur (assoc fmap (name sym) (cons `fn meths)) (next fs))
+ (throw (IllegalArgumentException.
+ (str "Method '" (name sym) "' redefined")))))
+ fmap)))
+ p#)))
+
+(defn proxy-call-with-super [call this meth]
+ (let [m (proxy-mappings this)]
+ (update-proxy this (assoc m meth nil))
+ (let [ret (call)]
+ (update-proxy this m)
+ ret)))
+
+(defmacro proxy-super
+ "Use to call a superclass method in the body of a proxy method.
+ Note, expansion captures 'this"
+ [meth & args]
+ `(proxy-call-with-super (fn [] (. ~'this ~meth ~@args)) ~'this ~(name meth)))
+
+(defn bean
+ "Takes a Java object and returns a read-only implementation of the
+ map abstraction based upon its JavaBean properties."
+ [#^Object x]
+ (let [c (. x (getClass))
+ pmap (reduce (fn [m #^java.beans.PropertyDescriptor pd]
+ (let [name (. pd (getName))
+ method (. pd (getReadMethod))]
+ (if (and method (zero? (alength (. method (getParameterTypes)))))
+ (assoc m (keyword name) (fn [] (clojure.lang.Reflector/prepRet (. method (invoke x nil)))))
+ m)))
+ {}
+ (seq (.. java.beans.Introspector
+ (getBeanInfo c)
+ (getPropertyDescriptors))))
+ v (fn [k] ((pmap k)))
+ snapshot (fn []
+ (reduce (fn [m e]
+ (assoc m (key e) ((val e))))
+ {} (seq pmap)))]
+ (proxy [clojure.lang.APersistentMap]
+ []
+ (containsKey [k] (contains? pmap k))
+ (entryAt [k] (when (contains? pmap k) (new clojure.lang.MapEntry k (v k))))
+ (valAt ([k] (v k))
+ ([k default] (if (contains? pmap k) (v k) default)))
+ (cons [m] (conj (snapshot) m))
+ (count [] (count pmap))
+ (assoc [k v] (assoc (snapshot) k v))
+ (without [k] (dissoc (snapshot) k))
+ (seq [] ((fn thisfn [plseq]
+ (lazy-seq
+ (when-let [pseq (seq plseq)]
+ (cons (new clojure.lang.MapEntry (first pseq) (v (first pseq)))
+ (thisfn (rest pseq)))))) (keys pmap))))))
+
+
+
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html 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.
+
+(in-ns 'clojure.core)
+
+(import '(java.lang.reflect Modifier Constructor)
+ '(clojure.asm ClassWriter ClassVisitor Opcodes Type)
+ '(clojure.asm.commons Method GeneratorAdapter)
+ '(clojure.lang IPersistentMap))
+
+;(defn method-sig [#^java.lang.reflect.Method meth]
+; [(. meth (getName)) (seq (. meth (getParameterTypes)))])
+
+(defn- non-private-methods [#^Class c]
+ (loop [mm {}
+ considered #{}
+ c c]
+ (if c
+ (let [[mm considered]
+ (loop [mm mm
+ considered considered
+ meths (seq (concat
+ (seq (. c (getDeclaredMethods)))
+ (seq (. c (getMethods)))))]
+ (if meths
+ (let [#^java.lang.reflect.Method meth (first meths)
+ mods (. meth (getModifiers))
+ mk (method-sig meth)]
+ (if (or (considered mk)
+ (not (or (Modifier/isPublic mods) (Modifier/isProtected mods)))
+ ;(. Modifier (isPrivate mods))
+ (. Modifier (isStatic mods))
+ (. Modifier (isFinal mods))
+ (= "finalize" (.getName meth)))
+ (recur mm (conj considered mk) (next meths))
+ (recur (assoc mm mk meth) (conj considered mk) (next meths))))
+ [mm considered]))]
+ (recur mm considered (. c (getSuperclass))))
+ mm)))
+
+(defn- ctor-sigs [#^Class super]
+ (for [#^Constructor ctor (. super (getDeclaredConstructors))
+ :when (not (. Modifier (isPrivate (. ctor (getModifiers)))))]
+ (apply vector (. ctor (getParameterTypes)))))
+
+(defn- escape-class-name [#^Class c]
+ (.. (.getSimpleName c)
+ (replace "[]" "<>")))
+
+(defn- overload-name [mname pclasses]
+ (if (seq pclasses)
+ (apply str mname (interleave (repeat \-)
+ (map escape-class-name pclasses)))
+ (str mname "-void")))
+
+(defn- #^java.lang.reflect.Field find-field [#^Class c f]
+ (let [start-class c]
+ (loop [c c]
+ (if (= c Object)
+ (throw (new Exception (str "field, " f ", not defined in class, " start-class ", or its ancestors")))
+ (let [dflds (.getDeclaredFields c)
+ rfld (first (filter #(= f (.getName #^java.lang.reflect.Field %)) dflds))]
+ (or rfld (recur (.getSuperclass c))))))))
+
+;(distinct (map first(keys (mapcat non-private-methods [Object IPersistentMap]))))
+
+(def #^{:private true} prim->class
+ {'int Integer/TYPE
+ 'long Long/TYPE
+ 'float Float/TYPE
+ 'double Double/TYPE
+ 'void Void/TYPE
+ 'short Short/TYPE
+ 'boolean Boolean/TYPE
+ 'byte Byte/TYPE
+ 'char Character/TYPE})
+
+(defn- #^Class the-class [x]
+ (cond
+ (class? x) x
+ (contains? prim->class x) (prim->class x)
+ :else (let [strx (str x)]
+ (clojure.lang.RT/classForName
+ (if (some #{\.} strx)
+ strx
+ (str "java.lang." strx))))))
+
+(defn- generate-class [options-map]
+ (let [default-options {:prefix "-" :load-impl-ns true :impl-ns (ns-name *ns*)}
+ {:keys [name extends implements constructors methods main factory state init exposes
+ exposes-methods prefix load-impl-ns impl-ns post-init]}
+ (merge default-options options-map)
+ name (str name)
+ super (if extends (the-class extends) Object)
+ interfaces (map the-class implements)
+ supers (cons super interfaces)
+ ctor-sig-map (or constructors (zipmap (ctor-sigs super) (ctor-sigs super)))
+ cv (new ClassWriter (. ClassWriter COMPUTE_MAXS))
+ cname (. name (replace "." "/"))
+ pkg-name name
+ impl-pkg-name (str impl-ns)
+ impl-cname (.. impl-pkg-name (replace "." "/") (replace \- \_))
+ ctype (. Type (getObjectType cname))
+ iname (fn [#^Class c] (.. Type (getType c) (getInternalName)))
+ totype (fn [#^Class c] (. Type (getType c)))
+ to-types (fn [cs] (if (pos? (count cs))
+ (into-array (map totype cs))
+ (make-array Type 0)))
+ obj-type #^Type (totype Object)
+ arg-types (fn [n] (if (pos? n)
+ (into-array (replicate n obj-type))
+ (make-array Type 0)))
+ super-type #^Type (totype super)
+ init-name (str init)
+ post-init-name (str post-init)
+ factory-name (str factory)
+ state-name (str state)
+ main-name "main"
+ var-name (fn [s] (str s "__var"))
+ class-type (totype Class)
+ rt-type (totype clojure.lang.RT)
+ var-type #^Type (totype clojure.lang.Var)
+ ifn-type (totype clojure.lang.IFn)
+ iseq-type (totype clojure.lang.ISeq)
+ ex-type (totype java.lang.UnsupportedOperationException)
+ all-sigs (distinct (concat (map #(let[[m p] (key %)] {m [p]}) (mapcat non-private-methods supers))
+ (map (fn [[m p]] {(str m) [p]}) methods)))
+ sigs-by-name (apply merge-with concat {} all-sigs)
+ overloads (into {} (filter (fn [[m s]] (next s)) sigs-by-name))
+ var-fields (concat (when init [init-name])
+ (when post-init [post-init-name])
+ (when main [main-name])
+ ;(when exposes-methods (map str (vals exposes-methods)))
+ (distinct (concat (keys sigs-by-name)
+ (mapcat (fn [[m s]] (map #(overload-name m (map the-class %)) s)) overloads)
+ (mapcat (comp (partial map str) vals val) exposes))))
+ emit-get-var (fn [#^GeneratorAdapter gen v]
+ (let [false-label (. gen newLabel)
+ end-label (. gen newLabel)]
+ (. gen getStatic ctype (var-name v) var-type)
+ (. gen dup)
+ (. gen invokeVirtual var-type (. Method (getMethod "boolean isBound()")))
+ (. gen ifZCmp (. GeneratorAdapter EQ) false-label)
+ (. gen invokeVirtual var-type (. Method (getMethod "Object get()")))
+ (. gen goTo end-label)
+ (. gen mark false-label)
+ (. gen pop)
+ (. gen visitInsn (. Opcodes ACONST_NULL))
+ (. gen mark end-label)))
+ emit-unsupported (fn [#^GeneratorAdapter gen #^Method m]
+ (. gen (throwException ex-type (str (. m (getName)) " ("
+ impl-pkg-name "/" prefix (.getName m)
+ " not defined?)"))))
+ emit-forwarding-method
+ (fn [mname pclasses rclass as-static else-gen]
+ (let [pclasses (map the-class pclasses)
+ rclass (the-class rclass)
+ ptypes (to-types pclasses)
+ rtype #^Type (totype rclass)
+ m (new Method mname rtype ptypes)
+ is-overload (seq (overloads mname))
+ gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (if as-static (. Opcodes ACC_STATIC) 0))
+ m nil nil cv)
+ found-label (. gen (newLabel))
+ else-label (. gen (newLabel))
+ end-label (. gen (newLabel))]
+ (. gen (visitCode))
+ (if (> (count pclasses) 18)
+ (else-gen gen m)
+ (do
+ (when is-overload
+ (emit-get-var gen (overload-name mname pclasses))
+ (. gen (dup))
+ (. gen (ifNonNull found-label))
+ (. gen (pop)))
+ (emit-get-var gen mname)
+ (. gen (dup))
+ (. gen (ifNull else-label))
+ (when is-overload
+ (. gen (mark found-label)))
+ ;if found
+ (.checkCast gen ifn-type)
+ (when-not as-static
+ (. gen (loadThis)))
+ ;box args
+ (dotimes [i (count ptypes)]
+ (. gen (loadArg i))
+ (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i))))
+ ;call fn
+ (. gen (invokeInterface ifn-type (new Method "invoke" obj-type
+ (to-types (replicate (+ (count ptypes)
+ (if as-static 0 1))
+ Object)))))
+ ;(into-array (cons obj-type
+ ; (replicate (count ptypes) obj-type))))))
+ ;unbox return
+ (. gen (unbox rtype))
+ (when (= (. rtype (getSort)) (. Type VOID))
+ (. gen (pop)))
+ (. gen (goTo end-label))
+
+ ;else call supplied alternative generator
+ (. gen (mark else-label))
+ (. gen (pop))
+
+ (else-gen gen m)
+
+ (. gen (mark end-label))))
+ (. gen (returnValue))
+ (. gen (endMethod))))
+ ]
+ ;start class definition
+ (. cv (visit (. Opcodes V1_5) (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_SUPER))
+ cname nil (iname super)
+ (when-let [ifc (seq interfaces)]
+ (into-array (map iname ifc)))))
+
+ ;static fields for vars
+ (doseq [v var-fields]
+ (. cv (visitField (+ (. Opcodes ACC_PRIVATE) (. Opcodes ACC_FINAL) (. Opcodes ACC_STATIC))
+ (var-name v)
+ (. var-type getDescriptor)
+ nil nil)))
+
+ ;instance field for state
+ (when state
+ (. cv (visitField (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_FINAL))
+ state-name
+ (. obj-type getDescriptor)
+ nil nil)))
+
+ ;static init to set up var fields and load init
+ (let [gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC))
+ (. Method getMethod "void <clinit> ()")
+ nil nil cv)]
+ (. gen (visitCode))
+ (doseq [v var-fields]
+ (. gen push impl-pkg-name)
+ (. gen push (str prefix v))
+ (. gen (invokeStatic var-type (. Method (getMethod "clojure.lang.Var internPrivate(String,String)"))))
+ (. gen putStatic ctype (var-name v) var-type))
+
+ (when load-impl-ns
+ (. gen push "clojure.core")
+ (. gen push "load")
+ (. gen (invokeStatic rt-type (. Method (getMethod "clojure.lang.Var var(String,String)"))))
+ (. gen push (str "/" impl-cname))
+ (. gen (invokeInterface ifn-type (new Method "invoke" obj-type (to-types [Object]))))
+; (. gen push (str (.replace impl-pkg-name \- \_) "__init"))
+; (. gen (invokeStatic class-type (. Method (getMethod "Class forName(String)"))))
+ (. gen pop))
+
+ (. gen (returnValue))
+ (. gen (endMethod)))
+
+ ;ctors
+ (doseq [[pclasses super-pclasses] ctor-sig-map]
+ (let [pclasses (map the-class pclasses)
+ super-pclasses (map the-class super-pclasses)
+ ptypes (to-types pclasses)
+ super-ptypes (to-types super-pclasses)
+ m (new Method "<init>" (. Type VOID_TYPE) ptypes)
+ super-m (new Method "<init>" (. Type VOID_TYPE) super-ptypes)
+ gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)
+ no-init-label (. gen newLabel)
+ end-label (. gen newLabel)
+ no-post-init-label (. gen newLabel)
+ end-post-init-label (. gen newLabel)
+ nth-method (. Method (getMethod "Object nth(Object,int)"))
+ local (. gen newLocal obj-type)]
+ (. gen (visitCode))
+
+ (if init
+ (do
+ (emit-get-var gen init-name)
+ (. gen dup)
+ (. gen ifNull no-init-label)
+ (.checkCast gen ifn-type)
+ ;box init args
+ (dotimes [i (count pclasses)]
+ (. gen (loadArg i))
+ (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i))))
+ ;call init fn
+ (. gen (invokeInterface ifn-type (new Method "invoke" obj-type
+ (arg-types (count ptypes)))))
+ ;expecting [[super-ctor-args] state] returned
+ (. gen dup)
+ (. gen push 0)
+ (. gen (invokeStatic rt-type nth-method))
+ (. gen storeLocal local)
+
+ (. gen (loadThis))
+ (. gen dupX1)
+ (dotimes [i (count super-pclasses)]
+ (. gen loadLocal local)
+ (. gen push i)
+ (. gen (invokeStatic rt-type nth-method))
+ (. clojure.lang.Compiler$HostExpr (emitUnboxArg nil gen (nth super-pclasses i))))
+ (. gen (invokeConstructor super-type super-m))
+
+ (if state
+ (do
+ (. gen push 1)
+ (. gen (invokeStatic rt-type nth-method))
+ (. gen (putField ctype state-name obj-type)))
+ (. gen pop))
+
+ (. gen goTo end-label)
+ ;no init found
+ (. gen mark no-init-label)
+ (. gen (throwException ex-type (str impl-pkg-name "/" prefix init-name " not defined")))
+ (. gen mark end-label))
+ (if (= pclasses super-pclasses)
+ (do
+ (. gen (loadThis))
+ (. gen (loadArgs))
+ (. gen (invokeConstructor super-type super-m)))
+ (throw (new Exception ":init not specified, but ctor and super ctor args differ"))))
+
+ (when post-init
+ (emit-get-var gen post-init-name)
+ (. gen dup)
+ (. gen ifNull no-post-init-label)
+ (.checkCast gen ifn-type)
+ (. gen (loadThis))
+ ;box init args
+ (dotimes [i (count pclasses)]
+ (. gen (loadArg i))
+ (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i))))
+ ;call init fn
+ (. gen (invokeInterface ifn-type (new Method "invoke" obj-type
+ (arg-types (inc (count ptypes))))))
+ (. gen pop)
+ (. gen goTo end-post-init-label)
+ ;no init found
+ (. gen mark no-post-init-label)
+ (. gen (throwException ex-type (str impl-pkg-name "/" prefix post-init-name " not defined")))
+ (. gen mark end-post-init-label))
+
+ (. gen (returnValue))
+ (. gen (endMethod))
+ ;factory
+ (when factory
+ (let [fm (new Method factory-name ctype ptypes)
+ gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC))
+ fm nil nil cv)]
+ (. gen (visitCode))
+ (. gen newInstance ctype)
+ (. gen dup)
+ (. gen (loadArgs))
+ (. gen (invokeConstructor ctype m))
+ (. gen (returnValue))
+ (. gen (endMethod))))))
+
+ ;add methods matching supers', if no fn -> call super
+ (let [mm (non-private-methods super)]
+ (doseq [#^java.lang.reflect.Method meth (vals mm)]
+ (emit-forwarding-method (.getName meth) (.getParameterTypes meth) (.getReturnType meth) false
+ (fn [#^GeneratorAdapter gen #^Method m]
+ (. gen (loadThis))
+ ;push args
+ (. gen (loadArgs))
+ ;call super
+ (. gen (visitMethodInsn (. Opcodes INVOKESPECIAL)
+ (. super-type (getInternalName))
+ (. m (getName))
+ (. m (getDescriptor)))))))
+ ;add methods matching interfaces', if no fn -> throw
+ (reduce (fn [mm #^java.lang.reflect.Method meth]
+ (if (contains? mm (method-sig meth))
+ mm
+ (do
+ (emit-forwarding-method (.getName meth) (.getParameterTypes meth) (.getReturnType meth) false
+ emit-unsupported)
+ (assoc mm (method-sig meth) meth))))
+ mm (mapcat #(.getMethods #^Class %) interfaces))
+ ;extra methods
+ (doseq [[mname pclasses rclass :as msig] methods]
+ (emit-forwarding-method (str mname) pclasses rclass (:static ^msig)
+ emit-unsupported))
+ ;expose specified overridden superclass methods
+ (doseq [[local-mname #^java.lang.reflect.Method m] (reduce (fn [ms [[name _ _] m]]
+ (if (contains? exposes-methods (symbol name))
+ (conj ms [((symbol name) exposes-methods) m])
+ ms)) [] (seq mm))]
+ (let [ptypes (to-types (.getParameterTypes m))
+ rtype (totype (.getReturnType m))
+ exposer-m (new Method (str local-mname) rtype ptypes)
+ target-m (new Method (.getName m) rtype ptypes)
+ gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) exposer-m nil nil cv)]
+ (. gen (loadThis))
+ (. gen (loadArgs))
+ (. gen (visitMethodInsn (. Opcodes INVOKESPECIAL)
+ (. super-type (getInternalName))
+ (. target-m (getName))
+ (. target-m (getDescriptor))))
+ (. gen (returnValue))
+ (. gen (endMethod)))))
+ ;main
+ (when main
+ (let [m (. Method getMethod "void main (String[])")
+ gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC))
+ m nil nil cv)
+ no-main-label (. gen newLabel)
+ end-label (. gen newLabel)]
+ (. gen (visitCode))
+
+ (emit-get-var gen main-name)
+ (. gen dup)
+ (. gen ifNull no-main-label)
+ (.checkCast gen ifn-type)
+ (. gen loadArgs)
+ (. gen (invokeStatic rt-type (. Method (getMethod "clojure.lang.ISeq seq(Object)"))))
+ (. gen (invokeInterface ifn-type (new Method "applyTo" obj-type
+ (into-array [iseq-type]))))
+ (. gen pop)
+ (. gen goTo end-label)
+ ;no main found
+ (. gen mark no-main-label)
+ (. gen (throwException ex-type (str impl-pkg-name "/" prefix main-name " not defined")))
+ (. gen mark end-label)
+ (. gen (returnValue))
+ (. gen (endMethod))))
+ ;field exposers
+ (doseq [[f {getter :get setter :set}] exposes]
+ (let [fld (find-field super (str f))
+ ftype (totype (.getType fld))
+ static? (Modifier/isStatic (.getModifiers fld))
+ acc (+ Opcodes/ACC_PUBLIC (if static? Opcodes/ACC_STATIC 0))]
+ (when getter
+ (let [m (new Method (str getter) ftype (to-types []))
+ gen (new GeneratorAdapter acc m nil nil cv)]
+ (. gen (visitCode))
+ (if static?
+ (. gen getStatic ctype (str f) ftype)
+ (do
+ (. gen loadThis)
+ (. gen getField ctype (str f) ftype)))
+ (. gen (returnValue))
+ (. gen (endMethod))))
+ (when setter
+ (let [m (new Method (str setter) Type/VOID_TYPE (into-array [ftype]))
+ gen (new GeneratorAdapter acc m nil nil cv)]
+ (. gen (visitCode))
+ (if static?
+ (do
+ (. gen loadArgs)
+ (. gen putStatic ctype (str f) ftype))
+ (do
+ (. gen loadThis)
+ (. gen loadArgs)
+ (. gen putField ctype (str f) ftype)))
+ (. gen (returnValue))
+ (. gen (endMethod))))))
+ ;finish class def
+ (. cv (visitEnd))
+ [cname (. cv (toByteArray))]))
+
+(defmacro gen-class
+ "When compiling, generates compiled bytecode for a class with the
+ given package-qualified :name (which, as all names in these
+ parameters, can be a string or symbol), and writes the .class file
+ to the *compile-path* directory. When not compiling, does
+ nothing. The gen-class construct contains no implementation, as the
+ implementation will be dynamically sought by the generated class in
+ functions in an implementing Clojure namespace. Given a generated
+ class org.mydomain.MyClass with a method named mymethod, gen-class
+ will generate an implementation that looks for a function named by
+ (str prefix mymethod) (default prefix: \"-\") in a
+ Clojure namespace specified by :impl-ns
+ (defaults to the current namespace). All inherited methods,
+ generated methods, and init and main functions (see :methods, :init,
+ and :main below) will be found similarly prefixed. By default, the
+ static initializer for the generated class will attempt to load the
+ Clojure support code for the class as a resource from the classpath,
+ e.g. in the example case, ``org/mydomain/MyClass__init.class``. This
+ behavior can be controlled by :load-impl-ns
+
+ Note that methods with a maximum of 18 parameters are supported.
+
+ In all subsequent sections taking types, the primitive types can be
+ referred to by their Java names (int, float etc), and classes in the
+ java.lang package can be used without a package qualifier. All other
+ classes must be fully qualified.
+
+ Options should be a set of key/value pairs, all except for :name are optional:
+
+ :name aname
+
+ The package-qualified name of the class to be generated
+
+ :extends aclass
+
+ Specifies the superclass, the non-private methods of which will be
+ overridden by the class. If not provided, defaults to Object.
+
+ :implements [interface ...]
+
+ One or more interfaces, the methods of which will be implemented by the class.
+
+ :init name
+
+ If supplied, names a function that will be called with the arguments
+ to the constructor. Must return [ [superclass-constructor-args] state]
+ If not supplied, the constructor args are passed directly to
+ the superclass constructor and the state will be nil
+
+ :constructors {[param-types] [super-param-types], ...}
+
+ By default, constructors are created for the generated class which
+ match the signature(s) of the constructors for the superclass. This
+ parameter may be used to explicitly specify constructors, each entry
+ providing a mapping from a constructor signature to a superclass
+ constructor signature. When you supply this, you must supply an :init
+ specifier.
+
+ :post-init name
+
+ If supplied, names a function that will be called with the object as
+ the first argument, followed by the arguments to the constructor.
+ It will be called every time an object of this class is created,
+ immediately after all the inherited constructors have completed.
+ It's return value is ignored.
+
+ :methods [ [name [param-types] return-type], ...]
+
+ The generated class automatically defines all of the non-private
+ methods of its superclasses/interfaces. This parameter can be used
+ to specify the signatures of additional methods of the generated
+ class. Static methods can be specified with #^{:static true} in the
+ signature's metadata. Do not repeat superclass/interface signatures
+ here.
+
+ :main boolean
+
+ If supplied and true, a static public main function will be generated. It will
+ pass each string of the String[] argument as a separate argument to
+ a function called (str prefix main).
+
+ :factory name
+
+ If supplied, a (set of) public static factory function(s) will be
+ created with the given name, and the same signature(s) as the
+ constructor(s).
+
+ :state name
+
+ If supplied, a public final instance field with the given name will be
+ created. You must supply an :init function in order to provide a
+ value for the state. Note that, though final, the state can be a ref
+ or agent, supporting the creation of Java objects with transactional
+ or asynchronous mutation semantics.
+
+ :exposes {protected-field-name {:get name :set name}, ...}
+
+ Since the implementations of the methods of the generated class
+ occur in Clojure functions, they have no access to the inherited
+ protected fields of the superclass. This parameter can be used to
+ generate public getter/setter methods exposing the protected field(s)
+ for use in the implementation.
+
+ :exposes-methods {super-method-name exposed-name, ...}
+
+ It is sometimes necessary to call the superclass' implementation of an
+ overridden method. Those methods may be exposed and referred in
+ the new method implementation by a local name.
+
+ :prefix string
+
+ Default: \"-\" Methods called e.g. Foo will be looked up in vars called
+ prefixFoo in the implementing ns.
+
+ :impl-ns name
+
+ Default: the name of the current ns. Implementations of methods will be looked up in this namespace.
+
+ :load-impl-ns boolean
+
+ Default: true. Causes the static initializer for the generated class
+ to reference the load code for the implementing namespace. Should be
+ true when implementing-ns is the default, false if you intend to
+ load the code via some other method."
+
+ [& options]
+ (when *compile-files*
+ (let [options-map (apply hash-map options)
+ [cname bytecode] (generate-class options-map)]
+ (clojure.lang.Compiler/writeClassFile cname bytecode))))
+
+;;;;;;;;;;;;;;;;;;;; gen-interface ;;;;;;;;;;;;;;;;;;;;;;
+;; based on original contribution by Chris Houser
+
+(defn- #^Type asm-type
+ "Returns an asm Type object for c, which may be a primitive class
+ (such as Integer/TYPE), any other class (such as Double), or a
+ fully-qualified class name given as a string or symbol
+ (such as 'java.lang.String)"
+ [c]
+ (if (or (instance? Class c) (prim->class c))
+ (Type/getType (the-class c))
+ (let [strx (str c)]
+ (Type/getObjectType
+ (.replace (if (some #{\.} strx)
+ strx
+ (str "java.lang." strx))
+ "." "/")))))
+
+(defn- generate-interface
+ [{:keys [name extends methods]}]
+ (let [iname (.replace (str name) "." "/")
+ cv (ClassWriter. ClassWriter/COMPUTE_MAXS)]
+ (. cv visit Opcodes/V1_5 (+ Opcodes/ACC_PUBLIC
+ Opcodes/ACC_ABSTRACT
+ Opcodes/ACC_INTERFACE)
+ iname nil "java/lang/Object"
+ (when (seq extends)
+ (into-array (map #(.getInternalName (asm-type %)) extends))))
+ (doseq [[mname pclasses rclass] methods]
+ (. cv visitMethod (+ Opcodes/ACC_PUBLIC Opcodes/ACC_ABSTRACT)
+ (str mname)
+ (Type/getMethodDescriptor (asm-type rclass)
+ (if pclasses
+ (into-array Type (map asm-type pclasses))
+ (make-array Type 0)))
+ nil nil))
+ (. cv visitEnd)
+ [iname (. cv toByteArray)]))
+
+(defmacro gen-interface
+ "When compiling, generates compiled bytecode for an interface with
+ the given package-qualified :name (which, as all names in these
+ parameters, can be a string or symbol), and writes the .class file
+ to the *compile-path* directory. When not compiling, does nothing.
+
+ In all subsequent sections taking types, the primitive types can be
+ referred to by their Java names (int, float etc), and classes in the
+ java.lang package can be used without a package qualifier. All other
+ classes must be fully qualified.
+
+ Options should be a set of key/value pairs, all except for :name are
+ optional:
+
+ :name aname
+
+ The package-qualified name of the class to be generated
+
+ :extends [interface ...]
+
+ One or more interfaces, which will be extended by this interface.
+
+ :methods [ [name [param-types] return-type], ...]
+
+ This parameter is used to specify the signatures of the methods of
+ the generated interface. Do not repeat superinterface signatures
+ here."
+
+ [& options]
+ (when *compile-files*
+ (let [options-map (apply hash-map options)
+ [cname bytecode] (generate-interface options-map)]
+ (clojure.lang.Compiler/writeClassFile cname bytecode))))
+
+(comment
+
+(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."
+
+ [& options]
+ (let [options-map (apply hash-map options)
+ [cname bytecode] (generate-class options-map)]
+ (.. (clojure.lang.RT/getRootClassLoader) (defineClass cname bytecode))))
+
+)
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html 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.
+
+(ns clojure.inspector
+ (:import
+ (java.awt BorderLayout)
+ (java.awt.event ActionEvent ActionListener)
+ (javax.swing.tree TreeModel)
+ (javax.swing.table TableModel AbstractTableModel)
+ (javax.swing JPanel JTree JTable JScrollPane JFrame JToolBar JButton SwingUtilities)))
+
+(defn atom? [x]
+ (not (coll? x)))
+
+(defn collection-tag [x]
+ (cond
+ (instance? java.util.Map$Entry x) :entry
+ (instance? java.util.Map x) :map
+ (sequential? x) :seq
+ :else :atom))
+
+(defmulti is-leaf collection-tag)
+(defmulti get-child (fn [parent index] (collection-tag parent)))
+(defmulti get-child-count collection-tag)
+
+(defmethod is-leaf :default [node]
+ (atom? node))
+(defmethod get-child :default [parent index]
+ (nth parent index))
+(defmethod get-child-count :default [parent]
+ (count parent))
+
+(defmethod is-leaf :entry [e]
+ (is-leaf (val e)))
+(defmethod get-child :entry [e index]
+ (get-child (val e) index))
+(defmethod get-child-count :entry [e]
+ (count (val e)))
+
+(defmethod is-leaf :map [m]
+ false)
+(defmethod get-child :map [m index]
+ (nth (seq m) index))
+
+(defn tree-model [data]
+ (proxy [TreeModel] []
+ (getRoot [] data)
+ (addTreeModelListener [treeModelListener])
+ (getChild [parent index]
+ (get-child parent index))
+ (getChildCount [parent]
+ (get-child-count parent))
+ (isLeaf [node]
+ (is-leaf node))
+ (valueForPathChanged [path newValue])
+ (getIndexOfChild [parent child]
+ -1)
+ (removeTreeModelListener [treeModelListener])))
+
+
+(defn old-table-model [data]
+ (let [row1 (first data)
+ colcnt (count row1)
+ cnt (count data)
+ vals (if (map? row1) vals identity)]
+ (proxy [TableModel] []
+ (addTableModelListener [tableModelListener])
+ (getColumnClass [columnIndex] Object)
+ (getColumnCount [] colcnt)
+ (getColumnName [columnIndex]
+ (if (map? row1)
+ (name (nth (keys row1) columnIndex))
+ (str columnIndex)))
+ (getRowCount [] cnt)
+ (getValueAt [rowIndex columnIndex]
+ (nth (vals (nth data rowIndex)) columnIndex))
+ (isCellEditable [rowIndex columnIndex] false)
+ (removeTableModelListener [tableModelListener]))))
+
+(defn inspect-tree
+ "creates a graphical (Swing) inspector on the supplied hierarchical data"
+ [data]
+ (doto (JFrame. "Clojure Inspector")
+ (.add (JScrollPane. (JTree. (tree-model data))))
+ (.setSize 400 600)
+ (.setVisible true)))
+
+(defn inspect-table
+ "creates a graphical (Swing) inspector on the supplied regular
+ data, which must be a sequential data structure of data structures
+ of equal length"
+ [data]
+ (doto (JFrame. "Clojure Inspector")
+ (.add (JScrollPane. (JTable. (old-table-model data))))
+ (.setSize 400 600)
+ (.setVisible true)))
+
+
+(defmulti list-provider class)
+
+(defmethod list-provider :default [x]
+ {:nrows 1 :get-value (fn [i] x) :get-label (fn [i] (.getName (class x)))})
+
+(defmethod list-provider java.util.List [c]
+ (let [v (if (vector? c) c (vec c))]
+ {:nrows (count v)
+ :get-value (fn [i] (v i))
+ :get-label (fn [i] i)}))
+
+(defmethod list-provider java.util.Map [c]
+ (let [v (vec (sort (map (fn [[k v]] (vector k v)) c)))]
+ {:nrows (count v)
+ :get-value (fn [i] ((v i) 1))
+ :get-label (fn [i] ((v i) 0))}))
+
+(defn list-model [provider]
+ (let [{:keys [nrows get-value get-label]} provider]
+ (proxy [AbstractTableModel] []
+ (getColumnCount [] 2)
+ (getRowCount [] nrows)
+ (getValueAt [rowIndex columnIndex]
+ (cond
+ (= 0 columnIndex) (get-label rowIndex)
+ (= 1 columnIndex) (print-str (get-value rowIndex)))))))
+
+(defmulti table-model class)
+
+(defmethod table-model :default [x]
+ (proxy [AbstractTableModel] []
+ (getColumnCount [] 2)
+ (getRowCount [] 1)
+ (getValueAt [rowIndex columnIndex]
+ (if (zero? columnIndex)
+ (class x)
+ x))))
+
+;(defn make-inspector [x]
+; (agent {:frame frame :data x :parent nil :index 0}))
+
+
+(defn inspect
+ "creates a graphical (Swing) inspector on the supplied object"
+ [x]
+ (doto (JFrame. "Clojure Inspector")
+ (.add
+ (doto (JPanel. (BorderLayout.))
+ (.add (doto (JToolBar.)
+ (.add (JButton. "Back"))
+ (.addSeparator)
+ (.add (JButton. "List"))
+ (.add (JButton. "Table"))
+ (.add (JButton. "Bean"))
+ (.add (JButton. "Line"))
+ (.add (JButton. "Bar"))
+ (.addSeparator)
+ (.add (JButton. "Prev"))
+ (.add (JButton. "Next")))
+ BorderLayout/NORTH)
+ (.add
+ (JScrollPane.
+ (doto (JTable. (list-model (list-provider x)))
+ (.setAutoResizeMode JTable/AUTO_RESIZE_LAST_COLUMN)))
+ BorderLayout/CENTER)))
+ (.setSize 400 400)
+ (.setVisible true)))
+
+
+(comment
+
+(load-file "src/inspector.clj")
+(refer 'inspector)
+(inspect-tree {:a 1 :b 2 :c [1 2 3 {:d 4 :e 5 :f [6 7 8]}]})
+(inspect-table [[1 2 3][4 5 6][7 8 9][10 11 12]])
+
+)
+;; Copyright (c) Rich Hickey All rights reserved. The use and
+;; distribution terms for this software are covered by the Eclipse Public
+;; License 1.0 (http://opensource.org/licenses/eclipse-1.0.php) which can be found
+;; in the file epl-v10.html 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.
+
+;; Originally contributed by Stephen C. Gilardi
+
+(ns clojure.main
+ (:import (clojure.lang Compiler Compiler$CompilerException
+ LineNumberingPushbackReader RT)))
+
+(declare main)
+
+(defmacro with-bindings
+ "Executes body in the context of thread-local bindings for several vars
+ that often need to be set!: *ns* *warn-on-reflection* *print-meta*
+ *print-length* *print-level* *compile-path* *command-line-args* *1
+ *2 *3 *e"
+ [& body]
+ `(binding [*ns* *ns*
+ *warn-on-reflection* *warn-on-reflection*
+ *print-meta* *print-meta*
+ *print-length* *print-length*
+ *print-level* *print-level*
+ *compile-path* (System/getProperty "clojure.compile.path" "classes")
+ *command-line-args* *command-line-args*
+ *1 nil
+ *2 nil
+ *3 nil
+ *e nil]
+ ~@body))
+
+(defn repl-prompt
+ "Default :prompt hook for repl"
+ []
+ (printf "%s=> " (ns-name *ns*)))
+
+(defn skip-if-eol
+ "If the next character on stream s is a newline, skips it, otherwise
+ leaves the stream untouched. Returns :line-start, :stream-end, or :body
+ to indicate the relative location of the next character on s. The stream
+ must either be an instance of LineNumberingPushbackReader or duplicate
+ its behavior of both supporting .unread and collapsing all of CR, LF, and
+ CRLF to a single \\newline."
+ [s]
+ (let [c (.read s)]
+ (cond
+ (= c (int \newline)) :line-start
+ (= c -1) :stream-end
+ :else (do (.unread s c) :body))))
+
+(defn skip-whitespace
+ "Skips whitespace characters on stream s. Returns :line-start, :stream-end,
+ or :body to indicate the relative location of the next character on s.
+ Interprets comma as whitespace and semicolon as comment to end of line.
+ Does not interpret #! as comment to end of line because only one
+ character of lookahead is available. The stream must either be an
+ instance of LineNumberingPushbackReader or duplicate its behavior of both
+ supporting .unread and collapsing all of CR, LF, and CRLF to a single
+ \\newline."
+ [s]
+ (loop [c (.read s)]
+ (cond
+ (= c (int \newline)) :line-start
+ (= c -1) :stream-end
+ (= c (int \;)) (do (.readLine s) :line-start)
+ (or (Character/isWhitespace c) (= c (int \,))) (recur (.read s))
+ :else (do (.unread s c) :body))))
+
+(defn repl-read
+ "Default :read hook for repl. Reads from *in* which must either be an
+ instance of LineNumberingPushbackReader or duplicate its behavior of both
+ supporting .unread and collapsing all of CR, LF, and CRLF into a single
+ \\newline. repl-read:
+ - skips whitespace, then
+ - returns request-prompt on start of line, or
+ - returns request-exit on end of stream, or
+ - reads an object from the input stream, then
+ - skips the next input character if it's end of line, then
+ - returns the object."
+ [request-prompt request-exit]
+ (or ({:line-start request-prompt :stream-end request-exit}
+ (skip-whitespace *in*))
+ (let [input (read)]
+ (skip-if-eol *in*)
+ input)))
+
+(defn- root-cause
+ "Returns the initial cause of an exception or error by peeling off all of
+ its wrappers"
+ [throwable]
+ (loop [cause throwable]
+ (if-let [cause (.getCause cause)]
+ (recur cause)
+ cause)))
+
+(defn repl-exception
+ "Returns CompilerExceptions in tact, but only the root cause of other
+ throwables"
+ [throwable]
+ (if (instance? Compiler$CompilerException throwable)
+ throwable
+ (root-cause throwable)))
+
+(defn repl-caught
+ "Default :caught hook for repl"
+ [e]
+ (.println *err* (repl-exception e)))
+
+(defn repl
+ "Generic, reusable, read-eval-print loop. By default, reads from *in*,
+ writes to *out*, and prints exception summaries to *err*. If you use the
+ default :read hook, *in* must either be an instance of
+ LineNumberingPushbackReader or duplicate its behavior of both supporting
+ .unread and collapsing CR, LF, and CRLF into a single \\newline. Options
+ are sequential keyword-value pairs. Available options and their defaults:
+
+ - :init, function of no arguments, initialization hook called with
+ bindings for set!-able vars in place.
+ default: #()
+
+ - :need-prompt, function of no arguments, called before each
+ read-eval-print except the first, the user will be prompted if it
+ returns true.
+ default: (if (instance? LineNumberingPushbackReader *in*)
+ #(.atLineStart *in*)
+ #(identity true))
+
+ - :prompt, function of no arguments, prompts for more input.
+ default: repl-prompt
+
+ - :flush, function of no arguments, flushes output
+ default: flush
+
+ - :read, function of two arguments, reads from *in*:
+ - returns its first argument to request a fresh prompt
+ - depending on need-prompt, this may cause the repl to prompt
+ before reading again
+ - returns its second argument to request an exit from the repl
+ - else returns the next object read from the input stream
+ default: repl-read
+
+ - :eval, funtion of one argument, returns the evaluation of its
+ argument
+ default: eval
+
+ - :print, function of one argument, prints its argument to the output
+ default: prn
+
+ - :caught, function of one argument, a throwable, called when
+ read, eval, or print throws an exception or error
+ default: repl-caught"
+ [& options]
+ (let [{:keys [init need-prompt prompt flush read eval print caught]
+ :or {init #()
+ need-prompt (if (instance? LineNumberingPushbackReader *in*)
+ #(.atLineStart *in*)
+ #(identity true))
+ prompt repl-prompt
+ flush flush
+ read repl-read
+ eval eval
+ print prn
+ caught repl-caught}}
+ (apply hash-map options)
+ request-prompt (Object.)
+ request-exit (Object.)
+ read-eval-print
+ (fn []
+ (try
+ (let [input (read request-prompt request-exit)]
+ (or (#{request-prompt request-exit} input)
+ (let [value (eval input)]
+ (print value)
+ (set! *3 *2)
+ (set! *2 *1)
+ (set! *1 value))))
+ (catch Throwable e
+ (caught e)
+ (set! *e e))))]
+ (with-bindings
+ (try
+ (init)
+ (catch Throwable e
+ (caught e)
+ (set! *e e)))
+ (prompt)
+ (flush)
+ (loop []
+ (when-not (= (read-eval-print) request-exit)
+ (when (need-prompt)
+ (prompt)
+ (flush))
+ (recur))))))
+
+(defn load-script
+ "Loads Clojure source from a file or resource given its path. Paths
+ beginning with @ or @/ are considered relative to classpath."
+ [path]
+ (if (.startsWith path "@")
+ (RT/loadResourceScript
+ (.substring path (if (.startsWith path "@/") 2 1)))
+ (Compiler/loadFile path)))
+
+(defn- init-opt
+ "Load a script"
+ [path]
+ (load-script path))
+
+(defn- eval-opt
+ "Evals expressions in str, prints each non-nil result using prn"
+ [str]
+ (let [eof (Object.)]
+ (with-in-str str
+ (loop [input (read *in* false eof)]
+ (when-not (= input eof)
+ (let [value (eval input)]
+ (when-not (nil? value)
+ (prn value))
+ (recur (read *in* false eof))))))))
+
+(defn- init-dispatch
+ "Returns the handler associated with an init opt"
+ [opt]
+ ({"-i" init-opt
+ "--init" init-opt
+ "-e" eval-opt
+ "--eval" eval-opt} opt))
+
+(defn- initialize
+ "Common initialize routine for repl, script, and null opts"
+ [args inits]
+ (in-ns 'user)
+ (set! *command-line-args* args)
+ (doseq [[opt arg] inits]
+ ((init-dispatch opt) arg)))
+
+(defn- repl-opt
+ "Start a repl with args and inits. Print greeting if no eval options were
+ present"
+ [[_ & args] inits]
+ (when-not (some #(= eval-opt (init-dispatch (first %))) inits)
+ (println "Clojure" (clojure-version)))
+ (repl :init #(initialize args inits))
+ (prn)
+ (System/exit 0))
+
+(defn- script-opt
+ "Run a script from a file, resource, or standard in with args and inits"
+ [[path & args] inits]
+ (with-bindings
+ (initialize args inits)
+ (if (= path "-")
+ (load-reader *in*)
+ (load-script path))))
+
+(defn- null-opt
+ "No repl or script opt present, just bind args and run inits"
+ [args inits]
+ (with-bindings
+ (initialize args inits)))
+
+(defn- help-opt
+ "Print help text for main"
+ [_ _]
+ (println (:doc (meta (var main)))))
+
+(defn- main-dispatch
+ "Returns the handler associated with a main option"
+ [opt]
+ (or
+ ({"-r" repl-opt
+ "--repl" repl-opt
+ nil null-opt
+ "-h" help-opt
+ "--help" help-opt
+ "-?" help-opt} opt)
+ script-opt))
+
+(defn- legacy-repl
+ "Called by the clojure.lang.Repl.main stub to run a repl with args
+ specified the old way"
+ [args]
+ (let [[inits [sep & args]] (split-with (complement #{"--"}) args)]
+ (repl-opt (concat ["-r"] args) (map vector (repeat "-i") inits))))
+
+(defn- legacy-script
+ "Called by the clojure.lang.Script.main stub to run a script with args
+ specified the old way"
+ [args]
+ (let [[inits [sep & args]] (split-with (complement #{"--"}) args)]
+ (null-opt args (map vector (repeat "-i") inits))))
+
+(defn main
+ "Usage: java -cp clojure.jar clojure.main [init-opt*] [main-opt] [arg*]
+
+ With no options or args, runs an interactive Read-Eval-Print Loop
+
+ init options:
+ -i, --init path Load a file or resource
+ -e, --eval string Evaluate expressions in string; print non-nil values
+
+ main options:
+ -r, --repl Run a repl
+ path Run a script from from a file or resource
+ - Run a script from standard input
+ -h, -?, --help Print this help message and exit
+
+ operation:
+
+ - Establishes thread-local bindings for commonly set!-able vars
+ - Enters the user namespace
+ - Binds *command-line-args* to a seq of strings containing command line
+ args that appear after any main option
+ - Runs all init options in order
+ - Runs a repl or script if requested
+
+ The init options may be repeated and mixed freely, but must appear before
+ any main option. The appearance of any eval option before running a repl
+ suppresses the usual repl greeting message: \"Clojure ~(clojure-version)\".
+
+ Paths may be absolute or relative in the filesystem or relative to
+ classpath. Classpath-relative paths have prefix of @ or @/"
+ [& args]
+ (try
+ (if args
+ (loop [[opt arg & more :as args] args inits []]
+ (if (init-dispatch opt)
+ (recur more (conj inits [opt arg]))
+ ((main-dispatch opt) args inits)))
+ (repl-opt nil nil))
+ (finally
+ (flush))))
+
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html 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.
+
+(ns clojure.parallel)
+(alias 'parallel 'clojure.parallel)
+
+(comment "
+The parallel library wraps the ForkJoin library scheduled for inclusion in JDK 7:
+
+http://gee.cs.oswego.edu/dl/concurrency-interest/index.html
+
+You'll need jsr166y.jar in your classpath in order to use this
+library. The basic idea is that Clojure collections, and most
+efficiently vectors, can be turned into parallel arrays for use by
+this library with the function par, although most of the functions
+take collections and will call par if needed, so normally you will
+only need to call par explicitly in order to attach bound/filter/map
+ops. Parallel arrays support the attachment of bounds, filters and
+mapping functions prior to realization/calculation, which happens as
+the result of any of several operations on the
+array (pvec/psort/pfilter-nils/pfilter-dupes). Rather than perform
+composite operations in steps, as would normally be done with
+sequences, maps and filters are instead attached and thus composed by
+providing ops to par. Note that there is an order sensitivity to the
+attachments - bounds precede filters precede mappings. All operations
+then happen in parallel, using multiple threads and a sophisticated
+work-stealing system supported by fork-join, either when the array is
+realized, or to perform aggregate operations like preduce/pmin/pmax
+etc. A parallel array can be realized into a Clojure vector using
+pvec.
+")
+
+(import '(jsr166y.forkjoin ParallelArray ParallelArrayWithBounds ParallelArrayWithFilter
+ ParallelArrayWithMapping
+ Ops$Op Ops$BinaryOp Ops$Reducer Ops$Predicate Ops$BinaryPredicate
+ Ops$IntAndObjectPredicate Ops$IntAndObjectToObject))
+
+(defn- op [f]
+ (proxy [Ops$Op] []
+ (op [x] (f x))))
+
+(defn- binary-op [f]
+ (proxy [Ops$BinaryOp] []
+ (op [x y] (f x y))))
+
+(defn- int-and-object-to-object [f]
+ (proxy [Ops$IntAndObjectToObject] []
+ (op [i x] (f x i))))
+
+(defn- reducer [f]
+ (proxy [Ops$Reducer] []
+ (op [x y] (f x y))))
+
+(defn- predicate [f]
+ (proxy [Ops$Predicate] []
+ (op [x] (boolean (f x)))))
+
+(defn- binary-predicate [f]
+ (proxy [Ops$BinaryPredicate] []
+ (op [x y] (boolean (f x y)))))
+
+(defn- int-and-object-predicate [f]
+ (proxy [Ops$IntAndObjectPredicate] []
+ (op [i x] (boolean (f x i)))))
+
+(defn par
+ "Creates a parallel array from coll. ops, if supplied, perform
+ on-the-fly filtering or transformations during parallel realization
+ or calculation. ops form a chain, and bounds must precede filters,
+ must precede maps. ops must be a set of keyword value pairs of the
+ following forms:
+
+ :bound [start end]
+
+ Only elements from start (inclusive) to end (exclusive) will be
+ processed when the array is realized.
+
+ :filter pred
+
+ Filter preds remove elements from processing when the array is realized. pred
+ must be a function of one argument whose return will be processed
+ via boolean.
+
+ :filter-index pred2
+
+ pred2 must be a function of two arguments, which will be an element
+ of the collection and the corresponding index, whose return will be
+ processed via boolean.
+
+ :filter-with [pred2 coll2]
+
+ pred2 must be a function of two arguments, which will be
+ corresponding elements of the 2 collections.
+
+ :map f
+
+ Map fns will be used to transform elements when the array is
+ realized. f must be a function of one argument.
+
+ :map-index f2
+
+ f2 must be a function of two arguments, which will be an element of
+ the collection and the corresponding index.
+
+ :map-with [f2 coll2]
+
+ f2 must be a function of two arguments, which will be corresponding
+ elements of the 2 collections."
+
+ ([coll]
+ (if (instance? ParallelArrayWithMapping coll)
+ coll
+ (. ParallelArray createUsingHandoff
+ (to-array coll)
+ (. ParallelArray defaultExecutor))))
+ ([coll & ops]
+ (reduce (fn [pa [op args]]
+ (cond
+ (= op :bound) (. pa withBounds (args 0) (args 1))
+ (= op :filter) (. pa withFilter (predicate args))
+ (= op :filter-with) (. pa withFilter (binary-predicate (args 0)) (par (args 1)))
+ (= op :filter-index) (. pa withIndexedFilter (int-and-object-predicate args))
+ (= op :map) (. pa withMapping (parallel/op args))
+ (= op :map-with) (. pa withMapping (binary-op (args 0)) (par (args 1)))
+ (= op :map-index) (. pa withIndexedMapping (int-and-object-to-object args))
+ :else (throw (Exception. (str "Unsupported par op: " op)))))
+ (par coll)
+ (partition 2 ops))))
+
+;;;;;;;;;;;;;;;;;;;;; aggregate operations ;;;;;;;;;;;;;;;;;;;;;;
+(defn pany
+ "Returns some (random) element of the coll if it satisfies the bound/filter/map"
+ [coll]
+ (. (par coll) any))
+
+(defn pmax
+ "Returns the maximum element, presuming Comparable elements, unless
+ a Comparator comp is supplied"
+ ([coll] (. (par coll) max))
+ ([coll comp] (. (par coll) max comp)))
+
+(defn pmin
+ "Returns the minimum element, presuming Comparable elements, unless
+ a Comparator comp is supplied"
+ ([coll] (. (par coll) min))
+ ([coll comp] (. (par coll) min comp)))
+
+(defn- summary-map [s]
+ {:min (.min s) :max (.max s) :size (.size s) :min-index (.indexOfMin s) :max-index (.indexOfMax s)})
+
+(defn psummary
+ "Returns a map of summary statistics (min. max, size, min-index, max-index,
+ presuming Comparable elements, unless a Comparator comp is supplied"
+ ([coll] (summary-map (. (par coll) summary)))
+ ([coll comp] (summary-map (. (par coll) summary comp))))
+
+(defn preduce
+ "Returns the reduction of the realized elements of coll
+ using function f. Note f will not necessarily be called
+ consecutively, and so must be commutative. Also note that
+ (f base an-element) might be performed many times, i.e. base is not
+ an initial value as with sequential reduce."
+ [f base coll]
+ (. (par coll) (reduce (reducer f) base)))
+
+;;;;;;;;;;;;;;;;;;;;; collection-producing operations ;;;;;;;;;;;;;;;;;;;;;;
+
+(defn- pa-to-vec [pa]
+ (vec (. pa getArray)))
+
+(defn- pall
+ "Realizes a copy of the coll as a parallel array, with any bounds/filters/maps applied"
+ [coll]
+ (if (instance? ParallelArrayWithMapping coll)
+ (. coll all)
+ (par coll)))
+
+(defn pvec
+ "Returns the realized contents of the parallel array pa as a Clojure vector"
+ [pa] (pa-to-vec (pall pa)))
+
+(defn pdistinct
+ "Returns a parallel array of the distinct elements of coll"
+ [coll]
+ (pa-to-vec (. (pall coll) allUniqueElements)))
+
+;this doesn't work, passes null to reducer?
+(defn- pcumulate [coll f init]
+ (.. (pall coll) (precumulate (reducer f) init)))
+
+(defn psort
+ "Returns a new vector consisting of the realized items in coll, sorted,
+ presuming Comparable elements, unless a Comparator comp is supplied"
+ ([coll] (pa-to-vec (. (pall coll) sort)))
+ ([coll comp] (pa-to-vec (. (pall coll) sort comp))))
+
+(defn pfilter-nils
+ "Returns a vector containing the non-nil (realized) elements of coll"
+ [coll]
+ (pa-to-vec (. (pall coll) removeNulls)))
+
+(defn pfilter-dupes
+ "Returns a vector containing the (realized) elements of coll,
+ without any consecutive duplicates"
+ [coll]
+ (pa-to-vec (. (pall coll) removeConsecutiveDuplicates)))
+
+
+(comment
+(load-file "src/parallel.clj")
+(refer 'parallel)
+(pdistinct [1 2 3 2 1])
+;(pcumulate [1 2 3 2 1] + 0) ;broken, not exposed
+(def a (make-array Object 1000000))
+(dotimes i (count a)
+ (aset a i (rand-int i)))
+(time (reduce + 0 a))
+(time (preduce + 0 a))
+(time (count (distinct a)))
+(time (count (pdistinct a)))
+
+(preduce + 0 [1 2 3 2 1])
+(preduce + 0 (psort a))
+(pvec (par [11 2 3 2] :filter-index (fn [x i] (> i x))))
+(pvec (par [11 2 3 2] :filter-with [(fn [x y] (> y x)) [110 2 33 2]]))
+
+(psummary ;or pvec/pmax etc
+ (par [11 2 3 2]
+ :filter-with [(fn [x y] (> y x))
+ [110 2 33 2]]
+ :map #(* % 2)))
+
+(preduce + 0
+ (par [11 2 3 2]
+ :filter-with [< [110 2 33 2]]))
+
+(time (reduce + 0 (map #(* % %) (range 1000000))))
+(time (preduce + 0 (par (range 1000000) :map-index *)))
+(def v (range 1000000))
+(time (preduce + 0 (par v :map-index *)))
+(time (preduce + 0 (par v :map #(* % %))))
+(time (reduce + 0 (map #(* % %) v)))
+); Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html 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.
+
+(ns clojure.set)
+
+(defn- bubble-max-key [k coll]
+ "Move a maximal element of coll according to fn k (which returns a number)
+ to the front of coll."
+ (let [max (apply max-key k coll)]
+ (cons max (remove #(identical? max %) coll))))
+
+(defn union
+ "Return a set that is the union of the input sets"
+ ([] #{})
+ ([s1] s1)
+ ([s1 s2]
+ (if (< (count s1) (count s2))
+ (reduce conj s2 s1)
+ (reduce conj s1 s2)))
+ ([s1 s2 & sets]
+ (let [bubbled-sets (bubble-max-key count (conj sets s2 s1))]
+ (reduce into (first bubbled-sets) (rest bubbled-sets)))))
+
+(defn intersection
+ "Return a set that is the intersection of the input sets"
+ ([s1] s1)
+ ([s1 s2]
+ (if (< (count s2) (count s1))
+ (recur s2 s1)
+ (reduce (fn [result item]
+ (if (contains? s2 item)
+ result
+ (disj result item)))
+ s1 s1)))
+ ([s1 s2 & sets]
+ (let [bubbled-sets (bubble-max-key #(- (count %)) (conj sets s2 s1))]
+ (reduce intersection (first bubbled-sets) (rest bubbled-sets)))))
+
+(defn difference
+ "Return a set that is the first set without elements of the remaining sets"
+ ([s1] s1)
+ ([s1 s2]
+ (if (< (count s1) (count s2))
+ (reduce (fn [result item]
+ (if (contains? s2 item)
+ (disj result item)
+ result))
+ s1 s1)
+ (reduce disj s1 s2)))
+ ([s1 s2 & sets]
+ (reduce difference s1 (conj sets s2))))
+
+
+(defn select
+ "Returns a set of the elements for which pred is true"
+ [pred xset]
+ (reduce (fn [s k] (if (pred k) s (disj s k)))
+ xset xset))
+
+(defn project
+ "Returns a rel of the elements of xrel with only the keys in ks"
+ [xrel ks]
+ (set (map #(select-keys % ks) xrel)))
+
+(defn rename-keys
+ "Returns the map with the keys in kmap renamed to the vals in kmap"
+ [map kmap]
+ (reduce
+ (fn [m [old new]]
+ (if (not= old new)
+ (-> m (assoc new (m old)) (dissoc old))
+ m))
+ map kmap))
+
+(defn rename
+ "Returns a rel of the maps in xrel with the keys in kmap renamed to the vals in kmap"
+ [xrel kmap]
+ (set (map #(rename-keys % kmap) xrel)))
+
+(defn index
+ "Returns a map of the distinct values of ks in the xrel mapped to a
+ set of the maps in xrel with the corresponding values of ks."
+ [xrel ks]
+ (reduce
+ (fn [m x]
+ (let [ik (select-keys x ks)]
+ (assoc m ik (conj (get m ik #{}) x))))
+ {} xrel))
+
+(defn map-invert
+ "Returns the map with the vals mapped to the keys."
+ [m] (reduce (fn [m [k v]] (assoc m v k)) {} m))
+
+(defn join
+ "When passed 2 rels, returns the rel corresponding to the natural
+ join. When passed an additional keymap, joins on the corresponding
+ keys."
+ ([xrel yrel] ;natural join
+ (if (and (seq xrel) (seq yrel))
+ (let [ks (intersection (set (keys (first xrel))) (set (keys (first yrel))))
+ [r s] (if (<= (count xrel) (count yrel))
+ [xrel yrel]
+ [yrel xrel])
+ idx (index r ks)]
+ (reduce (fn [ret x]
+ (let [found (idx (select-keys x ks))]
+ (if found
+ (reduce #(conj %1 (merge %2 x)) ret found)
+ ret)))
+ #{} s))
+ #{}))
+ ([xrel yrel km] ;arbitrary key mapping
+ (let [[r s k] (if (<= (count xrel) (count yrel))
+ [xrel yrel (map-invert km)]
+ [yrel xrel km])
+ idx (index r (vals k))]
+ (reduce (fn [ret x]
+ (let [found (idx (rename-keys (select-keys x (keys k)) k))]
+ (if found
+ (reduce #(conj %1 (merge %2 x)) ret found)
+ ret)))
+ #{} s))))
+
+(comment
+(refer 'set)
+(def xs #{{:a 11 :b 1 :c 1 :d 4}
+ {:a 2 :b 12 :c 2 :d 6}
+ {:a 3 :b 3 :c 3 :d 8 :f 42}})
+
+(def ys #{{:a 11 :b 11 :c 11 :e 5}
+ {:a 12 :b 11 :c 12 :e 3}
+ {:a 3 :b 3 :c 3 :e 7 }})
+
+(join xs ys)
+(join xs (rename ys {:b :yb :c :yc}) {:a :a})
+
+(union #{:a :b :c} #{:c :d :e })
+(difference #{:a :b :c} #{:c :d :e})
+(intersection #{:a :b :c} #{:c :d :e})
+
+(index ys [:b])
+)
+
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html 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.
+
+(ns clojure.xml
+ (:import (org.xml.sax ContentHandler Attributes SAXException)
+ (javax.xml.parsers SAXParser SAXParserFactory)))
+
+(def *stack*)
+(def *current*)
+(def *state*) ; :element :chars :between
+(def *sb*)
+
+(defstruct element :tag :attrs :content)
+
+(def tag (accessor element :tag))
+(def attrs (accessor element :attrs))
+(def content (accessor element :content))
+
+(def content-handler
+ (let [push-content (fn [e c]
+ (assoc e :content (conj (or (:content e) []) c)))
+ push-chars (fn []
+ (when (and (= *state* :chars)
+ (some (complement #(. Character (isWhitespace %))) (str *sb*)))
+ (set! *current* (push-content *current* (str *sb*)))))]
+ (new clojure.lang.XMLHandler
+ (proxy [ContentHandler] []
+ (startElement [uri local-name q-name #^Attributes atts]
+ (let [attrs (fn [ret i]
+ (if (neg? i)
+ ret
+ (recur (assoc ret
+ (. clojure.lang.Keyword (intern (symbol (. atts (getQName i)))))
+ (. atts (getValue i)))
+ (dec i))))
+ e (struct element
+ (. clojure.lang.Keyword (intern (symbol q-name)))
+ (when (pos? (. atts (getLength)))
+ (attrs {} (dec (. atts (getLength))))))]
+ (push-chars)
+ (set! *stack* (conj *stack* *current*))
+ (set! *current* e)
+ (set! *state* :element))
+ nil)
+ (endElement [uri local-name q-name]
+ (push-chars)
+ (set! *current* (push-content (peek *stack*) *current*))
+ (set! *stack* (pop *stack*))
+ (set! *state* :between)
+ nil)
+ (characters [ch start length]
+ (when-not (= *state* :chars)
+ (set! *sb* (new StringBuilder)))
+ (let [#^StringBuilder sb *sb*]
+ (. sb (append ch start length))
+ (set! *state* :chars))
+ nil)
+ (setDocumentLocator [locator])
+ (startDocument [])
+ (endDocument [])
+ (startPrefixMapping [prefix uri])
+ (endPrefixMapping [prefix])
+ (ignorableWhitespace [ch start length])
+ (processingInstruction [target data])
+ (skippedEntity [name])
+ ))))
+
+(defn startparse-sax [s ch]
+ (.. SAXParserFactory (newInstance) (newSAXParser) (parse s ch)))
+
+(defn parse
+ "Parses and loads the source s, which can be a File, InputStream or
+ String naming a URI. Returns a tree of the xml/element struct-map,
+ which has the keys :tag, :attrs, and :content. and accessor fns tag,
+ attrs, and content. Other parsers can be supplied by passing
+ startparse, a fn taking a source and a ContentHandler and returning
+ a parser"
+ ([s] (parse s startparse-sax))
+ ([s startparse]
+ (binding [*stack* nil
+ *current* (struct element)
+ *state* :between
+ *sb* nil]
+ (startparse s content-handler)
+ ((:content *current*) 0))))
+
+(defn emit-element [e]
+ (if (instance? String e)
+ (println e)
+ (do
+ (print (str "<" (name (:tag e))))
+ (when (:attrs e)
+ (doseq [attr (:attrs e)]
+ (print (str " " (name (key attr)) "='" (val attr)"'"))))
+ (if (:content e)
+ (do
+ (println ">")
+ (doseq [c (:content e)]
+ (emit-element c))
+ (println (str "</" (name (:tag e)) ">")))
+ (println "/>")))))
+
+(defn emit [x]
+ (println "<?xml version='1.0' encoding='UTF-8'?>")
+ (emit-element x))
+
+;(export '(tag attrs content parse element emit emit-element))
+
+;(load-file "/Users/rich/dev/clojure/src/xml.clj")
+;(def x (xml/parse "http://arstechnica.com/journals.rssx"))
+; Copyright (c) Rich Hickey. All rights reserved.
+; The use and distribution terms for this software are covered by the
+; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+; which can be found in the file epl-v10.html 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.
+
+;functional hierarchical zipper, with navigation, editing and enumeration
+;see Huet
+
+(ns clojure.zip
+ (:refer-clojure :exclude (replace remove next)))
+
+(defn zipper
+ "Creates a new zipper structure.
+
+ branch? is a fn that, given a node, returns true if can have
+ children, even if it currently doesn't.
+
+ children is a fn that, given a branch node, returns a seq of its
+ children.
+
+ make-node is a fn that, given an existing node and a seq of
+ children, returns a new branch node with the supplied children.
+ root is the root node."
+ [branch? children make-node root]
+ #^{:zip/branch? branch? :zip/children children :zip/make-node make-node}
+ [root nil])
+
+(defn seq-zip
+ "Returns a zipper for nested sequences, given a root sequence"
+ [root]
+ (zipper seq? identity (fn [node children] children) root))
+
+(defn vector-zip
+ "Returns a zipper for nested vectors, given a root vector"
+ [root]
+ (zipper vector? seq (fn [node children] (apply vector children)) root))
+
+(defn xml-zip
+ "Returns a zipper for xml elements (as from xml/parse),
+ given a root element"
+ [root]
+ (zipper (complement string?)
+ (comp seq :content)
+ (fn [node children]
+ (assoc node :content (and children (apply vector children))))
+ root))
+
+(defn node
+ "Returns the node at loc"
+ [loc] (loc 0))
+
+(defn branch?
+ "Returns true if the node at loc is a branch"
+ [loc]
+ ((:zip/branch? ^loc) (node loc)))
+
+(defn children
+ "Returns a seq of the children of node at loc, which must be a branch"
+ [loc]
+ ((:zip/children ^loc) (node loc)))
+
+(defn make-node
+ "Returns a new branch node, given an existing node and new
+ children. The loc is only used to supply the constructor."
+ [loc node children]
+ ((:zip/make-node ^loc) node children))
+
+(defn path
+ "Returns a seq of nodes leading to this loc"
+ [loc]
+ (:pnodes (loc 1)))
+
+(defn lefts
+ "Returns a seq of the left siblings of this loc"
+ [loc]
+ (seq (:l (loc 1))))
+
+(defn rights
+ "Returns a seq of the right siblings of this loc"
+ [loc]
+ (:r (loc 1)))
+
+
+(defn down
+ "Returns the loc of the leftmost child of the node at this loc, or
+ nil if no children"
+ [loc]
+ (let [[node path] loc
+ [c & cnext :as cs] (children loc)]
+ (when cs
+ (with-meta [c {:l []
+ :pnodes (if path (conj (:pnodes path) node) [node])
+ :ppath path
+ :r cnext}] ^loc))))
+
+(defn up
+ "Returns the loc of the parent of the node at this loc, or nil if at
+ the top"
+ [loc]
+ (let [[node {l :l, ppath :ppath, pnodes :pnodes r :r, changed? :changed?, :as path}] loc]
+ (when pnodes
+ (let [pnode (peek pnodes)]
+ (with-meta (if changed?
+ [(make-node loc pnode (concat l (cons node r)))
+ (and ppath (assoc ppath :changed? true))]
+ [pnode ppath])
+ ^loc)))))
+
+(defn root
+ "zips all the way up and returns the root node, reflecting any
+ changes."
+ [loc]
+ (if (= :end (loc 1))
+ (node loc)
+ (let [p (up loc)]
+ (if p
+ (recur p)
+ (node loc)))))
+
+(defn right
+ "Returns the loc of the right sibling of the node at this loc, or nil"
+ [loc]
+ (let [[node {l :l [r & rnext :as rs] :r :as path}] loc]
+ (when (and path rs)
+ (with-meta [r (assoc path :l (conj l node) :r rnext)] ^loc))))
+
+(defn rightmost
+ "Returns the loc of the rightmost sibling of the node at this loc, or self"
+ [loc]
+ (let [[node {l :l r :r :as path}] loc]
+ (if (and path r)
+ (with-meta [(last r) (assoc path :l (apply conj l node (butlast r)) :r nil)] ^loc)
+ loc)))
+
+(defn left
+ "Returns the loc of the left sibling of the node at this loc, or nil"
+ [loc]
+ (let [[node {l :l r :r :as path}] loc]
+ (when (and path (seq l))
+ (with-meta [(peek l) (assoc path :l (pop l) :r (cons node r))] ^loc))))
+
+(defn leftmost
+ "Returns the loc of the leftmost sibling of the node at this loc, or self"
+ [loc]
+ (let [[node {l :l r :r :as path}] loc]
+ (if (and path (seq l))
+ (with-meta [(first l) (assoc path :l [] :r (concat (rest l) [node] r))] ^loc)
+ loc)))
+
+(defn insert-left
+ "Inserts the item as the left sibling of the node at this loc,
+ without moving"
+ [loc item]
+ (let [[node {l :l :as path}] loc]
+ (if (nil? path)
+ (throw (new Exception "Insert at top"))
+ (with-meta [node (assoc path :l (conj l item) :changed? true)] ^loc))))
+
+(defn insert-right
+ "Inserts the item as the right sibling of the node at this loc,
+ without moving"
+ [loc item]
+ (let [[node {r :r :as path}] loc]
+ (if (nil? path)
+ (throw (new Exception "Insert at top"))
+ (with-meta [node (assoc path :r (cons item r) :changed? true)] ^loc))))
+
+(defn replace
+ "Replaces the node at this loc, without moving"
+ [loc node]
+ (let [[_ path] loc]
+ (with-meta [node (assoc path :changed? true)] ^loc)))
+
+(defn edit
+ "Replaces the node at this loc with the value of (f node args)"
+ [loc f & args]
+ (replace loc (apply f (node loc) args)))
+
+(defn insert-child
+ "Inserts the item as the leftmost child of the node at this loc,
+ without moving"
+ [loc item]
+ (replace loc (make-node loc (node loc) (cons item (children loc)))))
+
+(defn append-child
+ "Inserts the item as the rightmost child of the node at this loc,
+ without moving"
+ [loc item]
+ (replace loc (make-node loc (node loc) (concat (children loc) [item]))))
+
+(defn next
+ "Moves to the next loc in the hierarchy, depth-first. When reaching
+ the end, returns a distinguished loc detectable via end?. If already
+ at the end, stays there."
+ [loc]
+ (if (= :end (loc 1))
+ loc
+ (or
+ (and (branch? loc) (down loc))
+ (right loc)
+ (loop [p loc]
+ (if (up p)
+ (or (right (up p)) (recur (up p)))
+ [(node p) :end])))))
+
+(defn prev
+ "Moves to the previous loc in the hierarchy, depth-first. If already
+ at the root, returns nil."
+ [loc]
+ (if-let [lloc (left loc)]
+ (loop [loc lloc]
+ (if-let [child (and (branch? loc) (down loc))]
+ (recur (rightmost child))
+ loc))
+ (up loc)))
+
+(defn end?
+ "Returns true if loc represents the end of a depth-first walk"
+ [loc]
+ (= :end (loc 1)))
+
+(defn remove
+ "Removes the node at loc, returning the loc that would have preceded
+ it in a depth-first walk."
+ [loc]
+ (let [[node {l :l, ppath :ppath, pnodes :pnodes, rs :r, :as path}] loc]
+ (if (nil? path)
+ (throw (new Exception "Remove at top"))
+ (if (pos? (count l))
+ (loop [loc (with-meta [(peek l) (assoc path :l (pop l) :changed? true)] ^loc)]
+ (if-let [child (and (branch? loc) (down loc))]
+ (recur (rightmost child))
+ loc))
+ (with-meta [(make-node loc (peek pnodes) rs)
+ (and ppath (assoc ppath :changed? true))]
+ ^loc)))))
+
+(comment
+
+(load-file "/Users/rich/dev/clojure/src/zip.clj")
+(refer 'zip)
+(def data '[[a * b] + [c * d]])
+(def dz (vector-zip data))
+
+(right (down (right (right (down dz)))))
+(lefts (right (down (right (right (down dz))))))
+(rights (right (down (right (right (down dz))))))
+(up (up (right (down (right (right (down dz)))))))
+(path (right (down (right (right (down dz))))))
+
+(-> dz down right right down right)
+(-> dz down right right down right (replace '/) root)
+(-> dz next next (edit str) next next next (replace '/) root)
+(-> dz next next next next next next next next next remove root)
+(-> dz next next next next next next next next next remove (insert-right 'e) root)
+(-> dz next next next next next next next next next remove up (append-child 'e) root)
+
+(end? (-> dz next next next next next next next next next remove next))
+
+(-> dz next remove next remove root)
+
+(loop [loc dz]
+ (if (end? loc)
+ (root loc)
+ (recur (next (if (= '* (node loc))
+ (replace loc '/)
+ loc)))))
+
+(loop [loc dz]
+ (if (end? loc)
+ (root loc)
+ (recur (next (if (= '* (node loc))
+ (remove loc)
+ loc)))))
+)