this function is taken from Acorn [1], written by Marijn Haverbeke [1] https://github.com/marijnh/acorn
329286
function makePredicate(words) {
      if (!(words instanceof Array)) words = words.split(" ");
      var f = "", cats = [];
      out: for (var i = 0; i < words.length; ++i) {
          for (var j = 0; j < cats.length; ++j)
              if (cats[j][0].length == words[i].length) {
                  cats[j].push(words[i]);
                  continue out;
              }
          cats.push([words[i]]);
      }
      function quote(word) {
          return JSON.stringify(word).replace(/[\u2028\u2029]/g, function(s) {
              switch (s) {
                  case "\u2028": return "\\u2028";
                  case "\u2029": return "\\u2029";
              }
              return s;
          });
      }
      function compareTo(arr) {
          if (arr.length == 1) return f += "return str === " + quote(arr[0]) + ";";
          f += "switch(str){";
          for (var i = 0; i < arr.length; ++i) f += "case " + quote(arr[i]) + ":";
          f += "return true}return false;";
      }
      // When there are more than three length categories, an outer
      // switch first dispatches on the lengths, to save on comparisons.
      if (cats.length > 3) {
          cats.sort(function(a, b) {return b.length - a.length;});
          f += "switch(str.length){";
          for (var i = 0; i < cats.length; ++i) {
              var cat = cats[i];
              f += "case " + cat[0].length + ":";
              compareTo(cat);
          }
          f += "}";
          // Otherwise, simply generate a flat `switch` statement.
      } else {
          compareTo(words);
      }
      return new Function("str", f);
  }
1092239
function rectIntersect(r1, r2) {
    var right1 = r1.x + r1.width;
    var bottom1 = r1.y + r1.height;
    var right2 = r2.x + r2.width;
    var bottom2 = r2.y + r2.height;

    var x = Math.max(r1.x, r2.x);
    var y = Math.max(r1.y, r2.y);
    var w = Math.max(Math.min(right1, right2) - x, 0);
    var h = Math.max(Math.min(bottom1, bottom2) - y, 0);
    return {x: x, y: y, width: w, height: h};
}
238750
function getMatrixSetFunction( type, location, gl, context ){
  context;
  var fname = getUniformSetFunctionName( type );
  return function(){
    if( arguments.length > 0 && arguments[0].length !== undefined ){
      var transpose = (arguments.length > 1) ? !!arguments[1] : false;
      gl[fname+'v']( location, transpose, arguments[0] );
    }
    return location;
  };
}
1314882
def fpbspl(t, n, k, x, l, h, hh):
    
    h[0] = 1.0
    for j in range(1, k + 1):
        hh[0:j] = h[0:j]
        h[0] = 0.0
        for i in range(j):
            li = l+i
            f = hh[i]/(t[li] - t[li - j])
            h[i] = h[i] + f*(t[li] - x)
            h[i + 1] = f*(x - t[li - j])
209821
function multMod_(x, y, n) {
  var i;
  if (s0.length!=2*x.length)
    s0=new Array(2*x.length);
  copyInt_(s0, 0);
  for (i=0; i<y.length; i++)
    if (y[i])
      linCombShift_(s0, x, y[i], i);   //s0=1*s0+y[i]*(x<<(i*bpe))
  mod_(s0, n);
  copy_(x, s0);
}
1117910
function (m, p, c) {
        var r, all
        this.all(function(a){
            all = a.map(function(e){ return e[p] })
            r = Math[m].apply(Math, all)
        })
        this.fn(m, c).call(this, r)
    }
154095
function linCombShift_(x, y, b, ys) {
  var i, c, k, kk;
  k=x.length<ys+y.length ? x.length : ys+y.length;
  kk=x.length;
  for (c=0, i=ys; i<k; i++) {
    c+=x[i]+b*y[i-ys];
    x[i]=c & mask;
    c>>=bpe;
  }
  for (i=k; c && i<kk; i++) {
    c+=x[i];
    x[i]=c & mask;
    c>>=bpe;
  }
}
893017
function(key, items) {
      var offsets = {};

      var stack = d3.layout.stack()
        .values(function(d) {
          return d.values;
        })
        .x(function(d) {
          return d[key];
        })
        .y(function(d) {
          return +d[opts.value.key];
        })
        .out(function(d, y0, y) {
          d.y = y;
          if (d.y >= 0) {
            d.y0 = offsets[d[key] + 'Pos'] = offsets[d[key] + 'Pos'] || 0;
            offsets[d[key] + 'Pos'] += y;
          } else {
            d.y0 = offsets[d[key] + 'Neg'] = offsets[d[key] + 'Neg'] || 0;
            offsets[d[key] + 'Neg'] -= Math.abs(y);
          }
        });
      stack(items.reverse());
    }
