aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--clojurescript/PersistentVector-proto.js189
-rw-r--r--clojurescript/PersistentVector-test.js118
-rw-r--r--clojurescript/PersistentVector.js172
-rw-r--r--clojurescript/README.txt17
-rw-r--r--clojurescript/clojurescript-compiler.patch434
-rw-r--r--clojurescript/test.html11
-rw-r--r--clojurescript/tojs.clj145
7 files changed, 1086 insertions, 0 deletions
diff --git a/clojurescript/PersistentVector-proto.js b/clojurescript/PersistentVector-proto.js
new file mode 100644
index 00000000..cbfdbee4
--- /dev/null
+++ b/clojurescript/PersistentVector-proto.js
@@ -0,0 +1,189 @@
+RT = { EMPTY_ARRAY: [] };
+
+function APersistentVector( _meta ) {
+ this._meta = _meta;
+}
+
+APersistentVector.prototype.meta = function() {
+ return this._meta;
+};
+
+APersistentVector.prototype.peek = function() {
+ if( this.count() > 0 ) {
+ return this.nth( this.count() - 1 );
+ }
+ return null;
+};
+
+function PersistentVector( _meta, cnt, shift, root, tail ) {
+ APersistentVector.call( this, _meta );
+ this._meta = _meta;
+ this.cnt = cnt;
+ this.shift = shift;
+ this.root = root;
+ this.tail = tail;
+}
+
+PersistentVector.prototype = new APersistentVector( null );
+PersistentVector.contraction = PersistentVector;
+
+PersistentVector.prototype.tailoff = function() {
+ return this.cnt - this.tail.length;
+};
+
+PersistentVector.prototype.nth = function( i ) {
+ if( i >= 0 && i < this.cnt ) {
+ if( i >= this.tailoff() ) {
+ return this.tail[ i & 0x01f ];
+ }
+ var arr = this.root;
+ for( var level = this.shift; level > 0; level -= 5 ) {
+ arr = arr[ (i >>> level) & 0x01f ];
+ }
+ return arr[ i & 0x01f ];
+ }
+ throw "IndexOutOfBoundsException";
+};
+
+PersistentVector.prototype.assocN = function( i, val ) {
+ if( i >= 0 && i < this.cnt ) {
+ if( i >= this.tailoff() ) {
+ var newTail = this.tail.slice( 0 );
+ newTail[ i & 0x01f ] = val;
+ return new PersistentVector(
+ this.meta(), this.cnt, this.shift, this.root, newTail );
+ }
+ return new PersistentVector(
+ this.meta(), this.cnt, this.shift,
+ this.doAssoc( this.shift, this.root, i, val), this.tail );
+ }
+ if( i == this.cnt ) {
+ return this.cons( val );
+ }
+ throw "IndexOutOfBoundsException";
+};
+
+PersistentVector.prototype.doAssoc = function( level, arr, i, val ) {
+ var ret = arr.slice( 0 );
+ if( level == 0 ) {
+ ret[ i & 0x01f ] = val;
+ }
+ else {
+ var subidx = (i >>> level) & 0x01f;
+ ret[ subidx ] = this.doAssoc( level - 5, arr[ subidx ], i, val );
+ }
+ return ret;
+};
+
+PersistentVector.prototype.count = function() {
+ return this.cnt;
+};
+
+PersistentVector.prototype.withMeta = function( _meta ) {
+ return new PersistentVector(
+ _meta, this.cnt, this.shift, this.root, this.tail );
+};
+
+PersistentVector.prototype.cons = function( val ) {
+ if( this.tail.length < 32 ) {
+ var newTail = this.tail.concat( [val] );
+ return new PersistentVector(
+ this.meta(), this.cnt + 1, this.shift, this.root, newTail );
+ }
+ var expansion = [null];
+ var newroot = this.pushTail( this.shift - 5, this.root, this.tail, expansion);
+ var newshift = this.shift;
+ if( expansion[0] != null ) {
+ newroot = [newroot, expansion[0]];
+ newshift += 5;
+ }
+ return new PersistentVector(
+ this.meta(), this.cnt+1, newshift, newroot, [val] );
+};
+
+PersistentVector.prototype.empty = function() {
+ return PersistentVector.EMPTY.withMeta( this.meta() );
+};
+
+PersistentVector.prototype.pushTail = function( level, arr, tailNode, expansion)
+{
+ var newchild;
+ if( level == 0 ) {
+ newchild = tailNode;
+ }
+ else {
+ newchild = this.pushTail(
+ level - 5, arr[arr.length - 1], tailNode, expansion);
+ if( expansion[0] == null ) {
+ var ret = arr.slice( 0 );
+ ret[ arr.length - 1 ] = newchild;
+ return ret;
+ }
+ else {
+ newchild = expansion[0];
+ }
+ }
+ //expansion
+ if( arr.length == 32 ) {
+ expansion[0] = [newchild];
+ return arr;
+ }
+ expansion[0] = null;
+ return arr.concat([newchild]);
+};
+
+PersistentVector.prototype.pop = function() {
+ if( this.cnt == 0 ) {
+ throw "IllegalStateException: Can't pop empty vector";
+ }
+ if( this.cnt == 1 ) {
+ return PersistentVector.EMPTY.withMeta( this.meta() );
+ }
+ if( this.tail.length > 1 ) {
+ var newTail = this.tail.slice( 0, this.tail.length - 1 );
+ return new PersistentVector(
+ this.meta(), this.cnt - 1, this.shift, this.root, newTail );
+ }
+ var ptail = [null];
+ var newroot = this.popTail( this.shift - 5, this.root, ptail );
+ var newshift = this.shift;
+ if( newroot == null ) {
+ newroot = RT.EMPTY_ARRAY;
+ }
+ if( this.shift > 5 && newroot.length == 1 ) {
+ newroot = newroot[0];
+ newshift -= 5;
+ }
+ return new PersistentVector(
+ this.meta(), this.cnt - 1, newshift, newroot, ptail[0] );
+};
+
+PersistentVector.prototype.popTail = function( shift, arr, ptail ) {
+ if( shift > 0 ) {
+ var newchild = this.popTail( shift - 5, arr[ arr.length - 1 ], ptail );
+ if( newchild != null ) {
+ var ret = arr.slice( 0 );
+ ret[ arr.length - 1 ] = newchild;
+ return ret;
+ }
+ }
+ if( shift == 0 ) {
+ ptail[0] = arr[ arr.length - 1 ];
+ }
+ //contraction
+ if( arr.length == 1 ) {
+ return null;
+ }
+ return arr.slice( 0, arr.length - 1 );
+};
+
+PersistentVector.EMPTY = new PersistentVector(
+ {}, 0, 5, RT.EMPTY_ARRAY, RT.EMPTY_ARRAY );
+
+PersistentVector.create = function( items ) {
+ var ret = PersistentVector.EMPTY;
+ for( var i = 0; i < items.length; ++i ) {
+ ret = ret.cons( items[ i ] );
+ }
+ return ret;
+}
diff --git a/clojurescript/PersistentVector-test.js b/clojurescript/PersistentVector-test.js
new file mode 100644
index 00000000..240aa129
--- /dev/null
+++ b/clojurescript/PersistentVector-test.js
@@ -0,0 +1,118 @@
+function vToString( v ) {
+ var a = new Array( v.count() );
+ for( var i = 0; i < v.count(); ++i ) {
+ a[ i ] = v.nth( i );
+ }
+ return ['[', a.join(' '), ']'].join('');
+}
+
+var v = PersistentVector.EMPTY;
+for( var i = 0; i < 100; ++i ) {
+ v = v.cons( i * 10 );
+}
+print( vToString( v ) );
+print( vToString( v.assocN( 20, 999 ) ) );
+
+var a = [];
+for( v2 = v; v2.count() > 0; v2 = v2.pop() ) {
+ a.push( v2.peek() );
+}
+print( a );
+
+v = PersistentVector.EMPTY;
+for( var i = 0; i < 100000; ++i ) { v = v.cons( i ); }
+for(; v.count() > 0; v = v.pop() ) { v.peek() };
+
+
+print( vToString( PersistentVector.create( [ 'a', 'b', 'c', 'd', 'e' ] ) ) );
+
+function time( msg, fn, reps ) {
+ reps = reps || 1;
+ var start = new Date();
+ var last;
+ for( var i = 0; i < reps; ++i ) {
+ last = fn();
+ }
+ var end = new Date();
+ print( msg + ': ' + (end - start) + ' msecs' );
+ return last;
+}
+
+var Rand = (function(){
+ var cycle = 1000000;
+ var rnd = new Array( cycle );
+ var idx = -1;
+ for( var i = 0; i < cycle; ++i ) {
+ rnd[i] = Math.random();
+ }
+ return {
+ reset: function() { idx = -1; },
+ next: function( r ) {
+ idx = (idx + 1) % cycle;
+ return Math.floor( rnd[ idx ] * r );
+ }
+ };
+})();
+
+function suite( size, writes, reads, reps ) {
+ print( "Suite size: " + size + ", writes: " + writes + ", reads: " + reads );
+
+ var a = [];
+ var p = PersistentVector.EMPTY;
+
+ time( " Array push", function() {
+ for( var i = 0; i < size; i++ ) {
+ a.push( i );
+ }
+ }, reps );
+
+ time( " PV cons ", function() {
+ for( var i = 0; i < size; i++ ) {
+ p = p.cons( i );
+ }
+ }, reps );
+
+ var ta = 0;
+ time( "Array", function() {
+ Rand.reset();
+ for( var i = 0; i < writes; ++i ) {
+ a[ Rand.next( size ) ] = i;
+ }
+ for( var j = 0; j < reads; ++j ) {
+ ta += a[ Rand.next( size ) ];
+ }
+ }, reps);
+
+ var tp = 0;
+ time( "PV ", function() {
+ Rand.reset();
+ for( var i = 0; i < writes; ++i ) {
+ p = p.assocN( Rand.next( size ), i );
+ }
+ for( var j = 0; j < reads; ++j ) {
+ tp += p.nth( Rand.next( size ) );
+ }
+ }, reps);
+
+ print( "Done: " + ta + ", " + tp + "\n" );
+}
+
+suite( 100000, 10000, 20000 );
+suite( 30, 10000, 20000, 1000 );
+suite( 100000, 10000, 0 );
+suite( 30, 10000, 0 );
+suite( 100000, 0, 20000 );
+suite( 30, 0, 20000 );
+
+/*
+var p = PersistentVector.EMPTY;
+for( var i = 0; i < 1088; i++ ) {
+//for( var i = 0; i < 1056; i++ ) {
+ p = p.cons( i );
+}
+print( p.nth( p.count() - 33 ) )
+print( p.cons("oops").nth( p.count() - 33 ) )
+*/
+
+//print( PersistentVector.EMPTY.constructor );
+print('done');
diff --git a/clojurescript/PersistentVector.js b/clojurescript/PersistentVector.js
new file mode 100644
index 00000000..26e60330
--- /dev/null
+++ b/clojurescript/PersistentVector.js
@@ -0,0 +1,172 @@
+RT = { EMPTY_ARRAY: [] };
+
+function APersistentVector( meta_arg ) {
+
+ this.meta = function() {
+ return meta_arg;
+ };
+
+ this.peek = function() {
+ if( this.count() > 0 ) {
+ return this.nth( this.count() - 1 );
+ }
+ return null;
+ };
+};
+
+function PersistentVector( meta_arg, cnt, shift, root, tail ) {
+ APersistentVector.call( this, meta_arg );
+
+ function tailoff() {
+ return cnt - tail.length;
+ };
+
+ this.nth = function( i ) {
+ if( i >= 0 && i < cnt ) {
+ if( i >= tailoff() ) {
+ return tail[ i & 0x01f ];
+ }
+ var arr = root;
+ for( var level = shift; level > 0; level -= 5 ) {
+ arr = arr[ (i >>> level) & 0x01f ];
+ }
+ return arr[ i & 0x01f ];
+ }
+ throw "IndexOutOfBoundsException";
+ };
+
+ this.assocN = function( i, val ) {
+ if( i >= 0 && i < cnt ) {
+ if( i >= tailoff() ) {
+ var newTail = tail.slice( 0 );
+ newTail[ i & 0x01f ] = val;
+ return new PersistentVector( this.meta(), cnt, shift, root, newTail );
+ }
+ return new PersistentVector(
+ this.meta(), cnt, shift, doAssoc( shift, root, i, val), tail );
+ }
+ if( i == cnt ) {
+ return this.cons( val );
+ }
+ throw "IndexOutOfBoundsException";
+ };
+
+ function doAssoc( level, arr, i, val ) {
+ var ret = arr.slice( 0 );
+ if( level == 0 ) {
+ ret[ i & 0x01f ] = val;
+ }
+ else {
+ var subidx = (i >>> level) & 0x01f;
+ ret[ subidx ] = doAssoc( level - 5, arr[ subidx ], i, val );
+ }
+ return ret;
+ };
+
+ this.count = function() {
+ return cnt;
+ };
+
+ this.withMeta = function( meta_arg ) {
+ return new PersistentVector( meta_arg, cnt, shift, root, tail );
+ };
+
+ this.cons = function( val ) {
+ if( tail.length < 32 ) {
+ var newTail = tail.concat( [val] );
+ return new PersistentVector( this.meta(), cnt + 1, shift, root, newTail );
+ }
+ var expansion = [null];
+ var newroot = pushTail( shift - 5, root, tail, expansion );
+ var newshift = shift;
+ if( expansion[0] != null ) {
+ newroot = [newroot, expansion[0]];
+ newshift += 5;
+ }
+ return new PersistentVector( this.meta(), cnt+1, newshift, newroot, [val] );
+ };
+
+ this.empty = function() {
+ return PersistentVector.EMPTY.withMeta( this.meta() );
+ };
+
+ function pushTail( level, arr, tailNode, expansion ) {
+ var newchild;
+ if( level == 0 ) {
+ newchild = tailNode;
+ }
+ else {
+ newchild = pushTail( level - 5, arr[arr.length - 1], tailNode, expansion);
+ if( expansion[0] == null ) {
+ var ret = arr.slice( 0 );
+ ret[ arr.length - 1 ] = newchild;
+ return ret;
+ }
+ else {
+ newchild = expansion[0];
+ }
+ }
+ //expansion
+ if( arr.length == 32 ) {
+ expansion[0] = [newchild];
+ return arr;
+ }
+ expansion[0] = null;
+ return arr.concat([newchild]);
+ };
+
+ this.pop = function() {
+ if( cnt == 0 ) {
+ throw "IllegalStateException: Can't pop empty vector";
+ }
+ if( cnt == 1 ) {
+ return PersistentVector.EMPTY.withMeta( this.meta() );
+ }
+ if( tail.length > 1 ) {
+ var newTail = tail.slice( 0, tail.length - 1 );
+ return new PersistentVector( this.meta(), cnt - 1, shift, root, newTail );
+ }
+ var ptail = [null];
+ var newroot = popTail( shift - 5, root, ptail );
+ var newshift = shift;
+ if( newroot == null ) {
+ newroot = RT.EMPTY_ARRAY;
+ }
+ if( shift > 5 && newroot.length == 1 ) {
+ newroot = newroot[0];
+ newshift -= 5;
+ }
+ return new PersistentVector(
+ this.meta(), cnt - 1, newshift, newroot, ptail[0] );
+ };
+
+ function popTail( shift, arr, ptail ) {
+ if( shift > 0 ) {
+ var newchild = popTail( shift - 5, arr[ arr.length - 1 ], ptail );
+ if( newchild != null ) {
+ var ret = arr.slice( 0 );
+ ret[ arr.length - 1 ] = newchild;
+ return ret;
+ }
+ }
+ if( shift == 0 ) {
+ ptail[0] = arr[ arr.length - 1 ];
+ }
+ //contraction
+ if( arr.length == 1 ) {
+ return null;
+ }
+ return arr.slice( 0, arr.length - 1 );
+ };
+}
+
+PersistentVector.EMPTY = new PersistentVector(
+ {}, 0, 5, RT.EMPTY_ARRAY, RT.EMPTY_ARRAY );
+
+PersistentVector.create = function( items ) {
+ var ret = PersistentVector.EMPTY;
+ for( var i = 0; i < items.length; ++i ) {
+ ret = ret.cons( items[ i ] );
+ }
+ return ret;
+}
diff --git a/clojurescript/README.txt b/clojurescript/README.txt
new file mode 100644
index 00000000..387a4b7d
--- /dev/null
+++ b/clojurescript/README.txt
@@ -0,0 +1,17 @@
+This directory contains work in progress on what may eventually become
+ClojureScript. If it worked, it would allow code written in a subset
+of Clojure to be automatically translated to JavaScript.
+
+The .js files are hand-written ports of Java included in Clojure, or
+hand-written tests for the above.
+
+tojs.clj is Clojure code to translate Clojure forms to Javascript.
+
+clojurescript-compiler.patch is a patch file to add features to
+Clojure that are required by tojs.clj.
+
+There's plenty more to do. If you'd like to help, contact the Clojure
+Google group: clojure@googlegroups.com
+
+--Chouser
+12 Sept 2008
diff --git a/clojurescript/clojurescript-compiler.patch b/clojurescript/clojurescript-compiler.patch
new file mode 100644
index 00000000..532c2646
--- /dev/null
+++ b/clojurescript/clojurescript-compiler.patch
@@ -0,0 +1,434 @@
+diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
+index afb25de..fd3cc49 100644
+--- a/src/jvm/clojure/lang/Compiler.java
++++ b/src/jvm/clojure/lang/Compiler.java
+@@ -195,7 +195,7 @@ static final public Var RET_LOCAL_NUM = Var.create();
+ //DynamicClassLoader
+ static final public Var LOADER = Var.create();
+
+-enum C{
++public enum C{
+ STATEMENT, //value ignored
+ EXPRESSION, //value required
+ RETURN, //tail position relative to enclosing recur frame
+@@ -212,7 +212,7 @@ interface Expr{
+ Class getJavaClass() throws Exception;
+ }
+
+-static abstract class UntypedExpr implements Expr{
++public static abstract class UntypedExpr implements Expr{
+
+ public Class getJavaClass(){
+ throw new IllegalArgumentException("Has no Java class");
+@@ -256,11 +256,11 @@ static Symbol resolveSymbol(Symbol sym){
+
+ }
+
+-static class DefExpr implements Expr{
+- final Var var;
+- final Expr init;
+- final Expr meta;
+- final boolean initProvided;
++public static class DefExpr implements Expr{
++ public final Var var;
++ public final Expr init;
++ public final Expr meta;
++ public final boolean initProvided;
+ final static Method bindRootMethod = Method.getMethod("void bindRoot(Object)");
+ final static Method setTagMethod = Method.getMethod("void setTag(clojure.lang.Symbol)");
+ final static Method setMetaMethod = Method.getMethod("void setMeta(clojure.lang.IPersistentMap)");
+@@ -341,10 +341,9 @@ static class DefExpr implements Expr{
+ }
+ }
+
+-static class AssignExpr implements Expr{
+- final AssignableExpr target;
+- final Expr val;
+-
++public static class AssignExpr implements Expr{
++ public final AssignableExpr target;
++ public final Expr val;
+
+ public AssignExpr(AssignableExpr target, Expr val){
+ this.target = target;
+@@ -380,9 +379,9 @@ static class AssignExpr implements Expr{
+ }
+ }
+
+-static class VarExpr implements Expr, AssignableExpr{
+- final Var var;
+- final Object tag;
++public static class VarExpr implements Expr, AssignableExpr{
++ public final Var var;
++ public final Object tag;
+ final static Method getMethod = Method.getMethod("Object get()");
+ final static Method setMethod = Method.getMethod("Object set(Object)");
+
+@@ -426,8 +425,8 @@ static class VarExpr implements Expr, AssignableExpr{
+ }
+ }
+
+-static class TheVarExpr implements Expr{
+- final Var var;
++public static class TheVarExpr implements Expr{
++ public final Var var;
+
+ public TheVarExpr(Var var){
+ this.var = var;
+@@ -462,8 +461,8 @@ static class TheVarExpr implements Expr{
+ }
+ }
+
+-static class KeywordExpr implements Expr{
+- final Keyword k;
++public static class KeywordExpr implements Expr{
++ public final Keyword k;
+
+ public KeywordExpr(Keyword k){
+ this.k = k;
+@@ -489,7 +488,7 @@ static class KeywordExpr implements Expr{
+ }
+ }
+
+-static abstract class LiteralExpr implements Expr{
++public static abstract class LiteralExpr implements Expr{
+ abstract Object val();
+
+ public Object eval(){
+@@ -909,9 +908,9 @@ static class InstanceFieldExpr extends FieldExpr implements AssignableExpr{
+
+ static class StaticFieldExpr extends FieldExpr implements AssignableExpr{
+ //final String className;
+- final String fieldName;
+- final Class c;
+- final java.lang.reflect.Field field;
++ public final String fieldName;
++ public final Class c;
++ public final java.lang.reflect.Field field;
+ final static Method getStaticFieldMethod = Method.getMethod("Object getStaticField(String,String)");
+ final static Method setStaticFieldMethod = Method.getMethod("Object setStaticField(String,String,Object)");
+ final int line;
+@@ -1171,11 +1170,11 @@ static class InstanceMethodExpr extends MethodExpr{
+
+ static class StaticMethodExpr extends MethodExpr{
+ //final String className;
+- final Class c;
+- final String methodName;
+- final IPersistentVector args;
+- final int line;
+- final java.lang.reflect.Method method;
++ public final Class c;
++ public final String methodName;
++ public final IPersistentVector args;
++ public final int line;
++ public final java.lang.reflect.Method method;
+ final static Method invokeStaticMethodMethod =
+ Method.getMethod("Object invokeStaticMethod(String,String,Object[])");
+
+@@ -1284,8 +1283,8 @@ static class StaticMethodExpr extends MethodExpr{
+ static class ConstantExpr extends LiteralExpr{
+ //stuff quoted vals in classloader at compile time, pull out at runtime
+ //this won't work for static compilation...
+- final Object v;
+- final int id;
++ public final Object v;
++ public final int id;
+
+ public ConstantExpr(Object v){
+ this.v = v;
+@@ -1404,7 +1403,7 @@ final static BooleanExpr TRUE_EXPR = new BooleanExpr(true);
+ final static BooleanExpr FALSE_EXPR = new BooleanExpr(false);
+
+ static class StringExpr extends LiteralExpr{
+- final String str;
++ public final String str;
+
+ public StringExpr(String str){
+ this.str = str;
+@@ -1575,12 +1574,12 @@ static class MonitorExitExpr extends UntypedExpr{
+
+ }
+
+-static class TryExpr implements Expr{
+- final Expr tryExpr;
+- final Expr finallyExpr;
+- final PersistentVector catchExprs;
+- final int retLocal;
+- final int finallyLocal;
++public static class TryExpr implements Expr{
++ public final Expr tryExpr;
++ public final Expr finallyExpr;
++ public final PersistentVector catchExprs;
++ public final int retLocal;
++ public final int finallyLocal;
+
+ static class CatchClause{
+ //final String className;
+@@ -1955,10 +1954,10 @@ static int getMatchingParams(String methodName, ArrayList<Class[]> paramlists, I
+ return matchIdx;
+ }
+
+-static class NewExpr implements Expr{
+- final IPersistentVector args;
+- final Constructor ctor;
+- final Class c;
++public static class NewExpr implements Expr{
++ public final IPersistentVector args;
++ public final Constructor ctor;
++ public final Class c;
+ final static Method invokeConstructorMethod =
+ Method.getMethod("Object invokeConstructor(Class,Object[])");
+ final static Method forNameMethod = Method.getMethod("Class classForName(String)");
+@@ -2170,9 +2169,9 @@ static class NewExpr implements Expr{
+ // }
+ //}
+
+-static class MetaExpr implements Expr{
+- final Expr expr;
+- final MapExpr meta;
++public static class MetaExpr implements Expr{
++ public final Expr expr;
++ public final MapExpr meta;
+ final static Type IOBJ_TYPE = Type.getType(IObj.class);
+ final static Method withMetaMethod = Method.getMethod("clojure.lang.IObj withMeta(clojure.lang.IPersistentMap)");
+
+@@ -2207,11 +2206,11 @@ static class MetaExpr implements Expr{
+ }
+ }
+
+-static class IfExpr implements Expr{
+- final Expr testExpr;
+- final Expr thenExpr;
+- final Expr elseExpr;
+- final int line;
++public static class IfExpr implements Expr{
++ public final Expr testExpr;
++ public final Expr thenExpr;
++ public final Expr elseExpr;
++ public final int line;
+
+
+ public IfExpr(int line, Expr testExpr, Expr thenExpr, Expr elseExpr){
+@@ -2336,8 +2335,8 @@ static public String munge(String name){
+ return sb.toString();
+ }
+
+-static class EmptyExpr implements Expr{
+- final Object coll;
++public static class EmptyExpr implements Expr{
++ public final Object coll;
+ final static Type HASHMAP_TYPE = Type.getType(PersistentHashMap.class);
+ final static Type HASHSET_TYPE = Type.getType(PersistentHashSet.class);
+ final static Type VECTOR_TYPE = Type.getType(PersistentVector.class);
+@@ -2388,8 +2387,8 @@ static class EmptyExpr implements Expr{
+ }
+ }
+
+-static class ListExpr implements Expr{
+- final IPersistentVector args;
++public static class ListExpr implements Expr{
++ public final IPersistentVector args;
+ final static Method arrayToListMethod = Method.getMethod("clojure.lang.ISeq arrayToList(Object[])");
+
+
+@@ -2421,8 +2420,8 @@ static class ListExpr implements Expr{
+
+ }
+
+-static class MapExpr implements Expr{
+- final IPersistentVector keyvals;
++public static class MapExpr implements Expr{
++ public final IPersistentVector keyvals;
+ final static Method mapMethod = Method.getMethod("clojure.lang.IPersistentMap map(Object[])");
+
+
+@@ -2470,8 +2469,8 @@ static class MapExpr implements Expr{
+ }
+ }
+
+-static class SetExpr implements Expr{
+- final IPersistentVector keys;
++public static class SetExpr implements Expr{
++ public final IPersistentVector keys;
+ final static Method setMethod = Method.getMethod("clojure.lang.IPersistentSet set(Object[])");
+
+
+@@ -2518,8 +2517,8 @@ static class SetExpr implements Expr{
+ }
+ }
+
+-static class VectorExpr implements Expr{
+- final IPersistentVector args;
++public static class VectorExpr implements Expr{
++ public final IPersistentVector args;
+ final static Method vectorMethod = Method.getMethod("clojure.lang.IPersistentVector vector(Object[])");
+
+
+@@ -2563,11 +2562,11 @@ static class VectorExpr implements Expr{
+
+ }
+
+-static class InvokeExpr implements Expr{
+- final Expr fexpr;
+- final Object tag;
+- final IPersistentVector args;
+- final int line;
++public static class InvokeExpr implements Expr{
++ public final Expr fexpr;
++ public final Object tag;
++ public final IPersistentVector args;
++ public final int line;
+
+ public InvokeExpr(int line, Symbol tag, Expr fexpr, IPersistentVector args){
+ this.fexpr = fexpr;
+@@ -2661,7 +2660,7 @@ static public class FnExpr implements Expr{
+ String internalName;
+ String thisName;
+ Type fntype;
+- final Object tag;
++ public final Object tag;
+ //localbinding->itself
+ IPersistentMap closes = PersistentHashMap.EMPTY;
+ //Keyword->KeywordExpr
+@@ -2671,6 +2670,20 @@ static public class FnExpr implements Expr{
+ int line;
+ PersistentVector constants;
+ int constantsID;
++ public final IPersistentCollection methods() { return methods;}
++ public final FnMethod variadicMethod() { return variadicMethod;}
++ public final String name() { return name;}
++ public final String simpleName() { return simpleName;}
++ public final String internalName() { return internalName;}
++ public final String thisName() { return thisName;}
++ public final Type fntype() { return fntype;}
++ public final IPersistentMap closes() { return closes;}
++ public final IPersistentMap keywords() { return keywords;}
++ public final IPersistentMap vars() { return vars;}
++ public final Class compiledClass() { return compiledClass;}
++ public final int line() { return line;}
++ public final PersistentVector constants() { return constants;}
++ public final int constantsID() { return constantsID;}
+
+ final static Method kwintern = Method.getMethod("clojure.lang.Keyword intern(String, String)");
+ final static Method symcreate = Method.getMethod("clojure.lang.Symbol create(String)");
+@@ -3098,10 +3111,10 @@ enum PSTATE{
+ }
+
+
+-static class FnMethod{
++public static class FnMethod{
+ //when closures are defined inside other closures,
+ //the closed over locals need to be propagated to the enclosing fn
+- final FnMethod parent;
++ public final FnMethod parent;
+ //localbinding->localbinding
+ IPersistentMap locals = null;
+ //localbinding->localbinding
+@@ -3113,6 +3126,14 @@ static class FnMethod{
+ int maxLocal = 0;
+ int line;
+ PersistentHashSet localsUsedInCatchFinally = PersistentHashSet.EMPTY;
++ public final IPersistentMap locals() { return locals;}
++ public final PersistentVector reqParms() { return reqParms;}
++ public final LocalBinding restParm() { return restParm;}
++ public final Expr body() { return body;}
++ public final FnExpr fn() { return fn;}
++ public final PersistentVector argLocals() { return argLocals;}
++ public final int maxLocal() { return maxLocal;}
++ public final int line() { return line;}
+
+ public FnMethod(FnExpr fn, FnMethod parent){
+ this.parent = parent;
+@@ -3252,12 +3273,12 @@ static class FnMethod{
+ }
+ }
+
+-static class LocalBinding{
+- final Symbol sym;
+- final Symbol tag;
+- final Expr init;
+- final int idx;
+- final String name;
++public static class LocalBinding{
++ public final Symbol sym;
++ public final Symbol tag;
++ public final Expr init;
++ public final int idx;
++ public final String name;
+
+ public LocalBinding(int num, Symbol sym, Symbol tag, Expr init) throws Exception{
+ if(maybePrimitiveType(init) != null && tag != null)
+@@ -3288,9 +3309,9 @@ static class LocalBinding{
+ }
+ }
+
+-static class LocalBindingExpr implements Expr, MaybePrimitiveExpr{
+- final LocalBinding b;
+- final Symbol tag;
++public static class LocalBindingExpr implements Expr, MaybePrimitiveExpr{
++ public final LocalBinding b;
++ public final Symbol tag;
+
+ public LocalBindingExpr(LocalBinding b, Symbol tag) throws Exception{
+ if(b.getPrimitiveType() != null && tag != null)
+@@ -3325,8 +3346,9 @@ static class LocalBindingExpr implements Expr, MaybePrimitiveExpr{
+
+ }
+
+-static class BodyExpr implements Expr{
++public static class BodyExpr implements Expr{
+ PersistentVector exprs;
++ public final PersistentVector exprs() { return exprs;}
+
+ public BodyExpr(PersistentVector exprs){
+ this.exprs = exprs;
+@@ -3386,9 +3408,11 @@ static class BodyExpr implements Expr{
+ }
+ }
+
+-static class BindingInit{
++public static class BindingInit{
+ LocalBinding binding;
+ Expr init;
++ public final LocalBinding binding() { return binding;}
++ public final Expr init() { return init;}
+
+ public BindingInit(LocalBinding binding, Expr init){
+ this.binding = binding;
+@@ -3396,10 +3420,10 @@ static class BindingInit{
+ }
+ }
+
+-static class LetExpr implements Expr{
+- final PersistentVector bindingInits;
+- final Expr body;
+- final boolean isLoop;
++public static class LetExpr implements Expr{
++ public final PersistentVector bindingInits;
++ public final Expr body;
++ public final boolean isLoop;
+
+ public LetExpr(PersistentVector bindingInits, Expr body, boolean isLoop){
+ this.bindingInits = bindingInits;
+@@ -3522,9 +3546,9 @@ static class LetExpr implements Expr{
+ }
+ }
+
+-static class RecurExpr implements Expr{
+- final IPersistentVector args;
+- final IPersistentVector loopLocals;
++public static class RecurExpr implements Expr{
++ public final IPersistentVector args;
++ public final IPersistentVector loopLocals;
+
+ public RecurExpr(IPersistentVector loopLocals, IPersistentVector args){
+ this.loopLocals = loopLocals;
+@@ -3626,7 +3650,7 @@ private static int getAndIncLocalNum(){
+ return num;
+ }
+
+-private static Expr analyze(C context, Object form) throws Exception{
++public static Expr analyze(C context, Object form) throws Exception{
+ return analyze(context, form, null);
+ }
+
diff --git a/clojurescript/test.html b/clojurescript/test.html
new file mode 100644
index 00000000..f277f414
--- /dev/null
+++ b/clojurescript/test.html
@@ -0,0 +1,11 @@
+<html>
+ <body>
+ <textarea rows="24" cols="80" id="ta"></textarea>
+ <script type="text/javascript">
+ var ta = document.getElementById( 'ta' );
+ function print( x ) { ta.value += x + "\n"; }
+ </script>
+ <script type="text/javascript" src="PersistentVector-proto.js"></script>
+ <script type="text/javascript" src="PersistentVector-test.js"></script>
+ </body>
+</html>
diff --git a/clojurescript/tojs.clj b/clojurescript/tojs.clj
new file mode 100644
index 00000000..867aa3b9
--- /dev/null
+++ b/clojurescript/tojs.clj
@@ -0,0 +1,145 @@
+(ns tojs
+ (:import (clojure.lang Compiler Compiler$C))
+ (:require [clojure.contrib.duck-streams :as ds]))
+
+(defn vstr [v]
+ (let [sb (StringBuilder.)
+ lvl (fn lvl [v]
+ (doseq i v
+ (if (vector? i)
+ (lvl i)
+ (.append sb (str i)))))]
+ (lvl v)
+ (str sb)))
+
+(defmulti tojs (fn [e ctx] (class e)))
+
+(defmethod tojs clojure.lang.Var [e ctx]
+ (-> e str (.substring 2) (.replace "/" ".")))
+
+(defmethod tojs clojure.lang.Compiler$DefExpr [e ctx]
+ (str (tojs (.var e) ctx) "=" (tojs (.init e) ctx) ";\n"))
+
+(defn fnmethod [fm ctx]
+ (let [lm (into {} (map (fn [[lb lb] i] [lb (str (.name lb) "_" i)])
+ (.locals fm) (iterate inc 0)))]
+ (vstr
+ [
+ "var " (vec (interpose "," (vals lm))) ";\n"
+ (vec (for [lb (.reqParms fm)]
+ [(lm lb) "=arguments[" (dec (.idx lb)) "];\n"]))
+ (when-let lb (.restParm fm)
+ ["var " (lm lb) "=clojure.lang.ArraySeq.create(arguments).drop("
+ (count (.reqParms fm)) ");\n"])
+ "return (" (tojs (.body fm) (assoc ctx :localmap lm)) ")"])))
+
+(defmethod tojs clojure.lang.Compiler$FnExpr [e ctx]
+ (vstr ["function " (.thisName e) "(){\n"
+ (vec (for [fm (.methods e) :when (not= fm (.variadicMethod e))]
+ ["if(arguments.length=" (count (.argLocals fm)) "){\n"
+ (fnmethod fm ctx)
+ "}\n"]))
+ (if (.variadicMethod e)
+ [(fnmethod (.variadicMethod e) ctx) "\n"]
+ ["throw \"Wrong number of args passed to: " (.thisName e) "\";\n"])
+ "}"]))
+
+(defmethod tojs clojure.lang.Compiler$BodyExpr [e ctx]
+ (apply str (interpose ",\n" (map #(tojs % ctx) (.exprs e)))))
+
+(defmethod tojs clojure.lang.Compiler$LetExpr [e ctx]
+ (vstr ["("
+ (vec (for [bi (.bindingInits e)]
+ ["(" ((:localmap ctx) (.binding bi))
+ "=" (tojs (.init bi) ctx) "),\n"]))
+ (tojs (.body e) ctx)
+ ")"]))
+
+;(defmethod tojs clojure.lang.Compiler$LetExpr [e ctx]
+; (let [names (map #(.name (.binding %)) (.bindingInits e))
+; inits (map #(.init %) (.bindingInits e))]
+; (vstr ["(function("
+; (vec (interpose ",\n" names))
+; "){\n" (tojs (.body e) ctx) "})(\n "
+; (vec (interpose ",\n " (map tojs inits)))
+; ");\n"])))
+
+(defmethod tojs clojure.lang.Compiler$VectorExpr [e ctx]
+ (vstr ["clojure.lang.PersistentVector.create(["
+ (vec (interpose "," (map #(tojs % ctx) (.args e))))
+ "])"]))
+
+(defmethod tojs clojure.lang.Compiler$ConstantExpr [e ctx]
+ (if (symbol? (.v e))
+ (str \" (.v e) \")
+ (str (.v e))))
+
+(defmethod tojs clojure.lang.Compiler$InvokeExpr [e ctx]
+ (vstr [(tojs (.fexpr e) ctx)
+ "("
+ (vec (interpose "," (map #(tojs % ctx) (.args e))))
+ ")"]))
+
+(defmethod tojs clojure.lang.Compiler$LocalBindingExpr [e ctx]
+ ((:localmap ctx) (.b e)))
+
+(defmethod tojs clojure.lang.Compiler$NilExpr [e ctx]
+ "null")
+
+(defmethod tojs clojure.lang.Compiler$StringExpr [e ctx]
+ (str \" (.str e) \"))
+
+(defmethod tojs clojure.lang.Compiler$VarExpr [e ctx]
+ (tojs (.var e) ctx))
+
+(defmethod tojs clojure.lang.Compiler$StaticFieldExpr [e ctx]
+ (str (.getCanonicalName (.c e)) "." (.fieldName e)))
+
+(defmethod tojs clojure.lang.Compiler$StaticMethodExpr [e ctx]
+ (vstr [(.getCanonicalName (.c e)) "." (.methodName e) "("
+ (vec (interpose "," (map #(tojs % ctx) (.args e))))
+ ")"]))
+
+(defmethod tojs clojure.lang.Compiler$IfExpr [e ctx]
+ (str "(" (tojs (.testExpr e) ctx)
+ "?" (tojs (.thenExpr e) ctx)
+ ":" (tojs (.elseExpr e) ctx) ")"))
+
+(defn formtojs [f]
+ (tojs (Compiler/analyze Compiler$C/STATEMENT f) {}))
+
+(defn testboot []
+ (let [boot "/home/chouser/build/clojure/src/clj/clojure/boot.clj"
+ bootreader (java.io.Pu