diff options
-rw-r--r-- | clojurescript/PersistentVector-proto.js | 189 | ||||
-rw-r--r-- | clojurescript/PersistentVector-test.js | 118 | ||||
-rw-r--r-- | clojurescript/PersistentVector.js | 172 | ||||
-rw-r--r-- | clojurescript/README.txt | 17 | ||||
-rw-r--r-- | clojurescript/clojurescript-compiler.patch | 434 | ||||
-rw-r--r-- | clojurescript/test.html | 11 | ||||
-rw-r--r-- | clojurescript/tojs.clj | 145 |
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 |