securityos/node_modules/seq/index.js

521 lines
16 KiB
JavaScript
Executable File

var EventEmitter = require('events').EventEmitter;
var Hash = require('hashish');
var Chainsaw = require('chainsaw');
module.exports = Seq;
function Seq (xs) {
if (xs && !Array.isArray(xs) || arguments.length > 1) {
throw new Error('Optional argument to Seq() is exactly one Array');
}
var ch = Chainsaw(function (saw) {
builder.call(this, saw, xs || []);
});
process.nextTick(function () {
ch['catch'](function (err) {
console.error(err.stack ? err.stack : err)
});
});
return ch;
}
Seq.ap = Seq; // for compatability with versions <0.3
function builder (saw, xs) {
var context = {
vars : {},
args : {},
stack : xs,
error : null
};
context.stack_ = context.stack;
function action (step, key, f, g) {
var cb = function (err) {
var args = [].slice.call(arguments, 1);
if (err) {
context.error = { message : err, key : key };
saw.jump(lastPar);
saw.down('catch');
g();
}
else {
if (typeof key == 'number') {
context.stack_[key] = args[0];
context.args[key] = args;
}
else {
context.stack_.push.apply(context.stack_, args);
if (key !== undefined) {
context.vars[key] = args[0];
context.args[key] = args;
}
}
if (g) g(args, key);
}
};
Hash(context).forEach(function (v,k) { cb[k] = v });
cb.into = function (k) {
key = k;
return cb;
};
cb.next = function (err, xs) {
context.stack_.push.apply(context.stack_, xs);
cb.apply(cb, [err].concat(context.stack));
};
cb.pass = function (err) {
cb.apply(cb, [err].concat(context.stack));
};
cb.ok = cb.bind(cb, null);
f.apply(cb, context.stack);
}
var running = 0;
var errors = 0;
this.seq = function (key, cb) {
var bound = [].slice.call(arguments, 2);
if (typeof key === 'function') {
if (arguments.length > 1) bound.unshift(cb);
cb = key;
key = undefined;
}
if (context.error) saw.next()
else if (running === 0) {
action(saw.step, key,
function () {
context.stack_ = [];
var args = [].slice.call(arguments);
args.unshift.apply(args, bound.map(function (arg) {
return arg === Seq ? this : arg
}, this));
cb.apply(this, args);
}, function () {
context.stack = context.stack_;
saw.next()
}
);
}
};
var lastPar = null;
this.par = function (key, cb) {
lastPar = saw.step;
if (running == 0) {
// empty the active stack for the first par() in a chain
context.stack_ = [];
}
var bound = [].slice.call(arguments, 2);
if (typeof key === 'function') {
if (arguments.length > 1) bound.unshift(cb);
cb = key;
key = context.stack_.length;
context.stack_.push(null);
}
var cb_ = function () {
var args = [].slice.call(arguments);
args.unshift.apply(args, bound.map(function (arg) {
return arg === Seq ? this : arg
}, this));
cb.apply(this, args);
};
running ++;
var step = saw.step;
process.nextTick(function () {
action(step, key, cb_, function (args) {
if (!args) errors ++;
running --;
if (running == 0) {
context.stack = context.stack_.slice();
saw.step = lastPar;
if (errors > 0) saw.down('catch');
errors = 0;
saw.next();
}
});
});
saw.next();
};
[ 'seq', 'par' ].forEach(function (name) {
this[name + '_'] = function (key) {
var args = [].slice.call(arguments);
var cb = typeof key === 'function'
? args[0] : args[1];
var fn = function () {
var argv = [].slice.call(arguments);
argv.unshift(this);
cb.apply(this, argv);
};
if (typeof key === 'function') {
args[0] = fn;
}
else {
args[1] = fn;
}
this[name].apply(this, args);
};
}, this);
this['catch'] = function (cb) {
if (context.error) {
cb.call(context, context.error.message, context.error.key);
context.error = null;
}
saw.next();
};
this.forEach = function (cb) {
this.seq(function () {
context.stack_ = context.stack.slice();
var end = context.stack.length;
if (end === 0) this(null)
else context.stack.forEach(function (x, i) {
action(saw.step, i, function () {
cb.call(this, x, i);
if (i == end - 1) saw.next();
});
});
});
};
this.seqEach = function (cb) {
this.seq(function () {
context.stack_ = context.stack.slice();
var xs = context.stack.slice();
if (xs.length === 0) this(null);
else (function next (i) {
action(
saw.step, i,
function () { cb.call(this, xs[i], i) },
function (args) {
if (!args || i === xs.length - 1) saw.next();
else next(i + 1);
}
);
}).bind(this)(0);
});
};
this.parEach = function (limit, cb) {
var xs = context.stack.slice();
if (cb === undefined) { cb = limit; limit = xs.length }
context.stack_ = [];
var active = 0;
var finished = 0;
var queue = [];
if (xs.length === 0) saw.next()
else xs.forEach(function call (x, i) {
if (active >= limit) {
queue.push(call.bind(this, x, i));
}
else {
active ++;
action(saw.step, i,
function () {
cb.call(this, x, i);
},
function () {
active --;
finished ++;
if (queue.length > 0) queue.shift()();
else if (finished === xs.length) {
saw.next();
}
}
);
}
});
};
this.parMap = function (limit, cb) {
var res = [];
var len = context.stack.length;
if (cb === undefined) { cb = limit; limit = len }
var res = [];
Seq()
.extend(context.stack)
.parEach(limit, function (x, i) {
var self = this;
var next = function () {
res[i] = arguments[1];
self.apply(self, arguments);
};
next.stack = self.stack;
next.stack_ = self.stack_;
next.vars = self.vars;
next.args = self.args;
next.error = self.error;
next.into = function (key) {
return function () {
res[key] = arguments[1];
self.apply(self, arguments);
};
};
next.ok = function () {
var args = [].slice.call(arguments);
args.unshift(null);
return next.apply(next, args);
};
cb.apply(next, arguments);
})
.seq(function () {
context.stack = res;
saw.next();
})
;
};
this.seqMap = function (cb) {
var res = [];
var lastIdx = context.stack.length - 1;
this.seqEach(function (x, i) {
var self = this;
var next = function () {
res[i] = arguments[1];
if (i === lastIdx)
context.stack = res;
self.apply(self, arguments);
};
next.stack = self.stack;
next.stack_ = self.stack_;
next.vars = self.vars;
next.args = self.args;
next.error = self.error;
next.into = function (key) {
return function () {
res[key] = arguments[1];
if (i === lastIdx)
context.stack = res;
self.apply(self, arguments);
};
};
next.ok = function () {
var args = [].slice.call(arguments);
args.unshift(null);
return next.apply(next, args);
};
cb.apply(next, arguments);
});
};
/**
* Consumes any errors that occur in `cb`. Calls to `this.into(i)` will place
* that value, if accepted by the filter, at the index in the results as
* if it were the i-th index before filtering. (This means it will never
* override another value, and will only actually appear at i if the filter
* accepts all values before i.)
*/
this.parFilter = function (limit, cb) {
var res = [];
var len = context.stack.length;
if (cb === undefined) { cb = limit; limit = len }
var res = [];
Seq()
.extend(context.stack)
.parEach(limit, function (x, i) {
var self = this;
var next = function (err, ok) {
if (!err && ok)
res.push([i, x]);
arguments[0] = null; // discard errors
self.apply(self, arguments);
};
next.stack = self.stack;
next.stack_ = self.stack_;
next.vars = self.vars;
next.args = self.args;
next.error = self.error;
next.into = function (key) {
return function (err, ok) {
if (!err && ok)
res.push([key, x]);
arguments[0] = null; // discard errors
self.apply(self, arguments);
};
};
next.ok = function () {
var args = [].slice.call(arguments);
args.unshift(null);
return next.apply(next, args);
};
cb.apply(next, arguments);
})
.seq(function () {
context.stack = res.sort().map(function(pair){ return pair[1]; });
saw.next();
})
;
};
/**
* Consumes any errors that occur in `cb`. Calls to `this.into(i)` will place
* that value, if accepted by the filter, at the index in the results as
* if it were the i-th index before filtering. (This means it will never
* override another value, and will only actually appear at i if the filter
* accepts all values before i.)
*/
this.seqFilter = function (cb) {
var res = [];
var lastIdx = context.stack.length - 1;
this.seqEach(function (x, i) {
var self = this;
var next = function (err, ok) {
if (!err && ok)
res.push([i, x]);
if (i === lastIdx)
context.stack = res.sort().map(function(pair){ return pair[1]; });
arguments[0] = null; // discard errors
self.apply(self, arguments);
};
next.stack = self.stack;
next.stack_ = self.stack_;
next.vars = self.vars;
next.args = self.args;
next.error = self.error;
next.into = function (key) {
return function (err, ok) {
if (!err && ok)
res.push([key, x]);
if (i === lastIdx)
context.stack = res.sort().map(function(pair){ return pair[1]; });
arguments[0] = null; // discard errors
self.apply(self, arguments);
};
};
next.ok = function () {
var args = [].slice.call(arguments);
args.unshift(null);
return next.apply(next, args);
};
cb.apply(next, arguments);
});
};
[ 'forEach', 'seqEach', 'parEach', 'seqMap', 'parMap', 'seqFilter', 'parFilter' ]
.forEach(function (name) {
this[name + '_'] = function (cb) {
this[name].call(this, function () {
var args = [].slice.call(arguments);
args.unshift(this);
cb.apply(this, args);
});
};
}, this)
;
['push','pop','shift','unshift','splice','reverse']
.forEach(function (name) {
this[name] = function () {
context.stack[name].apply(
context.stack,
[].slice.call(arguments)
);
saw.next();
return this;
};
}, this)
;
[ 'map', 'filter', 'reduce' ]
.forEach(function (name) {
this[name] = function () {
var res = context.stack[name].apply(
context.stack,
[].slice.call(arguments)
);
// stack must be an array, or bad things happen
context.stack = (Array.isArray(res) ? res : [res]);
saw.next();
return this;
};
}, this)
;
this.extend = function (xs) {
if (!Array.isArray(xs)) {
throw new Error('argument to .extend() is not an Array');
}
context.stack.push.apply(context.stack, xs);
saw.next();
};
this.flatten = function (pancake) {
var xs = [];
// should we fully flatten this array? (default: true)
if (pancake === undefined) { pancake = true; }
context.stack.forEach(function f (x) {
if (Array.isArray(x) && pancake) x.forEach(f);
else if (Array.isArray(x)) xs = xs.concat(x);
else xs.push(x);
});
context.stack = xs;
saw.next();
};
this.unflatten = function () {
context.stack = [context.stack];
saw.next();
};
this.empty = function () {
context.stack = [];
saw.next();
};
this.set = function (stack) {
context.stack = stack;
saw.next();
};
this['do'] = function (cb) {
saw.nest(cb, context);
};
}