130223
function x(){function c(a,d){this.x=a||0;this.y=d||0}function B(a,d){this.pos=a||new c;this.r=d||0}function m(a,d){this.pos=a||new c;this.angle=0;this.offset=new c;this.u(d||[])}function p(a,d,b){this.pos=a||new c;this.w=d||0;this.h=b||0}function w(){this.b=this.a=null;this.overlapN=new c;this.overlapV=new c;this.clear()}function C(a,d,b){for(var g=Number.MAX_VALUE,c=-Number.MAX_VALUE,l=a.length,v=0;v<l;v++){var e=a[v].f(d);e<g&&(g=e);e>c&&(c=e)}b[0]=g;b[1]=c}function y(a,d,b,g,c,l){var k=q.pop(),
e=q.pop();a=h.pop().c(d).sub(a);d=a.f(c);C(b,c,k);C(g,c,e);e[0]+=d;e[1]+=d;if(k[0]>e[1]||e[0]>k[1])return h.push(a),q.push(k),q.push(e),!0;l&&(k[0]<e[0]?(l.aInB=!1,k[1]<e[1]?(b=k[1]-e[0],l.bInA=!1):(b=k[1]-e[0],g=e[1]-k[0],b=b<g?b:-g)):(l.bInA=!1,k[1]>e[1]?(b=k[0]-e[1],l.aInB=!1):(b=k[1]-e[0],g=e[1]-k[0],b=b<g?b:-g)),g=Math.abs(b),g<l.overlap&&(l.overlap=g,l.overlapN.c(c),0>b&&l.overlapN.reverse()));h.push(a);q.push(k);q.push(e);return!1}function z(a,d){var b=a.g(),g=d.f(a);return 0>g?-1:g>b?1:0}
function D(a,d,b){for(var g=h.pop().c(d.pos).sub(a.pos),c=d.r,l=c*c,v=a.calcPoints,e=v.length,t=h.pop(),f=h.pop(),m=0;m<e;m++){var u=m===e-1?0:m+1,q=0===m?e-1:m-1,p=0,r=null;t.c(a.edges[m]);f.c(g).sub(v[m]);b&&f.g()>l&&(b.aInB=!1);var n=z(t,f);if(-1===n){t.c(a.edges[q]);u=h.pop().c(g).sub(v[q]);n=z(t,u);if(1===n){n=f.j();if(n>c)return h.push(g),h.push(t),h.push(f),h.push(u),!1;b&&(b.bInA=!1,r=f.normalize(),p=c-n)}h.push(u)}else if(1===n){if(t.c(a.edges[u]),f.c(g).sub(v[u]),n=z(t,f),-1===n){n=f.j();
if(n>c)return h.push(g),h.push(t),h.push(f),!1;b&&(b.bInA=!1,r=f.normalize(),p=c-n)}}else{u=t.m().normalize();n=f.f(u);q=Math.abs(n);if(0<n&&q>c)return h.push(g),h.push(u),h.push(f),!1;b&&(r=u,p=c-n,0<=n||p<2*c)&&(b.bInA=!1)}r&&b&&Math.abs(p)<Math.abs(b.overlap)&&(b.overlap=p,b.overlapN.c(r))}b&&(b.a=a,b.b=d,b.overlapV.c(b.overlapN).scale(b.overlap));h.push(g);h.push(t);h.push(f);return!0}function E(a,d,b){for(var c=a.calcPoints,k=c.length,l=d.calcPoints,f=l.length,e=0;e<k;e++)if(y(a.pos,d.pos,c,
l,a.normals[e],b))return!1;for(e=0;e<f;e++)if(y(a.pos,d.pos,c,l,d.normals[e],b))return!1;b&&(b.a=a,b.b=d,b.overlapV.c(b.overlapN).scale(b.overlap));return!0}var f={};f.Vector=c;f.V=c;c.prototype.copy=c.prototype.c=function(a){this.x=a.x;this.y=a.y;return this};c.prototype.clone=c.prototype.clone=function(){return new c(this.x,this.y)};c.prototype.perp=c.prototype.m=function(){var a=this.x;this.x=this.y;this.y=-a;return this};c.prototype.rotate=c.prototype.rotate=function(a){var d=this.x,b=this.y;
this.x=d*Math.cos(a)-b*Math.sin(a);this.y=d*Math.sin(a)+b*Math.cos(a);return this};c.prototype.reverse=c.prototype.reverse=function(){this.x=-this.x;this.y=-this.y;return this};c.prototype.normalize=c.prototype.normalize=function(){var a=this.j();0<a&&(this.x/=a,this.y/=a);return this};c.prototype.add=c.prototype.add=function(a){this.x+=a.x;this.y+=a.y;return this};c.prototype.sub=c.prototype.sub=function(a){this.x-=a.x;this.y-=a.y;return this};c.prototype.scale=c.prototype.scale=function(a,d){this.x*=
a;this.y*="undefined"!=typeof d?d:a;return this};c.prototype.project=c.prototype.o=function(a){var d=this.f(a)/a.g();this.x=d*a.x;this.y=d*a.y;return this};c.prototype.projectN=c.prototype.s=function(a){var d=this.f(a);this.x=d*a.x;this.y=d*a.y;return this};c.prototype.reflect=function(a){var d=this.x,b=this.y;this.o(a).scale(2);this.x-=d;this.y-=b;return this};c.prototype.reflectN=function(a){var d=this.x,b=this.y;this.s(a).scale(2);this.x-=d;this.y-=b;return this};c.prototype.dot=c.prototype.f=
function(a){return this.x*a.x+this.y*a.y};c.prototype.len2=c.prototype.g=function(){return this.f(this)};c.prototype.len=c.prototype.j=function(){return Math.sqrt(this.g())};f.Circle=B;B.prototype.getAABB=function(){var a=this.r,d=this.pos.clone().sub(new c(a,a));return(new p(d,2*a,2*a)).l()};f.Polygon=m;m.prototype.setPoints=m.prototype.u=function(a){if(!this.points||this.points.length!==a.length){var d,b=this.calcPoints=[],g=this.edges=[],k=this.normals=[];for(d=0;d<a.length;d++)b.push(new c),g.push(new c),
k.push(new c)}this.points=a;this.i()};m.prototype.setAngle=function(a){this.angle=a;this.i();return this};m.prototype.setOffset=function(a){this.offset=a;this.i();return this};m.prototype.rotate=m.prototype.rotate=function(a){for(var d=this.points,b=d.length,c=0;c<b;c++)d[c].rotate(a);this.i();return this};m.prototype.translate=m.prototype.translate=function(a,d){for(var b=this.points,c=b.length,k=0;k<c;k++)b[k].x+=a,b[k].y+=d;this.i();return this};m.prototype.i=function(){var a=this.calcPoints,c=
this.edges,b=this.normals,g=this.points,k=this.offset,l=this.angle,f=g.length,e;for(e=0;e<f;e++){var h=a[e].c(g[e]);h.x+=k.x;h.y+=k.y;0!==l&&h.rotate(l)}for(e=0;e<f;e++)g=a[e],g=c[e].c(e<f-1?a[e+1]:a[0]).sub(g),b[e].c(g).m().normalize()};m.prototype.getAABB=function(){for(var a=this.calcPoints,d=a.length,b=a[0].x,g=a[0].y,k=a[0].x,l=a[0].y,f=1;f<d;f++){var e=a[f];e.x<b?b=e.x:e.x>k&&(k=e.x);e.y<g?g=e.y:e.y>l&&(l=e.y)}return(new p(this.pos.clone().add(new c(b,g)),k-b,l-g)).l()};m.prototype.getCentroid=
function(){for(var a=this.calcPoints,d=a.length,b=0,g=0,k=0,f=0;f<d;f++){var h=a[f],e=f===d-1?a[0]:a[f+1],m=h.x*e.y-e.x*h.y;b+=(h.x+e.x)*m;g+=(h.y+e.y)*m;k+=m}k*=3;return new c(b/k,g/k)};f.Box=p;p.prototype.toPolygon=p.prototype.l=function(){var a=this.pos,d=this.w,b=this.h;return new m(new c(a.x,a.y),[new c,new c(d,0),new c(d,b),new c(0,b)])};f.Response=w;w.prototype.clear=w.prototype.clear=function(){this.bInA=this.aInB=!0;this.overlap=Number.MAX_VALUE;return this};for(var h=[],r=0;10>r;r++)h.push(new c);
var q=[];for(r=0;5>r;r++)q.push([]);var A=new w,F=(new p(new c,1E-6,1E-6)).l();f.isSeparatingAxis=y;f.pointInCircle=function(a,c){var b=h.pop().c(a).sub(c.pos),d=c.r*c.r,f=b.g();h.push(b);return f<=d};f.pointInPolygon=function(a,c){F.pos.c(a);A.clear();var b=E(F,c,A);b&&(b=A.aInB);return b};f.testCircleCircle=function(a,c,b){var d=h.pop().c(c.pos).sub(a.pos),f=a.r+c.r,l=d.g();if(l>f*f)return h.push(d),!1;b&&(l=Math.sqrt(l),b.a=a,b.b=c,b.overlap=f-l,b.overlapN.c(d.normalize()),b.overlapV.c(d).scale(b.overlap),
b.aInB=a.r<=c.r&&l<=c.r-a.r,b.bInA=c.r<=a.r&&l<=a.r-c.r);h.push(d);return!0};f.testPolygonCircle=D;f.testCirclePolygon=function(a,c,b){if((a=D(c,a,b))&&b){c=b.a;var d=b.aInB;b.overlapN.reverse();b.overlapV.reverse();b.a=b.b;b.b=c;b.aInB=b.bInA;b.bInA=d}return a};f.testPolygonPolygon=E;return f}
718513
function BaseStemmer() {
    this.setCurrent = function(value) {
      this.current = value;
      this.cursor = 0;
      this.limit = this.current.length;
      this.limit_backward = 0;
      this.bra = this.cursor;
      this.ket = this.limit;
    };

    this.getCurrent = function() {
      return this.current;
    };

    this.copy_from = function(other) {
      this.current = other.current;
      this.cursor = other.cursor;
      this.limit = other.limit;
      this.limit_backward = other.limit_backward;
      this.bra = other.bra;
      this.ket = other.ket;
    };

    this.in_grouping = function(s, min, max) {
      if (this.cursor >= this.limit) return false;
      var ch = this.current.charCodeAt(this.cursor);
      if (ch > max || ch < min) return false;
      ch -= min;
      if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false;
      this.cursor++;
      return true;
    };

    this.in_grouping_b = function(s, min, max) {
      if (this.cursor <= this.limit_backward) return false;
      var ch = this.current.charCodeAt(this.cursor - 1);
      if (ch > max || ch < min) return false;
      ch -= min;
      if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) return false;
      this.cursor--;
      return true;
    };

    this.out_grouping = function(s, min, max) {
      if (this.cursor >= this.limit) return false;
      var ch = this.current.charCodeAt(this.cursor);
      if (ch > max || ch < min) {
        this.cursor++;
        return true;
      }
      ch -= min;
      if ((s[ch >>> 3] & (0X1 << (ch & 0x7))) == 0) {
        this.cursor++;
        return true;
      }
      return false;
    };

    this.out_grouping_b = function(s, min, max) {
      if (this.cursor <= this.limit_backward) return false;
      var ch = this.current.charCodeAt(this.cursor - 1);
      if (ch > max || ch < min) {
        this.cursor--;
        return true;
      }
      ch -= min;
      if ((s[ch >>> 3] & (0x1 << (ch & 0x7))) == 0) {
        this.cursor--;
        return true;
      }
      return false;
    };

    this.eq_s = function(s) {
      if (this.limit - this.cursor < s.length) return false;
      if (this.current.slice(this.cursor, this.cursor + s.length) != s) {
        return false;
      }
      this.cursor += s.length;
      return true;
    };

    this.eq_s_b = function(s) {
      if (this.cursor - this.limit_backward < s.length) return false;
      if (this.current.slice(this.cursor - s.length, this.cursor) != s) {
        return false;
      }
      this.cursor -= s.length;
      return true;
    };

    /** @return {number} */
    this.find_among = function(v) {
      var i = 0;
      var j = v.length;

      var c = this.cursor;
      var l = this.limit;

      var common_i = 0;
      var common_j = 0;

      var first_key_inspected = false;

      while (true) {
        var k = i + ((j - i) >>> 1);
        var diff = 0;
        var common = common_i < common_j ? common_i : common_j; // smaller
        var w = v[k];
        var i2;
        /// s : string, substring_i : int, result : int, method
        for (i2 = common; i2 < w[0].length; i2++) {
          if (c + common == l) {
            diff = -1;
            break;
          }
          diff = this.current.charCodeAt(c + common) - w[0].charCodeAt(i2);
          if (diff != 0) break;
          common++;
        }
        if (diff < 0) {
          j = k;
          common_j = common;
        } else {
          i = k;
          common_i = common;
        }
        if (j - i <= 1) {
          if (i > 0) break; // v->s has been inspected
          if (j == i) break; // only one item in v

          // - but now we need to go round once more to get
          // v->s inspected. This looks messy, but is actually
          // the optimal approach.

          if (first_key_inspected) break;
          first_key_inspected = true;
        }
      }
      while (true) {
        var w = v[i];
        if (common_i >= w[0].length) {
          this.cursor = c + w[0].length;
          if (w.length < 4) return w[2];
          var res = w[3](this);
          this.cursor = c + w[0].length;
          if (res) return w[2];
        }
        i = w[1];
        if (i < 0) return 0;
      }
    };

    // find_among_b is for backwards processing. Same comments apply
    this.find_among_b = function(v) {
      var i = 0;
      var j = v.length

      var c = this.cursor;
      var lb = this.limit_backward;

      var common_i = 0;
      var common_j = 0;

      var first_key_inspected = false;

      while (true) {
        var k = i + ((j - i) >> 1);
        var diff = 0;
        var common = common_i < common_j ? common_i : common_j;
        var w = v[k];
        var i2;
        for (i2 = w[0].length - 1 - common; i2 >= 0; i2--) {
          if (c - common == lb) {
            diff = -1;
            break;
          }
          diff = this.current.charCodeAt(c - 1 - common) - w[0].charCodeAt(i2);
          if (diff != 0) break;
          common++;
        }
        if (diff < 0) {
          j = k;
          common_j = common;
        } else {
          i = k;
          common_i = common;
        }
        if (j - i <= 1) {
          if (i > 0) break;
          if (j == i) break;
          if (first_key_inspected) break;
          first_key_inspected = true;
        }
      }
      while (true) {
        var w = v[i];
        if (common_i >= w[0].length) {
          this.cursor = c - w[0].length;
          if (w.length < 4) return w[2];
          var res = w[3](this);
          this.cursor = c - w[0].length;
          if (res) return w[2];
        }
        i = w[1];
        if (i < 0) return 0;
      }
    };

    /* to replace chars between c_bra and c_ket in this.current by the
     * chars in s.
     */
    this.replace_s = function(c_bra, c_ket, s) {
      var adjustment = s.length - (c_ket - c_bra);
      this.current = this.current.slice(0, c_bra) + s + this.current.slice(c_ket);
      this.limit += adjustment;
      if (this.cursor >= c_ket) this.cursor += adjustment;
      else if (this.cursor > c_bra) this.cursor = c_bra;
      return adjustment;
    };

    this.slice_check = function() {
      if (this.bra < 0 ||
        this.bra > this.ket ||
        this.ket > this.limit ||
        this.limit > this.current.length) {
        return false;
      }
      return true;
    };

    this.slice_from = function(s) {
      var result = false;
      if (this.slice_check()) {
        this.replace_s(this.bra, this.ket, s);
        result = true;
      }
      return result;
    };

    this.slice_del = function() {
      return this.slice_from("");
    };

    this.insert = function(c_bra, c_ket, s) {
      var adjustment = this.replace_s(c_bra, c_ket, s);
      if (c_bra <= this.bra) this.bra += adjustment;
      if (c_bra <= this.ket) this.ket += adjustment;
    };

    this.slice_to = function() {
      var result = '';
      if (this.slice_check()) {
        result = this.current.slice(this.bra, this.ket);
      }
      return result;
    };

    this.assign_to = function() {
      return this.current.slice(0, this.limit);
    };
  }
