diff options
author | Rich Hickey <richhickey@gmail.com> | 2006-06-11 15:40:13 +0000 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2006-06-11 15:40:13 +0000 |
commit | b58d5badd4ed397f0f6e9be306ac12311c7049d9 (patch) | |
tree | e523068b29066fdfe14b5422af508179bbbf59b7 /src | |
parent | c66573864e1cdc9ba5cbef711ce70f0137c50f02 (diff) |
Diffstat (limited to 'src')
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(')'); + } +} |