aboutsummaryrefslogtreecommitdiff
path: root/clojurescript/clj.js
diff options
context:
space:
mode:
authorChouser <chouser@n01se.net>2008-09-27 16:04:16 +0000
committerChouser <chouser@n01se.net>2008-09-27 16:04:16 +0000
commit0dd11ec2d6d20b5272deba4436c65db661e93e74 (patch)
treedf1aa98b3e8ba7c1cf79ef3f42a99ba8050622f8 /clojurescript/clj.js
parentaf98c8231c19fef92164602220ca158cb98cc82f (diff)
ClojureScript: add classes Keyword, PersistentHashMap, PersistentHashSet, MultiFn; mutlimethod print now works; add many functions to clojure runtime; support calling keywords and collections; fixed several small bugs including extra null arg on some variadic calls
Diffstat (limited to 'clojurescript/clj.js')
-rw-r--r--clojurescript/clj.js893
1 files changed, 862 insertions, 31 deletions
diff --git a/clojurescript/clj.js b/clojurescript/clj.js
index 12f2bbed..1dcc067c 100644
--- a/clojurescript/clj.js
+++ b/clojurescript/clj.js
@@ -38,7 +38,7 @@ clojure = new clojure.lang.Namespace({
(typeof coll) + " " + coll.constructor.name);
},
apply: function( f ) {
- if( f.isVariatic ) {
+ if( f.isVariadic ) {
// lazy
var i, args = [];
var eagercount = Math.min( f.arity, arguments.length - 2 );
@@ -61,9 +61,10 @@ clojure = new clojure.lang.Namespace({
for( ; s && args.length < f.arity; s = s.rest() ) {
args.push( s.first() );
}
- args.push( s );
+ if( s )
+ args.push( s );
}
- return f.apply( clojure.JS.variatic_sentinel, args );
+ return f.apply( clojure.JS.variadic_sentinel, args );
}
else {
// non-lazy
@@ -103,13 +104,44 @@ clojure = new clojure.lang.Namespace({
}
return coll.entryAt( key );
},
- hash_map: function() {
- // This just makes a seq for now
- var pairs = [];
- for( var i = 0; i < arguments.length; i += 2 ) {
- pairs.push( [ arguments[i], arguments[i + 1] ] )
+ get: function(coll, key, notFound ) {
+ if( coll === null )
+ return null;
+ if( coll.valAt )
+ return coll.valAt( key, notFound );
+ if( coll.containsKey ) {
+ if( notFound === undefined || coll.containsKey( key ) )
+ return coll.get( key );
+ return notFound;
+ }
+ if( coll.contains ) {
+ if( notFound === undefined || coll.contains( key ) )
+ return coll.get( key );
+ return notFound;
}
- return clojure.lang.ArraySeq.create( pairs );
+ if( notFound === undefined || key in coll )
+ return coll[ key ];
+ return notFound;
+ },
+ contains_QMARK_: function(coll, key) {
+ if( coll === null )
+ return false;
+ if( coll.containsKey )
+ return coll.containsKey( key ) ? true : false;
+ if( coll.contains )
+ return coll.contains( key ) ? true : false;
+ return key in coll;
+ },
+ hash_map: function() {
+ return clojure.lang.PersistentHashMap.create( arguments );
+ },
+ hash_set: function() {
+ return clojure.lang.PersistentHashSet.create( arguments );
+ },
+ keyword: function(a,b) {
+ if( b === undefined )
+ return clojure.lang.Keyword.intern( "", a );
+ return clojure.lang.Keyword.intern( a, b );
},
assoc: function( coll, key, val ) {
if( coll === null )
@@ -122,18 +154,29 @@ clojure = new clojure.lang.Namespace({
if( x.length != undefined ) return x.length;
throw ("count not supported on: " + (typeof x) + " " + x.constructor);
},
+ class_: function(o) {
+ if( o === null )
+ return null;
+ return o.constructor || typeof o;
+ },
import_: function() {
// do nothing
},
identical_QMARK_: function( a, b ) {
return a === b;
},
+ keys: function(coll) {
+ return clojure.lang.APersistentMap.KeySeq.create(clojure.seq(coll));
+ },
+ vals: function(coll) {
+ return clojure.lang.APersistentMap.ValSeq.create(clojure.seq(coll));
+ },
JS: {
merge: clojure.JS.merge,
global: clojure.JS.global,
- variatic: function( arity, f ) {
+ variadic: function( arity, f ) {
f.arity = arity;
- f.isVariatic = true;
+ f.isVariadic = true;
return f;
},
resolveVar: function( sym, ctxns ) {
@@ -145,23 +188,29 @@ clojure = new clojure.lang.Namespace({
v.push( init );
return v;
},
- variatic_sentinel: {},
+ variadic_sentinel: {},
rest_args: function( varflag, args, i ) {
- if( varflag === clojure.JS.variatic_sentinel )
+ if( varflag === clojure.JS.variadic_sentinel )
return args[ args.length - 1 ];
return new clojure.lang.ArraySeq( null, args, i );
},
+ invoke: function( obj, methodname, args ) {
+ return obj[ methodname ].apply( obj, args );
+ },
lit_list: function( a ) {
return new clojure.lang.ArraySeq( null, a, 0 );
},
implement: function( cls, name, extend, implement ) {
cls.classname = name;
cls.classset = {};
+ cls.hashCode = function() { return clojure.lang.Util.hash( name ); };
+ cls.getSuperclass = function() { return extend || null; };
+ cls.getInterfaces = function() { return implement || null; };
cls.classset[ name ] = true;
if( implement ) {
for( var i = 0; i < implement.length; ++i ) {
if( ! implement[ i ] )
- throw "Can't implement null";
+ throw "Can't implement null: " + name;
clojure.JS.merge( cls.classset, implement[ i ].classset );
}
}
@@ -188,7 +237,16 @@ clojure = new clojure.lang.Namespace({
return false;
if( o.constructor === c )
return true;
- return o.constructor.classset[ c ];
+ if( ! o.constructor.classset )
+ return false; // builtin class that doesn't match?
+ return o.constructor.classset[ c.classname ];
+ },
+ bitcount: function(n){
+ var rtn = 0;
+ for( ; n; n >>= 1) {
+ rtn += n & 1;
+ }
+ return rtn;
},
ObjSeq: {
create: function( obj ) {
@@ -204,10 +262,33 @@ clojure = new clojure.lang.Namespace({
Namespace: clojure.lang.Namespace,
Numbers: {
isPos: function(x) { return x > 0; },
+ lt: function(x,y) { return x < y; },
+ gt: function(x,y) { return x > y; },
inc: function(x) { return x + 1; },
- dec: function(x) { return x - 1; }
+ dec: function(x) { return x - 1; },
+ unchecked_inc: function(x) { return x + 1; }
},
Util: {
+ hash: function(o){
+ if( o === null )
+ return 0;
+ if( typeof o == typeof "" ) {
+ var ret = 0;
+ for( var i = 0; i < o.length; ++i ) {
+ ret ^= o.charCodeAt(i) << ((i % 4) * 8);
+ }
+ return ret;
+ }
+ if( o == String )
+ return clojure.lang.Util.hash("stringtype");
+ if( o == Number )
+ return clojure.lang.Util.hash("numbertype");
+ if( o == RegExp )
+ return clojure.lang.Util.hash("regexptype");
+ if( o == Object )
+ return clojure.lang.Util.hash("objecttype");
+ return o.hashCode();
+ },
equal: function(x,y) { return x == y; },
isInteger: function(x) { return typeof x == typeof 0; }
},
@@ -230,18 +311,21 @@ clojure = new clojure.lang.Namespace({
ret[ i ] = s.first();
return ret;
},
- print: function( o, w ) {
- if( o !== null )
- w.write( "" + o );
- }
- },
- IReduce: {}
+ intCast: function(i) {
+ return parseInt(i);
+ },
+ }
}
});
-java = { util: {}, lang: {} };
+java = { util: { regex: {} }, lang: {}, math: {} };
clojure.JS.definterface( java.util, "Map" );
clojure.JS.definterface( java.util, "Collection" );
+clojure.JS.definterface( java.util.regex, "Pattern" );
+clojure.JS.definterface( java.lang, "Character" );
+clojure.JS.definterface( java.lang, "Class" );
+clojure.JS.definterface( java.math, "BigDecimal", [Number] );
+clojure.JS.definterface( java.math, "BigInteger", [Number] );
clojure.JS.defclass( java.lang, "StringBuilder", {
init: function( x ) { this.a = [ x ]; },
methods: {
@@ -250,6 +334,17 @@ clojure.JS.defclass( java.lang, "StringBuilder", {
}
});
+clojure.JS.defclass( java.lang, "String", {
+ init: function(s) {
+ this.s = s;
+ this.length = s.length;
+ },
+ methods: {
+ charAt: function(x) { return this.s.charAt(x); },
+ toString: function() { return this.s; }
+ }
+});
+
clojure.JS.definterface( clojure.lang, "IObj" );
clojure.JS.defclass( clojure.lang, "Obj", {
@@ -260,6 +355,16 @@ clojure.JS.defclass( clojure.lang, "Obj", {
}
});
+clojure.JS.definterface( clojure.lang, "IReduce" );
+
+clojure.JS.definterface( clojure.lang, "IPersistentCollection" );
+
+clojure.JS.definterface( clojure.lang, "ISeq",
+ [clojure.lang.IPersistentCollection] );
+
+clojure.JS.definterface( clojure.lang, "IndexedSeq",
+ [clojure.lang.ISeq] );
+
clojure.JS.defclass( clojure.lang, "ASeq", {
methods: {
equals: function( obj ) {
@@ -309,6 +414,7 @@ clojure.JS.defclass( clojure.lang, "Cons", {
clojure.JS.defclass( clojure.lang, "ArraySeq", {
extend: clojure.lang.ASeq,
+ implement: [clojure.lang.IndexedSeq, clojure.lang.IReduce],
init: function( _meta, a, i, len ) {
this._meta = _meta;
this.a = a;
@@ -398,9 +504,9 @@ clojure.JS.defclass( clojure.lang, "Var", {
pushThreadBindings: function( m ) {
var vars=[], b;
for( var bs = m.seq(); bs; bs = bs.rest()) {
- b = bs.first();
- vars.push( b[0] );
- b[0].push( b[1] );
+ e = bs.first();
+ vars.push( e.key() );
+ e.key().push( e.val() );
}
clojure.lang.Var.stack.push( vars );
},
@@ -425,11 +531,23 @@ clojure.JS.defclass( clojure.lang, "Var", {
this.push( val );
},
hasRoot: function() { return this.stack.length > 0; },
- setMacro: function() { this.macro = true; }
+ hashCode: function() {
+ return clojure.lang.Util.hash( this.ns + "/" + this.name );
+ }
}
});
-clojure.JS.definterface( clojure.lang, "IPersistentCollection" );
+clojure.JS.definterface( clojure.lang, "IFn" );
+
+clojure.JS.defclass( clojure.lang, "AFn", {
+ extend: clojure.lang.Obj,
+ implement: [clojure.lang.IFn],
+ methods: {
+ apply: function( obj, args ){
+ return this.invoke.apply( this, args );
+ }
+ }
+});
clojure.JS.definterface( clojure.lang, "IPersistentStack",
[clojure.lang.IPersistentCollection] );
@@ -438,6 +556,54 @@ clojure.JS.definterface( clojure.lang, "Sequential" );
clojure.JS.definterface( clojure.lang, "Reversible" );
+clojure.JS.definterface( clojure.lang, "Named" );
+
+clojure.JS.defclass( clojure.lang, "Keyword", {
+ extend: clojure.lang.AFn,
+ implement: [clojure.lang.Named],
+ init: function( ns, name ) {
+ this._ns = ns;
+ this._name = name;
+ },
+ statics: {
+ table: {},
+ intern: function( ns, name ) {
+ var key = ns + "/" + name;
+ var obj = clojure.lang.Keyword.table[ key ];
+ if( obj )
+ return obj;
+ return clojure.lang.Keyword.table[ key ] =
+ new clojure.lang.Keyword( ns, name );
+ }
+ },
+ methods: {
+ toString: function() {
+ return ":" + (this.ns ? this.ns+"/" : "") + this._name;
+ },
+ compareTo: function(o) {
+ if( this == o )
+ return 0;
+ if( this._ns === null && o._ns !== null )
+ return -1;
+ if( this._ns !== null ) {
+ if( o._ns === null )
+ return 1;
+ var nsc = clojure.JS.compare(this._ns, o._ns);
+ if( nsc !== 0 )
+ return nsc;
+ }
+ return clojure.JS.compare(this._name, o._name);
+ },
+ getNamespace: function() { return this._ns; },
+ getName: function() { return this._name; },
+ hashCode: function() {
+ return clojure.lang.Util.hash( this._ns + "/" + this._name );
+ },
+ invoke: function(coll, notFound) { return clojure.get( coll,this,notFound);}
+ }
+});
+
+
clojure.JS.definterface( clojure.lang, "IPersistentList",
[clojure.lang.Sequential, clojure.lang.IPersistentStack] );
@@ -485,6 +651,7 @@ clojure.JS.defclass( clojure.lang, "AMapEntry", {
},
hashCode: function(){ throw "not implemented yet"; },
toString: function(){
+ return this.key() + " " + this.val();
var sw = new java.io.StringWriter();
clojure.lang.RT.print( this, sw );
return sw.toString();
@@ -532,6 +699,8 @@ clojure.JS.defclass( clojure.lang, "MapEntry", {
});
clojure.JS.defclass( clojure.lang, "PersistentList", {
+ extend: clojure.lang.ASeq,
+ implement: [clojure.lang.IPersistentList, clojure.lang.IReduce],
init: function( _meta, _first, _rest, _count ) {
this._meta = _meta || null;
this._first = _first;
@@ -659,9 +828,11 @@ clojure.JS.defclass( clojure.lang, "APersistentVector", {
if( i >= 0 && i < this.count() )
return this.nth(i);
}
+ if( notFound === undefined )
+ return null;
return notFound;
},
- toArray: function(){ return clojure.lang.RT.setToArray( this.seq() ); },
+ toArray: function(){ return clojure.lang.RT.seqToArray( this.seq() ); },
containsAll: function(){ throw "not implemented yet"; },
size: function(){ return this.count(); },
isEmpty: function(){ return this.count() === 0; },
@@ -902,6 +1073,664 @@ clojure.lang.PersistentVector.EMPTY =
new clojure.lang.PersistentVector(
{}, 0, 5, clojure.lang.RT.EMPTY_ARRAY, clojure.lang.RT.EMPTY_ARRAY );
+clojure.JS.definterface( clojure.lang, "IPersistentMap",
+ [clojure.lang.Associative]);
+
+clojure.JS.defclass( clojure.lang, "APersistentMap", {
+ extend: clojure.lang.AFn,
+ implement: [clojure.lang.IPersistentMap, java.util.Collection],
+ init: function(_meta) {
+ this._meta = _meta;
+ this._hash = -1;
+ },
+ methods: {
+ cons: function(o){
+ if( clojure.JS.instanceq( clojure.lang.IPersistentVector, o ) ) {
+ if( o.count() != 2 )
+ throw "Vector arg to map conj must be a pair";
+ return this.assoc( o.nth(0), o.nth(1) );
+ }
+ var e, ret = this;
+ for( var es = clojure.seq( o ); es; es = es.rest() ) {
+ e = es.first();
+ ret = ret.assoc( e.getKey(), e.getValue() );
+ }
+ return ret;
+ },
+ equals: function(m){
+ if( ! clojure.JS.instanceq( clojure.lang.IPersistentMap, m ) )
+ return false;
+ if( m.count() != this.count() || m.hashCode() != this.hashCode() )
+ return false;
+ var e, me;
+ for( var s = this.seq(); s; s = s.rest() ) {
+ e = s.first();
+ me = m.entryAt( e.getKey() );
+ if( me === null
+ || ! clojure.lang.Util.equal( e.getValue(), me.getValue() ))
+ {
+ return false;
+ }
+ }
+ return true;
+ },
+ hashCode: function(){
+ if( this._hash == -1 ) {
+ var e, hash = this.count();
+ for( s = this.seq(); s; s = s.rest() ) {
+ e = s.first();
+ hash ^= clojure.lang.Util.hashCombine(
+ clojure.lang.Util.hash( e.getKey() ),
+ clojure.lang.Util.hash( e.getValue() ) );
+ }
+ this._hash = hash;
+ }
+ return _hash;
+ },
+ containsAll: function(){ throw "not implemented yet"; },
+ invoke: function(k,notFound){ return this.valAt(k,notFound); },
+ toArray: function(){ return clojure.lang.RT.seqToArray( this.seq() ); },
+ size: function(){ return this.count(); },
+ isEmpty: function(){ return this.count() === 0; },
+ contains: function(e){
+ if( clojure.JS.instanceq( clojure.lang.MapEntry, e ) ) {
+ var v = this.entryAt( e.getKey() );
+ return (v!==null && clojure.lang.Util.equal(v.getValue(),e.getValue()));
+ }
+ return false;
+ }
+ }
+});
+
+clojure.JS.defclass( clojure.lang.APersistentMap, "KeySeq", {
+ extend: clojure.lang.ASeq,
+ init: function(_meta, _seq) {
+ this._meta = _meta;
+ this._seq = _seq;
+ },
+ statics: {
+ create: function(seq){
+ if(seq === null)
+ return null;
+ return new clojure.lang.APersistentMap.KeySeq(seq);
+ }
+ },
+ methods: {
+ first: function(){ return this._seq.first().getKey(); },
+ rest: function(){
+ return clojure.lang.APersistentMap.KeySeq.create( this._seq.rest() );
+ },
+ withMeta: function(_meta){
+ return new clojure.lang.APersistentMap.KeySeq( _meta, this._seq );
+ }
+ }
+});
+
+clojure.JS.defclass( clojure.lang.APersistentMap, "ValSeq", {
+ extend: clojure.lang.ASeq,
+ init: function(_meta, _seq) {
+ this._meta = _meta;
+ this._seq = _seq;
+ },
+ statics: {
+ create: function(seq){
+ if(seq === null)
+ return null;
+ return new clojure.lang.APersistentMap.ValSeq(seq);
+ }
+ },
+ methods: {
+ first: function(){ return this._seq.first().getValue(); },
+ rest: function(){
+ return clojure.lang.APersistentMap.ValSeq.create( this._seq.rest() );
+ },
+ withMeta: function(_meta){
+ return new clojure.lang.APersistentMap.ValSeq( _meta, this._seq );
+ }
+ }
+});
+
+clojure.JS.defclass( clojure.lang, "PersistentHashMap", {
+ extend: clojure.lang.APersistentMap,
+ init: function(_meta, _count, _root){
+ this._meta = _meta;
+ this._count = _count;
+ this._root = _root;
+ },
+ statics: {
+ create: function(init){
+ var ret = clojure.lang.PersistentHashMap.EMPTY;
+ for( var s = clojure.seq( init ); s; s=s.rest().rest() ){
+ if( s.rest() === null )
+ throw "No value supplied for key: " + s.first();
+ ret = ret.assoc( s.first(), clojure.second( s ) );
+ }
+ return ret;
+ },
+ mask: function(hash, shift){ return (hash >>> shift) & 0x01f; }
+ },
+ methods:{
+ containsKey: function(key){ return this.entryAt(key) !== null; },
+ entryAt: function(k){ return this._root.find(clojure.lang.Util.hash(k),k);},
+ assoc: function(k,v){
+ var addedLeaf=[null];
+ var newroot = this._root.assoc(
+ 0, clojure.lang.Util.hash(k), k, v, addedLeaf );
+ if( newroot == this._root )
+ return this;
+ return new clojure.lang.PersistentHashMap(
+ this._meta, this._count + (addedLeaf[0] === null ? 0 : 1), newroot );
+ },
+ valAt: function(k, notFound){
+ var e = this.entryAt(k);
+ if( e !== null )
+ return e.val();
+ if( notFound === undefined )
+ return null;
+ return notFound;
+ },
+ assocEx: function(k,v){
+ if(this.containsKey(k))
+ throw "Key already present";
+ return this.assoc(k,v);
+ },
+ without: function(k){
+ var newroot = this._root.without( clojure.lang.Util.hash(k), k );
+ if( newroot == this._root )
+ return this;
+ if( newroot == null )
+ return this.empty();
+ return new cloljure.lang.PersistentHashMap(
+ this._meta, this._count-1, newroot );
+ },
+ count: function(){ return this._count; },
+ seq: function(){ return this._root.nodeSeq(); },
+ empty: function(){
+ return clojure.lang.PersistentHashMap.EMPTY.withMeta( this._meta );
+ },
+ withMeta: function(_meta){
+ return new clojure.lang.PersistentHashMap( _meta, this._count,this._root);
+ }
+ }
+});
+
+clojure.JS.defclass( clojure.lang.PersistentHashMap, "EmptyNode", {
+ methods: {
+ assoc: function(shift, hash, key, val, addedLeaf){
+ var ret = new clojure.lang.PersistentHashMap.LeafNode( hash, key, val );
+ addedLeaf[0] = ret;
+ return ret;
+ },
+ without: function(h,k){ return this; },
+ find: function(h,k){ return null; },
+ nodeSeq: function(){ return null;},
+ getHash: function(){ return 0;}
+ }
+});
+
+clojure.lang.PersistentHashMap.EMPTY = new clojure.lang.PersistentHashMap(
+ null, 0, new clojure.lang.PersistentHashMap.EmptyNode() );
+
+clojure.JS.defclass( clojure.lang.PersistentHashMap, "FullNode", {
+ init: function(nodes, shift){
+ this._nodes = nodes;
+ this._shift = shift;
+ this._hash = nodes[0].getHash();
+ },
+ statics: {
+ bitpos: function(hash, shift) {
+ return 1 << clojure.lang.PersistentHashMap.mask( hash, shift );
+ }
+ },
+ methods: {
+ assoc: function( levelShift, hash, key, val, addedLeaf ) {
+ var PHM = clojure.lang.PersistentHashMap;
+ var idx = PHM.mask( hash, this._shift );
+ var n = this._nodes[idx].assoc( this._shift+5, hash, key, val, addedLeaf);
+ if( n == this._nodes[idx] )
+ return this;
+ else {
+ var newnodes = nodes.slice();
+ newnodes[idx] = n;
+ return new PHM.FullNode( newnodes, this._shift );
+ }
+ },
+ without: function(hash, key){
+ var PHM = clojure.lang.PersistentHashMap;
+ var idx = PHM.mask( hash, this._shift );
+ var n = this._nodes[idx].without( hash, key );
+ if( n != this._nodes[idx] ) {
+ var newnodes = nodes.slice();
+ if( n == null ) {
+ nodes.splice( idx, 1 );
+ return new PHM.BitmapIndexedNode(
+ ~PHM.FullNode.bitpos(hash,this._shift), newnodes, this._shift );
+ }
+ newnodes[ idx ] = n;
+ return new PHM.FullNode( newnodes, this._shift );
+ }
+ return this;
+ },
+ find: function(hash, key) {
+ return (nodes[clojure.lang.PersistentHashMap.mask( hash, this._shift )]
+ .find( hash, key ));
+ },
+ nodeSeq: function(){
+ return clojure.lang.PersistentHashMap.FullNode.Seq.create( this, 0 );
+ },
+ getHash: function(){ return this._hash; }
+ }
+});
+
+clojure.JS.defclass( clojure.lang.PersistentHashMap.FullNode, "Seq", {
+ extend: clojure.lang.ASeq,
+ init: function( _meta, s, i, node ) {
+ this._meta = _meta;
+ this.s = s;
+ this.i = i;
+ this.node = node;
+ },
+ statics: {
+ create: function(node, i){
+ if( i >= node.nodes.length )
+ return null;
+ return new clojure.lang.PersistentHashMap.FullNode.Seq(
+ null, node.nodes[i].nodeSeq(), i, node );
+ }
+ },
+ methods: {
+ first: function(){ return this.s.first(); },
+ rest: function(){
+ var Seq = clojure.lang.PersistentHashMap.FullNode.Seq;
+ var nexts = this.s.rest();
+ if( nexts )
+ return new Seq( null, nexts, this.i, this.node );
+ return Seq.create( node,this.i+1);
+ },
+ withMeta: function(_meta){
+ return new clojure.lang.PersistentHashMap.FullNode.Seq(
+ _meta, this.s, this.i, this.node );
+ }
+ }
+});
+
+clojure.JS.defclass( clojure.lang.PersistentHashMap, "BitmapIndexedNode", {
+ init: function(bitmap, nodes, shift) {
+ this.bitmap = bitmap;
+ this.nodes = nodes;
+ this.shift = shift;
+ this._hash = nodes[0].getHash();
+ },
+ statics: {
+ bitpos: function(hash, shift) {
+ return 1 << clojure.lang.PersistentHashMap.mask( hash, shift );
+ },
+ createA: function(bitmap, nodes, shift){
+ var PHM = clojure.lang.PersistentHashMap;
+ if(bitmap == -1)
+ return new PHM.FullNode( nodes, shift );
+ return new PHM.BitmapIndexedNode( bitmap, nodes, shift );
+ },
+ createB: function(shift, branch, hash, key, val, addedLeaf){
+ var PHM = clojure.lang.PersistentHashMap;
+ return (new PHM.BitmapIndexedNode(
+ PHM.BitmapIndexedNode.bitpos(branch.getHash(), shift),
+ [branch],
+ shift)).assoc( shift, hash, key, val, addedLeaf );
+ }
+ },
+ methods: {
+ index: function(bit) { return clojure.JS.bitcount(this.bitmap & (bit-1) );},
+ assoc: function(levelShift, hash, key, val, addedLeaf){
+ var BIN = clojure.lang.PersistentHashMap.BitmapIndexedNode;
+ var bit = BIN.bitpos( hash, this.shift );
+ var idx = this.index( bit );
+ if((this.bitmap & bit) != 0) {
+ var n = this.nodes[idx].assoc( this.shift+5, hash, key, val, addedLeaf);
+ if( n == this.nodes[idx] )
+ return this;
+ else {
+ var newnodes = this.nodes.slice();
+ newnodes[idx] = n;
+ return new BIN( this.bitmap, newnodes, this.shift );
+ }
+ }
+ else {
+ addedLeaf[0]= new clojure.lang.PersistentHashMap.LeafNode(hash,key,val);
+ var newnodes = this.nodes.slice();
+ newnodes.splice( idx, 0, addedLeaf[0] );
+ return BIN.createA( this.bitmap | bit, newnodes, this.shift );
+ }
+ },
+ without: function( hash, key ) {
+ var BIN = clojure.lang.PersistentHashMap.BitmapIndexedNode;
+ var bit = BIN.bitpos( hash, this.shift );
+ if((this.bitmap & bit) !== 0) {
+ var idx = this.index( bit );
+ var n = this.nodes[ idx ].without( hash, key );
+ if( n != this.nodes[ idx ] ) {
+ if( n === null ) {
+ if( this.bitmap == bit )
+ return null;
+ var newnodes = this.nodes.slice();
+ newnodes.splice( idx, 1 );
+ return new BIN( bitmap & ~bit, newnodes, this.shift );
+ }
+ var newnodes = this.nodes.slice();
+ newnodes[ idx ] = n;
+ return new BIN( bitmap, newnodes, this.shift );
+ }
+ }
+ return this;
+ },
+ find: function( hash, key ) {
+ var BIN = clojure.lang.PersistentHashMap.BitmapIndexedNode;
+ var bit = BIN.bitpos( hash, this.shift );
+ if((this.bitmap & bit) !== 0)
+ return this.nodes[ this.index(bit) ].find( hash, key );
+ return null;
+ },
+ getHash: function(){ return this._hash; },
+ nodeSeq: function(){
+ return clojure.lang.PersistentHashMap.BitmapIndexedNode.Seq.create(
+ this, 0 );
+ }
+ }
+});
+
+clojure.JS.defclass( clojure.lang.PersistentHashMap.BitmapIndexedNode, "Seq", {
+ extend: clojure.lang.ASeq,
+ init: function( _meta, s, i, node ) {
+ this._meta = _meta;
+ this.s = s;
+ this.i = i;
+ this.node = node;
+ },
+ statics: {
+ create: function(node, i){
+ if( i >= node.nodes.length )
+ return null;
+ return new clojure.lang.PersistentHashMap.BitmapIndexedNode.Seq(
+ null, node.nodes[i].nodeSeq(), i, node );
+ }
+ },
+ methods: {
+ first: function(){ return this.s.first(); },
+ rest: function(){
+ var Seq = clojure.lang.PersistentHashMap.BitmapIndexedNode.Seq;
+ var nexts = this.s.rest();
+ if( nexts )
+ return new Seq( null, nexts, this.i, this.node );
+ return Seq.create( this.node, this.i+1 );
+ },
+ withMeta: function(_meta){
+ return new clojure.lang.PersistentHashMap.BitmapIndexedNode.Seq(
+ _meta, this.s, this.i, this.node );
+ }
+ }
+});
+
+clojure.JS.defclass( clojure.lang.PersistentHashMap, "LeafNode", {
+ extend: clojure.lang.AMapEntry,
+ init: function( hash, key, val ) {
+ this.hash = hash;
+ this._key = key;
+ this._val = val;
+ },
+ methods: {
+ assoc: function(shift, hash, key, val, addedLeaf) {
+ var PHM = clojure.lang.PersistentHashMap;
+ if( hash == this.hash ) {
+ if( clojure.lang.Util.equal( key, this._key ) ) {
+ if( val == this._val )
+ return this;
+ return new PHM.LeafNode( hash, key, val );
+ }
+ var newLeaf = new PHM.LeafNode( hash, key, val );
+ addedLeaf[0] = newLeaf;
+ return new PHM.HashCollisionNode( hash, [this, newLeaf] );
+ }
+ return PHM.BitmapIndexedNode.createB(
+ shift, this, hash, key, val, addedLeaf );
+ },
+ without: function(hash, key){
+ if(hash == this.hash && clojure.lang.Util.equal( key, this._key ))
+ return null;
+ return this;
+ },
+ find: function(hash, key){
+ if(hash == this.hash && clojure.lang.Util.equal( key, this._key ))
+ return this;
+ return null;
+ },
+ nodeSeq: function(){ return clojure.cons( this, null ); },
+ getHash: function(){ return this.hash; },
+ key: function(){ return this._key; },
+ val: function(){ return this._val; },
+ getKey: function(){ return this._key; },
+ getValue: function(){ return this._val; }
+ }
+});
+
+clojure.JS.defclass( clojure.lang.PersistentHashMap, "HashCollisionNode", {
+ init: function(hash, leaves){
+ this.hash = hash;
+ this.leaves = leaves;
+ },
+ methods: {
+ assoc: function(shift, hash, key, val, addedLeaf) {
+ var PHM = clojure.lang.PersistentHashMap;
+ if( hash == this.hash ) {
+ var idx = this.findIndex( hash, key );
+ if( idx != -1 ) {
+ if( this.leaves[idx].val == val )
+ return this;
+ var newLeaves = this.leaves.slice();
+ newLeaves[idx] = new PHM.LeafNode( hash, key, val );
+ return new PHM.HashCollisionNode( has, newLeaves );
+ }
+ addedLeaf[0] = new PHM.LeafNode( hash, key, val );
+ var newLeaves = this.leaves.concat( addedLeaf );
+ return new PHM.HashCollisionNode( hash, newLeaves );
+ }
+ return PHM.BitmapIndexedNode.createB(shift,this,hash,key,val,addedLeaf);
+ },
+ without: function(hash, key){
+ var idx = this.findIndex( hash, key );
+ if( idx != -1 )
+ return leaves[ idx ];
+ return null;
+ },
+ nodeSeq: function(){
+ return clojure.lang.ArraySeq.create(this.leaves);
+ },
+ findIndex: function(hash, key){
+ for( var i = 0; i < this.leaves.length; ++i ) {
+ if( this.leaves[i].find( hash, key ) != null )
+ return i;
+ }
+ return -1;
+ },
+ getHash: function(){ return this.hash; }
+ }
+});
+
+clojure.JS.definterface( clojure.lang, "IPersistentSet",
+ [ clojure.lang.IPersistentCollection ] );
+
+clojure.JS.defclass( clojure.lang, "APersistentSet", {
+ extend: clojure.lang.AFn,
+ implement: [ clojure.lang.IPersistentSet ],
+ init: function( meta, impl ) {
+ this._meta = meta;
+ this.impl = impl;
+ this._hash = -1;
+ },
+ methods: {
+ contains: function(key){ return this.impl.containsKey(key); },
+ get: function(key){ return this.impl.valAt(key); },
+ count: function(){ return this.impl.count(); },
+ seq: function(){ return clojure.keys( this.impl ); },
+ invoke: function(key){ return this.get(key); },
+ equals: function(m) {
+ if( ! clojure.instanceq( clojure.lang.IPersistentSet ) )
+ return false;
+ if( m.count() != this.count() || m.hashCode() != this.hashCode() )
+ return false;
+ for( var s = this.seq(); s; s = s.rest() ) {
+ if( ! m.contains( s.first() ) )
+ return false;
+ }
+ return true;
+ },
+ hashCode: function() {
+ if( this._hash == -1 ) {
+ var hash = this.count();
+ for( var s = this.seq(); s; s = s.rest() ) {
+ hash = clojure.lang.Util.hashCombine(
+ hash, clojure.lang.Util.hash( s.first() ) );
+ }
+ this._hash = hash;
+ }
+ return this._hash;
+ },
+ toArray: function(){ return clojure.lang.RT.seqToArray( this.seq() ); },
+ containsAll: function(c){ throw "not yet implemented"; },
+ size: function(){ return this.count(); },
+ isEmpty: function(){ return this.count() == 0; }
+ }
+});
+
+clojure.JS.defclass( clojure.lang, "PersistentHashSet", {
+ extend: clojure.lang.APersistentSet,
+ init: function( meta, impl ) {
+ clojure.lang.APersistentSet.call( this, meta, impl );
+ },
+ statics: {
+ create: function(init){
+ var ret = clojure.lang.PersistentHashSet.EMPTY;
+ for( var s = clojure.seq( init ); s; s=s.rest() ){
+ ret = ret.cons( s.first() );
+ }
+ return ret;
+ }
+ },
+ methods: {
+ disjoin: function(key) {
+ if( this.contains(key) )
+ return new clojure.lang.PersistentHashSet(
+ this._meta, this.impl.without(key));
+ return this;
+ },
+ cons: function(o) {
+ if( this.contains(o) )
+ return this;
+ return new clojure.lang.PersistentHashSet(
+ this._meta, this.impl.assoc(o));
+ },
+ empty: function(){
+ return clojure.lang.PersistentHashSet.EMPTY.withMeta( this._meta );
+ },
+ withMeta: function(_meta){
+ return new clojure.lang.PersistentHashSet( _meta, this.impl );
+ }
+ }
+});
+
+clojure.lang.PersistentHashSet.EMPTY = new clojure.lang.PersistentHashSet(
+ null, clojure.lang.PersistentHashMap.EMPTY );
+
+clojure.JS.defclass( clojure.lang, "MultiFn", {
+ extend: clojure.lang.AFn,
+ init: function( dispatchFn, defaultDispatchVal ) {
+ this.dispatchFn = dispatchFn;
+ this.defaultDispatchVal = defaultDispatchVal;
+ this.methodTable = clojure.lang.PersistentHashMap.EMPTY;
+ this.methodCache = clojure.lang.PersistentHashMap.EMPTY;
+ this.preferTable = clojure.lang.PersistentHashMap.EMPTY;
+ this.cachedHierarchy = null;
+ },
+ methods: {
+ addMethod: function( dispatchVal, method ){
+ this.methodTable = this.methodTable.assoc( dispatchVal, method );
+ this.resetCache();
+ return this;
+ },
+ removeMethod: function( dispatchVal ){
+ this.methodTable = this.methodTable.without( dispatchVal );
+ this.resetCache();
+ return this;
+ },
+ preferMethod: function( dispatchValX, dispatchValY ){
+ if( this.prefers( dispatchValY, dispatchValX ) )
+ throw ("Preference conflict: " + dispatchValY +
+ " is already preferred to" + dispatchValX);
+ var oldset = clojure.get(
+ this.preferTable, dispatchValX, clojure.lang.PersistentHashSet.EMPTY);
+ this.preferTable = this.preferTable.assoc(
+ dispatchValX, clojure.conj( oldset, dispatchValY ) );
+ this.resetCache();
+ return this;
+ },
+ prefers: function(x,y) {
+ var xprefs = this.preferTable.valAt(x);
+ return xprefs && xprefs.contains(y);
+ },
+ isA: function(x,y) { return clojure.isa_QMARK_( x, y ); },
+ dominates: function(x,y) { return this.prefers(x,y) || this.isA(x,y); },
+ resetCache: function() {
+ this.methodCache = this.methodTable;
+ this.cachedHierarchy = clojure.global_hierarchy;
+ return this.methodCache;
+ },
+ getFn: function(dispatchVal) {
+ if( this.cachedHierarchy != clojure.global_hierarchy )
+ this.resetCache();
+ var targetFn =
+ this.methodCache.valAt( dispatchVal ) ||
+ this.findAndCacheBestMethod( dispatchVal ) ||
+ this.methodTable.valAt( this.defaultDispatchVal );
+ if( targetFn === null )
+ throw "No method for dispatch value: " + dispatchVal;
+ return targetFn;
+ },
+ findAndCacheBestMethod: function( dispatchVal ) {
+ var e, bestEntry = null;
+ for( var s = this.methodTable.seq(); s; s = s.rest() ) {
+ e = s.first();
+ if( this.isA( dispatchVal, e.getKey() ) ) {
+ if( bestEntry===null || this.dominates(e.getKey(),bestEntry.getKey()))
+ bestEntry = e;
+ if( ! this.dominates( bestEntry.getKey(), e.getKey() ) )
+ throw ["Multiple methods match dispatch value:", dispatchVal,
+ "->", e.getKey(), "and", bestEntry.getKey(),
+ "and neither is preferred"].join(' ');
+ }
+ }
+ if( bestEntry === null )
+ return null;
+ // skip multi-threading protection
+ this.methodCache = this.methodCache.assoc(
+ dispatchVal, bestEntry.getValue());
+ return bestEntry.getValue();
+ },
+ invoke: function() {
+ return (this.getFn( this.dispatchFn.apply( null, arguments ) )
+ .apply( null, arguments ));
+ }
+ }
+});
+
+clojure.print_method = new clojure.lang.MultiFn(
+ function (x, writer){ return clojure.class_(x); },
+ clojure.keyword("","