735759
function () {
	var len, attr, g, args;

	args = arguments;

	len = args.length;

	if (len === 0) {
		g = noop;
	}
	else if (len === 1) {
		attr = args[0];

		g = function (obj) {
			return obj[attr];
		};
	}
	else {
		g = function (obj) {
			var i, tuple;

			tuple = [];

			for (i = 0; i < len; ++i) {
				tuple.push(obj[args[i]]);
			}

			return tuple;
		};
	}



	return g;
}
899738
function distBBox(node, k, p, toBBox, destNode) {
    if (!destNode) destNode = createNode(null);
    destNode.minX = Infinity;
    destNode.minY = Infinity;
    destNode.maxX = -Infinity;
    destNode.maxY = -Infinity;

    for (var i = k, child; i < p; i++) {
        child = node.children[i];
        extend(destNode, node.leaf ? toBBox(child) : child);
    }

    return destNode;
}
1342671
function(g, x, y, z, w) {
		var n = g[0] * x + g[1] * y;
		if(z){
			n += g[2] * z;
			if(w){
				n += g[3] * w;
			}
		}
		return n;
	}
1174769
function(node, l, b, r, t)
{
    return (max(node.bb_r, r) - min(node.bb_l, l))*(max(node.bb_t, t) - min(node.bb_b, b));
}
26495
function luJs() {
                var A = this.dup();
                var L = Matrix$b.I(A.rows());
                var P = Matrix$b.I(A.rows());
                var U = Matrix$b.Zeros(A.rows(), A.cols());
                var p = 1;

                for(var k = 1; k <= Math.min(A.cols(), A.rows()); k++) {
            	P = A.partialPivot(k, p, P, A, L);
            	
            	for(var i = k + 1; i <= A.rows(); i++) {
            	    var l = A.e(i, p) / A.e(k, p);
            	    L.elements[i - 1][k - 1] = l;
            	    
            	    for(var j = k + 1 ; j <= A.cols(); j++) {
            		A.elements[i - 1][j - 1] -= A.e(k, j) * l;
            	    }
            	}
            	
            	for(var j = k; j <= A.cols(); j++) {
            	    U.elements[k - 1][j - 1] = A.e(k, j);
            	}

            	if(p < A.cols())
            	    p++;
                }    
                
                return {L: L, U: U, P: P};
            }
