diff options
author | Rich Hickey <richhickey@gmail.com> | 2008-07-21 18:59:53 +0000 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2008-07-21 18:59:53 +0000 |
commit | 5f52000facfdbc65ee4e52834dfe9626040e54fe (patch) | |
tree | 39a47b48dbeca1558f872fed0ebf28b6725c7176 | |
parent | 6dad2754a87b142ba536c2b7c8ab546a60a70892 (diff) |
new clj code hierarchy, old still in place
-rw-r--r-- | src/clojure/boot.clj | 2670 | ||||
-rw-r--r-- | src/clojure/genclass.clj | 517 | ||||
-rw-r--r-- | src/clojure/inspector/inspector.clj | 110 | ||||
-rw-r--r-- | src/clojure/parallel/parallel.clj | 248 | ||||
-rw-r--r-- | src/clojure/proxy.clj | 307 | ||||
-rw-r--r-- | src/clojure/set/set.clj | 116 | ||||
-rw-r--r-- | src/clojure/xml/xml.clj | 117 | ||||
-rw-r--r-- | src/clojure/zip/zip.clj | 248 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Compiler.java | 11 | ||||
-rw-r--r-- | src/jvm/clojure/lang/RT.java | 11 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Reflector.java | 11 |
11 files changed, 4356 insertions, 10 deletions
diff --git a/src/clojure/boot.clj b/src/clojure/boot.clj new file mode 100644 index 00000000..89cda99d --- /dev/null +++ b/src/clojure/boot.clj @@ -0,0 +1,2670 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) +; which can be found in the file CPL.TXT at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(in-ns 'clojure) + +(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]) + :doc "Returns a seq of the items after the first. Calls seq on its + argument. If there are no more items, returns nil."} + rest (fn rest [x] (. clojure.lang.RT (rest 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) (rest xs)) + (conj coll x))))) + +(def + #^{:doc "Same as (first (rest x))" + :arglists '([x])} + second (fn second [x] (first (rest x)))) + +(def + #^{:doc "Same as (first (first x))" + :arglists '([x])} + ffirst (fn ffirst [x] (first (first x)))) + +(def + #^{:doc "Same as (rest (first x))" + :arglists '([x])} + rfirst (fn rfirst [x] (rest (first x)))) + +(def + #^{:doc "Same as (first (rest x))" + :arglists '([x])} + frest (fn frest [x] (first (rest x)))) + +(def + #^{:doc "Same as (rest (rest x))" + :arglists '([x])} + rrest (fn rrest [x] (rest (rest x)))) + +(def + #^{:arglists '([coll]) + :doc "Sequence. Returns a new ISeq 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))) (rest 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) (rrest 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.IObj x) + (. #^clojure.lang.IObj 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 (rest s) + (recur (rest s)) + (first s)))) + +(def + #^{:arglists '([coll]) + :doc "Return a sequence of all but the last item in coll, in linear time"} + butlast (fn butlast [s] + (loop [ret [] s s] + (if (rest s) + (recur (conj ret (first s)) (rest 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)) + (rest fdecl) + fdecl) + m (if (map? (first fdecl)) + (conj m (first fdecl)) + m) + fdecl (if (map? (first fdecl)) + (rest 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()." + [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))))) + +(. (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] + (loop [sb (new StringBuilder #^String (str x)) more ys] + (if more + (recur (. sb (append (str (first more)))) (rest more)) + (str sb))))) + +(defn symbol + "Returns a Symbol with the given namespace and 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] (. 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) + (second clauses) + (cons 'cond (rest (rest clauses)))))) + +(defn spread + {:private true} + [arglist] + (cond + (nil? arglist) nil + (nil? (rest arglist)) (seq (first arglist)) + :else (cons (first arglist) (spread (rest 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 list* + "Creates a new list containing the item prepended to more." + [item & more] + (spread (cons item more))) + +(defmacro delay + "Takes a body of expressions and yields a function than will invoke + the body only the first time it is called, and will cache the result + and return it on all calls" + [& body] + (list 'new 'clojure.lang.Delay (list* `fn [] body))) + +(defn fnseq + "Returns a seq object whose first is first and whose rest is the + value produced by calling restfn with no arguments. restfn will be + called at most once per step in the sequence, e.g. calling rest + repeatedly on the head of the seq calls restfn once - the value it + yields is cached." + [first restfn] + (new clojure.lang.FnSeq first restfn)) + +(defmacro lazy-cons + "Expands to code which produces a seq object whose first is + first-expr and whose rest is rest-expr, neither of which is + evaluated until first/rest is called. Each expr will be evaluated at most + once per step in the sequence, e.g. calling first/rest repeatedly on the + same node of the seq evaluates first/rest-expr once - the values they yield are + cached." + [first-expr & rest-expr] + (list 'new 'clojure.lang.LazySeq (list `fn [] first-expr) (list* `fn [] rest-expr))) + +(defn concat + "Returns a lazy seq representing the concatenation of the elements in x + xs." + ([] nil) + ([x & xs] + (cond + (nil? xs) (seq x) + (nil? (seq x)) (recur (first xs) (rest xs)) + :else (lazy-cons (first x) (apply concat (rest x) xs))))) + +;;;;;;;;;;;;;;;;at this point all the support for syntax-quote exists;;;;;;;;;;;;;;;;;;;;;; +(defn = + "Equality. Returns true if obj1 equals obj2, false if not. Same as + Java obj1.equals(obj2) except it also works for nil, and compares + numbers 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 equal ~x ~y))} + [x y] (. clojure.lang.Util (equal x y))) + +(defn not= + "Same as (not (= obj1 obj2))" + {:tag Boolean} + [x y] (not (= x y))) + +(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 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 & rest] + `(let [and# ~x] + (if and# (and ~@rest) 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 & rest] + `(let [or# ~x] + (if or# or# (or ~@rest))))) + +;;;;;;;;;;;;;;;;;;; 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) (rest 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)) (rest s)) + val)) f val s))))) + +(defn reverse + "Returns a seq of the items in coll in reverse order. Not lazy." + [coll] + (reduce conj nil 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 (rest more) + (recur y (first more) (rest 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 (rest more) + (recur y (first more) (rest 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 (rest more) + (recur y (first more) (rest 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 (rest more) + (recur y (first more) (rest 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 (rest more) + (recur y (first more) (rest 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 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 + "rem[ainder] 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" + [x] (. clojure.lang.Numbers not x)) + + +(defn bit-and + "Bitwise and" + [x y] (. clojure.lang.Numbers and x y)) + +(defn bit-or + "Bitwise or" + [x y] (. clojure.lang.Numbers or x y)) + +(defn bit-xor + "Bitwise exclusive or" + [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 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 [& args] + (not (apply f args)))) + +(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 rest/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, else false." + [map key] (. clojure.lang.RT (contains map 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) (rest 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) (rest 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) + (rest 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 sequence of the items in rev (which + can be a vector or sorted-map), in reverse order." + [#^clojure.lang.Reversible rev] + (. rev (rseq))) + +(defn name + "Returns the name String of a symbol or keyword." + [#^clojure.lang.Named x] + (. x (getName))) + +(defn namespace + "Returns the namespace String of a symbol or keyword, or nil if not present." + [#^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 -> + "Macro. 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 ~@(rest form)) + (list form x))) + ([x form & more] `(-> (-> ~x ~form) ~@more))) + +;;multimethods +(defmacro defmulti + "Creates a new multimethod with the associated dispatch function. If + default-dispatch-val is supplied it becomes the default dispatch + value of the multimethod, otherwise the default dispatch value + is :default." + ([name dispatch-fn] `(defmulti ~name ~dispatch-fn :default)) + ([name dispatch-fn default-val] + `(def ~name (new clojure.lang.MultiFn ~dispatch-fn ~default-val)))) + +(defmacro defmethod + "Creates and installs a new method of multimethod associated with dispatch-value. " + [multifn dispatch-val & fn-tail] + `(let [pvar# (var ~multifn)] + (. pvar# (commuteRoot (fn [#^clojure.lang.MultiFn mf#] + (. mf# (assoc ~dispatch-val (fn ~@fn-tail)))))))) + +(defmacro remove-method + "Removes the method of multimethod associated with dispatch-value." + [multifn dispatch-val] + `(let [pvar# (var ~multifn)] + (. pvar# (commuteRoot (fn [#^clojure.lang.MultiFn mf#] + (. mf# (dissoc ~dispatch-val))))))) + +;;;;;;;;; var stuff + +(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] + (let [var-ize (fn [var-vals] + (loop [ret [] vvs (seq var-vals)] + (if vvs + (recur (conj (conj ret `(var ~(first vvs))) (second vvs)) + (rest (rest 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 agent + "Creates and returns an agent with an initial value of state." + [state] (new clojure.lang.Agent state)) + +(defn ! [& args] (throw (new Exception "! is now send. See also send-off"))) + +(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 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 ref + "Creates and returns a Ref with an initial value of x." + [x] (new clojure.lang.Ref x)) + +(defn deref + "Also reader macro: @ref/@agent Within a transaction, returns the + in-transaction-value of ref, else returns the + most-recently-committed value of ref. When applied to an agent, + returns its current state." + [#^clojure.lang.IRef ref] (. ref (get))) + + +(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 (get))) + +(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)))) + + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 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 (rest fs)] + (if fs + (recur ((first fs) ret) (rest 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 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 (rest 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." + [pred coll] + (when (seq coll) + (or (pred (first coll)) (recur pred (rest 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 seq 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] + (when (seq coll) + (lazy-cons (f (first coll)) (map f (rest coll))))) + ([f coll & colls] + (when (and (seq coll) (every? seq colls)) + (lazy-cons (apply f (first coll) (map first colls)) + (apply map f (rest coll) (map rest colls)))))) + +(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 seq of the items in coll for which + (pred item) returns true." + [pred coll] + (when (seq coll) + (if (pred (first coll)) + (lazy-cons (first coll) (filter pred (rest coll))) + (recur pred (rest coll))))) + +(defn take + "Returns a lazy seq of the first n items in coll, or all items if + there are fewer than n." + [n coll] + (when (and (pos? n) (seq coll)) + (lazy-cons (first coll) (take (dec n) (rest coll))))) + +(defn take-while + "Returns a lazy seq of successive items from coll while + (pred item) returns true." + [pred coll] + (when (and (seq coll) (pred (first coll))) + (lazy-cons (first coll) (take-while pred (rest coll))))) + +(defn drop + "Returns a lazy seq of all but the first n items in coll." + [n coll] + (if (and (pos? n) (seq coll)) + (recur (dec n) (rest coll)) + (seq coll))) + +(defn drop-last + "Return a lazy seq of all but the last n (default 1) items in coll" + ([s] (drop-last 1 s)) + ([n s] (map (fn [x _] x) (seq s) (drop n s)))) + +(defn drop-while + "Returns a lazy seq of the items in coll starting from the first + item for which (pred item) returns nil." + [pred coll] + (if (and (seq coll) (pred (first coll))) + (recur pred (rest coll)) + (seq coll))) + +(defn cycle + "Returns a lazy (infinite!) seq of repetitions of the items in + coll." + [coll] + (when (seq coll) + (let [rep (fn thisfn [xs] + (if xs + (lazy-cons (first xs) (thisfn (rest xs))) + (recur (seq coll))))] + (rep (seq coll))))) + +(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!) seq of xs." + [x] (lazy-cons x (repeat x))) + +(defn replicate + "Returns a lazy seq of n xs." + [n x] (take n (repeat x))) + +(defn iterate + "Returns a lazy seq of x, (f x), (f (f x)) etc." + [f x] (lazy-cons x (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) (< 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] (reduce conj 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] + (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 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)) + (rest ks) + (rest 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] + (let [line (. rdr (readLine))] + (when line + (lazy-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." + ([#^java.util.Collection coll] + (sort compare coll)) + ([#^java.util.Comparator comp #^java.util.Collection coll] + (when (and coll (not (. coll (isEmpty)))) + (let [a (. coll (toArray))] + (. 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))) + +;; evaluation + +(defn eval + "Evaluates the form data structure (not text!) and returns the result." + [form] (. clojure.lang.Compiler (eval form))) + +;(defn defimports [& imports-maps] +; (def *imports* (apply merge imports-maps))) + +(defmacro doseq + "Repeatedly executes body (presumably for side-effects) with + binding-form bound to successive items from coll. Does not retain + the head of the sequence. Returns nil." + [item list & body] + `(loop [list# (seq ~list)] + (when list# + (let [~item (first list#)] + ~@body) + (recur (rest list#))))) + +(defn scan [& args] (throw (new Exception "scan is now called dorun"))) +(defn touch [& args] (throw (new Exception "touch is now called doall"))) + +(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 rests of + the seq, does not retain the head and returns nil." + ([coll] + (when (seq coll) + (recur (rest coll)))) + ([n coll] + (when (and (seq coll) (pos? n)) + (recur (dec n) (rest 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 rests 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] + (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] + (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 + "Repeatedly executes body (presumably for side-effects) with name + bound to integers from 0 through n-1." + [i n & body] + `(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." + [& import-lists] + (when import-lists + (let [#^clojure.lang.Namespace ns *ns* + pkg (ffirst import-lists) + classes (rfirst import-lists)] + (doseq c classes + (. ns (importClass c (. Class (forName (str pkg "." c)))))) ) + (apply import (rest import-lists)))) + +(defn into-array + "Returns an array of the type of the first element in coll, + containing the contents of coll, which must be of a compatible + type." + [aseq] + (. clojure.lang.RT (seqToTypedArray (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)) (rest items)) + ret))) + +(defn #^{:private true} + array [& items] + (into-array items)) + +(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] + (. clojure.lang.RT (print x *out*)) + nil) + ([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 eof-error? eof-value recursive?)))) + +(defn read-line + "Reads the next line from stream that is the current value of *in* ." + [] (. *in* (readLine))) + +(defmacro with-open + "Evaluates body in a try expression with name bound to the value of + init, and a finally clause that calls (. name (close))." + [name init & body] + `(let [~name ~init] + (try + ~@body + (finally + (. ~name (close)))))) + +(defmacro doto + "Evaluates x then calls all of the methods with the supplied + arguments in succession on the resulting object, returning it. + + (doto (new java.util.HashMap) (put \"a\" 1) (put \"b\" 2))" + [x & members] + (let [gx (gensym)] + `(let [~gx ~x] + (do + ~@(map (fn [m] (list '. gx m)) + members)) + ~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#)) + +(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} + [#^Number x] (. x (shortValue))) + +(defn byte + "Coerce to byte" + {:tag Byte} + [#^Number x] (. x (byteValue))) + +(defn char + "Coerce to char" + {:tag Character} + [x] (. clojure.lang.RT (charCast x))) + +(defn boolean + "Coerce to boolean" + {:tag Boolean} + [x] (if x true false)) + +(defn bigint + "Coerce to BigInteger" + {:tag BigInteger} + [x] (. BigInteger valueOf x)) + +(defn bigdec + "Coerce to BigDecimal" + {:tag BigDecimal} + [x] (. BigDecimal valueOf x)) + +(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] + (. 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." + [#^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) (rest xs)))) + ret)) + +(import '(java.util.concurrent Executors LinkedBlockingQueue)) + +(defn pmap + ([f coll] + (let [nthreads (.. Runtime (getRuntime) (availableProcessors)) + exec (. Executors (newFixedThreadPool nthreads)) + todo (ref (seq coll)) + out (ref 0) + q (new LinkedBlockingQueue) + produce (fn [] + (let [job (sync nil + (when @todo + (let [item (first @todo)] + (alter todo rest) + (commute out inc) + (list item))))] + (when job + (. q (put (f (first job)))) + (recur)))) + tasks (doseq dnu (map (fn [task] + (. exec (submit #^java.util.concurrent.Callable task))) + (replicate nthreads produce))) + consume (fn thisfn [] + (if (sync nil (and (or @todo (pos? @out)) + (commute out dec))) + (fnseq (. q (take)) thisfn) + (do + (. exec (shutdown)) + (doseq x tasks) + nil)))] + (consume))) + ([f coll & colls] + (pmap (fn [items] (apply f items)) + (let [encl-fn (fn thisfn [collseq] + (when (every? seq collseq) + (lazy-cons (map first collseq) + (thisfn (map rest collseq)))))] + (encl-fn (cons coll colls)))))) + +(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 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)))) + +(defn load + "Sequentially read and evaluate the set of forms contained in the + stream/file" + [rdr] (. clojure.lang.Compiler (load rdr))) + +(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 (memfn toLowerCase)) + (map (fn [i] (. rsmeta (getColumnName i))) idxs)) + row-struct (apply create-struct keys) + row-values (fn [] (map (fn [#^Integer i] (. rs (getObject i))) idxs)) + rows (fn thisfn [] + (when (. rs (next)) + (lazy-cons (apply struct row-struct (row-values)) (thisfn))))] + (rows))) + +(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))) (rest es)) + (recur ret (rest 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 ns-name + "Returns the name of the namespace, a symbol." + [#^clojure.lang.Namespace ns] + (. ns (getName))) + +(defn ns-map + "Returns a map of all the mappings for the namespace." + [#^clojure.lang.Namespace ns] + (. ns (getMappings))) + +(defn ns-unmap + "Removes the mappings for the symbol from the namespace." + [#^clojure.lang.Namespace ns sym] + (. ns (unmap 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." + [#^clojure.lang.Namespace ns] + (filter-key val (fn [#^clojure.lang.Var v] (and (instance? clojure.lang.Var v) + (= ns (. v ns)) + (. v (isPublic)))) + (ns-map ns))) + +(defn ns-imports + "Returns a map of the import mappings for the namespace." + [#^clojure.lang.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." + [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." + [#^clojure.lang.Namespace ns] + (filter-key val (fn [#^clojure.lang.Var v] (and (instance? clojure.lang.Var v) + (not= ns (. v ns)))) + (ns-map ns))) + +(defn ns-interns + "Returns a map of the intern mappings for the namespace." + [#^clojure.lang.Namespace ns] + (filter-key val (fn [#^clojure.lang.Var v] (and (instance? clojure.lang.Var v) + (= ns (. v ns)))) + (ns-map ns))) + +(defn take-nth + "Returns a lazy seq of every nth item in coll." + [n coll] + (when (seq coll) + (lazy-cons (first coll) (take-nth n (drop n coll))))) + +(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] + `(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 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 nthrest + "Returns the nth rest of coll, (seq coll) when n is 0." + [coll n] + (loop [n n xs (seq coll)] + (if (and xs (pos? n)) + (recur (dec n) (rest xs)) + xs))) + +(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)) + +;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 bs + (let [firstb (first bs)] + (cond + (= firstb '&) (recur (pb ret (second bs) (list `nthrest gvec n)) + n + (rrest 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) + (rest 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 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))) + (rest 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] + `(let* ~(destructure bindings) ~@body)) + +;redefine fn with destructuring +(defmacro fn + "(fn name? [params* ] exprs*) + (fn name? ([params* ] exprs*)+) + + params => positional-params* , or positional-params* & rest-param + positional-param => binding-form + rest-param => binding-form + name => symbol + + Defines a function" + [& sigs] + (let [name (if (symbol? (first sigs)) (first sigs) nil) + sigs (if name (rest 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 (rest params) (conj new-params (first params)) lets) + (let [gparam (gensym "p__")] + (recur (rest 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] + (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 + "Same as (when (seq xs) (let [x (first xs)] body))" + [x xs & body] + `(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." + ([coll] `(seq ~coll)) + ([coll & colls] + `(let [iter# (fn iter# [coll#] + (if (seq coll#) + (lazy-cons (first coll#) (iter# (rest coll#))) + (lazy-cat ~@colls)))] + (iter# ~coll)))) + + + +(defmacro for + "List comprehension. Takes a vector of one or more + binding-form/collection-expr pairs, each followed by an optional filtering + :when/:while expression (:when test or :while test), 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. + + (take 100 (for [x (range 100000000) y (range 1000000) :while (< y x)] [x y]))" + ([seq-exprs expr] + (let [pargs (fn [xs] + (loop [ret [] + [b e & [w f & wr :as r] :as xs] (seq xs)] + (if xs + (cond + (= w :when) (recur (conj ret {:b b :e e :f f :w :when}) wr) + (= w :while) (recur (conj ret {:b b :e e :f f :w :while}) wr) + :else (recur (conj ret {:b b :e e :f true :w :while}) r)) + (seq ret)))) + emit (fn emit [[{b :b f :f w :w} & [{ys :e} :as rses]]] + (let [giter (gensym "iter__") gxs (gensym "s__")] + `(fn ~giter [~gxs] + (when-first ~b ~gxs + (if ~f + ~(if rses + `(let [iterys# ~(emit rses)] + (lazy-cat (iterys# ~ys) + (~giter (rest ~gxs)))) + `(lazy-cons ~expr (~giter (rest ~gxs)))) + ~(if (= w :when) + `(recur (rest ~gxs)) + nil))))))] + `(let [iter# ~(emit (pargs 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#)))) + +(defn pr-str + "pr to a string, returning it" + [& xs] + (with-out-str + (apply pr xs))) + +(defn prn-str + "prn to a string, returning it" + [& xs] + (with-out-str + (apply prn xs))) + +(defn print-str + "print to a string, returning it" + [& xs] + (with-out-str + (apply print xs))) + +(defn println-str + "println to a string, returning it" + [& 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] (. 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 [] + (when (. m (find)) + (lazy-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 + 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" + [re-string] + (let [re (re-pattern re-string)] + (dorun (for [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))))) + +(defmacro doc + "Prints documentation for the var named by varname" + [varname] + `(print-doc (var ~varname))) + +(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, must be a branch." + [branch? children root] + (let [walk (fn walk [nodes] + (when-first node nodes + (lazy-cons + node + (if (branch? node) + (lazy-cat (walk (children node)) + (walk (rest nodes))) + (walk (rest nodes))))))] + (lazy-cons root (walk (children 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 class + "Returns the Class of x" + [#^Object x] (. x (getClass))) + +(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 [[f & r :as xs] seen] + (when xs + (if (seen f) (recur r seen) + (lazy-cons f (step r (conj seen f))))))] + (step (seq coll) #{}))) + +(defmacro if-let + "if test is true, evaluates then with binding-form bound to the value of test, if not, yields else" + ([binding-form test then] + `(if-let ~binding-form ~test ~then nil)) + ([binding-form test then else] + `(let [temp# ~test] + (if temp# + (let [~binding-form temp#] + ~then) + ~else)))) + +(defmacro when-let + "when test is true, evaluates body with binding-form bound to the value of test" + [binding-form test & body] + `(let [temp# ~test] + (when temp# + (let [~binding-form temp#] + ~@body)))) + +(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) + [(rest (rest 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 (rest 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 (rest 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 (rest 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 (rest 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-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))) + +(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] + (when (seq coll) + (let [p (take n coll)] + (when (= n (count p)) + (lazy-cons p (partition n step (drop step 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" + [name & decl] + (let [[args expr] (drop-while (comp not vector?) decl) + inline (eval (list 'fn args expr))] + `(do + (defn ~name ~args ~(apply inline args)) + (let [v# (var ~name)] + (.setMeta v# (assoc ^v# :inline ~inline)))))) + +(defn empty + "Returns an empty collection of the same category as coll, or nil" + [#^clojure.lang.IPersistentCollection coll] + (.empty 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 [] + (let [x (.take q)] + (if (identical? x q) ;q itself is eos sentinel + @agt ;will be nil - touch agent just to propagate errors + (do + (send-off agt fill) + (lazy-cons (if (identical? x NIL) nil x) (drain))))))] + (send-off agt fill) + (drain))))
\ No newline at end of file diff --git a/src/clojure/genclass.clj b/src/clojure/genclass.clj new file mode 100644 index 00000000..cdc09845 --- /dev/null +++ b/src/clojure/genclass.clj @@ -0,0 +1,517 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) +; which can be found in the file CPL.TXT at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(in-ns 'clojure) + +(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 (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) + (. Modifier (isPrivate mods)) + (. Modifier (isStatic mods)) + (. Modifier (isFinal mods)) + (= "finalize" (.getName meth))) + (recur mm (conj considered mk) (rest meths)) + (recur (assoc mm mk meth) (conj considered mk) (rest meths)))) + [mm considered]))] + (recur mm considered (. c (getSuperclass)))) + mm))) + +(defn- ctor-sigs [super] + (for [#^Constructor ctor (. super (getDeclaredConstructors)) + :when (not (. Modifier (isPrivate (. ctor (getModifiers)))))] + (apply vector (. ctor (getParameterTypes))))) + +(defn- escape-class-name [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"))) + +;(distinct (map first(keys (mapcat non-private-methods [Object IPersistentMap])))) + +(defn gen-class + "Generates compiled bytecode for a class with the given + package-qualified cname (which, as all names in these parameters, can + be a string or symbol). The gen-class construct contains no + implementation, as the implementation will be dynamically sought by + the generated class in functions in a corresponding Clojure + namespace. Given a generated class org.mydomain.MyClass, methods + will be implemented that look for same-named functions in a Clojure + namespace called org.domain.MyClass. The init and main + functions (see below) will be found similarly. The static + initializer for the generated class will attempt to load the Clojure + support code for the class as a resource from the claspath, e.g. in + the example case, org/mydomain/MyClass.clj + + Returns a map containing :name and :bytecode. Most uses will be + satisfied by the higher-level gen-and-load-class and + gen-and-store-class functions, which generate and immediately load, + or generate and store to disk, respectively. + + Options should be a set of key/value pairs, all of which are optional: + + :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. + + :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 '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." + + [cname & options] + (let [name (str cname) + {:keys [extends implements constructors methods main factory state init exposes]} (apply hash-map options) + super (or extends Object) + interfaces implements + supers (cons super (seq interfaces)) + ctor-sig-map (or constructors (zipmap (ctor-sigs super) (ctor-sigs super))) + cv (new ClassWriter (. ClassWriter COMPUTE_MAXS)) + cname (. name (replace "." "/")) + ctype (. Type (getObjectType cname)) + iname (fn [c] (.. Type (getType c) (getInternalName))) + totype (fn [c] (. Type (getType c))) + to-types (fn [cs] (if (pos? (count cs)) + (into-array (map totype cs)) + (make-array Type 0))) + obj-type (totype Object) + arg-types (fn [n] (if (pos? n) + (into-array (replicate n obj-type)) + (make-array Type 0))) + super-type (totype super) + init-name (str init) + factory-name (str factory) + state-name (str state) + main-name "main" + var-name (fn [s] (str s "__var")) + rt-type (totype clojure.lang.RT) + var-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]] (rest s)) sigs-by-name)) + var-fields (concat (and init [init-name]) + (and main [main-name]) + (distinct (concat (keys sigs-by-name) + (mapcat (fn [[m s]] (map #(overload-name m %) s)) overloads) + (mapcat (comp (partial map str) vals val) exposes)))) + emit-get-var (fn [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-forwarding-method + (fn [mname pclasses rclass as-static else-gen] + (let [ptypes (to-types pclasses) + rtype (totype rclass) + m (new Method mname rtype ptypes) + is-overload (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)) + (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 + (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 interfaces + (into-array (map iname interfaces))))) + + ;static fields for vars + (doseq v var-fields + (. cv (visitField (+ (. Opcodes ACC_PUBLIC) (. 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 clj + (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 name) + (. gen push v) + (. gen (invokeStatic rt-type (. Method (getMethod "clojure.lang.Var var(String,String)")))) + (. gen putStatic ctype (var-name v) var-type)) + + (. gen push ctype) + (. gen push (str (. name replace \. (. java.io.File separatorChar)) ".clj")) + (. gen (invokeStatic rt-type (. Method (getMethod "void loadResourceScript(Class,String)")))) + + (. gen (returnValue)) + (. gen (endMethod))) + + ;ctors + (doseq [pclasses super-pclasses] ctor-sig-map + (let [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) + 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) + ;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 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")))) + + (. 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 [gen 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 + (doseq #^Class iface interfaces + (doseq #^java.lang.reflect.Method meth (. iface (getMethods)) + (when-not (contains? mm (method-sig meth)) + (emit-forwarding-method (.getName meth) (.getParameterTypes meth) (.getReturnType meth) false + (fn [gen m] + (. gen (throwException ex-type (. m (getName))))))))) + ;extra methods + (doseq [mname pclasses rclass :as msig] methods + (emit-forwarding-method (str mname) pclasses rclass (:static ^msig) + (fn [gen m] + (. gen (throwException ex-type (. m (getName)))))))) + + ;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) + (. 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 main-name " not defined"))) + (. gen mark end-label) + (. gen (returnValue)) + (. gen (endMethod)))) + ;field exposers + (doseq [f {getter :get setter :set}] exposes + (let [fld (.getField super (str f)) + ftype (totype (.getType fld))] + (when getter + (let [m (new Method (str getter) ftype (to-types [])) + gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)] + (. gen (visitCode)) + (. 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 (. Opcodes ACC_PUBLIC) m nil nil cv)] + (. gen (visitCode)) + (. gen loadThis) + (. gen loadArgs) + (. gen putField ctype (str f) ftype) + (. gen (returnValue)) + (. gen (endMethod)))))) + ;finish class def + (. cv (visitEnd)) + {:name name :bytecode (. cv (toByteArray))})) + +(defn gen-and-load-class + "Generates and immediately loads the bytecode for the specified + class. Note that a class generated this way can be loaded only once + - the JVM supports only one class with a given name per + classloader. Subsequent to generation you can import it into any + desired namespaces just like any other class. See gen-class for a + description of the options." + + [name & options] + (let [{:keys [name bytecode]} + (apply gen-class (str name) options)] + (.. clojure.lang.RT ROOT_CLASSLOADER (defineClass (str name) bytecode)))) + +(defn gen-and-save-class + "Generates the bytecode for the named class and stores in a .class + file in a subpath of the supplied path, the directories for which + must already exist. See gen-class for a description of the options" + + [path name & options] + (let [{:keys [name bytecode]} (apply gen-class (str name) options) + file (java.io.File. path (str (. name replace \. (. java.io.File separatorChar)) ".class"))] + (.createNewFile file) + (with-open f (java.io.FileOutputStream. file) + (.write f bytecode)))) + +(comment +;usage +(gen-class + package-qualified-name + ;all below are optional + :extends aclass + :implements [interface ...] + :constructors {[param-types] [super-param-types], } + :methods [[name [param-types] return-type], ] + :main boolean + :factory name + :state name + :init name + :exposes {protected-field {:get name :set name}, }) + +(gen-and-load-class +;(clojure/gen-and-save-class +; "/Users/rich/Downloads" + 'fred.lucy.Ethel + :extends clojure.lang.Box ;APersistentMap + :implements [clojure.lang.IPersistentMap] + :state 'state + ;:constructors {[Object] [Object]} + ;:init 'init + :main true + :factory 'create + :methods [#^{:static true} ['foo [Object] Object] + ['bar [] Object]] + :exposes {'val {:get 'getVal :set 'setVal}}) + +(in-ns 'fred.lucy.Ethel) +(clojure/refer 'clojure :exclude '(assoc seq count cons empty)) +(defn init [n] [[] n]) +(defn foo + ([x] x)) +(defn main [x y] (println x y)) +(in-ns 'user) +(def ethel (new fred.lucy.Ethel__2276 42)) +(def ethel (fred.lucy.Ethel__2276.create 21)) +(fred.lucy.Ethel__2276.main (into-array ["lucy" "ricky"])) +(.state ethel) +(.foo ethel 7) +(.foo ethel) +(.getVal ethel) +(.setVal ethel 12) + +(gen-class org.clojure.MyComparator :implements [Comparator]) +(in-ns 'org.clojure.MyComparator) +(defn compare [this x y] ...) + +(load-file "/Users/rich/dev/clojure/src/genclass.clj") + +(clojure/gen-and-save-class "/Users/rich/dev/clojure/gen/" + 'org.clojure.ClojureServlet + :extends javax.servlet.http.HttpServlet) + +) diff --git a/src/clojure/inspector/inspector.clj b/src/clojure/inspector/inspector.clj new file mode 100644 index 00000000..ad5a374e --- /dev/null +++ b/src/clojure/inspector/inspector.clj @@ -0,0 +1,110 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) +; which can be found in the file CPL.TXT at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(in-ns 'clojure.inspector) +(clojure/refer 'clojure) + +(import '(javax.swing.tree TreeModel) + '(javax.swing.table TableModel) + '(javax.swing JTree JTable JScrollPane JFrame)) + +(defn atom? [x] + (not (instance? clojure.lang.IPersistentCollection x))) + +(defn collection-tag [x] + (cond + (instance? java.util.Map$Entry x) :entry + (instance? clojure.lang.IPersistentMap x) :map + (instance? java.util.Map x) :map + (instance? clojure.lang.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 table-model [data] + (let [row1 (first data) + colcnt (count row1) + cnt (count data) + vals (if (instance? clojure.lang.IPersistentMap row1) vals identity)] + (proxy [TableModel] [] + (addTableModelListener [tableModelListener]) + (getColumnClass [columnIndex] Object) + (getColumnCount [] colcnt) + (getColumnName [columnIndex] + (if (instance? clojure.lang.IPersistentMap 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 (new JFrame "Clojure Inspector") + (add (new JScrollPane (new 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 (new JFrame "Clojure Inspector") + (add (new JScrollPane (new JTable (table-model data)))) + (setSize 400 600) + (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]]) + +)
\ No newline at end of file diff --git a/src/clojure/parallel/parallel.clj b/src/clojure/parallel/parallel.clj new file mode 100644 index 00000000..a9959b3a --- /dev/null +++ b/src/clojure/parallel/parallel.clj @@ -0,0 +1,248 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) +; which can be found in the file CPL.TXT at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(in-ns 'clojure.parallel) +(clojure/refer 'clojure) + +(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))) +)
\ No newline at end of file diff --git a/src/clojure/proxy.clj b/src/clojure/proxy.clj new file mode 100644 index 00000000..f72111a4 --- /dev/null +++ b/src/clojure/proxy.clj @@ -0,0 +1,307 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) +; which can be found in the file CPL.TXT at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(in-ns 'clojure) + +(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)) + +(def *proxy-classes* (ref {})) + +(defn method-sig [#^java.lang.reflect.Method meth] + [(. meth (getName)) (seq (. meth (getParameterTypes))) (. meth getReturnType)]) + +(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 [bases (if (. (first bases) (isInterface)) + (cons Object bases) + bases) + [super & interfaces] bases] + (or (get @*proxy-classes* bases) + (let [cv (new ClassWriter (. ClassWriter COMPUTE_MAXS)) + cname (str "clojure/lang/" (gensym "Proxy__")) + ctype (. Type (getObjectType cname)) + iname (fn [c] (.. Type (getType c) (getInternalName))) + fmap "__clojureFnMap" + totype (fn [c] (. Type (getType c))) + to-types (fn [cs] (if (pos? (count cs)) + (into-array (map totype cs)) + (make-array Type 0))) + super-type (totype super) + map-type (totype PersistentHashMap) + 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-method + (fn [#^java.lang.reflect.Method meth else-gen] + (let [pclasses (. meth (getParameterTypes)) + ptypes (to-types pclasses) + rtype (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)) + (. gen (loadThis)) + (. gen (getField ctype fmap map-type)) + ;get symbol corresponding to name + (. gen (push (. meth (getName)))) + (. gen (invokeStatic sym-type (. Method (getMethod "clojure.lang.Symbol create(String)")))) + ;lookup fn in map + (. gen (invokeStatic rt-type (. Method (getMethod "Object get(Object, Object)")))) + (. gen (dup)) + (. gen (ifNull else-label)) + ;if found + (. 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 (. map-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)) + ;init fmap + (. gen (getStatic map-type "EMPTY" map-type)) + (. gen (putField ctype fmap map-type)) + + (. gen (returnValue)) + (. gen (endMethod))))) + ;add IProxy methods + (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 map-type)) + (. gen (loadArgs)) + (. gen (invokeInterface (totype clojure.lang.IPersistentCollection) + (. Method (getMethod "clojure.lang.IPersistentCollection cons(Object)")))) + (. gen (checkCast map-type)) + (. gen (putField ctype fmap map-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 map-type)) + (. gen (returnValue)) + (. gen (endMethod))) + + ;calc set of supers' non-private instance methods + (let [mm (loop [mm {} considered #{} c super] + (if c + (let [[mm considered] + (loop [mm mm + considered considered + meths (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) + (. Modifier (isPrivate mods)) + (. Modifier (isStatic mods)) + (. Modifier (isFinal mods)) + (= "finalize" (.getName meth))) + (recur mm (conj considered mk) (rest meths)) + (recur (assoc mm mk meth) (conj considered mk) (rest meths)))) + [mm considered]))] + (recur mm considered (. c (getSuperclass)))) + mm))] + ;add methods matching supers', if no mapping -> call super + (doseq #^java.lang.reflect.Method meth (vals mm) + (gen-method meth + (fn [gen 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 #^Class iface interfaces + (doseq #^java.lang.reflect.Method meth (. iface (getMethods)) + (when-not (contains? mm (method-sig meth)) + (gen-method meth + (fn [gen m] + (. gen (throwException ex-type (. m (getName)))))))))) + + ;finish class def + (. cv (visitEnd)) + ;generate, cache and return class object + (let [loader (. RT ROOT_CLASSLOADER) + c (. loader (defineClass (. cname (replace "/" ".")) + (. cv (toByteArray))))] + (sync nil (commute *proxy-classes* assoc bases c)) + c))))) + +(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 update-proxy + "Takes a proxy instance and a map of symbols (whose names 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 [pc# (get-proxy-class ~@class-and-interfaces) + p# (construct-proxy pc# ~@args)] + (update-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)] + (recur (assoc fmap (list `quote (symbol (name sym))) (cons `fn meths)) (rest fs))) + 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 '~(symbol (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 [] (. 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 [pseq] + (when pseq + (lazy-cons (new clojure.lang.MapEntry (first pseq) (v (first pseq))) + (thisfn (rest pseq))))) (keys pmap)))))) diff --git a/src/clojure/set/set.clj b/src/clojure/set/set.clj new file mode 100644 index 00000000..668de4b4 --- /dev/null +++ b/src/clojure/set/set.clj @@ -0,0 +1,116 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) +; which can be found in the file CPL.TXT at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(in-ns 'clojure.set) +(clojure/refer 'clojure) + +(defn union + "Returns a set that is the union of the two sets." + [xset yset] + (reduce conj xset yset)) + +(defn difference + "Returns a set that is xset without the elements of yset." + [xset yset] + (reduce disj xset yset)) + +(defn intersection + "Returns a set of the elements present in both xset and yset." + [xset yset] + (difference xset (difference xset yset))) + +(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]) +) + diff --git a/src/clojure/xml/xml.clj b/src/clojure/xml/xml.clj new file mode 100644 index 00000000..55a8f140 --- /dev/null +++ b/src/clojure/xml/xml.clj @@ -0,0 +1,117 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) +; which can be found in the file CPL.TXT at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +(in-ns 'clojure.xml) +(clojure/refer 'clojure) + +(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")) diff --git a/src/clojure/zip/zip.clj b/src/clojure/zip/zip.clj new file mode 100644 index 00000000..40c765e5 --- /dev/null +++ b/src/clojure/zip/zip.clj @@ -0,0 +1,248 @@ +; Copyright (c) Rich Hickey. All rights reserved. +; The use and distribution terms for this software are covered by the +; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) +; which can be found in the file CPL.TXT at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +;functional hierarchical zipper, with navigation, editing and enumeration +;see Huet + +(in-ns 'clojure.zip) +(clojure/refer 'clojure :exclude '(replace)) + +(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 & crest :as cs] (children loc)] + (when cs + (with-meta [c {:l [] + :pnodes (if path (conj (:pnodes path) node) [node]) + :ppath path + :r crest}] ^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 path + (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 & rrest :as rs] :r :as path}] loc] + (when (and path rs) + (with-meta [r (assoc path :l (conj l node) :r rrest)] ^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 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 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)) + (with-meta [(peek l) (assoc path :l (pop l) :changed? true)] ^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))))) +) diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 7bacaecb..700116c3 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -2286,7 +2286,7 @@ static class IfExpr implements Expr{ static final public IPersistentMap CHAR_MAP = PersistentHashMap.create('-', "_", - '.', "_DOT_", +// '.', "_DOT_", ':', "_COLON_", '+', "_PLUS_", '>', "_GT_", @@ -2683,7 +2683,8 @@ static public class FnExpr implements Expr{ //fn.thisName = name; String basename = enclosingMethod != null ? (enclosingMethod.fn.name + "$") - : "clojure.fns." + (munge(currentNS().name.name) + "."); + : //"clojure.fns." + + (munge(currentNS().name.name) + "."); if(RT.second(form) instanceof Symbol) name = ((Symbol) RT.second(form)).name; fn.simpleName = ((name != null ? @@ -3827,9 +3828,11 @@ public static Object eval(Object form) throws Exception{ } try { - if(form instanceof IPersistentCollection) + if(form instanceof IPersistentCollection + && !(RT.first(form) instanceof Symbol + && ((Symbol)RT.first(form)).name.startsWith("def"))) { - FnExpr fexpr = (FnExpr) analyze(C.EXPRESSION, RT.list(FN, PersistentVector.EMPTY, form), "repl"); + FnExpr fexpr = (FnExpr) analyze(C.EXPRESSION, RT.list(FN, PersistentVector.EMPTY, form), "eval"); IFn fn = (IFn) fexpr.eval(); return fn.invoke(); } diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java index cba00ec0..23261e75 100644 --- a/src/jvm/clojure/lang/RT.java +++ b/src/jvm/clojure/lang/RT.java @@ -289,11 +289,12 @@ static public void init() throws Exception{ } static void doInit() throws Exception{ - loadResourceScript(RT.class, "boot.clj"); - loadResourceScript(RT.class, "proxy.clj"); - loadResourceScript(RT.class, "zip.clj"); - loadResourceScript(RT.class, "xml.clj"); - loadResourceScript(RT.class, "set.clj"); + loadResourceScript(RT.class, "clojure/boot.clj"); + loadResourceScript(RT.class, "clojure/proxy.clj"); + loadResourceScript(RT.class, "clojure/genclass.clj"); + loadResourceScript(RT.class, "clojure/zip/zip.clj"); + loadResourceScript(RT.class, "clojure/xml/xml.clj"); + loadResourceScript(RT.class, "clojure/set/set.clj"); Var.pushThreadBindings( RT.map(CURRENT_NS, CURRENT_NS.get(), diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java index da9edf5f..d5b14edc 100644 --- a/src/jvm/clojure/lang/Reflector.java +++ b/src/jvm/clojure/lang/Reflector.java @@ -36,7 +36,7 @@ public static Object invokeInstanceMethod(Object target, String methodName, Obje } static Object invokeMatchingMethod(String methodName, List methods, Object target, Object[] args) - throws IllegalAccessException, InvocationTargetException, NoSuchMethodException{ + throws Exception{ Method m = null; Object[] boxedArgs = null; if(methods.isEmpty()) @@ -77,7 +77,16 @@ static Object invokeMatchingMethod(String methodName, List methods, Object targe } if(m == null) throw new IllegalArgumentException("No matching method found: " + methodName); + try{ return prepRet(m.getReturnType(), m.invoke(target, boxedArgs)); + } + catch(InvocationTargetException e) + { + if(e.getCause() instanceof Exception) + throw (Exception) e.getCause(); + throw e; + } + } public static Method getAsMethodOfPublicBase(Class c, Method m){ |