2021-03-14 11:09:02 +01:00

413 lines
11 KiB
JavaScript

var Tail = require('./tail');
module.exports = function() {
var procedure = function(fn) {
return function() {
var cb = arguments[arguments.length - 1];
try {
fn.apply(this, arguments);
} catch(e) {
handleError(e, cb);
}
};
};
var handleError = function(e, cb) {
cb(e);
};
var callback = function(cb, fn) {
var called = false;
return function(err) {
if (called) {
if (err) {
console.log(err);
if (err.stack) {
console.log(err.stack);
}
}
throw new Error('Continuation is being called more than once!');
}
called = true;
try {
if (err) {
handleError(err, cb);
} else {
fn.apply(this, arguments);
}
} catch(e) {
handleError(e, cb);
}
};
};
var _seq = procedure(function(procs, i, res, cb) {
if (i >= procs.length) {
return cb(null, res);
}
var proc = procs[i];
proc(res, callback(cb, function(err, res) {
// return _seq(procs, i+1, res, cb);
return Tail.run(function() {
_seq(procs, i+1, res, cb);
});
}));
});
var seq = function(procs, cb) {
return _seq(procs, 0, null, cb);
};
var rescue = procedure(function(procBundle, cb) {
var tryProc = procBundle['try'];
var catchProc = procBundle['catch'] || function(err, cb) {cb(err);};
var finallyProc = procBundle['finally'] || function(cb) {cb();};
var applyTry = procedure(function(cb) {
tryProc(cb);
});
var applyCatch = procedure(function(err, cb) {
catchProc(err, cb);
});
var applyFinallyOk = procedure(function(res0, cb) {
finallyProc(callback(cb, function(err, res) {
cb(null, res0);
}));
});
var applyFinallyError = procedure(function(err0, cb) {
finallyProc(callback(cb, function(err, res) {
cb(err0);
}));
});
applyTry(function(err, res) {
if (err) {
applyCatch(err, function(err, res) {
if (err) {
applyFinallyError(err, cb);
} else {
applyFinallyOk(res, cb);
}
});
} else {
applyFinallyOk(res, cb);
}
});
});
var pwhile = procedure(function(procBool, procBody, cb) {
seq([
function(_, cb) {
procBool(cb);
},
function(_, cb) {
if (_) {
seq([
function(_, cb) {
procBody(cb);
},
function(_, cb) {
pwhile(procBool, procBody, cb);
}
], cb);
} else {
cb();
}
}
], cb);
});
var peach = procedure(function(arr, proc, cb) {
var i = 0;
pwhile(
function(cb) {
cb(null, i < arr.length);
},
function(cb) {
seq([
function(_, cb) {
proc(arr[i], cb);
},
function(_, cb) {
i++;
cb();
}
], cb);
},
cb
)
});
var pfor = procedure(function(n, proc, cb) {
var i = 0;
pwhile(
function(cb) {
cb(null, i < n);
},
function(cb) {
seq([
function(_, cb) {
proc(i, cb);
},
function(_, cb) {
i++;
cb();
}
], cb);
},
cb
);
});
var pmap = procedure(function(arr, proc, cb) {
var l = [];
seq([
function(_, cb) {
peach(arr, function(e, cb) {
seq([
function(_, cb) {
proc(e, cb);
},
function(_, cb) {
l.push(_);
cb();
}
], cb);
}, cb);
},
function(_, cb) {
cb(null, l);
}
], cb);
});
var _parallel2 = procedure(function(proc1, proc2, cb) {
var state1 = 'start';
var state2 = 'start';
var res1;
var res2;
var err1;
var err2;
var applyProc1 = procedure(function(cb) {
proc1(cb);
});
var applyProc2 = procedure(function(cb) {
proc2(cb);
});
applyProc1(function(err, res) {Tail.run(function() {
if (err) {
state1 = 'error';
err1 = err;
switch(state2) {
case 'start':
break;
case 'done':
cb(null, [
{status: 'error', error: err1},
{status: 'ok', data: res2}
]);
break;
case 'error':
cb(null, [
{status: 'error', error: err1},
{status: 'error', error: err2}
]);
break;
default:
}
} else {
state1 = 'done';
res1 = res;
switch(state2) {
case 'start':
break;
case 'done':
cb(null, [
{status: 'ok', data: res1},
{status: 'ok', data: res2}
]);
break;
case 'error':
cb(null, [
{status: 'ok', data: res1},
{status: 'error', error: err2}
]);
break;
default:
}
}
})});
applyProc2(function(err, res) {Tail.run(function() {
if (err) {
state2 = 'error';
err2 = err;
switch(state1) {
case 'start':
break;
case 'done':
cb(null, [
{status: 'ok', data: res1},
{status: 'error', error: err2}
]);
break;
case 'error':
cb(null, [
{status: 'error', error: err1},
{status: 'error', error: err2}
]);
break;
default:
}
} else {
state2 = 'done';
res2 = res;
switch(state1) {
case 'start':
break;
case 'done':
cb(null, [
{status: 'ok', data: res1},
{status: 'ok', data: res2}
]);
break;
case 'error':
cb(null, [
{status: 'error', error: err1},
{status: 'ok', data: res2}
]);
break;
default:
}
}
})});
});
var _parallel = procedure(function(procs, i, cb) {
if (procs.length == 0) {
return cb();
}
if (i == procs.length - 1) {
return procs[i](function(err, res) {
if (err) {
cb(null, [{status: 'error', error: err}]);
} else {
cb(null, [{status: 'ok', data: res}]);
}
});
}
if (i < procs.length) {
_parallel2(
procs[i],
function(cb) {
_parallel(procs, i+1, cb);
},
callback(cb, function(err, res) {
cb(null, [res[0]].concat(res[1].data));
})
);
}
});
var parallel = procedure(function(procs, cb) {
_parallel(procs, 0, cb);
});
var noFail = function() {
var proc, handler, cb;
proc = arguments[0];
cb = arguments[arguments.length - 1];
if (arguments.length == 2) {
handler = function(err) {
console.log('ERROR caught by cps.noFail: ', err);
if (err.stack) {
console.log(err.stack);
}
};
} else if (arguments.length == 3) {
handler = arguments[1];
} else {
handleError(new Error('Incorrect number of arguments in calling cps.noFail.'), cb);
}
rescue({
'try': function(cb) {
proc(cb);
},
'catch': function(err, cb) {
handler(err);
cb();
}
}, cb);
};
var run = function(proc, cfg) {
cfg = cfg || {};
var cb = function(err, res) {
try {
if (err) {
if (cfg['error']) {
cfg['error'](err);
} else {
console.log('cps.run ERROR: ', err);
if (err.stack) {
console.log(err.stack);
}
}
} else {
if (cfg['ok']) {
cfg['ok'](res);
} else {
console.log('cps.run OK: ', res);
}
}
} catch(e) {
if (cfg['topLevelError']) {
cfg['topLevelError'](e);
} else {
console.log('cps.run TOP_LEVEL_ERROR: ', e);
}
} finally {
if (cfg['finally']) {
try {
cfg['finally']();
} catch(e) {
console.log('cps.run FINALLY_ERROR: ', e);
}
}
}
};
proc(cb);
};
return {
seq: seq,
peach: peach,
pwhile: pwhile,
pmap: pmap,
pfor: pfor,
rescue: rescue,
parallel: parallel,
noFail: noFail,
run: run
};
}();