summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRich Hickey <richhickey@gmail.com>2006-06-11 15:40:13 +0000
committerRich Hickey <richhickey@gmail.com>2006-06-11 15:40:13 +0000
commitb58d5badd4ed397f0f6e9be306ac12311c7049d9 (patch)
treee523068b29066fdfe14b5422af508179bbbf59b7 /src
parentc66573864e1cdc9ba5cbef711ce70f0137c50f02 (diff)
Diffstat (limited to 'src')
-rw-r--r--src/jvm/clojure/runtime/AFn.java101
-rw-r--r--src/jvm/clojure/runtime/AGenerator.java25
-rw-r--r--src/jvm/clojure/runtime/Accessor.java83
-rw-r--r--src/jvm/clojure/runtime/ArrayMap.java169
-rw-r--r--src/jvm/clojure/runtime/BigNum.java220
-rw-r--r--src/jvm/clojure/runtime/Box.java23
-rw-r--r--src/jvm/clojure/runtime/Cons.java36
-rw-r--r--src/jvm/clojure/runtime/DoubleNum.java244
-rw-r--r--src/jvm/clojure/runtime/FixNum.java239
-rw-r--r--src/jvm/clojure/runtime/FloatNum.java17
-rw-r--r--src/jvm/clojure/runtime/HashtableMap.java280
-rw-r--r--src/jvm/clojure/runtime/IFn.java34
-rw-r--r--src/jvm/clojure/runtime/IMap.java31
-rw-r--r--src/jvm/clojure/runtime/IMapEntry.java17
-rw-r--r--src/jvm/clojure/runtime/IObj.java26
-rw-r--r--src/jvm/clojure/runtime/IdentityArrayMap.java27
-rw-r--r--src/jvm/clojure/runtime/IdentityHashtableMap.java38
-rw-r--r--src/jvm/clojure/runtime/Indexer.java16
-rw-r--r--src/jvm/clojure/runtime/IntegerNum.java17
-rw-r--r--src/jvm/clojure/runtime/Iter.java38
-rw-r--r--src/jvm/clojure/runtime/IteratorIter.java44
-rw-r--r--src/jvm/clojure/runtime/Keyword.java89
-rw-r--r--src/jvm/clojure/runtime/LineNumberingPushbackReader.java27
-rw-r--r--src/jvm/clojure/runtime/ListMap.java252
-rw-r--r--src/jvm/clojure/runtime/Namespace.java70
-rw-r--r--src/jvm/clojure/runtime/Num.java220
-rw-r--r--src/jvm/clojure/runtime/Obj.java44
-rw-r--r--src/jvm/clojure/runtime/PersistentArray.java324
-rw-r--r--src/jvm/clojure/runtime/PersistentHashMap.java101
-rw-r--r--src/jvm/clojure/runtime/RBTree.java759
-rw-r--r--src/jvm/clojure/runtime/RT.java389
-rw-r--r--src/jvm/clojure/runtime/RatioNum.java226
-rw-r--r--src/jvm/clojure/runtime/RationalNum.java17
-rw-r--r--src/jvm/clojure/runtime/RealNum.java17
-rw-r--r--src/jvm/clojure/runtime/Reflector.java320
-rw-r--r--src/jvm/clojure/runtime/RestFn0.java60
-rw-r--r--src/jvm/clojure/runtime/RestFn1.java65
-rw-r--r--src/jvm/clojure/runtime/RestFn2.java66
-rw-r--r--src/jvm/clojure/runtime/RestFn3.java67
-rw-r--r--src/jvm/clojure/runtime/RestFn4.java70
-rw-r--r--src/jvm/clojure/runtime/RestFn5.java73
-rw-r--r--src/jvm/clojure/runtime/Symbol.java84
-rw-r--r--src/jvm/clojure/runtime/TObj.java36
-rw-r--r--src/jvm/clojure/runtime/TRef.java33
-rw-r--r--src/jvm/clojure/runtime/TVal.java39
-rw-r--r--src/jvm/clojure/runtime/ThreadLocalData.java61
-rw-r--r--src/jvm/clojure/runtime/Transaction.java234
-rw-r--r--src/jvm/clojure/runtime/Var.java140
-rw-r--r--src/jvm/clojure/tools/TypeDump.java199
49 files changed, 5807 insertions, 0 deletions
diff --git a/src/jvm/clojure/runtime/AFn.java b/src/jvm/clojure/runtime/AFn.java
new file mode 100644
index 00000000..323fe732
--- /dev/null
+++ b/src/jvm/clojure/runtime/AFn.java
@@ -0,0 +1,101 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 25, 2006 4:05:37 PM */
+
+package org.clojure.runtime;
+
+public class AFn extends Obj implements IFn{
+
+public Object invoke(ThreadLocalData tld) throws Exception
+ {
+ return throwArity();
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1) throws Exception
+ {
+ return throwArity();
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2) throws Exception
+ {
+ return throwArity();
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3) throws Exception
+ {
+ return throwArity();
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception
+ {
+ return throwArity();
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
+ throws Exception
+ {
+ return throwArity();
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Cons args)
+ throws Exception
+ {
+ return throwArity();
+ }
+
+public Object applyTo(ThreadLocalData tld, Cons arglist) throws Exception {
+ return applyToHelper(this, tld, arglist);
+}
+static public Object applyToHelper(IFn ifn,ThreadLocalData tld, Cons arglist) throws Exception
+ {
+ switch(RT.boundedLength(arglist, 5))
+ {
+ case 0:
+ return ifn.invoke(tld);
+ case 1:
+ return ifn.invoke(tld, arglist.first);
+ case 2:
+ return ifn.invoke(tld, arglist.first
+ , (arglist = arglist.rest).first
+ );
+ case 3:
+ return ifn.invoke(tld, arglist.first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ );
+ case 4:
+ return ifn.invoke(tld, arglist.first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ );
+ case 5:
+ return ifn.invoke(tld, arglist.first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ );
+ default:
+ return ifn.invoke(tld, arglist.first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , arglist.rest);
+ }
+ }
+
+public static Object throwArity()
+ {
+ throw new IllegalArgumentException("Wrong number of args passed");
+ }
+}
diff --git a/src/jvm/clojure/runtime/AGenerator.java b/src/jvm/clojure/runtime/AGenerator.java
new file mode 100644
index 00000000..03b35dac
--- /dev/null
+++ b/src/jvm/clojure/runtime/AGenerator.java
@@ -0,0 +1,25 @@
+/**
+ * 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.
+ **/
+
+/* rich Apr 3, 2006 */
+
+package org.clojure.runtime;
+
+public abstract class AGenerator implements Iter{
+
+Object __val;
+int __state = 0;
+
+public Object get()
+ {
+ return __val;
+ }
+
+}
diff --git a/src/jvm/clojure/runtime/Accessor.java b/src/jvm/clojure/runtime/Accessor.java
new file mode 100644
index 00000000..b69bb0bb
--- /dev/null
+++ b/src/jvm/clojure/runtime/Accessor.java
@@ -0,0 +1,83 @@
+/**
+ * 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.
+ **/
+
+/* rich Apr 19, 2006 */
+
+package org.clojure.runtime;
+
+public class Accessor extends Symbol implements IFn{
+
+String memberName;
+Accessor(String name)
+ {
+ super(name);
+ memberName = name.substring(1);
+ }
+
+
+public Object invoke(ThreadLocalData tld) throws Exception {
+ return AFn.throwArity();
+}
+/**
+ * Indexer implements IFn for attr access
+ * This single arg version is the getter
+ * @param tld
+ * @param obj - must be Obj
+ * @return the value of the attr or nil if not found
+ * @throws Exception
+ */
+public Object invoke(ThreadLocalData tld, Object obj) throws Exception
+ {
+
+ return Reflector.invokeInstanceMember(memberName,obj);
+ }
+
+/**
+ * Indexer implements IFn for attr access
+ * This two arg version is the setter
+ * @param tld
+ * @param obj - must be Obj
+ * @param val
+ * @return val
+ * @throws Exception
+ */
+public Object invoke(ThreadLocalData tld, Object obj, Object val) throws Exception
+ {
+
+ return Reflector.invokeInstanceMember(memberName,obj,val);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3) throws Exception
+ {
+ return Reflector.invokeInstanceMember(memberName,arg1,arg2,arg3);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception
+ {
+ return Reflector.invokeInstanceMember(memberName,arg1,arg2,arg3,arg4);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
+ throws Exception
+ {
+ return Reflector.invokeInstanceMember(memberName,arg1,arg2,arg3,arg4,arg5);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Cons args)
+ throws Exception
+ {
+ return Reflector.invokeInstanceMember(memberName,arg1,arg2,arg3,arg4,arg5,args);
+ }
+
+public Object applyTo(ThreadLocalData tld, Cons arglist) throws Exception {
+ return AFn.applyToHelper(this, tld, arglist);
+}
+
+}
diff --git a/src/jvm/clojure/runtime/ArrayMap.java b/src/jvm/clojure/runtime/ArrayMap.java
new file mode 100644
index 00000000..49dec167
--- /dev/null
+++ b/src/jvm/clojure/runtime/ArrayMap.java
@@ -0,0 +1,169 @@
+/**
+ * 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.
+ **/
+
+package org.clojure.runtime;
+
+import java.util.Iterator;
+
+/**
+ * Simple implementation of persistent map on an array
+
+ * Note that instances of this class are constant values
+ * i.e. add/remove etc return new values
+ *
+ * Copies array on every change, so only appropriate for _very_small_ maps
+ *
+ * null keys and values are ok, but you won't be able to distinguish a null value via get - use contains/find
+ */
+
+public class ArrayMap implements IMap {
+
+final Object[] array;
+
+public ArrayMap(){
+ this.array = RT.EMPTY_ARRAY;
+}
+
+/**
+ * This ctor captures/aliases the passed array, so do not modify later
+ * @param init {key1,val1,key2,val2,...}
+ */
+public ArrayMap(Object[] init){
+ this.array = init;
+}
+
+public int count() {
+ return array.length/2;
+}
+
+public boolean contains(Object key){
+ return indexOf(key) >= 0;
+}
+
+public IMapEntry find(Object key) {
+ int i = indexOf(key);
+ if(i >= 0)
+ return new Iter(array,i);
+ return null;
+}
+
+public IMap add(Object key) {
+
+ return put(key,null);
+}
+
+public IMap put(Object key, Object val) {
+ int i = indexOf(key);
+ Object[] newArray;
+ if(i >= 0) //already have key, same-sized replacement
+ {
+ if(array[i+1] == val) //no change, no op
+ return this;
+ newArray = array.clone();
+ newArray[i+1] = val;
+ }
+ else //didn't have key, grow
+ {
+ newArray = new Object[array.length + 2];
+ if(array.length > 0)
+ System.arraycopy(array,0,newArray,2,array.length);
+ newArray[0] = key;
+ newArray[1] = val;
+ }
+ return new ArrayMap(newArray);
+}
+
+public IMap remove(Object key) {
+ int i = indexOf(key);
+ if(i >= 0) //have key, will remove
+ {
+ Object[] newArray = new Object[array.length - 2];
+ for(int s=0,d=0;s<array.length;s += 2)
+ {
+ if(!equalKey(array[s],key)) //skip removal key
+ {
+ newArray[d] = array[s];
+ newArray[d+1] = array[s+1];
+ d += 2;
+ }
+ }
+ return new ArrayMap(newArray);
+ }
+ //don't have key, no op
+ return this;
+}
+
+public Object get(Object key) {
+ int i = indexOf(key);
+ if(i >= 0)
+ return array[i + 1];
+ return null;
+}
+
+public int capacity() {
+ return count();
+}
+
+int indexOf(Object key){
+ for(int i=0;i<array.length;i+=2)
+ {
+ if(equalKey(array[i],key))
+ return i;
+ }
+ return -1;
+}
+
+boolean equalKey(Object k1,Object k2){
+ if(k1 == null)
+ return k2 == null;
+ return k1.equals(k2);
+}
+
+public Iterator iterator() {
+ return new Iter(array);
+}
+
+static class Iter implements Iterator,IMapEntry{
+ Object[] array;
+ int i;
+
+ //for iterator
+ Iter(Object[] array){
+ this(array,-2);
+ }
+
+ //for find
+ Iter(Object[] array, int i){
+ this.array = array;
+ this.i = i;
+ }
+
+ public boolean hasNext() {
+ return i < array.length - 2;
+ }
+
+ public Object next() {
+ i+=2;
+ return this;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object key() {
+ return array[i];
+ }
+
+ public Object val() {
+ return array[i+1];
+ }
+}
+}
diff --git a/src/jvm/clojure/runtime/BigNum.java b/src/jvm/clojure/runtime/BigNum.java
new file mode 100644
index 00000000..3955fe6a
--- /dev/null
+++ b/src/jvm/clojure/runtime/BigNum.java
@@ -0,0 +1,220 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 28, 2006 10:08:33 AM */
+
+package org.clojure.runtime;
+
+import java.math.BigInteger;
+
+public class BigNum extends IntegerNum{
+public BigInteger val;
+
+public boolean equals(Object arg0)
+ {
+ return arg0 != null
+ && arg0 instanceof BigNum
+ && ((BigNum) arg0).val == val;
+ }
+
+public int hashCode()
+ {
+ return val.hashCode();
+ }
+
+public String toString()
+ {
+ return val.toString();
+ }
+
+public BigNum(long val)
+ {
+ this.val = BigInteger.valueOf(val);
+ }
+
+public BigNum(BigInteger val)
+ {
+ this.val = val;
+ }
+
+public double doubleValue()
+ {
+ return val.doubleValue();
+ }
+
+public float floatValue()
+ {
+ return val.floatValue();
+ }
+
+public int intValue()
+ {
+ return val.intValue();
+ }
+
+public long longValue()
+ {
+ return val.longValue();
+ }
+
+public boolean equiv(Num rhs)
+ {
+ return rhs.equivTo(val);
+ }
+
+public boolean equivTo(BigInteger x)
+ {
+ return x.equals(val);
+ }
+
+public boolean equivTo(int x)
+ {
+ //must be outside of range of int or would be one itself
+ return false;
+ }
+
+public boolean equivTo(RatioNum x)
+ {
+ //wouldn't still be a RatioNum if it was an integer
+ return false;
+ }
+
+public boolean lt(Num rhs)
+ {
+ return rhs.gt(val);
+ }
+
+public boolean gt(BigInteger x)
+ {
+ return x.compareTo(val) < 0;
+ }
+
+public boolean gt(int x)
+ {
+ return BigInteger.valueOf(x).compareTo(val) < 0;
+ }
+
+public boolean gt(RatioNum x)
+ {
+ return x.numerator.lt(x.denominator.multiply(val));
+ }
+
+public Num add(Num rhs)
+ {
+ return rhs.addTo(val);
+ }
+
+public Num addTo(BigInteger x)
+ {
+ return Num.from(x.add(val));
+ }
+
+public Num addTo(int x)
+ {
+ return Num.from(val.add(BigInteger.valueOf(x)));
+ }
+
+public Num addTo(RatioNum x)
+ {
+ return x.addTo(val);
+ }
+
+public Num subtractFrom(Num x)
+ {
+ return x.addTo(val.negate());
+ }
+
+public Num multiplyBy(Num rhs)
+ {
+ return rhs.multiply(val);
+ }
+
+public Num multiply(BigInteger x)
+ {
+ return Num.from(x.multiply(val));
+ }
+
+public Num multiply(int x)
+ {
+ return Num.from(val.multiply(BigInteger.valueOf(x)));
+ }
+
+public Num multiply(RatioNum x)
+ {
+ return x.multiply(val);
+ }
+
+public Num divideBy(Num rhs)
+ {
+ return rhs.divide(val);
+ }
+
+public Num divide(BigInteger n)
+ {
+ return Num.divide(n, val);
+ }
+
+public Num divide(int n)
+ {
+ return Num.divide(BigInteger.valueOf(n), val);
+ }
+
+public Num divide(RatioNum x)
+ {
+ return Num.divide(x.numerator, x.denominator.multiply(val));
+ }
+
+public Object truncateDivide(ThreadLocalData tld, Num num)
+ {
+ return num.truncateBy(tld, val);
+ }
+
+public Object truncateBy(ThreadLocalData tld, int div)
+ {
+ return Num.truncateBigints(tld, val, BigInteger.valueOf(div));
+ }
+
+public Object truncateBy(ThreadLocalData tld, BigInteger div)
+ {
+ return Num.truncateBigints(tld, val, div);
+ }
+
+public Object truncateBy(ThreadLocalData tld, RatioNum div)
+ {
+ Num q = (Num) Num.truncate(tld, div.denominator.multiply(val), div.numerator);
+ return RT.setValues(tld, q, q.multiplyBy(div).subtractFrom(this));
+ }
+
+public Num negate()
+ {
+ return Num.from(val.negate());
+ }
+
+public boolean minusp()
+ {
+ return val.signum() < 0;
+ }
+
+public boolean plusp()
+ {
+ return val.signum() > 0;
+ }
+
+public Num oneMinus()
+ {
+ return Num.from(val.subtract(BigInteger.ONE));
+ }
+
+public Num onePlus()
+ {
+ return Num.from(val.add(BigInteger.ONE));
+ }
+}
+
diff --git a/src/jvm/clojure/runtime/Box.java b/src/jvm/clojure/runtime/Box.java
new file mode 100644
index 00000000..e1129029
--- /dev/null
+++ b/src/jvm/clojure/runtime/Box.java
@@ -0,0 +1,23 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 27, 2006 8:40:19 PM */
+
+package org.clojure.runtime;
+
+public class Box{
+
+public Object val;
+
+public Box(Object val)
+ {
+ this.val = val;
+ }
+}
diff --git a/src/jvm/clojure/runtime/Cons.java b/src/jvm/clojure/runtime/Cons.java
new file mode 100644
index 00000000..8cdd3bd9
--- /dev/null
+++ b/src/jvm/clojure/runtime/Cons.java
@@ -0,0 +1,36 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 25, 2006 11:01:29 AM */
+
+package org.clojure.runtime;
+
+public class Cons extends Obj implements Iter{
+
+public final Object first;
+public final Cons rest;
+
+public Cons(Object first, Cons rest)
+ {
+ this.first = first;
+ this.rest = rest;
+ }
+
+public Object get()
+ {
+ return first;
+ }
+
+public Iter iterate()
+ {
+ return rest;
+ }
+
+}
diff --git a/src/jvm/clojure/runtime/DoubleNum.java b/src/jvm/clojure/runtime/DoubleNum.java
new file mode 100644
index 00000000..00ec7c7a
--- /dev/null
+++ b/src/jvm/clojure/runtime/DoubleNum.java
@@ -0,0 +1,244 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 28, 2006 10:13:45 AM */
+
+package org.clojure.runtime;
+
+import java.math.BigInteger;
+import java.math.BigDecimal;
+
+public class DoubleNum extends FloatNum{
+double val;
+
+public DoubleNum(double val)
+ {
+ this.val = val;
+ }
+
+public double doubleValue()
+ {
+ return val;
+ }
+
+public float floatValue()
+ {
+ return (float) val;
+ }
+
+public int intValue()
+ {
+ return (int) val;
+ }
+
+public long longValue()
+ {
+ return (long) val;
+ }
+
+final static BigInteger BIGTEN = BigInteger.valueOf(10);
+
+public Num toRational()
+ {
+ BigDecimal d = new BigDecimal(val);
+ return Num.divide(d.unscaledValue(), BIGTEN.pow(d.scale()));
+ }
+
+public boolean equiv(Num rhs)
+ {
+ if(rhs instanceof RatioNum)
+ return equivTo((RatioNum) rhs);
+ return val == rhs.doubleValue();
+ }
+
+public boolean equivTo(BigInteger x)
+ {
+ return val == x.doubleValue();
+ }
+
+public boolean equivTo(int x)
+ {
+ return x == val;
+ }
+
+public boolean equivTo(RatioNum x)
+ {
+ return toRational().equivTo(x);
+ }
+
+public boolean lt(Num rhs)
+ {
+ if(rhs instanceof RatioNum)
+ return toRational().lt(rhs);
+ return val < rhs.doubleValue();
+ }
+
+public boolean gt(BigInteger x)
+ {
+ return val > x.doubleValue();
+ }
+
+public boolean gt(int x)
+ {
+ return val > x;
+ }
+
+public boolean gt(RatioNum x)
+ {
+ return toRational().gt(x);
+ }
+
+public Num add(Num rhs)
+ {
+ return Num.from(val + rhs.doubleValue());
+ }
+
+public Num addTo(int x)
+ {
+ return Num.from(val + x);
+ }
+
+public Num addTo(BigInteger x)
+ {
+ return Num.from(val + x.doubleValue());
+ }
+
+public Num addTo(RatioNum x)
+ {
+ return Num.from(val + x.doubleValue());
+ }
+
+public Num subtractFrom(Num x)
+ {
+ return Num.from(x.doubleValue() - val);
+ }
+
+public Num multiplyBy(Num rhs)
+ {
+ return Num.from(val * rhs.doubleValue());
+ }
+
+public Num multiply(int x)
+ {
+ return Num.from(val * x);
+ }
+
+public Num multiply(BigInteger x)
+ {
+ return Num.from(val * x.doubleValue());
+ }
+
+public Num multiply(RatioNum x)
+ {
+ return Num.from(val * x.doubleValue());
+ }
+
+public Num divideBy(Num rhs)
+ {
+ return Num.from(val / rhs.doubleValue());
+ }
+
+public Num divide(int x)
+ {
+ return Num.from(x / val);
+ }
+
+public Num divide(BigInteger x)
+ {
+ return Num.from(x.doubleValue() / val);
+ }
+
+public Num divide(RatioNum x)
+ {
+ return Num.from(x.doubleValue() / val);
+ }
+
+static Object truncate(ThreadLocalData tld, double n, double d)
+ {
+ double q = n / d;
+ if(q <= Integer.MAX_VALUE && q >= Integer.MIN_VALUE)
+ {
+ return RT.setValues(tld, Num.from((int) q),
+ Num.from(n - ((int) q) * d));
+ }
+ else
+ { //bigint quotient
+ Num bq = Num.from(new BigDecimal(q).toBigInteger());
+ return RT.setValues(tld, bq,
+ Num.from(n - bq.doubleValue() * d));
+ }
+ }
+
+public Object truncateBy(ThreadLocalData tld, BigInteger x)
+ {
+ return truncate(tld, val, x.doubleValue());
+ }
+
+public Object truncateBy(ThreadLocalData tld, int x)
+ {
+ return truncate(tld, val, x);
+ }
+
+public Object truncateBy(ThreadLocalData tld, RatioNum x)
+ {
+ return truncate(tld, val, x.doubleValue());
+ }
+
+public Object truncateDivide(ThreadLocalData tld, Num num)
+ {
+ return truncate(tld, num.doubleValue(), val);
+ }
+
+public Num negate()
+ {
+ return Num.from(-val);
+ }
+
+public boolean equals(Object arg0)
+ {
+ return arg0 != null
+ && arg0 instanceof DoubleNum
+ && Double.doubleToLongBits(((DoubleNum) arg0).val) ==
+ Double.doubleToLongBits(val);
+ }
+
+public int hashCode()
+ {
+ long v = Double.doubleToLongBits(val);
+ return (int) (v ^ (v >>> 32));
+ }
+
+public String toString()
+ {
+ return Double.toString(val);
+ }
+
+public boolean minusp()
+ {
+ return val < 0;
+ }
+
+public boolean plusp()
+ {
+ return val > 0;
+ }
+
+public Num oneMinus()
+ {
+ return Num.from(val - 1);
+ }
+
+public Num onePlus()
+ {
+ return Num.from(val + 1);
+ }
+
+}
+
diff --git a/src/jvm/clojure/runtime/FixNum.java b/src/jvm/clojure/runtime/FixNum.java
new file mode 100644
index 00000000..31ecf1cb
--- /dev/null
+++ b/src/jvm/clojure/runtime/FixNum.java
@@ -0,0 +1,239 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 28, 2006 10:09:27 AM */
+
+package org.clojure.runtime;
+
+import java.math.BigInteger;
+
+public class FixNum extends IntegerNum{
+public int val;
+
+public boolean equals(Object arg0)
+ {
+ return arg0 != null
+ && arg0 instanceof FixNum
+ && ((FixNum) arg0).val == val;
+ }
+
+public int hashCode()
+ {
+ return val;
+ }
+
+public String toString()
+ {
+ return Integer.toString(val);
+ }
+
+public FixNum(int val)
+ {
+ this.val = val;
+ }
+
+public double doubleValue()
+ {
+ return (double) val;
+ }
+
+public float floatValue()
+ {
+ return (float) val;
+ }
+
+public int intValue()
+ {
+ return val;
+ }
+
+public long longValue()
+ {
+ return (long) val;
+ }
+
+public boolean equiv(Num rhs)
+ {
+ return rhs.equivTo(val);
+ }
+
+public boolean equivTo(BigInteger x)
+ {
+ //wouldn't still be a BigInteger if it fit in int
+ return false;
+ }
+
+public boolean equivTo(int x)
+ {
+ return x == val;
+ }
+
+public boolean equivTo(RatioNum x)
+ {
+ //wouldn't still be a RatioNum if it was an integer
+ return false;
+ }
+
+public boolean lt(Num rhs)
+ {
+ return rhs.gt(val);
+ }
+
+public boolean gt(BigInteger x)
+ {
+ return x.compareTo(BigInteger.valueOf(val)) < 0;
+ }
+
+public boolean gt(int x)
+ {
+ return x < val;
+ }
+
+public boolean gt(RatioNum x)
+ {
+ return x.numerator.lt(x.denominator.multiply(val));
+ }
+
+public Num add(Num rhs)
+ {
+ return rhs.addTo(val);
+ }
+
+public Num addTo(BigInteger x)
+ {
+ return Num.from(x.add(BigInteger.valueOf(val)));
+ }
+
+public Num addTo(int x)
+ {
+ return Num.from((long) x + val);
+ }
+
+public Num addTo(RatioNum x)
+ {
+ return x.addTo(val);
+ }
+
+public Num subtractFrom(Num x)
+ {
+ return x.addTo(-val);
+ }
+
+public Num multiplyBy(Num rhs)
+ {
+ return rhs.multiply(val);
+ }
+
+public Num multiply(BigInteger x)
+ {
+ return Num.from(x.multiply(BigInteger.valueOf(val)));
+ }
+
+public Num multiply(int x)
+ {
+ return Num.from((long) x * val);
+ }
+
+public Num multiply(RatioNum x)
+ {
+ return x.multiply(val);
+ }
+
+public Object truncateDivide(ThreadLocalData tld, Num num)
+ {
+ return num.truncateBy(tld, val);
+ }
+
+public Object truncateBy(ThreadLocalData tld, int div)
+ {
+ return RT.setValues(tld, Num.from(val / div), Num.from(val % div));
+ }
+
+public Object truncateBy(ThreadLocalData tld, BigInteger div)
+ {
+ return Num.truncateBigints(tld, BigInteger.valueOf(val), div);
+ }
+
+public Object truncateBy(ThreadLocalData tld, RatioNum div)
+ {
+ Num q = (Num) Num.truncate(tld, div.denominator.multiply(val), div.numerator);
+ return RT.setValues(tld, q, q.multiplyBy(div).subtractFrom(this));
+ }
+
+public Num divideBy(Num rhs)
+ {
+ return rhs.divide(val);
+ }
+
+public Num divide(BigInteger n)
+ {
+ return Num.divide(n, BigInteger.valueOf(val));
+ }
+
+static int gcd(int u, int v)
+ {
+ while(v != 0)
+ {
+ int r = u % v;
+ u = v;
+ v = r;
+ }
+ return u;
+ }
+
+public Num divide(int n)
+ {
+ int gcd = gcd(n, val);
+ if(gcd == 0)
+ return Num.ZERO;
+
+ n = n / gcd;
+ int d = val / gcd;
+ if(d == 1)
+ return Num.from(n);
+ if(d < 0)
+ {
+ n = -n;
+ d = -d;
+ }
+ return new RatioNum((IntegerNum) Num.from(n), (IntegerNum) Num.from(d));
+ }
+
+public Num divide(RatioNum x)
+ {
+ return Num.divide(x.numerator, x.denominator.multiply(val));
+ }
+
+public Num negate()
+ {
+ return Num.from(-val);
+ }
+
+public boolean minusp()
+ {
+ return val < 0;
+ }
+
+public boolean plusp()
+ {
+ return val > 0;
+ }
+
+public Num oneMinus()
+ {
+ return Num.from(val - 1);
+ }
+
+public Num onePlus()
+ {
+ return Num.from(val + 1);
+ }
+
+}
diff --git a/src/jvm/clojure/runtime/FloatNum.java b/src/jvm/clojure/runtime/FloatNum.java
new file mode 100644
index 00000000..8ea7b6de
--- /dev/null
+++ b/src/jvm/clojure/runtime/FloatNum.java
@@ -0,0 +1,17 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 28, 2006 10:17:21 AM */
+
+package org.clojure.runtime;
+
+public abstract class FloatNum extends RealNum {
+
+}
diff --git a/src/jvm/clojure/runtime/HashtableMap.java b/src/jvm/clojure/runtime/HashtableMap.java
new file mode 100644
index 00000000..a96a9761
--- /dev/null
+++ b/src/jvm/clojure/runtime/HashtableMap.java
@@ -0,0 +1,280 @@
+/**
+ * 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.
+ **/
+
+package org.clojure.runtime;
+
+import java.util.Iterator;
+import java.math.BigInteger;
+
+public class HashtableMap implements IMap, Iterable {
+
+static final float FILL_FACTOR = 0.75f;
+
+final PersistentArray array;
+final int _count;
+final int growAtCount;
+
+public HashtableMap(int initialCapacity) {
+ array = new PersistentArray(calcPrimeCapacity(initialCapacity));
+ _count = 0;
+ this.growAtCount = (int) (this.array.length()*FILL_FACTOR);
+}
+
+/**
+ * @param init {key1,val1,key2,val2,...}
+ */
+public HashtableMap(Object[] init){
+ //start halfway to a rehash
+ PersistentArray narray = new PersistentArray(calcPrimeCapacity(init.length));
+ for(int i=0;i<init.length;i+=2)
+ {
+ narray = doPut(bucketFor(init[i],narray),init[i], init[i + 1],narray);
+ }
+ this.array = narray;
+ this._count = init.length/2; //hmmm... presumes no dupe keys in init
+ this.growAtCount = (int) (this.array.length()*FILL_FACTOR);
+}
+
+HashtableMap(int count,PersistentArray array) {
+ this._count = count;
+ this.array = array;
+ this.growAtCount = (int) (this.array.length()*FILL_FACTOR);
+}
+
+HashtableMap(int count,PersistentArray array,int growAt) {
+ this._count = count;
+ this.array = array;
+ this.growAtCount = growAt;
+}
+
+int calcPrimeCapacity(int capacity) {
+ return BigInteger.valueOf((long) (capacity/FILL_FACTOR)).nextProbablePrime().intValue();
+}
+
+public int count() {
+ return _count;
+}
+
+public boolean contains(Object key) {
+ IMap entries = entriesFor(key);
+ return entries != null && entries.contains(key);
+}
+
+public IMapEntry find(Object key) {
+ IMap entries = entriesFor(key);
+ if(entries != null)
+ return entries.find(key);
+ return null;
+}
+
+public IMap add(Object key) {
+ return put(key, null);
+}
+
+public IMap put(Object key, Object val) {
+ if(_count > growAtCount)
+ return grow().put(key, val);
+ int i = bucketFor(key,array);
+ int incr = 1;
+ PersistentArray newArray = doPut(i, key, val, array);
+ if(newArray == array)
+ return this;
+ if(array.get(i) != null && ((IMap)newArray.get(i)).count() == ((IMap)array.get(i)).count()) //key already there, no growth
+ incr = 0;
+ return create(_count + incr, newArray, growAtCount);
+}
+
+PersistentArray doPut(int i,Object key,Object val,PersistentArray array){
+ IMap entries = (IMap) array.get(i);
+ IMap newEntries;
+ if (entries != null)
+ {
+ newEntries = entries.put(key, val);
+ if(newEntries == entries) //already there with same value, no op
+ return array;
+ }
+ else
+ newEntries = createListMap(key, val);
+ //newEntries = createArrayMap(new Object[]{key, val});
+
+ return array.set(i, newEntries);
+}
+
+public IMap remove(Object key) {
+ int i = bucketFor(key,array);
+ IMap entries = (IMap) array.get(i);
+ if (entries != null)
+ {
+ IMap newEntries = entries.remove(key);
+ if (newEntries != entries)
+ return create(_count - 1, array.set(i, newEntries));
+ }
+ //not there, no op
+ return this;
+}
+
+public Object get(Object key) {
+ IMap entries = entriesFor(key);
+ if(entries != null)
+ return entries.get(key);
+ return null;
+}
+
+public int capacity() {
+ return array.length();
+}
+
+public Iterator<IMapEntry> iterator() {
+ return new Iter(array);
+}
+
+
+IMap grow(){
+ PersistentArray newArray = new PersistentArray(calcPrimeCapacity(_count * 2));
+ for (Object o : this)
+ {
+ IMapEntry e = (IMapEntry) o;
+ newArray = doPut(bucketFor(e.key(),newArray),e.key(), e.val(),newArray);
+ }
+ return create(_count,newArray);
+}
+
+/*
+static class Iter implements Iterator, IMapEntry{
+ PersistentArray buckets;
+
+ int b;
+
+ Object[] nextEntries;
+
+ int nextE;
+
+ Object[] entries;
+
+ int e;
+
+ Iter(PersistentArray buckets){
+ this.buckets = buckets;
+ this.b = -1;
+ nextBucket();
+ }
+
+ private void nextBucket() {
+ nextEntries = null;
+ nextE = 0;
+ for(b = b+1;b<buckets.length();b++)
+ {
+ ArrayMap a = (ArrayMap) buckets.get(b);
+ if(a != null)
+ {
+ nextEntries = a.array;
+ break;
+ }
+ }
+ }
+
+ public boolean hasNext() {
+ return nextEntries != null;
+ }
+
+ public Object next() {
+ entries = nextEntries;
+ e = nextE;
+ nextE += 2;
+ if(nextE >= nextEntries.length)
+ nextBucket();
+ return this;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object key() {
+ return entries[e];
+ }
+
+ public Object val() {
+ return entries[e+1];
+ }
+
+}
+*/
+static class Iter implements Iterator{
+ PersistentArray buckets;
+ int b;
+ ListMap e;
+
+ Iter(PersistentArray buckets){
+ this.buckets = buckets;
+ this.b = -1;
+ nextBucket();
+ }
+
+ private void nextBucket() {
+ e = null;
+ for(b = b+1;b<buckets.length();b++)
+ {
+ ListMap a = (ListMap) buckets.get(b);
+ if(a != null && a != ListMap.EMPTY)
+ {
+ e = a;
+ break;
+ }
+ }
+ }
+
+ public boolean hasNext() {
+ return e != null;
+ }
+
+ public Object next() {
+ ListMap ret = e;
+ e = e.rest();
+ if(e == ListMap.EMPTY)
+ nextBucket();
+ return ret;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
+
+final IMap entriesFor(Object key){
+ return (IMap) array.get(bucketFor(key,array));
+}
+
+static int bucketFor(Object key, PersistentArray array) {
+ return (key.hashCode() & 0x7fffffff)%array.length();
+}
+
+IMap create(int capacity) {
+ return new HashtableMap(capacity);
+}
+
+IMap create(int count,PersistentArray array) {
+ return new HashtableMap(count, array);
+}
+
+private IMap create(int i, PersistentArray newArray, int growAtCount){
+ return new HashtableMap(i, newArray, growAtCount);
+}
+
+
+IMap createArrayMap(Object[] init) {
+ return new ArrayMap(init);
+}
+
+private IMap createListMap(Object key, Object val){
+ return ListMap.create(key,val);
+}
+
+}
diff --git a/src/jvm/clojure/runtime/IFn.java b/src/jvm/clojure/runtime/IFn.java
new file mode 100644
index 00000000..f5d1194f
--- /dev/null
+++ b/src/jvm/clojure/runtime/IFn.java
@@ -0,0 +1,34 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 25, 2006 3:54:03 PM */
+
+package org.clojure.runtime;
+
+public interface IFn{
+
+public Object invoke(ThreadLocalData tld) throws Exception;
+
+public Object invoke(ThreadLocalData tld, Object arg1) throws Exception;
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2) throws Exception;
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3) throws Exception;
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception;
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
+ throws Exception;
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5,
+ Cons args) throws Exception;
+
+public Object applyTo(ThreadLocalData tld, Cons arglist) throws Exception;
+}
diff --git a/src/jvm/clojure/runtime/IMap.java b/src/jvm/clojure/runtime/IMap.java
new file mode 100644
index 00000000..015df00d
--- /dev/null
+++ b/src/jvm/clojure/runtime/IMap.java
@@ -0,0 +1,31 @@
+/**
+ * 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.
+ */
+
+package org.clojure.runtime;
+
+
+public interface IMap extends Iterable {
+
+int count();
+
+boolean contains(Object key);
+
+IMapEntry find(Object key);
+
+IMap add(Object key);
+
+IMap put(Object key, Object val);
+
+IMap remove(Object key);
+
+Object get(Object key);
+
+int capacity();
+}
diff --git a/src/jvm/clojure/runtime/IMapEntry.java b/src/jvm/clojure/runtime/IMapEntry.java
new file mode 100644
index 00000000..fcf93a8b
--- /dev/null
+++ b/src/jvm/clojure/runtime/IMapEntry.java
@@ -0,0 +1,17 @@
+/**
+ * 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.
+ */
+
+package org.clojure.runtime;
+
+public interface IMapEntry {
+Object key();
+
+Object val();
+}
diff --git a/src/jvm/clojure/runtime/IObj.java b/src/jvm/clojure/runtime/IObj.java
new file mode 100644
index 00000000..e4284aba
--- /dev/null
+++ b/src/jvm/clojure/runtime/IObj.java
@@ -0,0 +1,26 @@
+/**
+ * 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.
+ **/
+
+package org.clojure.runtime;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: rich
+ * Date: May 31, 2006
+ * Time: 3:41:10 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public interface IObj {
+Object put(ThreadLocalData tld, Comparable key, Object val) throws Exception;
+
+Object get(ThreadLocalData tld, Comparable key) throws Exception;
+
+boolean has(ThreadLocalData tld, Comparable key) throws Exception;
+}
diff --git a/src/jvm/clojure/runtime/IdentityArrayMap.java b/src/jvm/clojure/runtime/IdentityArrayMap.java
new file mode 100644
index 00000000..ed0c7106
--- /dev/null
+++ b/src/jvm/clojure/runtime/IdentityArrayMap.java
@@ -0,0 +1,27 @@
+/**
+ * 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.
+ **/
+
+package org.clojure.runtime;
+
+/**
+ * ArrayMap using identity (==) comparison instead of equals
+ */
+public class IdentityArrayMap extends ArrayMap{
+public IdentityArrayMap() {
+}
+
+public IdentityArrayMap(Object[] init) {
+ super(init);
+}
+
+boolean equalKey(Object k1, Object k2) {
+ return k1 == k2;
+}
+}
diff --git a/src/jvm/clojure/runtime/IdentityHashtableMap.java b/src/jvm/clojure/runtime/IdentityHashtableMap.java
new file mode 100644
index 00000000..71d93eeb
--- /dev/null
+++ b/src/jvm/clojure/runtime/IdentityHashtableMap.java
@@ -0,0 +1,38 @@
+/**
+ * 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.
+ **/
+
+package org.clojure.runtime;
+
+public class IdentityHashtableMap extends HashtableMap{
+
+public IdentityHashtableMap(int initialCapacity) {
+ super(initialCapacity);
+}
+
+public IdentityHashtableMap(Object[] init) {
+ super(init);
+}
+
+IdentityHashtableMap(int count, PersistentArray array) {
+ super(count, array);
+}
+
+IMap create(int capacity) {
+ return new IdentityHashtableMap(capacity);
+}
+
+IMap create(int count, PersistentArray array) {
+ return new IdentityHashtableMap(count, array);
+}
+
+IMap createArrayMap(Object[] init) {
+ return new IdentityArrayMap(init);
+}
+}
diff --git a/src/jvm/clojure/runtime/Indexer.java b/src/jvm/clojure/runtime/Indexer.java
new file mode 100644
index 00000000..5eebe489
--- /dev/null
+++ b/src/jvm/clojure/runtime/Indexer.java
@@ -0,0 +1,16 @@
+/**
+ * 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.
+ **/
+
+/* rich Apr 19, 2006 */
+
+package org.clojure.runtime;
+
+public class Indexer extends AFn{
+}
diff --git a/src/jvm/clojure/runtime/IntegerNum.java b/src/jvm/clojure/runtime/IntegerNum.java
new file mode 100644
index 00000000..6c801dd3
--- /dev/null
+++ b/src/jvm/clojure/runtime/IntegerNum.java
@@ -0,0 +1,17 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 28, 2006 10:11:55 AM */
+
+package org.clojure.runtime;
+
+public abstract class IntegerNum extends RationalNum {
+
+} \ No newline at end of file
diff --git a/src/jvm/clojure/runtime/Iter.java b/src/jvm/clojure/runtime/Iter.java
new file mode 100644
index 00000000..82e1db5a
--- /dev/null
+++ b/src/jvm/clojure/runtime/Iter.java
@@ -0,0 +1,38 @@
+/**
+ * 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.
+ **/
+
+/* rich Apr 3, 2006 10:54:14 AM */
+
+package org.clojure.runtime;
+
+
+/**
+ * Implements a sequential iteration protocol
+ * <pre>
+ * for(Iter i = getAnIter();i!=null;i = i.iterate())
+ * {
+ * //use i.get()
+ * }
+ * </pre>
+ */
+public interface Iter{
+/**
+ * Multiple calls to get() are allowed prior to calling iterate()
+ * @return the currently referenced item/element/value
+ */
+public Object get();
+
+/**
+ * This may destroy or otherwise invalidate the object it is called upon
+ * so always capture and use the return value (even though sometimes you may find it is the same object)
+ * @return The next iter to use, or null if at end of sequence
+ */
+public Iter iterate();
+}
diff --git a/src/jvm/clojure/runtime/IteratorIter.java b/src/jvm/clojure/runtime/IteratorIter.java
new file mode 100644
index 00000000..bfcec41a
--- /dev/null
+++ b/src/jvm/clojure/runtime/IteratorIter.java
@@ -0,0 +1,44 @@
+/**
+ * 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.
+ **/
+
+/* rich Apr 3, 2006 */
+
+package org.clojure.runtime;
+
+import java.util.Iterator;
+
+public class IteratorIter implements Iter{
+
+Iterator i;
+Object val;
+
+IteratorIter(Iterator i)
+ {
+ if(!i.hasNext())
+ throw new IllegalStateException("Iterator must have elements to construct Iter");
+ this.i = i;
+ val = i.next();
+ }
+
+public Object get()
+ {
+ return val;
+ }
+
+public Iter iterate()
+ {
+ if(i.hasNext())
+ {
+ val = i.next();
+ return this;
+ }
+ return null;
+ }
+}
diff --git a/src/jvm/clojure/runtime/Keyword.java b/src/jvm/clojure/runtime/Keyword.java
new file mode 100644
index 00000000..63ea4d7d
--- /dev/null
+++ b/src/jvm/clojure/runtime/Keyword.java
@@ -0,0 +1,89 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 29, 2006 10:39:05 AM */
+
+package org.clojure.runtime;
+
+
+public class Keyword extends Symbol implements IFn{
+
+
+/**
+ * Used by intern()
+ *
+ * @param name
+
+ */
+Keyword(String name)
+ {
+ super(name);
+ }
+
+
+public Object invoke(ThreadLocalData tld) throws Exception {
+ return AFn.throwArity();
+}
+
+/**
+ * Indexer implements IFn for attr access
+ * This single arg version is the getter
+ * @param tld
+ * @param obj - must be Obj
+ * @return the value of the attr or nil if not found
+ * @throws Exception
+ */
+public Object invoke(ThreadLocalData tld, Object obj) throws Exception
+ {
+ if (obj == null)
+ return null;
+ return ((IObj)obj).get(tld,this);
+ }
+
+/**
+ * Indexer implements IFn for attr access
+ * This two arg version is the setter
+ * @param tld
+ * @param obj - must be Obj
+ * @param val
+ * @return val
+ * @throws Exception
+ */
+public Object invoke(ThreadLocalData tld, Object obj, Object val) throws Exception
+ {
+ return ((IObj)obj).put(tld,this,val);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3) throws Exception
+ {
+ return AFn.throwArity();
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception
+ {
+ return AFn.throwArity();
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
+ throws Exception
+ {
+ return AFn.throwArity();
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Cons args)
+ throws Exception
+ {
+ return AFn.throwArity();
+ }
+
+public Object applyTo(ThreadLocalData tld, Cons arglist) throws Exception {
+ return AFn.applyToHelper(this, tld, arglist);
+}
+}
diff --git a/src/jvm/clojure/runtime/LineNumberingPushbackReader.java b/src/jvm/clojure/runtime/LineNumberingPushbackReader.java
new file mode 100644
index 00000000..a3a10e1a
--- /dev/null
+++ b/src/jvm/clojure/runtime/LineNumberingPushbackReader.java
@@ -0,0 +1,27 @@
+/**
+ * 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.
+ */
+
+package org.clojure.runtime;
+
+import java.io.PushbackReader;
+import java.io.Reader;
+import java.io.LineNumberReader;
+
+
+public class LineNumberingPushbackReader extends PushbackReader {
+
+public LineNumberingPushbackReader(Reader r){
+ super(new LineNumberReader(r));
+}
+
+public int getLineNumber(){
+ return ((LineNumberReader)in).getLineNumber();
+}
+}
diff --git a/src/jvm/clojure/runtime/ListMap.java b/src/jvm/clojure/runtime/ListMap.java
new file mode 100644
index 00000000..16c77e9c
--- /dev/null
+++ b/src/jvm/clojure/runtime/ListMap.java
@@ -0,0 +1,252 @@
+/**
+ * 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.
+ **/
+
+/* rich Jun 6, 2006 */
+
+package org.clojure.runtime;
+
+import java.util.Iterator;
+
+/**
+ * Immplementation of persistent map on a linked list
+
+ * Note that instances of this class are constant values
+ * i.e. add/remove etc return new values
+ *
+ * Lookups/changes are linear, so only appropriate for _very_small_ maps
+ * ArrayMap is generally faster, but this class avoids the double allocation,
+ * and so is better/faster as a bucket for hash tables
+ *
+ * null keys and values are ok, but you won't be able to distinguish a null value via get - use contains/find
+ */
+public class ListMap implements IMap, IMapEntry
+{
+
+static public ListMap EMPTY = new ListMap();
+
+static public ListMap create(Object key, Object val){
+ return new Tail(key, val);
+}
+
+public Object key(){
+ return null;
+}
+
+public Object val(){
+ return null;
+}
+
+ListMap rest(){
+ return this;
+ }
+
+public int count(){
+ return 0;
+}
+
+public boolean contains(Object key){
+ return false;
+}
+
+public IMapEntry find(Object key){
+ return null;
+}
+
+public IMap add(Object key){
+ return put(key, null);
+}
+
+public ListMap put(Object key, Object val){
+ return new Tail(key, val);
+}
+
+public ListMap remove(Object key){
+ return this;
+}
+
+public Object get(Object key){
+ return null;
+}
+
+public int capacity(){
+ return 0;
+}
+
+static class Iter implements Iterator{
+ ListMap e;
+
+ Iter(ListMap e)
+ {
+ this.e = e;
+ }
+
+ public boolean hasNext(){
+ return e != EMPTY;
+ }
+
+ public Object next(){
+ ListMap ret = e;
+ e = e.rest();
+ return ret;
+ }
+
+ public void remove(){
+ throw new UnsupportedOperationException();
+ }
+}
+
+public Iterator iterator(){
+ return new Iter(this);
+}
+
+static class Tail extends ListMap{
+ final Object _key;
+ final Object _val;
+
+ Tail(Object key,Object val){
+ this._key = key;
+ this._val = val;
+ }
+
+ ListMap rest(){
+ return EMPTY;
+ }
+
+ public int count(){
+ return 1;
+ }
+
+ public Object get(Object key){
+ if(equalKey(key,_key))
+ return _val;
+ return null;
+ }
+
+ public int capacity(){
+ return 1;
+ }
+
+ public Object key(){
+ return _key;
+ }
+
+ public Object val(){
+ return _val;
+ }
+
+ public boolean contains(Object key){
+ return equalKey(key,_key);
+ }
+
+ public IMapEntry find(Object key){
+ if(equalKey(key,_key))
+ return this;
+ return null;
+ }
+
+ public ListMap put(Object key, Object val){
+ if(equalKey(key,_key)) //replace
+ {
+ if(val == _val)
+ return this;
+ return new Tail(key,val);
+ }
+ return new Link(key,val,this);
+ }
+
+ public ListMap remove(Object key){
+ if(equalKey(key,_key))
+ return EMPTY;
+ return this;
+ }
+}
+
+static class Link extends ListMap{
+ final Object _key;
+ final Object _val;
+ final ListMap _rest;
+
+ Link(Object key,Object val,ListMap rest){
+ this._key = key;
+ this._val = val;
+ this._rest = rest;
+ }
+
+ public Object key(){
+ return _key;
+ }
+
+ public Object val(){
+ return _val;
+ }
+
+ ListMap rest(){
+ return _rest;
+ }
+
+ public int count(){
+ return 1 + _rest.count();
+ }
+
+ public boolean contains(Object key){
+ return find(key) != null;
+ }
+
+ public IMapEntry find(Object key){
+ if(equalKey(key,_key))
+ return this;
+ return _rest.find(key);
+ }
+
+ public ListMap put(Object key, Object val){
+ IMapEntry e = find(key);
+ if(e != null)
+ {
+ if(e.val() == val)
+ return this;
+ return create(_key,_val,remove(key));
+ }
+ return new Link(key,val,this);
+ }
+
+ public ListMap remove(Object key){
+ if(equalKey(key,_key))
+ return _rest;
+ ListMap r = _rest.remove(key);
+ if(r == _rest) //not there
+ return this;
+ return create(_key,_val,r);
+ }
+
+ public Object get(Object key){
+ IMapEntry e = find(key);
+ if(e != null)
+ return e.val();
+ return null;
+ }
+
+ public int capacity(){
+ return count();
+ }
+
+ ListMap create(Object k,Object v,ListMap r){
+ if(r == EMPTY)
+ return new Tail(k,v);
+ return new Link(k, v, r);
+ }
+
+}
+
+boolean equalKey(Object k1,Object k2){
+ if(k1 == null)
+ return k2 == null;
+ return k1.equals(k2);
+}
+}
diff --git a/src/jvm/clojure/runtime/Namespace.java b/src/jvm/clojure/runtime/Namespace.java
new file mode 100644
index 00000000..04a7d40e
--- /dev/null
+++ b/src/jvm/clojure/runtime/Namespace.java
@@ -0,0 +1,70 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 27, 2006 1:29:39 PM */
+
+package org.clojure.runtime;
+
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+
+public class Namespace{
+
+/**
+ * String->Namespace
+ */
+static final public HashMap table = new HashMap();
+
+/**
+ * Symbol->Var
+ */
+final public IdentityHashMap vars = new IdentityHashMap();
+final public String name;
+
+Namespace(String name)
+ {
+ this.name = name;
+ table.put(name, this);
+ }
+
+static public Namespace find(String name)
+ {
+ return (Namespace) table.get(name);
+ }
+
+static public Namespace findOrCreate(String name)
+ {
+ synchronized(table)
+ {
+ Namespace ns = find(name);
+ if(ns == null)
+ ns = new Namespace(name);
+ return ns;
+ }
+ }
+
+static public Var intern(String ns,String name)
+ {
+ return findOrCreate(ns).intern(Symbol.intern(name));
+ }
+
+
+public Var intern(Symbol sym)
+ {
+ synchronized(vars)
+ {
+ Var var = (Var) vars.get(sym);
+ if(var == null)
+ vars.put(sym, var = new Var(sym, this));
+ return var;
+ }
+ }
+
+}
diff --git a/src/jvm/clojure/runtime/Num.java b/src/jvm/clojure/runtime/Num.java
new file mode 100644
index 00000000..182955e2
--- /dev/null
+++ b/src/jvm/clojure/runtime/Num.java
@@ -0,0 +1,220 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 28, 2006 10:07:33 AM */
+
+package org.clojure.runtime;
+
+import java.math.BigInteger;
+
+public abstract class Num extends Number implements Comparable{
+
+public final static Num ZERO = from(0);
+public final static Num ONE = from(1);
+
+static public Num from(int val)
+ {
+ //todo - cache a bunch of small fixnums
+ return new FixNum(val);
+ }
+
+static public Num from(double val)
+ {
+ return new DoubleNum(val);
+ }
+
+static public Num from(long val)
+ {
+ if(val <= Integer.MAX_VALUE && val >= Integer.MIN_VALUE)
+ return from((int) val);
+ else
+ return new BigNum(val);
+ }
+
+static public Num from(BigInteger val)
+ {
+ if(val.bitLength() < 32)
+ return from(val.intValue());
+ else
+ return new BigNum(val);
+ }
+
+static public Num from(Object x)
+ {
+ if(x instanceof Num)
+ return (Num) x;
+ else
+ {
+ Class c = x.getClass();
+ if(c == Integer.class)
+ return Num.from(((Integer) x).intValue());
+ else if(c == Double.class || c == Float.class)
+ return Num.from(((Number) x).doubleValue());
+ else if(c == Long.class)
+ return Num.from(((Long) x).longValue());
+ else if(c == BigInteger.class)
+ return Num.from((BigInteger) x);
+ else
+ return Num.from(((Number) x).intValue());
+ }
+ }
+
+static public Num add(Object x, Object y)
+ {
+ //if(x instanceof Num && y instanceof Num)
+ //return ((Num)x).add((Num) y);
+ return Num.from(x).add(Num.from(y));
+ }
+
+abstract public Num add(Num rhs);
+
+abstract public Num addTo(int x);
+
+abstract public Num addTo(BigInteger x);
+
+abstract public Num addTo(RatioNum x);
+
+static public Num subtract(Object x, Object y)
+ {
+ return Num.from(y).subtractFrom(Num.from(x));
+ }
+
+//this double-dispatches to addTo(-self)
+abstract public Num subtractFrom(Num rhs);
+
+static public Num multiply(Object x, Object y)
+ {
+ return Num.from(x).multiplyBy(Num.from(y));
+ }
+
+abstract public Num multiplyBy(Num rhs);
+
+abstract public Num multiply(int x);
+
+abstract public Num multiply(BigInteger x);
+
+abstract public Num multiply(RatioNum x);
+
+static public Num divide(Object x, Object y)
+ {
+ return Num.from(x).divideBy(Num.from(y));
+ }
+
+abstract public Num divideBy(Num rhs);
+
+abstract public Num divide(int x);
+
+abstract public Num divide(BigInteger x);
+
+abstract public Num divide(RatioNum x);
+
+static public Object truncate(ThreadLocalData tld, Object num, Object div)
+ {
+ return Num.from(div).truncateDivide(tld, Num.from(num));
+ }
+
+abstract public Object truncateDivide(ThreadLocalData tld, Num rhs);
+
+abstract public Object truncateBy(ThreadLocalData tld, int x);
+
+abstract public Object truncateBy(ThreadLocalData tld, BigInteger x);
+
+abstract public Object truncateBy(ThreadLocalData tld, RatioNum x);
+
+static public Object truncateBigints(ThreadLocalData tld, BigInteger n, BigInteger d)
+ {
+ BigInteger[] result = n.divideAndRemainder(d);
+ return RT.setValues(tld, Num.from(result[0]), Num.from(result[1]));
+ }
+
+static public Num divide(BigInteger n, BigInteger d)
+ {
+ BigInteger gcd = n.gcd(d);
+ if(gcd.equals(BigInteger.ZERO))
+ return Num.ZERO;
+ n = n.divide(gcd);
+ d = d.divide(gcd);
+ if(d.equals(BigInteger.ONE))
+ return Num.from(n);
+ return new RatioNum((IntegerNum) Num.from(d.signum() < 0 ? n.negate() : n),
+ (IntegerNum) Num.from(d.signum() < 0 ? d.negate() : d));
+ }
+
+static public boolean equiv(Object x, Object y)
+ {
+ return Num.from(x).equiv(Num.from(y));
+ }
+
+abstract public boolean equiv(Num rhs);
+
+abstract public boolean equivTo(int x);
+
+abstract public boolean equivTo(BigInteger x);
+
+abstract public boolean equivTo(RatioNum x);
+
+static public boolean lt(Object x, Object y)
+ {
+ return Num.from(x).lt(Num.from(y));
+ }
+
+static public boolean lte(Object x, Object y)
+ {
+ Num lx = Num.from(x);
+ Num ly = Num.from(y);
+ return lx.lt(ly) || lx.equiv(ly);
+ }
+
+static public boolean gt(Object x, Object y)
+ {
+ return Num.from(y).lt(Num.from(x));
+ }
+
+static public boolean gte(Object x, Object y)
+ {
+ Num lx = Num.from(x);
+ Num ly = Num.from(y);
+ return ly.lt(lx) || lx.equiv(ly);
+ }
+
+abstract public boolean lt(Num rhs);
+
+abstract public boolean gt(int x);
+
+abstract public boolean gt(BigInteger x);
+
+abstract public boolean gt(RatioNum x);
+
+static public Num negate(Object x)
+ {
+ return Num.from(x).negate();
+ }
+
+abstract public Num negate();
+
+abstract public boolean minusp();
+
+abstract public boolean plusp();
+
+abstract public Num oneMinus();
+
+abstract public Num onePlus();
+
+public int compareTo(Object object)
+ {
+ Num other = Num.from(object);
+ if(this.equiv(other))
+ return 0;
+ else if(this.lt(other))
+ return -1;
+ else
+ return 1;
+ }
+}
diff --git a/src/jvm/clojure/runtime/Obj.java b/src/jvm/clojure/runtime/Obj.java
new file mode 100644
index 00000000..c8aa532d
--- /dev/null
+++ b/src/jvm/clojure/runtime/Obj.java
@@ -0,0 +1,44 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 25, 2006 3:44:58 PM */
+
+package org.clojure.runtime;
+
+import java.util.IdentityHashMap;
+
+public class Obj implements IObj {
+
+IdentityHashMap attrs;
+public static final int INITIAL_SIZE = 7;
+
+public Object put(ThreadLocalData tld, Comparable key, Object val)
+ {
+ if(attrs == null)
+ attrs = new IdentityHashMap(INITIAL_SIZE);
+ attrs.put(key, val);
+ return val;
+ }
+
+public Object get(ThreadLocalData tld, Comparable key)
+ {
+ if(attrs == null)
+ return null;
+ return attrs.get(key);
+ }
+
+public boolean has(ThreadLocalData tld, Comparable key){
+ if(attrs == null)
+ return false;
+ return attrs.containsKey(key);
+ }
+
+
+}
diff --git a/src/jvm/clojure/runtime/PersistentArray.java b/src/jvm/clojure/runtime/PersistentArray.java
new file mode 100644
index 00000000..b581ecf6
--- /dev/null
+++ b/src/jvm/clojure/runtime/PersistentArray.java
@@ -0,0 +1,324 @@
+/**
+ * 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.
+ **/
+
+/* rich Jun 2, 2006 */
+
+package org.clojure.runtime;
+
+//import java.util.concurrent.atomic.AtomicInteger;
+//import java.util.concurrent.atomic.AtomicReferenceArray;
+import java.util.BitSet;
+import java.util.Iterator;
+import java.util.Vector;
+import java.util.Random;
+
+/**
+ * Note that instances of this class are constant values
+ * i.e. set() returns a new array, old one is intact
+ *
+ * Multiple revisions (thread-safely) share the same master array
+ *
+ * Constant time most-recent-revision lookups
+ * Amortized constant-time sequential revisions (when loadFactor > 1)
+ * where a sequential revision is a revision of the most recent revision
+ *
+ * Non-sequential revisions are O(length), but with a small constant multiplier of 1/32
+ * Worst-case O(r) lookups for oldest revs where r is number of revisions
+ * at index i since last (automatic or manual) isolate. If set()s are roughly evenly
+ * distributed, r should be approximately == loadFactor, i.e. constant
+ * In pathological case (all mods to same index), r == (loadFactor * length)
+ *
+ * (loadFactor * length) old values are retained, even if the array revisions aren't
+ * Default loadFactor is 2.1
+ * When the load exceeds (loadFactor * length) the next revision is automatically isolated
+ * You can determine how many values are in the shared master by calling load()
+ * and can trim them by calling isolate() or resize(), which yield a new array with no
+ * sharing and no old values
+ *
+ * See Cohen for basic idea
+ * I added hybrid most-recent-sequential-range + shared-bitset idea, multi-thread-safety
+ */
+
+public class PersistentArray implements Iterable{
+
+public Iterator iterator(){
+ return new ValIter(this);
+}
+
+static class Master{
+ final Entry[] array;
+ final Object defaultVal;
+ int rev;
+ int load;
+ final int maxLoad;
+ final float loadFactor;
+
+ Master(int size,Object defaultVal, float loadFactor){
+ this.array = new Entry[size];//new AtomicReferenceArray(size);
+ this.defaultVal = defaultVal;
+ this.rev = 0;//new AtomicInteger(0);
+ this.load = 0;//new AtomicInteger(0);
+ this.maxLoad = (int) (size * loadFactor);
+ this.loadFactor = loadFactor;
+ }
+}
+
+static class Entry{
+ final int rev;
+ final Object val;
+
+ Entry(int rev,Object val){
+ this.rev = rev;
+ this.val = val;
+ }
+
+ Entry rest(){
+ return null;
+ }
+
+ static Entry create(int rev,Object val,Entry rest){
+ if(rest == null)
+ return new Entry(rev,val);
+ return new EntryLink(rev, val, rest);
+ }
+}
+
+static class EntryLink extends Entry{
+ final Entry _rest;
+
+ EntryLink(int rev,Object val,Entry rest){
+ super(rev,val);
+ this._rest = rest;
+ }
+
+ Entry rest(){
+ return _rest;
+ }
+}
+
+static class ValIter implements Iterator{
+ PersistentArray p;
+ int i;
+
+ ValIter(PersistentArray p){
+ this.p = p;
+ this.i = 0;
+ }
+
+ public boolean hasNext(){
+ return i < p.length();
+ }
+
+ public Object next(){
+ return p.get(i++);
+ }
+
+ public void remove(){
+ throw new UnsupportedOperationException();
+ }
+}
+
+final Master master;
+final int rev;
+final int baseline;
+final BitSet history;
+
+public PersistentArray(int size){
+ this(size, null);
+}
+
+public PersistentArray(int size, Object defaultVal){
+ this(size,defaultVal,2.1f);
+}
+
+public PersistentArray(int size, Object defaultVal, float loadFactor){
+ this.master = new Master(size, defaultVal,loadFactor);
+ this.rev = 0;
+ this.baseline = 0;
+ this.history = null;
+}
+
+PersistentArray(Master master,int rev,int baseline, BitSet history){
+ this.master = master;
+ this.rev = rev;
+ this.baseline = baseline;
+ this.history = history;
+}
+
+
+
+final public int length(){
+ return master.array.length;
+ //return master.array.length();
+}
+
+final public Object get(int i) {
+ Entry e = getEntry(i);
+ if(e != null)
+ return e.val;
+ return master.defaultVal;
+}
+
+final public boolean has(int i){
+ return getEntry(i) != null;
+}
+
+final public PersistentArray resize(int newLength) {
+ PersistentArray ret = new PersistentArray(newLength, master.defaultVal, master.loadFactor);
+ int load = 0;
+ for(int i=0;i< Math.min(length(),newLength);i++)
+ {
+ Entry e = getEntry(i);
+ if(e != null)
+ {
+ ret.master.array[i] = new Entry(0,e.val);
+ //ret.master.array.set(i,Entry.create(0,e.val, null));
+ ++load;
+ }
+ }
+
+ ret.master.load = load;
+ //ret.master.load.set(load);
+
+ return ret;
+}
+
+/**
+ *
+ * @return number of values (of all revisions) stored in shared array
+ */
+final public int load(){
+ return master.load;
+ //return master.load.get();
+}
+
+final public PersistentArray isolate() {
+ return resize(length());
+}
+
+final Entry getEntry(int i){
+ for(Entry e = master.array[i];e != null;e = e.rest())
+ // for(Entry e = (Entry) master.array.get(i);e != null;e = e.rest())
+ {
+ if(e.rev <= rev)
+ {
+ if(e.rev >= baseline
+ || (history != null && history.get(e.rev)))
+ return e;
+ }
+ }
+ return null;
+}
+
+final public PersistentArray set(int i,Object val) {
+ if(master.load >= master.maxLoad)
+ //if(master.load.get() >= master.maxLoad)
+ return isolate().set(i,val);
+ synchronized(master){
+ PersistentArray ret = getSetArray();
+ ret.doSet(i, val);
+ return ret;
+ }
+}
+
+final void doSet(int i, Object val){
+// Entry oldEntry, newEntry;
+// do
+// {
+// oldEntry = (Entry) master.array.get(i);
+// newEntry = Entry.create(rev, val, oldEntry);
+// } while(!master.array.compareAndSet(i, oldEntry, newEntry));
+
+ //must now be called inside lock of master
+ master.array[i] = Entry.create(rev, val, master.array[i]);
+ //master.load.incrementAndGet();
+ ++master.load;
+}
+
+final PersistentArray getSetArray(){
+ //must now be called inside lock of master
+ //is this a sequential update?
+ if(master.rev == rev)
+ //if(master.rev.compareAndSet(rev, rev + 1))
+ {
+ return new PersistentArray(master, ++master.rev, baseline, history);
+ }
+ else //gap
+ {
+ //nextRev = master.rev.incrementAndGet();
+ int nextRev = ++master.rev;
+ BitSet nextHistory;
+ if(history != null)
+ nextHistory = (BitSet) history.clone();
+ else
+ nextHistory = new BitSet(rev+1);
+ nextHistory.set(baseline,rev+1);
+ return new PersistentArray(master, nextRev, nextRev, nextHistory);
+ }
+
+}
+
+
+static public void main(String[] args){
+ if(args.length != 3)
+ {
+ System.err.println("Usage: PersistentArray size writes reads");
+ return;
+ }
+ int size = Integer.parseInt(args[0]);
+ int writes = Integer.parseInt(args[1]);
+ int reads = Integer.parseInt(args[2]);
+ Vector v = new Vector(size);
+ v.setSize(size);
+ PersistentArray p = new PersistentArray(size);
+
+ for(int i = 0; i < size; i++)
+ {
+ v.set(i, 0);
+ p = p.set(i, 0);
+ }
+
+ Random rand;
+
+ rand = new Random(42);
+ long tv = 0;
+ System.out.println("Vector");
+ long startTime = System.nanoTime();
+ for(int i = 0; i < writes; i++)
+ {
+ v.set(rand.nextInt(size), i);
+ }
+ for(int i = 0; i < reads; i++)
+ {
+ tv += (Integer)v.get(rand.nextInt(size));
+ }
+ long estimatedTime = System.nanoTime() - startTime;
+ System.out.println("time: " + estimatedTime/1000000);
+ System.out.println("PersistentArray");
+ rand = new Random(42);
+ startTime = System.nanoTime();
+ long tp = 0;
+ for(int i = 0; i < writes; i++)
+ {
+ p = p.set(rand.nextInt(size), i);
+ //dummy set to force perverse branching
+ //p.set(i%size, i);
+ }
+ for(int i = 0; i < reads; i++)
+ {
+ tp += (Integer)p.get(rand.nextInt(size));
+ }
+ estimatedTime = System.nanoTime() - startTime;
+ System.out.println("time: " + estimatedTime/1000000);
+ System.out.println("Done: " + tv + ", " + tp);
+
+
+}
+}
diff --git a/src/jvm/clojure/runtime/PersistentHashMap.java b/src/jvm/clojure/runtime/PersistentHashMap.java
new file mode 100644
index 00000000..f4ac6330
--- /dev/null
+++ b/src/jvm/clojure/runtime/PersistentHashMap.java
@@ -0,0 +1,101 @@
+/**
+ * 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.
+ **/
+
+package org.clojure.runtime;
+
+import java.util.Iterator;
+
+public class PersistentHashMap implements IMap, Iterable{
+
+IMap impl;
+static final int CAPACITY_THRESHOLD = 42;
+
+public PersistentHashMap(Object[] init){
+ if(init.length/2 < CAPACITY_THRESHOLD)
+ impl = createArrayMap(init);
+ impl = createHashtableMap(init);
+}
+
+public PersistentHashMap(int initialCapacity){
+ if(initialCapacity < CAPACITY_THRESHOLD)
+ impl = createArrayMap();
+ else
+ impl = createHashtableMap(initialCapacity);
+}
+
+PersistentHashMap(IMap impl){
+ this.impl = impl;
+}
+
+public int count() {
+ return impl.count();
+}
+
+public boolean contains(Object key) {
+ return impl.contains(key);
+}
+
+public IMapEntry find(Object key) {
+ return impl.find(key);
+}
+
+public IMap add(Object key) {
+ return put(key, null);
+}
+
+public IMap put(Object key, Object val) {
+ IMap newImpl = impl.put(key,val);
+ if(newImpl.capacity() == CAPACITY_THRESHOLD)
+ {
+ newImpl = createHashtableMap(((ArrayMap)newImpl).array);
+ }
+ return create(newImpl);
+}
+
+public IMap remove(Object key) {
+ IMap newImpl = impl.remove(key);
+ if(newImpl != impl)
+ return create(newImpl);
+ return this;
+}
+
+public Object get(Object key) {
+ return impl.get(key);
+}
+
+public int capacity() {
+ return impl.capacity();
+}
+
+public Iterator iterator() {
+ return ((Iterable)impl).iterator();
+}
+
+public IMap create(IMap impl) {
+ return new PersistentHashMap(impl);
+}
+
+public ArrayMap createArrayMap(Object[] init) {
+ return new ArrayMap(init);
+}
+
+private IMap createArrayMap() {
+ return new ArrayMap();
+}
+
+private IMap createHashtableMap(Object[] init) {
+ return new HashtableMap(init);
+}
+
+private IMap createHashtableMap(int initialCapacity) {
+ return new HashtableMap(initialCapacity);
+}
+
+}
diff --git a/src/jvm/clojure/runtime/RBTree.java b/src/jvm/clojure/runtime/RBTree.java
new file mode 100644
index 00000000..6aec53e2
--- /dev/null
+++ b/src/jvm/clojure/runtime/RBTree.java
@@ -0,0 +1,759 @@
+/**
+ * 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.
+ **/
+
+/* rich May 20, 2006 */
+
+package org.clojure.runtime;
+
+import java.util.*;
+
+/**
+ * Persistent Red Black Tree
+ * Note that instances of this class are constant values
+ * i.e. add/remove etc return new values
+ * <p/>
+ * See Okasaki, Kahrs, Larsen et al
+ */
+
+public class RBTree implements Iterable, IMap {
+
+public final Comparator comp;
+public final Node tree;
+public final int _count;
+
+public RBTree(){
+ this(null);
+}
+
+public RBTree(Comparator comp){
+ this.comp = comp;
+ tree = null;
+ _count = 0;
+}
+
+public boolean contains(Object key){
+ return find(key) != null;
+}
+
+public RBTree add(Object key){
+ return put(key, null);
+}
+
+public RBTree put(Object key, Object val){
+ Box found = new Box(null);
+ Node t = add(tree, key, val, found);
+ if(t == null) //null == already contains key
+ {
+ Node foundNode = (Node) found.val;
+ if(foundNode.val() == val) //note only get same collection on identity of val, not equals()
+ return this;
+ return new RBTree(comp, replace(tree, key, val), _count);
+ }
+ return new RBTree(comp, t.blacken(), _count + 1);
+}
+
+
+public RBTree remove(Object key){
+ Box found = new Box(null);
+ Node t = remove(tree, key, found);
+ if(t == null)
+ {
+ if(found.val == null)//null == doesn't contain key
+ return this;
+ //empty
+ return new RBTree(comp);
+ }
+ return new RBTree(comp, t.blacken(), _count - 1);
+}
+
+
+public NodeIterator iterator(){
+ return new NodeIterator(tree, true);
+}
+
+public NodeIterator reverseIterator(){
+ return new NodeIterator(tree, false);
+}
+
+public Iterator keys(){
+ return keys(iterator());
+}
+
+public Iterator vals(){
+ return vals(iterator());
+}
+
+public Iterator keys(NodeIterator it){
+ return new KeyIterator(it);
+}
+
+public Iterator vals(NodeIterator it){
+ return new ValIterator(it);
+}
+
+public Object minKey(){
+ Node t = min();
+ return t!=null?t.key:null;
+}
+
+public Node min(){
+ Node t = tree;
+ if(t != null)
+ {
+ while(t.left() != null)
+ t = t.left();
+ }
+ return t;
+}
+
+public Object maxKey(){
+ Node t = max();
+ return t!=null?t.key:null;
+}
+
+public Node max(){
+ Node t = tree;
+ if(t != null)
+ {
+ while(t.right() != null)
+ t = t.right();
+ }
+ return t;
+}
+
+public int depth(){
+ return depth(tree);
+}
+
+int depth(Node t){
+ if(t == null)
+ return 0;
+ return 1 + Math.max(depth(t.left()), depth(t.right()));
+}
+
+public Object get(Object key){
+ Node n = find(key);
+ return (n != null) ? n.val() : null;
+}
+
+public int capacity() {
+ return _count;
+}
+
+public int count() {
+ return _count;
+}
+
+public Node find(Object key){
+ Node t = tree;
+ while(t != null)
+ {
+ int c = compare(key, t.key);
+ if(c == 0)
+ return t;
+ else if(c < 0)
+ t = t.left();
+ else
+ t = t.right();
+ }
+ return t;
+}
+
+int compare(Object k1, Object k2){
+ if(comp != null)
+ return comp.compare(k1, k2);
+ return ((Comparable) k1).compareTo(k2);
+}
+
+Node add(Node t, Object key, Object val, Box found){
+ if(t == null)
+ {
+ if(val == null)
+ return new Red(key);
+ return new RedVal(key, val);
+ }
+ int c = compare(key, t.key);
+ if(c == 0)
+ {
+ found.val = t;
+ return null;
+ }
+ Node ins = c < 0 ? add(t.left(), key, val, found) : add(t.right(), key, val, found);
+ if(ins == null) //found below
+ return null;
+ if(c < 0)
+ return t.addLeft(ins);
+ return t.addRight(ins);
+}
+
+Node remove(Node t, Object key, Box found){
+ if(t == null)
+ return null; //not found indicator
+ int c = compare(key, t.key);
+ if(c == 0)
+ {
+ found.val = t;
+ return append(t.left(), t.right());
+ }
+ Node del = c < 0 ? remove(t.left(), key, found) : remove(t.right(), key, found);
+ if(del == null && found.val == null) //not found below
+ return null;
+ if(c < 0)
+ {
+ if(t.left() instanceof Black)
+ return balanceLeftDel(t.key, t.val(), del, t.right());
+ else
+ return red(t.key, t.val(), del, t.right());
+ }
+ if(t.right() instanceof Black)
+ return balanceRightDel(t.key, t.val(), t.left(), del);
+ return red(t.key, t.val(), t.left(), del);
+// return t.removeLeft(del);
+// return t.removeRight(del);
+}
+
+static Node append(Node left, Node right){
+ if(left == null)
+ return right;
+ else if(right == null)
+ return left;
+ else if(left instanceof Red)
+ {
+ if(right instanceof Red)
+ {
+ Node app = append(left.right(), right.left());
+ if(app instanceof Red)
+ return red(app.key, app.val(),
+ red(left.key, left.val(), left.left(), app.left()),
+ red(right.key, right.val(), app.right(), right.right()));
+ else
+ return red(left.key, left.val(), left.left(), red(right.key, right.val(), app, right.right()));
+ }
+ else
+ return red(left.key, left.val(), left.left(), append(left.right(), right));
+ }
+ else if(right instanceof Red)
+ return red(right.key, right.val(), append(left, right.left()), right.right());
+ else //black/black
+ {
+ Node app = append(left.right(), right.left());
+ if(app instanceof Red)
+ return red(app.key, app.val(),
+ black(left.key, left.val(), left.left(), app.left()),
+ black(right.key, right.val(), app.right(), right.right()));
+ else
+ return balanceLeftDel(left.key, left.val(), left.left(), black(right.key, right.val(), app, right.right()));
+ }
+}
+
+static Node balanceLeftDel(Object key, Object val, Node del, Node right){
+ if(del instanceof Red)
+ return red(key, val, del.blacken(), right);
+ else if(right instanceof Black)
+ return rightBalance(key, val, del, right.redden());
+ else if(right instanceof Red && right.left() instanceof Black)
+ return red(right.left().key, right.left().val(),
+ black(key, val, del, right.left().left()),
+ rightBalance(right.key, right.val(), right.left().right(), right.right().redden()));
+ else
+ throw new UnsupportedOperationException("Invariant violation");
+}
+
+static Node balanceRightDel(Object key, Object val, Node left, Node del){
+ if(del instanceof Red)
+ return red(key, val, left, del.blacken());
+ else if(left instanceof Black)
+ return leftBalance(key, val, left.redden(), del);
+ else if(left instanceof Red && left.right() instanceof Black)
+ return red(left.right().key, left.right().val(),
+ leftBalance(left.key, left.val(), left.left().redden(), left.right().left()),
+ black(key, val, left.right().right(), del));
+ else
+ throw new UnsupportedOperationException("Invariant violation");
+}
+
+static Node leftBalance(Object key, Object val, Node ins, Node right){
+ if(ins instanceof Red && ins.left() instanceof Red)
+ return red(ins.key, ins.val(), ins.left().blacken(), black(key, val, ins.right(), right));
+ else if(ins instanceof Red && ins.right() instanceof Red)
+ return red(ins.right().key, ins.right().val(),
+ black(ins.key, ins.val(), ins.left(), ins.right().left()),
+ black(key, val, ins.right().right(), right));
+ else
+ return black(key, val, ins, right);
+}
+
+
+static Node rightBalance(Object key, Object val, Node left, Node ins){
+ if(ins instanceof Red && ins.right() instanceof Red)
+ return red(ins.key, ins.val(), black(key, val, left, ins.left()), ins.right().blacken());
+ else if(ins instanceof Red && ins.left() instanceof Red)
+ return red(ins.left().key, ins.left().val(),
+ black(key, val, left, ins.left().left()),
+ black(ins.key, ins.val(), ins.left().right(), ins.right()));
+ else
+ return black(key, val, left, ins);
+}
+
+Node replace(Node t, Object key, Object val){
+ int c = compare(key, t.key);
+ return t.replace(t.key,
+ c == 0 ? val : t.val(),
+ c < 0 ? replace(t.left(), key, val) : t.left(),
+ c > 0 ? replace(t.right(), key, val) : t.right());
+}
+
+RBTree(Comparator comp, Node tree, int count){
+ this.comp = comp;
+ this.tree = tree;
+ this._count = count;
+}
+
+static Red red(Object key, Object val, Node left, Node right){
+ if(left == null && right == null)
+ {
+ if(val == null)
+ return new Red(key);
+ return new RedVal(key, val);
+ }
+ if(val == null)
+ return new RedBranch(key, left, right);
+ return new RedBranchVal(key, val, left, right);
+}
+
+static Black black(Object key, Object val, Node left, Node right){
+ if(left == null && right == null)
+ {
+ if(val == null)
+ return new Black(key);
+ return new BlackVal(key, val);
+ }
+ if(val == null)
+ return new BlackBranch(key, left, right);
+ return new BlackBranchVal(key, val, left, right);
+}
+
+
+static abstract class Node implements IMapEntry {
+ final Object key;
+
+ Node(Object key){
+ this.key = key;
+ }
+
+ public Object key(){
+ return key;
+ }
+
+ public Object val(){
+ return null;
+ }
+
+ Node left(){
+ return null;
+ }
+
+ Node right(){
+ return null;
+ }
+
+ abstract Node addLeft(Node ins);
+
+ abstract Node addRight(Node ins);
+
+ abstract Node removeLeft(Node del);
+
+ abstract Node removeRight(Node del);
+
+ abstract Node blacken();
+
+ abstract Node redden();
+
+ Node balanceLeft(Node parent){
+ return black(parent.key, parent.val(), this, parent.right());
+ }
+
+ Node balanceRight(Node parent){
+ return black(parent.key, parent.val(), parent.left(), this);
+ }
+
+ abstract Node replace(Object key, Object val, Node left, Node right);
+}
+
+static class Black extends Node{
+ public Black(Object key){
+ super(key);
+ }
+
+ Node addLeft(Node ins){
+ return ins.balanceLeft(this);
+ }
+
+ Node addRight(Node ins){
+ return ins.balanceRight(this);
+ }
+
+ Node removeLeft(Node del){
+ return balanceLeftDel(key, val(), del, right());
+ }
+
+ Node removeRight(Node del){
+ return balanceRightDel(key, val(), left(), del);
+ }
+
+ Node blacken(){
+ return this;
+ }
+
+ Node redden(){
+ return new Red(key);
+ }
+
+ Node replace(Object key, Object val, Node left, Node right){
+ return black(key, val, left, right);
+ }
+}
+
+static class BlackVal extends Black{
+ final Object val;
+
+ public BlackVal(Object key, Object val){
+ super(key);
+ this.val = val;
+ }
+
+ public Object val(){
+ return val;
+ }
+
+ Node redden(){
+ return new RedVal(key, val);
+ }
+
+}
+
+static class BlackBranch extends Black{
+ final Node left;
+ final Node right;
+
+ public BlackBranch(Object key, Node left, Node right){
+ super(key);
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node left(){
+ return left;
+ }
+
+ public Node right(){
+ return right;
+ }
+
+ Node redden(){
+ return new RedBranch(key, left, right);
+ }
+
+}
+
+static class BlackBranchVal extends BlackBranch{
+ final Object val;
+
+ public BlackBranchVal(Object key, Object val, Node left, Node right){
+ super(key, left, right);
+ this.val = val;
+ }
+
+ public Object val(){
+ return val;
+ }
+
+ Node redden(){
+ return new RedBranchVal(key, val, left, right);
+ }
+
+}
+
+static class Red extends Node{
+ public Red(Object key){
+ super(key);
+ }
+
+ Node addLeft(Node ins){
+ return red(key, val(), ins, right());
+ }
+
+ Node addRight(Node ins){
+ return red(key, val(), left(), ins);
+ }
+
+ Node removeLeft(Node del){
+ return red(key, val(), del, right());
+ }
+
+ Node removeRight(Node del){
+ return red(key, val(), left(), del);
+ }
+
+ Node blacken(){
+ return new Black(key);
+ }
+
+ Node redden(){
+ throw new UnsupportedOperationException("Invariant violation");
+ }
+
+ Node replace(Object key, Object val, Node left, Node right){
+ return red(key, val, left, right);
+ }
+}
+
+static class RedVal extends Red{
+ final Object val;
+
+ public RedVal(Object key, Object val){
+ super(key);
+ this.val = val;
+ }
+
+ public Object val(){
+ return val;
+ }
+
+ Node blacken(){
+ return new BlackVal(key, val);
+ }
+}
+
+static class RedBranch extends Red{
+ final Node left;
+ final Node right;
+
+ public RedBranch(Object key, Node left, Node right){
+ super(key);
+ this.left = left;
+ this.right = right;
+ }
+
+ public Node left(){
+ return left;
+ }
+
+ public Node right(){
+ return right;
+ }
+
+ Node balanceLeft(Node parent){
+ if(left instanceof Red)
+ return red(key, val(), left.blacken(), black(parent.key, parent.val(), right, parent.right()));
+ else if(right instanceof Red)
+ return red(right.key, right.val(), black(key, val(), left, right.left()),
+ black(parent.key, parent.val(), right.right(), parent.right()));
+ else
+ return super.balanceLeft(parent);
+
+ }
+
+ Node balanceRight(Node parent){
+ if(right instanceof Red)
+ return red(key, val(), black(parent.key, parent.val(), parent.left(), left), right.blacken());
+ else if(left instanceof Red)
+ return red(left.key, left.val(), black(parent.key, parent.val(), parent.left(), left.left()),
+ black(key, val(), left.right(), right));
+ else
+ return super.balanceRight(parent);
+ }
+
+ Node blacken(){
+ return new BlackBranch(key, left, right);
+ }
+}
+
+static class RedBranchVal extends RedBranch{
+ final Object val;
+
+ public RedBranchVal(Object key, Object val, Node left, Node right){
+ super(key, left, right);
+ this.val = val;
+ }
+
+ public Object val(){
+ return val;
+ }
+
+ Node blacken(){
+ return new BlackBranchVal(key, val, left, right);
+ }
+}
+
+static public class NodeIterator implements Iterator{
+ Stack stack = new Stack();
+ boolean asc;
+
+ NodeIterator(Node t, boolean asc){
+ this.asc = asc;
+ push(t);
+ }
+
+ void push(Node t){
+ while(t != null)
+ {
+ stack.push(t);
+ t = asc ? t.left() : t.right();
+ }
+ }
+
+ public boolean hasNext(){
+ return !stack.isEmpty();
+ }
+
+ public Object next(){
+ Node t = (Node) stack.pop();
+ push(asc ? t.right() : t.left());
+ return t;
+ }
+
+ public void remove(){
+ throw new UnsupportedOperationException();
+ }
+}
+
+static class KeyIterator implements Iterator{
+ NodeIterator it;
+
+ KeyIterator(NodeIterator it){
+ this.it = it;
+ }
+
+ public boolean hasNext(){
+ return it.hasNext();
+ }
+
+ public Object next(){
+ return ((Node) it.next()).key;
+ }
+
+ public void remove(){
+ throw new UnsupportedOperationException();
+ }
+}
+
+static class ValIterator implements Iterator{
+ NodeIterator it;
+
+ ValIterator(NodeIterator it){
+ this.it = it;
+ }
+
+ public boolean hasNext(){
+ return it.hasNext();
+ }
+
+ public Object next(){
+ return ((Node) it.next()).val();
+ }
+
+ public void remove(){
+ throw new UnsupportedOperationException();
+ }
+}
+
+static public void main(String args[]){
+ if(args.length != 1)
+ System.err.println("Usage: RBTree n");
+ int n = Integer.parseInt(args[0]);
+ Integer[] ints = new Integer[n];
+ for(int i = 0; i < ints.length; i++)
+ {
+ ints[i] = new Integer(i);
+ }
+ Collections.shuffle(Arrays.asList(ints));
+ //force the ListMap class loading now
+ ListMap.EMPTY.add(1).add(2).add(3);
+ System.out.println("Building set");
+ IMap set = new PersistentHashMap(1001);
+ //IMap set = new HashtableMap(1001);
+ //IMap set = new ListMap();
+ //IMap set = new ArrayMap();
+ //IMap set = new RBTree();
+// for(int i = 0; i < ints.length; i++)
+// {
+// Integer anInt = ints[i];
+// set = set.add(anInt);
+// }
+ long startTime = System.nanoTime();
+ for(int i = 0; i < ints.length; i++)
+ {
+ Integer anInt = ints[i];
+ set = set.put(anInt, anInt);
+ }
+ //System.out.println("_count = " + set.count());
+
+
+// System.out.println("_count = " + set._count + ", min: " + set.minKey() + ", max: " + set.maxKey()
+// + ", depth: " + set.depth());
+ Iterator it = set.iterator();
+ while(it.hasNext())
+ {
+ IMapEntry o = (IMapEntry) it.next();
+ if(!set.contains(o.key()))
+ System.err.println("Can't find: " + o);
+ //else if(n < 2000)
+ // System.out.print(o.key().toString() + ",");
+ }
+
+ for(int i = 0; i < ints.length/2; i++)
+ {
+ Integer anInt = ints[i];
+ set = set.remove(anInt);
+ }
+
+ long estimatedTime = System.nanoTime() - startTime;
+ System.out.println();
+
+ System.out.println("_count = " + set.count() + ", time: " + estimatedTime/10000);
+
+ System.out.println("Building ht");
+ Hashtable ht = new Hashtable(1001);
+ startTime = System.nanoTime();
+// for(int i = 0; i < ints.length; i++)
+// {
+// Integer anInt = ints[i];
+// ht.put(anInt,null);
+// }
+ for(int i = 0; i < ints.length; i++)
+ {
+ Integer anInt = ints[i];
+ ht.put(anInt, anInt);
+ }
+ //System.out.println("size = " + ht.size());
+ it = ht.entrySet().iterator();
+ while(it.hasNext())
+ {
+ Map.Entry o = (Map.Entry) it.next();
+ if(!ht.containsKey(o.getKey()))
+ System.err.println("Can't find: " + o);
+ //else if(n < 2000)
+ // System.out.print(o.toString() + ",");
+ }
+
+ for(int i = 0; i < ints.length/2; i++)
+ {
+ Integer anInt = ints[i];
+ ht.remove(anInt);
+ }
+ estimatedTime = System.nanoTime() - startTime;
+ System.out.println();
+ System.out.println("size = " + ht.size() + ", time: " + estimatedTime/10000);
+
+// System.out.println("_count = " + set._count + ", min: " + set.minKey() + ", max: " + set.maxKey()
+// + ", depth: " + set.depth());
+}
+}
diff --git a/src/jvm/clojure/runtime/RT.java b/src/jvm/clojure/runtime/RT.java
new file mode 100644
index 00000000..4298a4c9
--- /dev/null
+++ b/src/jvm/clojure/runtime/RT.java
@@ -0,0 +1,389 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 25, 2006 4:28:27 PM */
+
+package org.clojure.runtime;
+
+import java.util.Iterator;
+import java.io.Reader;
+import java.io.PushbackReader;
+
+public class RT{
+
+ static public Symbol T = Symbol.intern("t");
+ static public final Object[] EMPTY_ARRAY = new Object[]{};
+ static public final Character[] chars;
+
+ static {
+ chars = new Character[256];
+ for(int i=0;i<chars.length;i++)
+ chars[i] = new Character((char)i);
+ }
+
+
+ static public Object eq(Object arg1, Object arg2) {
+ return (arg1 == arg2)?Boolean.TRUE:null;
+ }
+
+ static public Object eql(Object arg1, Object arg2) {
+ if(arg1 == arg2)
+ return Boolean.TRUE;
+ if(arg1 == null || arg2 == null)
+ return null;
+ if(arg1 instanceof Num
+ && arg1.getClass() == arg2.getClass()
+ && arg1.equals(arg2))
+ return Boolean.TRUE;
+ if(arg1.getClass() == Character.class
+ && arg2.getClass() == Character.class
+ && arg1.equals(arg2))
+ return Boolean.TRUE;
+ return null;
+ }
+
+ static public Object equal(Object arg1, Object arg2) {
+ if(arg1 == null)
+ return arg2 == null ? Boolean.TRUE : null;
+ else if(arg2 == null)
+ return null;
+ return (eql(arg1,arg2) != null
+ || (arg1.getClass() == Cons.class
+ && arg2.getClass() == Cons.class
+ && equal(((Cons)arg1).first,((Cons)arg2).first)!=null
+ && equal(((Cons)arg1).rest,((Cons)arg2).rest)!=null))
+ ?Boolean.TRUE:null;
+ }
+
+static public Iter iter(Object coll)
+ {
+ if(coll == null || coll instanceof Iter)
+ return (Iter) coll;
+ else if(coll instanceof Iterator)
+ {
+ Iterator i = (Iterator) coll;
+ if(i.hasNext())
+ return new IteratorIter(i);
+ return null;
+ }
+ else if(coll instanceof Iterable)
+ return new IteratorIter(((Iterable) coll).iterator());
+
+ else
+ throw new IllegalArgumentException("Don't know how to create Iter from arg");
+ }
+
+/************************ Boxing/casts *******************************/
+static public Object box(Object x)
+ {
+ return x;
+ }
+
+static public Character box(char x)
+ {
+ if(x < chars.length)
+ return chars[x];
+ return new Character(x);
+ }
+
+static public Boolean box(boolean x)
+ {
+ return Boolean.valueOf(x);
+ }
+
+static public Byte box(byte x)
+ {
+ return new Byte(x);
+ }
+
+static public Short box(short x)
+ {
+ return new Short(x);
+ }
+
+static public Integer box(int x)
+ {
+ return new Integer(x);
+ }
+
+static public Long box(long x)
+ {
+ return new Long(x);
+ }
+
+static public Float box(float x)
+ {
+ return new Float(x);
+ }
+
+static public Double box(double x)
+ {
+ return new Double(x);
+ }
+
+static public char charCast(Object x)
+ {
+ if(x instanceof Character)
+ return ((Character)x).charValue();
+ return (char) ((Number)x).intValue();
+ }
+
+static public boolean booleanCast(Object x)
+ {
+ if(x instanceof Boolean)
+ return ((Boolean)x).booleanValue();
+ return x != null;
+ }
+
+static public byte byteCast(Object x)
+ {
+ return ((Number)x).byteValue();
+ }
+
+static public short shortCast(Object x)
+ {
+ return ((Number)x).shortValue();
+ }
+
+static public int intCast(Object x)
+ {
+ return ((Number)x).intValue();
+ }
+
+static public long longCast(Object x)
+ {
+ return ((Number)x).longValue();
+ }
+
+static public float floatCast(Object x)
+ {
+ return ((Number)x).floatValue();
+ }
+
+static public double doubleCast(Object x)
+ {
+ return ((Number)x).doubleValue();
+ }
+
+
+/******************************************* list support ********************************/
+static public Cons cons(Object x, Cons y)
+ {
+ return new Cons(x, y);
+ }
+
+static public Cons list()
+ {
+ return null;
+ }
+
+static public Cons list(Object arg1)
+ {
+ return cons(arg1, null);
+ }
+
+static public Cons list(Object arg1, Object arg2)
+ {
+ return listStar(arg1, arg2, null);
+ }
+
+static public Cons list(Object arg1, Object arg2, Object arg3)
+ {
+ return listStar(arg1, arg2, arg3, null);
+ }
+
+static public Cons list(Object arg1, Object arg2, Object arg3, Object arg4)
+ {
+ return listStar(arg1, arg2, arg3, arg4, null);
+ }
+
+static public Cons list(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
+ {
+ return listStar(arg1, arg2, arg3, arg4, arg5, null);
+ }
+
+static public Cons listStar(Object arg1, Cons rest)
+ {
+ return cons(arg1, rest);
+ }
+
+static public Cons listStar(Object arg1, Object arg2, Cons rest)
+ {
+ return cons(arg1, cons(arg2, rest));
+ }
+
+static public Cons listStar(Object arg1, Object arg2, Object arg3, Cons rest)
+ {
+ return cons(arg1, cons(arg2, cons(arg3, rest)));
+ }
+
+static public Cons listStar(Object arg1, Object arg2, Object arg3, Object arg4, Cons rest)
+ {
+ return cons(arg1, cons(arg2, cons(arg3, cons(arg4, rest))));
+ }
+
+static public Cons listStar(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Cons rest)
+ {
+ return cons(arg1, cons(arg2, cons(arg3, cons(arg4, cons(arg5, rest)))));
+ }
+
+static public Cons arrayToList(Object[] a){
+ Cons ret = null;
+ for(int i=a.length-1;i>=0;--i)
+ ret = cons(a[i], ret);
+ return ret;
+}
+
+static public int length(Cons list)
+ {
+ int i = 0;
+ for(Cons c = list; c != null; c = c.rest)
+ {
+ i++;
+ }
+ return i;
+ }
+
+static public int boundedLength(Cons list, int limit)
+ {
+ int i = 0;
+ for(Cons c = list; c != null && i <= limit; c = c.rest)
+ {
+ i++;
+ }
+ return i;
+ }
+
+
+///////////////////////////////// reader support ////////////////////////////////
+
+static Object readRet(int ret){
+ if(ret == -1)
+ return null;
+ return box((char) ret);
+}
+
+static public Object readChar(Reader r) throws Exception {
+ int ret = r.read();
+ return readRet(ret);
+}
+
+static public Object peekChar(Reader r) throws Exception {
+ int ret;
+ if(r instanceof PushbackReader)
+ {
+ ret = r.read();
+ ((PushbackReader) r).unread(ret);
+ }
+ else
+ {
+ r.mark(1);
+ ret = r.read();
+ r.reset();
+ }
+
+ return readRet(ret);
+}
+
+static public int getLineNumber(Reader r){
+ if(r instanceof LineNumberingPushbackReader)
+ return ((LineNumberingPushbackReader)r).getLineNumber();
+ return 0;
+}
+
+static public Reader getLineNumberingReader(Reader r) {
+ if(isLineNumberingReader(r))
+ return r;
+ return new LineNumberingPushbackReader(r);
+}
+
+static public boolean isLineNumberingReader(Reader r) {
+ return r instanceof LineNumberingPushbackReader;
+}
+
+///////////////////////////////// values //////////////////////////
+
+static public Object setValues(ThreadLocalData tld, Object arg1)
+ {
+ if(tld == null)
+ tld = ThreadLocalData.get();
+ tld.mvCount = 1;
+ tld.mvArray[0] = arg1;
+ return arg1;
+ }
+
+static public Object setValues(ThreadLocalData tld, Object arg1, Object arg2)
+ {
+ if(tld == null)
+ tld = ThreadLocalData.get();
+ tld.mvCount = 2;
+ tld.mvArray[0] = arg1;
+ tld.mvArray[1] = arg2;
+ return arg1;
+ }
+
+static public Object setValues(ThreadLocalData tld, Object arg1, Object arg2, Object arg3)
+ {
+ if(tld == null)
+ tld = ThreadLocalData.get();
+ tld.mvCount = 3;
+ tld.mvArray[0] = arg1;
+ tld.mvArray[1] = arg2;
+ tld.mvArray[2] = arg3;
+ return arg1;
+ }
+
+static public Object setValues(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4)
+ {
+ if(tld == null)
+ tld = ThreadLocalData.get();
+ tld.mvCount = 4;
+ tld.mvArray[0] = arg1;
+ tld.mvArray[1] = arg2;
+ tld.mvArray[2] = arg3;
+ tld.mvArray[3] = arg4;
+ return arg1;
+ }
+
+static public Object setValues(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4,
+ Object arg5)
+ {
+ if(tld == null)
+ tld = ThreadLocalData.get();
+ tld.mvCount = 5;
+ tld.mvArray[0] = arg1;
+ tld.mvArray[1] = arg2;
+ tld.mvArray[2] = arg3;
+ tld.mvArray[3] = arg4;
+ tld.mvArray[4] = arg5;
+ return arg1;
+ }
+
+static public Object setValues(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4,
+ Object arg5, Cons args) throws Exception
+ {
+ if(tld == null)
+ tld = ThreadLocalData.get();
+ tld.mvCount = 5;
+ tld.mvArray[0] = arg1;
+ tld.mvArray[1] = arg2;
+ tld.mvArray[2] = arg3;
+ tld.mvArray[3] = arg4;
+ tld.mvArray[4] = arg5;
+ for(int i = 5; args != null && i < ThreadLocalData.MULTIPLE_VALUES_LIMIT; i++, args = args.rest)
+ {
+ tld.mvArray[i] = args.first;
+ }
+ if(args != null)
+ throw new IllegalArgumentException("Too many arguments to values (> ThreadLocalData.MULTIPLE_VALUES_LIMIT)");
+ return arg1;
+ }
+
+}
diff --git a/src/jvm/clojure/runtime/RatioNum.java b/src/jvm/clojure/runtime/RatioNum.java
new file mode 100644
index 00000000..e0efe5d1
--- /dev/null
+++ b/src/jvm/clojure/runtime/RatioNum.java
@@ -0,0 +1,226 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 28, 2006 10:14:44 AM */
+
+package org.clojure.runtime;
+
+import java.math.BigInteger;
+
+public class RatioNum extends RationalNum{
+public boolean equals(Object arg0)
+ {
+ return arg0 != null
+ && arg0 instanceof RatioNum
+ && ((RatioNum) arg0).numerator.equals(numerator)
+ && ((RatioNum) arg0).denominator.equals(denominator);
+ }
+
+public int hashCode()
+ {
+ return numerator.hashCode() ^ denominator.hashCode();
+ }
+
+public String toString()
+ {
+ return numerator.toString() + "/" + denominator.toString();
+ }
+
+public IntegerNum numerator;
+public IntegerNum denominator;
+
+public RatioNum(IntegerNum n, IntegerNum d)
+ {
+ this.numerator = n;
+ this.denominator = d;
+ }
+
+public double doubleValue()
+ {
+ return numerator.doubleValue() / denominator.doubleValue();
+ }
+
+public float floatValue()
+ {
+ return (float) doubleValue();
+ }
+
+public int intValue()
+ {
+ return (int) doubleValue();
+ }
+
+public long longValue()
+ {
+ return (long) doubleValue();
+ }
+
+public boolean equiv(Num rhs)
+ {
+ return rhs.equivTo(this);
+ }
+
+public boolean equivTo(BigInteger x)
+ {
+ return false;
+ }
+
+public boolean equivTo(int x)
+ {
+ return false;
+ }
+
+public boolean equivTo(RatioNum x)
+ {
+ return numerator.equiv(x.numerator) && denominator.equiv(x.denominator);
+ }
+
+public boolean lt(Num rhs)
+ {
+ return rhs.gt(this);
+ }
+
+public boolean gt(BigInteger x)
+ {
+ return denominator.multiply(x).lt(numerator);
+ }
+
+public boolean gt(int x)
+ {
+ return denominator.multiply(x).lt(numerator);
+ }
+
+public boolean gt(RatioNum x)
+ {
+ return x.numerator.multiplyBy(denominator).lt(numerator.multiplyBy(x.denominator));
+ }
+
+public Num add(Num rhs)
+ {
+ return rhs.addTo(this);
+ }
+
+public Num addTo(BigInteger x)
+ {
+ return Num.divide(numerator.add(denominator.multiply(x)), denominator);
+ }
+
+public Num addTo(int x)
+ {
+ return Num.divide(numerator.add(denominator.multiply(x)), denominator);
+ }
+
+public Num addTo(RatioNum x)
+ {
+ return Num.divide(numerator.multiplyBy(x.denominator)
+ .add(x.numerator.multiplyBy(denominator))
+ , denominator.multiplyBy(x.denominator));
+ }
+
+public Num subtractFrom(Num x)
+ {
+ return x.add(this.multiply(-1));
+ }
+
+public Num multiplyBy(Num rhs)
+ {
+ return rhs.multiply(this);
+ }
+
+public Num multiply(BigInteger x)
+ {
+ return Num.divide(numerator.multiply(x), denominator);
+ }
+
+public Num multiply(int x)
+ {
+ return Num.divide(numerator.multiply(x), denominator);
+ }
+
+public Num multiply(RatioNum x)
+ {
+ return Num.divide(numerator.multiplyBy(x.numerator)
+ , denominator.multiplyBy(x.denominator));
+ }
+
+public Num divideBy(Num rhs)
+ {
+ return rhs.divide(this);
+ }
+
+public Num divide(BigInteger n)
+ {
+ return Num.divide(denominator.multiply(n), numerator);
+ }
+
+public Num divide(int n)
+ {
+ return Num.divide(denominator.multiply(n), numerator);
+ }
+
+public Num divide(RatioNum n)
+ {
+ return Num.divide(denominator.multiplyBy(n.numerator)
+ , numerator.multiplyBy(n.denominator));
+ }
+
+
+public Object truncateDivide(ThreadLocalData tld, Num num)
+ {
+ return num.truncateBy(tld, this);
+ }
+
+public Object truncateBy(ThreadLocalData tld, int div)
+ {
+ Num q = (Num) Num.truncate(tld, numerator, denominator.multiply(div));
+ return RT.setValues(tld, q, q.multiply(div).subtractFrom(this));
+ }
+
+public Object truncateBy(ThreadLocalData tld, BigInteger div)
+ {
+ Num q = (Num) Num.truncate(tld, numerator, denominator.multiply(div));
+ return RT.setValues(tld, q, q.multiply(div).subtractFrom(this));
+ }
+
+public Object truncateBy(ThreadLocalData tld, RatioNum div)
+ {
+ Num q = (Num) Num.truncate(tld, numerator.multiplyBy(div.denominator),
+ denominator.multiplyBy(div.numerator));
+ return RT.setValues(tld, q, q.multiplyBy(div).subtractFrom(this));
+ }
+
+
+public Num negate()
+ {
+ return Num.divide(numerator.negate(), denominator);
+ }
+
+public boolean minusp()
+ {
+ return numerator.minusp();
+ }
+
+public boolean plusp()
+ {
+ return numerator.plusp();
+ }
+
+public Num oneMinus()
+ {
+ return addTo(-1);
+ }
+
+public Num onePlus()
+ {
+ return addTo(1);
+ }
+
+}
+
diff --git a/src/jvm/clojure/runtime/RationalNum.java b/src/jvm/clojure/runtime/RationalNum.java
new file mode 100644
index 00000000..81cd16b5
--- /dev/null
+++ b/src/jvm/clojure/runtime/RationalNum.java
@@ -0,0 +1,17 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 28, 2006 10:12:30 AM */
+
+package org.clojure.runtime;
+
+public abstract class RationalNum extends RealNum {
+
+}
diff --git a/src/jvm/clojure/runtime/RealNum.java b/src/jvm/clojure/runtime/RealNum.java
new file mode 100644
index 00000000..387f0a23
--- /dev/null
+++ b/src/jvm/clojure/runtime/RealNum.java
@@ -0,0 +1,17 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 28, 2006 10:13:00 AM */
+
+package org.clojure.runtime;
+
+public abstract class RealNum extends Num {
+
+}
diff --git a/src/jvm/clojure/runtime/Reflector.java b/src/jvm/clojure/runtime/Reflector.java
new file mode 100644
index 00000000..c81a6341
--- /dev/null
+++ b/src/jvm/clojure/runtime/Reflector.java
@@ -0,0 +1,320 @@
+/**
+ * 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.
+ **/
+
+/* rich Apr 19, 2006 */
+
+package org.clojure.runtime;
+
+import java.lang.reflect.*;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class Reflector{
+
+public static Object invokeInstanceMethod(String name, Object target, Object[] args) throws Exception
+ {
+ Class c = target.getClass();
+ List methods = getMethods(c, args.length, name,false);
+ return prepRet(invokeMatchingMethod(methods, target, args));
+ }
+
+private static Object invokeMatchingMethod(List methods, Object target, Object[] args)
+ throws IllegalAccessException, InvocationTargetException {
+ if(methods.isEmpty())
+ {
+ throw new IllegalArgumentException("No matching field or method found");
+ }
+ else if(methods.size() == 1)
+ {
+ Method m = (Method) methods.get(0);
+ return prepRet(m.invoke(target, boxArgs(m.getParameterTypes(), args)));
+ }
+ else //overloaded w/same arity
+ {
+ for(Iterator i = methods.iterator(); i.hasNext();)
+ {
+ Method m = (Method) i.next();
+
+ Class[] params = m.getParameterTypes();
+ if(isCongruent(params, args))
+ {
+ Object[] boxedArgs = boxArgs(params, args);
+ return prepRet(m.invoke(target, boxedArgs));
+ }
+ }
+ throw new IllegalArgumentException("No matching field or method found");
+
+ }
+}
+
+public static Object invokeConstructor(Class c, Object[] args) throws Exception
+ {
+ Constructor[] allctors = c.getConstructors();
+ ArrayList ctors = new ArrayList();
+ for(int i = 0; i < allctors.length; i++)
+ {
+ Constructor ctor = allctors[i];
+ if(ctor.getParameterTypes().length == args.length)
+ ctors.add(ctor);
+ }
+ if(ctors.isEmpty())
+ {
+ throw new IllegalArgumentException("No matching ctor found");
+ }
+ else if(ctors.size() == 1)
+ {
+ Constructor ctor = (Constructor) ctors.get(0);
+ return ctor.newInstance(boxArgs(ctor.getParameterTypes(), args));
+ }
+ else //overloaded w/same arity
+ {
+ for(Iterator iterator = ctors.iterator(); iterator.hasNext();)
+ {
+ Constructor ctor = (Constructor) iterator.next();
+ Class[] params = ctor.getParameterTypes();
+ if(isCongruent(params, args))
+ {
+ Object[] boxedArgs = boxArgs(params, args);
+ return ctor.newInstance(boxedArgs);
+ }
+ }
+ throw new IllegalArgumentException("No matching ctor found");
+ }
+ }
+
+public static Object invokeStaticMethod(String name, String className, Object[] args) throws Exception
+ {
+ Class c = Class.forName(className);
+ if(name.equals("new"))
+ return invokeConstructor(c, args);
+ List methods = getMethods(c, args.length, name,true);
+ return invokeMatchingMethod(methods, null, args);
+ }
+
+public static Object getStaticField(String name, String className) throws Exception
+ {
+ Class c = Class.forName(className);
+ Field f = getField(c, name,true);
+ if(f != null)
+ {
+ return prepRet(f.get(null));
+ }
+ throw new IllegalArgumentException("No matching field found");
+ }
+
+ public static Object setStaticField(String name, String className, Object arg1) throws Exception
+ {
+ Class c = Class.forName(className);
+ Field f = getField(c, name,true);
+ if(f != null)
+ {
+ f.set(null, boxArg(f.getType(), arg1));
+ return arg1;
+ }
+ throw new IllegalArgumentException("No matching field found");
+ }
+
+public static Object invokeInstanceMember(String name, Object target) throws Exception
+ {
+ //check for field first
+ Class c = target.getClass();
+ Field f = getField(c, name,false);
+ if(f != null) //field get
+ {
+ return prepRet(f.get(target));
+ }
+ return invokeInstanceMethod(name, target, RT.EMPTY_ARRAY);
+ }
+
+public static Object invokeInstanceMember(String name, Object target, Object arg1) throws Exception
+ {
+ //check for field first
+ Class c = target.getClass();
+ Field f = getField(c, name,false);
+ if(f != null) //field set
+ {
+ f.set(target, boxArg(f.getType(), arg1));
+ return arg1;
+ }
+ return invokeInstanceMethod(name, target, new Object[]{arg1});
+ }
+
+public static Object invokeInstanceMember(String name, Object target, Object arg1, Object arg2) throws Exception
+ {
+ return invokeInstanceMethod(name, target, new Object[]{arg1, arg2});
+ }
+
+public static Object invokeInstanceMember(String name, Object target, Object arg1, Object arg2, Object arg3)
+ throws Exception
+ {
+ return invokeInstanceMethod(name, target, new Object[]{arg1, arg2, arg3});
+ }
+
+public static Object invokeInstanceMember(String name, Object target, Object arg1, Object arg2, Object arg3,
+ Object arg4)
+ throws Exception
+ {
+ return invokeInstanceMethod(name, target, new Object[]{arg1, arg2, arg3, arg4});
+ }
+
+public static Object invokeInstanceMember(String name, Object target, Object arg1, Object arg2, Object arg3,
+ Object arg4,
+ Cons arglist)
+ throws Exception
+ {
+ Object[] args = new Object[4 + RT.length(arglist)];
+ args[0] = arg1;
+ args[1] = arg2;
+ args[2] = arg3;
+ args[3] = arg4;
+ for(int i = 4; arglist != null; i++, arglist = arglist.rest)
+ args[i] = arglist.first;
+ return invokeInstanceMethod(name, target, args);
+ }
+
+
+static public Field getField(Class c, String name, boolean getStatics)
+ {
+ Field[] allfields = c.getFields();
+ for(int i = 0; i < allfields.length; i++)
+ {
+ if(name.equals(allfields[i].getName())
+ && Modifier.isStatic(allfields[i].getModifiers()) == getStatics)
+ return allfields[i];
+ }
+ return null;
+ }
+
+static public List getMethods(Class c, int arity, String name, boolean getStatics)
+ {
+ Method[] allmethods = c.getMethods();
+ ArrayList methods = new ArrayList();
+ for(int i = 0; i < allmethods.length; i++)
+ {
+ if(name.equals(allmethods[i].getName())
+ && Modifier.isStatic(allmethods[i].getModifiers()) == getStatics
+ && allmethods[i].getParameterTypes().length == arity)
+ {
+ methods.add(allmethods[i]);
+ }
+ }
+ return methods;
+ }
+
+
+static Object boxArg(Class paramType, Object arg)
+ {
+ Class argType = arg.getClass();
+ if(primBoxTypeMatch(paramType, argType))
+ return arg;
+ if(paramType == boolean.class)
+ {
+ if(arg == null)
+ return Boolean.FALSE;
+ return Boolean.TRUE;
+ }
+ else if(paramType.isPrimitive() && arg instanceof Num)
+ {
+ Num n = (Num) arg;
+ if(paramType == int.class)
+ return RT.box(n.intValue());
+ else if(paramType == float.class)
+ return RT.box(n.floatValue());
+ else if(paramType == double.class)
+ return RT.box(n.doubleValue());
+ else if(paramType == long.class)
+ return RT.box(n.longValue());
+ else if(paramType == char.class)
+ return RT.box((char) n.intValue());
+ else if(paramType == short.class)
+ return RT.box(n.shortValue());
+ else
+ return RT.box(n.byteValue());
+ }
+ else
+ return arg;
+ }
+
+static Object[] boxArgs(Class[] params, Object[] args)
+ {
+ if(params.length == 0)
+ return null;
+ Object[] ret = new Object[params.length];
+ for(int i = 0; i < params.length; i++)
+ {
+ Object arg = args[i];
+ Class paramType = params[i];
+ ret[i] = boxArg(paramType, arg);
+ }
+ return ret;
+ }
+
+static public boolean primBoxTypeMatch(Class primType, Class boxType)
+ {
+ if(primType == int.class)
+ return boxType == Integer.class;
+ else if(primType == float.class)
+ return boxType == Float.class;
+ else if(primType == double.class)
+ return boxType == Double.class;
+ else if(primType == long.class)
+ return boxType == Long.class;
+ else if(primType == char.class)
+ return boxType == Character.class;
+ else if(primType == short.class)
+ return boxType == Short.class;
+ else if(primType == byte.class)
+ return boxType == Byte.class;
+ return false;
+ }
+
+static boolean isCongruent(Class[] params, Object[] args)
+ {
+ boolean ret = false;
+ if(args == null)
+ return params.length == 0;
+ if(params.length == args.length)
+ {
+ ret = true;
+ for(int i = 0; ret && i < params.length; i++)
+ {
+ Object arg = args[i];
+ Class argType = (arg == null) ? null : arg.getClass();
+ Class paramType = params[i];
+ if(paramType == boolean.class)
+ {
+ ret = arg == null || argType == Boolean.class;
+ }
+ else if(paramType.isPrimitive())
+ {
+ if(arg == null)
+ ret = false;
+ else
+ ret = primBoxTypeMatch(paramType, argType);
+ }
+ else
+ {
+ ret = arg == null
+ || argType == paramType
+ || paramType.isAssignableFrom(argType);
+ }
+ }
+ }
+ return ret;
+ }
+
+static Object prepRet(Object x)
+ {
+ if(x instanceof Boolean)
+ return ((Boolean)x).booleanValue()?RT.T:null;
+ return x;
+ }
+}
diff --git a/src/jvm/clojure/runtime/RestFn0.java b/src/jvm/clojure/runtime/RestFn0.java
new file mode 100644
index 00000000..b6886c8b
--- /dev/null
+++ b/src/jvm/clojure/runtime/RestFn0.java
@@ -0,0 +1,60 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 27, 2006 7:34:25 PM */
+
+package org.clojure.runtime;
+
+public abstract class RestFn0 extends AFn{
+
+protected abstract Object doInvoke(ThreadLocalData tld, Cons rest) throws Exception;
+
+public Object applyTo(ThreadLocalData tld, Cons arglist) throws Exception
+ {
+ return doInvoke(tld, arglist);
+ }
+
+public Object invoke(ThreadLocalData tld) throws Exception
+ {
+ return doInvoke(tld, null);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1) throws Exception
+ {
+ return doInvoke(tld, RT.list(arg1));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2) throws Exception
+ {
+ return doInvoke(tld, RT.list(arg1, arg2));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3) throws Exception
+ {
+ return doInvoke(tld, RT.list(arg1, arg2, arg3));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception
+ {
+ return doInvoke(tld, RT.list(arg1, arg2, arg3, arg4));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
+ throws Exception
+ {
+ return doInvoke(tld, RT.list(arg1, arg2, arg3, arg4, arg5));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Cons args)
+ throws Exception
+ {
+ return doInvoke(tld, RT.listStar(arg1, arg2, arg3, arg4, arg5, args));
+ }
+}
diff --git a/src/jvm/clojure/runtime/RestFn1.java b/src/jvm/clojure/runtime/RestFn1.java
new file mode 100644
index 00000000..f763b0c4
--- /dev/null
+++ b/src/jvm/clojure/runtime/RestFn1.java
@@ -0,0 +1,65 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 27, 2006 8:00:28 PM */
+
+package org.clojure.runtime;
+
+public abstract class RestFn1 extends AFn{
+
+protected abstract Object doInvoke(ThreadLocalData tld, Object arg1, Cons rest) throws Exception;
+
+public Object applyTo(ThreadLocalData tld, Cons arglist) throws Exception
+ {
+ switch(RT.boundedLength(arglist, 1))
+ {
+ case 0:
+ return invoke(tld);
+ case 1:
+ return invoke(tld,arglist.first);
+ default:
+ return doInvoke(tld, arglist.first
+ , arglist.rest);
+ }
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1) throws Exception
+ {
+ return doInvoke(tld, arg1, null);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2) throws Exception
+ {
+ return doInvoke(tld, arg1, RT.list(arg2));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3) throws Exception
+ {
+ return doInvoke(tld, arg1, RT.list(arg2, arg3));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception
+ {
+ return doInvoke(tld, arg1, RT.list(arg2, arg3, arg4));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
+ throws Exception
+ {
+ return doInvoke(tld, arg1, RT.list(arg2, arg3, arg4, arg5));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Cons args)
+ throws Exception
+ {
+ return doInvoke(tld, arg1, RT.listStar(arg2, arg3, arg4, arg5, args));
+ }
+}
+
diff --git a/src/jvm/clojure/runtime/RestFn2.java b/src/jvm/clojure/runtime/RestFn2.java
new file mode 100644
index 00000000..8f12ade0
--- /dev/null
+++ b/src/jvm/clojure/runtime/RestFn2.java
@@ -0,0 +1,66 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 27, 2006 8:05:10 PM */
+
+package org.clojure.runtime;
+
+public abstract class RestFn2 extends AFn{
+
+protected abstract Object doInvoke(ThreadLocalData tld, Object arg1, Object arg2, Cons rest) throws Exception;
+
+public Object applyTo(ThreadLocalData tld, Cons arglist) throws Exception
+ {
+ switch(RT.boundedLength(arglist, 2))
+ {
+ case 0:
+ return invoke(tld);
+ case 1:
+ return invoke(tld,arglist.first);
+ case 2:
+ return invoke(tld,arglist.first
+ , (arglist = arglist.rest).first
+ );
+ default:
+ return doInvoke(tld, arglist.first
+ , (arglist = arglist.rest).first
+ , arglist.rest);
+
+ }
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2) throws Exception
+ {
+ return doInvoke(tld, arg1, arg2, null);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3) throws Exception
+ {
+ return doInvoke(tld, arg1, arg2, RT.list(arg3));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception
+ {
+ return doInvoke(tld, arg1, arg2, RT.list(arg3, arg4));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
+ throws Exception
+ {
+ return doInvoke(tld, arg1, arg2, RT.list(arg3, arg4, arg5));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Cons args)
+ throws Exception
+ {
+ return doInvoke(tld, arg1, arg2, RT.listStar(arg3, arg4, arg5, args));
+ }
+}
+
diff --git a/src/jvm/clojure/runtime/RestFn3.java b/src/jvm/clojure/runtime/RestFn3.java
new file mode 100644
index 00000000..9efcbc3e
--- /dev/null
+++ b/src/jvm/clojure/runtime/RestFn3.java
@@ -0,0 +1,67 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 27, 2006 8:19:54 PM */
+
+package org.clojure.runtime;
+
+public abstract class RestFn3 extends AFn{
+
+protected abstract Object doInvoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Cons rest) throws Exception;
+
+public Object applyTo(ThreadLocalData tld, Cons arglist) throws Exception
+ {
+ switch(RT.boundedLength(arglist, 3))
+ {
+ case 0:
+ return invoke(tld);
+ case 1:
+ return invoke(tld,arglist.first);
+ case 2:
+ return invoke(tld,arglist.first
+ , (arglist = arglist.rest).first
+ );
+ case 3:
+ return invoke(tld,arglist.first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ );
+ default:
+ return doInvoke(tld, arglist.first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , arglist.rest);
+
+ }
+ }
+
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3) throws Exception
+ {
+ return doInvoke(tld, arg1, arg2, arg3,null);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception
+ {
+ return doInvoke(tld, arg1, arg2, arg3, RT.list(arg4));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
+ throws Exception
+ {
+ return doInvoke(tld, arg1, arg2, arg3, RT.list(arg4, arg5));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Cons args)
+ throws Exception
+ {
+ return doInvoke(tld, arg1, arg2, arg3, RT.listStar(arg4, arg5, args));
+ }
+}
diff --git a/src/jvm/clojure/runtime/RestFn4.java b/src/jvm/clojure/runtime/RestFn4.java
new file mode 100644
index 00000000..1114bce1
--- /dev/null
+++ b/src/jvm/clojure/runtime/RestFn4.java
@@ -0,0 +1,70 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 27, 2006 8:21:51 PM */
+
+package org.clojure.runtime;
+
+public abstract class RestFn4 extends AFn{
+
+protected abstract Object doInvoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Cons rest)
+ throws Exception;
+
+public Object applyTo(ThreadLocalData tld, Cons arglist) throws Exception
+ {
+ switch(RT.boundedLength(arglist, 4))
+ {
+ case 0:
+ return invoke(tld);
+ case 1:
+ return invoke(tld,arglist.first);
+ case 2:
+ return invoke(tld,arglist.first
+ , (arglist = arglist.rest).first
+ );
+ case 3:
+ return invoke(tld,arglist.first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ );
+ case 4:
+ return invoke(tld,arglist.first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ );
+ default:
+ return doInvoke(tld, arglist.first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , arglist.rest);
+
+ }
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception
+ {
+ return doInvoke(tld, arg1, arg2, arg3, arg4, null);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
+ throws Exception
+ {
+ return doInvoke(tld, arg1, arg2, arg3, arg4, RT.list(arg5));
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Cons args)
+ throws Exception
+ {
+ return doInvoke(tld, arg1, arg2, arg3, arg4, RT.listStar(arg5, args));
+ }
+}
+
diff --git a/src/jvm/clojure/runtime/RestFn5.java b/src/jvm/clojure/runtime/RestFn5.java
new file mode 100644
index 00000000..7b8b414a
--- /dev/null
+++ b/src/jvm/clojure/runtime/RestFn5.java
@@ -0,0 +1,73 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 27, 2006 8:24:31 PM */
+
+package org.clojure.runtime;
+
+public abstract class RestFn5 extends AFn{
+
+protected abstract Object doInvoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5,
+ Cons rest)
+ throws Exception;
+
+public Object applyTo(ThreadLocalData tld, Cons arglist) throws Exception
+ {
+ switch(RT.boundedLength(arglist, 5))
+ {
+ case 0:
+ return invoke(tld);
+ case 1:
+ return invoke(tld,arglist.first);
+ case 2:
+ return invoke(tld,arglist.first
+ , (arglist = arglist.rest).first
+ );
+ case 3:
+ return invoke(tld,arglist.first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ );
+ case 4:
+ return invoke(tld,arglist.first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ );
+ case 5:
+ return invoke(tld,arglist.first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ );
+ default:
+ return doInvoke(tld, arglist.first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , (arglist = arglist.rest).first
+ , arglist.rest);
+
+ }
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
+ throws Exception
+ {
+ return doInvoke(tld, arg1, arg2, arg3, arg4, arg5, null);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Cons args)
+ throws Exception
+ {
+ return doInvoke(tld, arg1, arg2, arg3, arg4, arg5, args);
+ }
+}
diff --git a/src/jvm/clojure/runtime/Symbol.java b/src/jvm/clojure/runtime/Symbol.java
new file mode 100644
index 00000000..269becfa
--- /dev/null
+++ b/src/jvm/clojure/runtime/Symbol.java
@@ -0,0 +1,84 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 25, 2006 11:42:47 AM */
+
+package org.clojure.runtime;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Random;
+
+public class Symbol extends Obj implements Comparable{
+
+final public static HashMap table = new HashMap();
+final public static HashSet hashes = new HashSet();
+final static Random rand = new Random(42);
+
+public final String name;
+int hash = 0;
+
+public String toString()
+ {
+ return name;
+ }
+
+public static Symbol intern(String name)
+ {
+ synchronized(table)
+ {
+ Symbol sym = (Symbol) table.get(name);
+ if(sym == null)
+ {
+ if(name.charAt(0) == ':')
+ sym = new Keyword(name);
+ else if(name.charAt(0) == '.')
+ sym = new Accessor(name);
+ else
+ sym = new Symbol(name);
+ table.put(name, sym);
+ }
+ return sym;
+ }
+ }
+
+/**
+ * Used by intern()
+ * @param name
+ */
+Symbol(String name)
+ {
+ this.name = name;
+ }
+
+ public int hashCode(){
+ if(hash == 0)
+ {
+ synchronized (hashes)
+ {
+ while (hash == 0)
+ {
+ int h = rand.nextInt();
+ if (h != 0 && !hashes.contains(h))
+ {
+ hash = h;
+ hashes.add(h);
+ }
+ }
+ }
+ }
+ return hash;
+ }
+
+
+public int compareTo(Object o) {
+ return hashCode() - ((Symbol)o).hashCode();
+}
+}
diff --git a/src/jvm/clojure/runtime/TObj.java b/src/jvm/clojure/runtime/TObj.java
new file mode 100644
index 00000000..8a0acb57
--- /dev/null
+++ b/src/jvm/clojure/runtime/TObj.java
@@ -0,0 +1,36 @@
+/**
+ * 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.
+ **/
+
+package org.clojure.runtime;
+
+public class TObj implements IObj{
+TRef attrs;
+
+public TObj(ThreadLocalData tld) throws Exception{
+ this.attrs = Transaction.tref(tld,new RBTree());
+}
+
+public Object put(ThreadLocalData tld, Comparable key, Object val) throws Exception {
+ RBTree t = (RBTree) Transaction.get(tld, attrs);
+ t = t.put(key, val);
+ Transaction.set(tld,attrs,t);
+ return val;
+}
+
+public Object get(ThreadLocalData tld, Comparable key) throws Exception {
+ RBTree t = (RBTree) Transaction.get(tld, attrs);
+ return t.get(key);
+}
+
+public boolean has(ThreadLocalData tld, Comparable key) throws Exception {
+ RBTree t = (RBTree) Transaction.get(tld, attrs);
+ return t.contains(key);
+}
+}
diff --git a/src/jvm/clojure/runtime/TRef.java b/src/jvm/clojure/runtime/TRef.java
new file mode 100644
index 00000000..a955cdf5
--- /dev/null
+++ b/src/jvm/clojure/runtime/TRef.java
@@ -0,0 +1,33 @@
+/**
+ * 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.
+ **/
+
+/* rich May 30, 2006 */
+
+package org.clojure.runtime;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class TRef extends TVal implements Comparable{
+static AtomicInteger nextSeq = new AtomicInteger(1);
+
+final int lockSeq;
+Lock lock;
+
+public TRef() {
+ this.lockSeq = nextSeq.getAndIncrement();
+ this.lock = new ReentrantLock();
+}
+
+public int compareTo(Object o){
+ return lockSeq - ((TRef) o).lockSeq;
+}
+}
diff --git a/src/jvm/clojure/runtime/TVal.java b/src/jvm/clojure/runtime/TVal.java
new file mode 100644
index 00000000..1bc72a9b
--- /dev/null
+++ b/src/jvm/clojure/runtime/TVal.java
@@ -0,0 +1,39 @@
+/**
+ * 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.
+ **/
+
+/* rich May 30, 2006 */
+
+package org.clojure.runtime;
+
+public class TVal{
+volatile Object val;
+volatile Transaction.Info tinfo;
+volatile TVal prior;
+
+TVal(){
+
+}
+
+TVal(Object val, Transaction.Info tinfo, TVal prior) {
+ this.val = val;
+ this.tinfo = tinfo;
+ this.prior = prior;
+}
+
+void push(Object val,Transaction.Info tinfo) throws Exception{
+ if(tinfo != null) //not newly created, clone tval part
+ {
+ this.prior = new TVal(this.val,this.tinfo,this.prior);
+ }
+ this.tinfo = tinfo;
+ this.val = val;
+}
+
+}
diff --git a/src/jvm/clojure/runtime/ThreadLocalData.java b/src/jvm/clojure/runtime/ThreadLocalData.java
new file mode 100644
index 00000000..0bc9c665
--- /dev/null
+++ b/src/jvm/clojure/runtime/ThreadLocalData.java
@@ -0,0 +1,61 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 25, 2006 11:45:22 AM */
+
+package org.clojure.runtime;
+
+import java.util.IdentityHashMap;
+
+public class ThreadLocalData{
+
+final public static int MULTIPLE_VALUES_LIMIT = 20;
+public int mvCount = 0;
+public Object[] mvArray = new Object[MULTIPLE_VALUES_LIMIT];
+
+IdentityHashMap dynamicBindings = new IdentityHashMap();
+Transaction transaction;
+
+public Transaction getTransaction() throws Exception{
+ if(transaction == null)
+ throw new Exception("No active transaction");
+ return transaction;
+}
+
+public ThreadLocalData(IdentityHashMap dynamicBindings)
+ {
+ this.mvCount = 0;
+ this.mvArray = new Object[MULTIPLE_VALUES_LIMIT];
+ this.dynamicBindings = dynamicBindings;
+ }
+
+public ThreadLocalData()
+ {
+ this(new IdentityHashMap());
+ }
+
+public static ThreadLocalData get()
+ {
+ return (ThreadLocalData) tld.get();
+ }
+
+static InheritableThreadLocal tld = new InheritableThreadLocal(){
+ protected Object childValue(Object object)
+ {
+ return new ThreadLocalData((IdentityHashMap) ((ThreadLocalData) object).dynamicBindings.clone());
+ }
+
+ protected Object initialValue()
+ {
+ return new ThreadLocalData();
+ }
+};
+
+}
diff --git a/src/jvm/clojure/runtime/Transaction.java b/src/jvm/clojure/runtime/Transaction.java
new file mode 100644
index 00000000..d6f4cab6
--- /dev/null
+++ b/src/jvm/clojure/runtime/Transaction.java
@@ -0,0 +1,234 @@
+/**
+ * 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.
+ **/
+
+/* rich May 30, 2006 */
+
+package org.clojure.runtime;
+
+import java.util.*;
+
+public class Transaction{
+
+public static final int COMMITTED = 0;
+public static final int WORKING = 1;
+static final Object lock = new Object();
+
+volatile static int nextSeq = 1;
+
+static int getNextSeq(){
+ synchronized(lock){
+ return nextSeq++;
+ }
+}
+
+public static class Info{
+int seq;
+int status;
+
+
+Info(int seq,int status){
+ this.seq = seq;
+ this.status = status;
+}
+}
+
+
+Info info;
+int startSeq;
+
+IdentityHashMap<TRef,Object> sets;
+IdentityHashMap<TRef,Cons> commutates;
+
+
+static public Object runInTransaction(ThreadLocalData tld,IFn fn) throws Exception{
+ if(tld.transaction != null)
+ return fn.invoke(tld);
+ tld.transaction = new Transaction();
+ try{
+ return tld.transaction.run(tld, fn);
+ }
+ finally{
+ tld.transaction = null;
+ }
+}
+
+static public TRef tref(ThreadLocalData tld, Object val) throws Exception{
+ Transaction trans = tld.getTransaction();
+ TRef tref = new TRef();
+ trans.set(tref, val);
+ return tref;
+}
+
+static public Object get(ThreadLocalData tld, TRef tref) throws Exception{
+ return tld.getTransaction().get(tref);
+}
+
+static public Object set(ThreadLocalData tld, TRef tref, Object val) throws Exception{
+ return tld.getTransaction().set(tref,val);
+}
+
+static public void touch(ThreadLocalData tld, TRef tref) throws Exception{
+ tld.getTransaction().touch(tref);
+}
+
+static public void commutate(ThreadLocalData tld, TRef tref, IFn fn) throws Exception{
+ tld.getTransaction().commutate(tref, fn);
+}
+
+
+Object run(ThreadLocalData tld, IFn fn) throws Exception{
+ boolean done = false;
+ Object ret = null;
+ ArrayList<TRef> locks = null;
+ ArrayList<TRef> locked = null;
+
+ loop:
+ while(!done){
+ try
+ {
+ ret = fn.invoke(tld);
+ if(locks == null && (sets != null || commutates != null))
+ locks = new ArrayList<TRef>();
+ if(sets != null)
+ locks.addAll(sets.keySet());
+ if(commutates != null)
+ locks.addAll(commutates.keySet());
+ if(locks != null)
+ {
+ if(locked == null)
+ locked = new ArrayList<TRef>(locks.size());
+ //lock in order, to avoid deadlocks
+ Collections.sort(locks);
+ for(TRef tref : locks)
+ {
+ //will block here
+ tref.lock.lock();
+ locked.add(tref);
+ if(sets.containsKey(tref))
+ {
+ //try again if the thing we are trying to set has changed since we started
+ TVal curr = getCurrent(tref);
+ if(curr != null && curr.tinfo.seq > startSeq)
+ continue loop;
+ }
+ }
+ }
+
+ //at this point all write targets are locked
+ //turn commutates into sets
+ for(Map.Entry<TRef, Cons> e : commutates.entrySet())
+ {
+ TRef tref = e.getKey();
+ //note this will npe if tref has never been set, as designed
+ Object val = getCurrent(tref).val;
+ for(Cons c = e.getValue();c!=null;c = c.rest)
+ {
+ IFn f = (IFn) c.first;
+ val = f.invoke(tld, val);
+ }
+ sets.put(tref, val);
+ }
+
+ //set the new vals
+ for(Map.Entry<TRef, Object> entry : sets.entrySet())
+ {
+ TRef tref = entry.getKey();
+ tref.push(entry.getValue(), info);
+ }
+
+ //atomic commit
+ synchronized(lock){
+ info.seq = getNextSeq();
+ info.status = COMMITTED;
+ }
+
+ done = true;
+ }
+ finally{
+ if(locked != null)
+ {
+ for(TRef tref : locked)
+ {
+ tref.lock.unlock();
+ }
+ locked.clear();
+ }
+ reset();
+ if(locks != null)
+ locks.clear();
+ }
+ }
+ return ret;
+}
+
+private void reset(){
+ if(sets != null)
+ sets.clear();
+ if(commutates != null)
+ commutates.clear();
+
+}
+
+
+Transaction(){
+ synchronized(lock){
+ int seq = getNextSeq();
+ this.info = new Info(seq, WORKING);
+ this.startSeq = seq;
+ }
+}
+
+Object get(TRef tref) throws Exception{
+ if(sets != null && sets.containsKey(tref))
+ return sets.get(tref);
+
+ for(TVal ver = tref;ver != null;ver = ver.prior)
+ {
+ //note this will npe if tref has never been set, as designed
+ if(ver.tinfo.status == COMMITTED && ver.tinfo.seq <= startSeq)
+ return ver.val;
+ }
+
+ throw new Exception("Version not found");
+}
+
+static TVal getCurrent(TRef tref) throws Exception{
+ for(TVal ver = tref;ver != null;ver = ver.prior)
+ {
+ if(ver.tinfo != null && ver.tinfo.status == COMMITTED)
+ return ver;
+ }
+ //this return only if no value was ever successfully set
+ return null;
+}
+
+Object set(TRef tref, Object val) throws Exception{
+ if(sets == null)
+ sets = new IdentityHashMap<TRef,Object>();
+ if(commutates != null && commutates.containsKey(tref))
+ throw new Exception("Can't commutate and set a TRef in the same transaction");
+
+ sets.put(tref,val);
+ return val;
+ }
+
+void touch(TRef tref) throws Exception{
+ set(tref, get(tref));
+ }
+
+void commutate(TRef tref, IFn fn) throws Exception{
+ if(commutates == null)
+ commutates = new IdentityHashMap<TRef,Cons>();
+ if(sets != null && sets.containsKey(tref))
+ throw new Exception("Can't commutate and set a TRef in the same transaction");
+ commutates.put(tref, RT.cons(fn, commutates.get(tref)));
+ }
+
+}
diff --git a/src/jvm/clojure/runtime/Var.java b/src/jvm/clojure/runtime/Var.java
new file mode 100644
index 00000000..9e6fe46f
--- /dev/null
+++ b/src/jvm/clojure/runtime/Var.java
@@ -0,0 +1,140 @@
+/**
+ * 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.
+ **/
+
+/* rich Apr 19, 2006 */
+
+package org.clojure.runtime;
+
+public class Var extends AFn{
+
+public final Symbol sym;
+public Namespace namespace;
+public Box binding;
+public IFn fn; //todo, bind to throw stub?
+public IFn setfn;
+
+Var(Symbol sym, Namespace ns)
+ {
+ if(!(sym.getClass() == Symbol.class))
+ throw new IllegalArgumentException("Only simple symbols can be vars");
+ this.namespace = ns;
+ this.sym = sym;
+ }
+
+public String toString()
+ {
+ if(namespace == null)
+ return "#:" + sym;
+ return namespace.name + ":" + sym;
+ }
+
+public Var bind(Object val)
+ {
+ if(binding == null)
+ binding = new Box(val);
+ else
+ binding.val = val;
+
+ if(val instanceof IFn)
+ this.fn = (IFn) val;
+ else
+ this.fn = null; //todo, bind to throw stub?
+
+ return this;
+ }
+
+public Box getBinding(ThreadLocalData tld)
+ {
+ Box b = getDynamicBinding(tld);
+ if(b != null)
+ return b;
+ return binding;
+ }
+
+public Object getValue(ThreadLocalData tld)
+ {
+ Box binding = getBinding(tld);
+ if(binding != null)
+ return binding.val;
+ throw new IllegalStateException(this.toString() + " is unbound.");
+ }
+
+public Object setValue(ThreadLocalData tld, Object val)
+ {
+ Box b = getDynamicBinding(tld);
+ if(b != null)
+ return b.val = val;
+ //allow global set to create binding like this?
+ if(binding == null)
+ throw new IllegalStateException(this.toString() + " is unbound.");
+
+ if(val instanceof IFn)
+ this.fn = (IFn) val;
+ else
+ this.fn = null; //todo, bind to throw stub?
+
+ return binding.val = val;
+ }
+
+final public Box getDynamicBinding(ThreadLocalData tld)
+ {
+ return (Box) tld.dynamicBindings.get(this);
+ }
+
+final public Box establishDynamicBinding(ThreadLocalData tld, Object val)
+ {
+ Box ret = getDynamicBinding(tld);
+ tld.dynamicBindings.put(this, new Box(val));
+ return ret;
+ }
+
+final public void restoreDynamicBinding(ThreadLocalData tld, Box old)
+ {
+ tld.dynamicBindings.put(this, old);
+ }
+
+public Object invoke(ThreadLocalData tld) throws Exception
+ {
+ return fn.invoke(tld);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1) throws Exception
+ {
+ return fn.invoke(tld,arg1);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2) throws Exception
+ {
+ return fn.invoke(tld,arg1,arg2);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3) throws Exception
+ {
+ return fn.invoke(tld,arg1,arg2,arg3);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4) throws Exception
+ {
+ return fn.invoke(tld,arg1,arg2,arg3,arg4);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5)
+ throws Exception
+ {
+ return fn.invoke(tld,arg1,arg2,arg3,arg4,arg5);
+ }
+
+public Object invoke(ThreadLocalData tld, Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Cons args)
+ throws Exception
+ {
+ return fn.invoke(tld,arg1,arg2,arg3,arg4,arg5,args);
+ }
+
+}
diff --git a/src/jvm/clojure/tools/TypeDump.java b/src/jvm/clojure/tools/TypeDump.java
new file mode 100644
index 00000000..14fe5645
--- /dev/null
+++ b/src/jvm/clojure/tools/TypeDump.java
@@ -0,0 +1,199 @@
+/**
+ * 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.
+ **/
+
+/* rich Mar 31, 2006 11:03:27 AM */
+
+package org.clojure.tools;
+
+import org.objectweb.asm.*;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Creates an sexpr dump of type info
+ */
+public class TypeDump implements ClassVisitor{
+
+PrintStream p;
+boolean ignore;
+static List packages;
+
+public TypeDump(PrintStream p)
+ {
+ this.p = p;
+ }
+
+static public void main(String args[])
+ {
+ if(args.length < 2)
+ {
+ System.err.println("Usage: java org.clojure.tools.TypeDump jarfile package [package ...]");
+ return;
+ }
+ packages = Arrays.asList(args).subList(1, args.length);
+ try
+ {
+ TypeDump v = new TypeDump(System.out);
+ ZipFile f = new ZipFile(args[0]);
+ Enumeration en = f.entries();
+ System.out.println('(');
+ while(en.hasMoreElements())
+ {
+ ZipEntry e = (ZipEntry) en.nextElement();
+ String name = e.getName();
+ if(name.endsWith(".class"))
+ {
+ ClassReader cr = new ClassReader(f.getInputStream(e));
+ cr.accept(v, false);
+ }
+ }
+ System.out.println(')');
+ }
+ catch(IOException e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ }
+
+public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
+ {
+ String pkg = name.substring(0,name.lastIndexOf('/')).replace('/','.');
+ if((access & Opcodes.ACC_PUBLIC) == 0
+ || !packages.contains(pkg))
+ {
+ ignore = true;
+ return;
+ }
+ else
+ ignore = false;
+ p.print('(');
+ p.println("(:name \"" + name + "\")");
+ p.print(" (:super \"" + superName + "\")");
+ if(interfaces.length > 0)
+ {
+ p.println();
+ p.print(" (:interfaces");
+ for(int i = 0; i < interfaces.length; i++)
+ {
+ String anInterface = interfaces[i];
+ p.print(" \"" + anInterface + "\"");
+ }
+ p.print(')');
+ }
+ }
+
+public void visitSource(String source, String debug)
+ {
+ }
+
+public void visitOuterClass(String owner, String name, String desc)
+ {
+ }
+
+public AnnotationVisitor visitAnnotation(String desc, boolean visible)
+ {
+ return null;
+ }
+
+public void visitAttribute(Attribute attribute)
+ {
+ }
+
+public void visitInnerClass(String name, String outerName, String innerName, int access)
+ {
+ }
+
+public FieldVisitor visitField(int access, String name, String desc, String signature, Object value)
+ {
+ if(!ignore && (access & Opcodes.ACC_PUBLIC) != 0)
+ {
+ p.println();
+ p.print(" (:field (:name \"");
+ p.print(name);
+ p.print("\") (:type \"");
+ p.print(internalName(Type.getType(desc)));
+ p.print("\")");
+ if((access & Opcodes.ACC_STATIC) != 0)
+ {
+ p.print(" (:static t)");
+ if(value != null && (access & Opcodes.ACC_FINAL) != 0)
+ {
+ p.print(" (:const-value ");
+ if(value instanceof String)
+ p.print('"');
+ p.print(value);
+ if(value instanceof String)
+ p.print('"');
+ p.print(")");
+ }
+ }
+ p.print(")");
+ }
+ return null;
+ }
+
+String internalName(Type t)
+ {
+ if(t.getSort() == Type.OBJECT)
+ return t.getInternalName();
+ return t.toString();
+ }
+
+public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
+ {
+ if(!ignore && (access & Opcodes.ACC_PUBLIC) != 0)
+ {
+ Type args[] = Type.getArgumentTypes(desc);
+
+ p.println();
+ if(name.equals("<init>"))
+ p.print(" (:ctor ");
+ else
+ p.print(" (:method (:name \"" + name + "\") (:ret \"" +
+ internalName(Type.getReturnType(desc)) + "\")");
+ p.print("(:arity " + args.length + ")");
+ if((access & Opcodes.ACC_STATIC) != 0)
+ {
+ p.print(" (:static t)");
+ }
+ if((access & Opcodes.ACC_VARARGS) != 0)
+ {
+ p.print(" (:varargs t)");
+ }
+ p.println();
+ p.print(" (:desc \"" + desc + "\")");
+ if(args.length > 0)
+ {
+ p.println();
+ p.print(" (:args");
+ for(int i = 0; i < args.length; i++)
+ {
+ Type arg = args[i];
+ p.print(" \"" + internalName(arg) + "\"");
+ }
+ p.print(')');
+ }
+ p.print(')');
+ }
+ return null;
+ }
+
+public void visitEnd()
+ {
+ if(!ignore)
+ p.println(')');
+ }
+}