1182778
function tinf_build_fixed_trees(lt, dt) {
  var i;

  /* build fixed length tree */
  for (i = 0; i < 7; ++i) lt.table[i] = 0;

  lt.table[7] = 24;
  lt.table[8] = 152;
  lt.table[9] = 112;

  for (i = 0; i < 24; ++i) lt.trans[i] = 256 + i;
  for (i = 0; i < 144; ++i) lt.trans[24 + i] = i;
  for (i = 0; i < 8; ++i) lt.trans[24 + 144 + i] = 280 + i;
  for (i = 0; i < 112; ++i) lt.trans[24 + 144 + 8 + i] = 144 + i;

  /* build fixed distance tree */
  for (i = 0; i < 5; ++i) dt.table[i] = 0;

  dt.table[5] = 32;

  for (i = 0; i < 32; ++i) dt.trans[i] = i;
}
1159601
function ast_walker() {
        function _vardefs(defs) {
                return [ this[0], MAP(defs, function(def){
                        var a = [ def[0] ];
                        if (def.length > 1)
                                a[1] = walk(def[1]);
                        return a;
                }) ];
        };
        function _block(statements) {
                var out = [ this[0] ];
                if (statements != null)
                        out.push(MAP(statements, walk));
                return out;
        };
        var walkers = {
                "string": function(str) {
                        return [ this[0], str ];
                },
                "num": function(num) {
                        return [ this[0], num ];
                },
                "name": function(name) {
                        return [ this[0], name ];
                },
                "toplevel": function(statements) {
                        return [ this[0], MAP(statements, walk) ];
                },
                "block": _block,
                "splice": _block,
                "var": _vardefs,
                "const": _vardefs,
                "try": function(t, c, f) {
                        return [
                                this[0],
                                MAP(t, walk),
                                c != null ? [ c[0], MAP(c[1], walk) ] : null,
                                f != null ? MAP(f, walk) : null
                        ];
                },
                "throw": function(expr) {
                        return [ this[0], walk(expr) ];
                },
                "new": function(ctor, args) {
                        return [ this[0], walk(ctor), MAP(args, walk) ];
                },
                "switch": function(expr, body) {
                        return [ this[0], walk(expr), MAP(body, function(branch){
                                return [ branch[0] ? walk(branch[0]) : null,
                                         MAP(branch[1], walk) ];
                        }) ];
                },
                "break": function(label) {
                        return [ this[0], label ];
                },
                "continue": function(label) {
                        return [ this[0], label ];
                },
                "conditional": function(cond, t, e) {
                        return [ this[0], walk(cond), walk(t), walk(e) ];
                },
                "assign": function(op, lvalue, rvalue) {
                        return [ this[0], op, walk(lvalue), walk(rvalue) ];
                },
                "dot": function(expr) {
                        return [ this[0], walk(expr) ].concat(slice(arguments, 1));
                },
                "call": function(expr, args) {
                        return [ this[0], walk(expr), MAP(args, walk) ];
                },
                "function": function(name, args, body) {
                        return [ this[0], name, args.slice(), MAP(body, walk) ];
                },
                "debugger": function() {
                        return [ this[0] ];
                },
                "defun": function(name, args, body) {
                        return [ this[0], name, args.slice(), MAP(body, walk) ];
                },
                "if": function(conditional, t, e) {
                        return [ this[0], walk(conditional), walk(t), walk(e) ];
                },
                "for": function(init, cond, step, block) {
                        return [ this[0], walk(init), walk(cond), walk(step), walk(block) ];
                },
                "for-in": function(vvar, key, hash, block) {
                        return [ this[0], walk(vvar), walk(key), walk(hash), walk(block) ];
                },
                "while": function(cond, block) {
                        return [ this[0], walk(cond), walk(block) ];
                },
                "do": function(cond, block) {
                        return [ this[0], walk(cond), walk(block) ];
                },
                "return": function(expr) {
                        return [ this[0], walk(expr) ];
                },
                "binary": function(op, left, right) {
                        return [ this[0], op, walk(left), walk(right) ];
                },
                "unary-prefix": function(op, expr) {
                        return [ this[0], op, walk(expr) ];
                },
                "unary-postfix": function(op, expr) {
                        return [ this[0], op, walk(expr) ];
                },
                "sub": function(expr, subscript) {
                        return [ this[0], walk(expr), walk(subscript) ];
                },
                "object": function(props) {
                        return [ this[0], MAP(props, function(p){
                                return p.length == 2
                                        ? [ p[0], walk(p[1]) ]
                                        : [ p[0], walk(p[1]), p[2] ]; // get/set-ter
                        }) ];
                },
                "regexp": function(rx, mods) {
                        return [ this[0], rx, mods ];
                },
                "array": function(elements) {
                        return [ this[0], MAP(elements, walk) ];
                },
                "stat": function(stat) {
                        return [ this[0], walk(stat) ];
                },
                "seq": function() {
                        return [ this[0] ].concat(MAP(slice(arguments), walk));
                },
                "label": function(name, block) {
                        return [ this[0], name, walk(block) ];
                },
                "with": function(expr, block) {
                        return [ this[0], walk(expr), walk(block) ];
                },
                "atom": function(name) {
                        return [ this[0], name ];
                }
        };

        var user = {};
        var stack = [];
        function walk(ast) {
                if (ast == null)
                        return null;
                try {
                        stack.push(ast);
                        var type = ast[0];
                        var gen = user[type];
                        if (gen) {
                                var ret = gen.apply(ast, ast.slice(1));
                                if (ret != null)
                                        return ret;
                        }
                        gen = walkers[type];
                        return gen.apply(ast, ast.slice(1));
                } finally {
                        stack.pop();
                }
        };

        function dive(ast) {
                if (ast == null)
                        return null;
                try {
                        stack.push(ast);
                        return walkers[ast[0]].apply(ast, ast.slice(1));
                } finally {
                        stack.pop();
                }
        };

        function with_walkers(walkers, cont){
                var save = {}, i;
                for (i in walkers) if (HOP(walkers, i)) {
                        save[i] = user[i];
                        user[i] = walkers[i];
                }
                var ret = cont();
                for (i in save) if (HOP(save, i)) {
                        if (!save[i]) delete user[i];
                        else user[i] = save[i];
                }
                return ret;
        };

        return {
                walk: walk,
                dive: dive,
                with_walkers: with_walkers,
                parent: function() {
                        return stack[stack.length - 2]; // last one is current node
                },
                stack: function() {
                        return stack;
                }
        };
}