Skip to content

Instantly share code, notes, and snippets.

@Floofies
Created August 20, 2018 23:01
Show Gist options
  • Save Floofies/75c2fa7516ccf06efb2f9e91224c02bd to your computer and use it in GitHub Desktop.
Save Floofies/75c2fa7516ccf06efb2f9e91224c02bd to your computer and use it in GitHub Desktop.
Coroutine Dispatcher Prototype 2
function Coroutine(functor, thisArg = null, args = null) {
this.type = "coroutine";
this.image = functor;
this.instance = null;
this.args = args;
this.thisArg = thisArg;
}
Coroutine.prototype.call = function (nextValue) {
if (this.instance === null) {
if (this.thisArg !== null) {
if (this.args !== null) {
this.instance = this.image.apply(this.thisArg, this.args);
} else {
this.instance = this.image.call(this.thisArg);
}
} else {
if (this.args !== null) {
this.instance = this.image(...this.args);
} else {
this.instance = this.image();
}
}
}
return this.instance.next(nextValue);
};
function Subroutine(functor, thisArg = null, args = null) {
this.type = "subroutine";
this.functor = functor;
this.args = args;
this.thisArg = thisArg;
}
Subroutine.prototype.call = function (args) {
if (this.thisArg !== null) {
if (this.args !== null) {
var state = this.functor.apply(this.thisArg, this.args);
} else {
var state = this.functor.call(this.thisArg);
}
} else {
if (this.args !== null) {
var state = this.functor(...this.args);
} else {
var state = this.functor();
}
}
return { done: true, value: state };
};
function Dispatcher() {
this.running = false;
this.task = null;
this.lastReturn = null;
this.stack = [];
}
Dispatcher.kernSym = Symbol("Kernelization Marker Symbol");
Dispatcher.nullSym = Symbol("Null Value Symbol");
Dispatcher.genSyn = Symbol("Generator Symbol");
Dispatcher.prototype.kernSym = Dispatcher.kernSym;
Dispatcher.prototype.nullSym = Dispatcher.nullSym;
// Dispatcher API Kernelizers
Dispatcher.prototype.kernMark = function (obj) {
obj[Dispatcher.kernSym] = null;
return obj;
};
Dispatcher.prototype.call = function (functor, thisArg = null, ...args) {
return this.kernMark({ type: "call", functor: functor, args: args, thisArg: thisArg });
};
Dispatcher.prototype.callSubroutine = function (functor, thisArg = null, ...args) {
return this.kernMark({ type: "subroutineCall", functor: functor, args: args, thisArg: thisArg });
};
Dispatcher.prototype.callMethod = function (object, prop, ...args) {
return this.kernMark({ type: "methodCall", object: object, prop: prop, args: args, thisArg: object });
};
Dispatcher.prototype.callSubroutineMethod = function (object, prop, ...args) {
return this.kernMark({ type: "subroutineMethodCall", object: object, prop: prop, args: args, thisArg: object });
};
Dispatcher.prototype.returnValue = function (value = Dispatcher.nullSym) {
return this.kernMark({ type: "return", returnValue: value });
};
Dispatcher.prototype.yieldValue = function (value = Dispatcher.nullSym) {
return this.kernMark({ type: "yield", yieldValue: value });
};
Dispatcher.prototype.initGenerator = function (functor, ...args) {
return this.kernMark({ type: "generatorInit", functor: functor, args: args });
};
Dispatcher.prototype.cycle = function () {
try {
if (this.stack.length === 0 || !this.running) {
this.running = false;
return;
}
this.task = this.stack[this.stack.length - 1];
const state = this.task.call(this.lastReturn !== null ? this.lastReturn : undefined);
if (this.task.type === "subroutine" || (typeof state.value) !== "object" || !(this.kernSym in state.value)) {
var node = this.returnValue(state.value);
} else {
var node = state.value;
}
if (node.type === "generatorInit") {
if (node.args.length === 0) node.args = null;
if (node.args !== null) {
this.lastReturn = node.functor(...node.args);
} else {
this.lastReturn = node.functor();
}
this.stack.pop();
return;
} else if (node.type === "return") {
this.lastReturn = node.returnValue !== this.nullSym ? node.returnValue : undefined;
this.stack.pop();
return;
} else if (node.type === "yield") {
this.lastReturn = node.yieldValue !== this.nullSym ? node.yieldValue : undefined;
this.stack.pop();
return;
} else if (state.done) {
this.stack.pop();
return;
}
this.lastReturn = null;
if (node.type === "call") {
if (node.args.length === 0) node.args = null;
this.stack.push(new Coroutine(node.functor, node.thisArg, node.args));
} else if (node.type === "methodCall") {
if (node.args.length === 0) node.args = null;
this.stack.push(new Coroutine(node.object[node.prop], node.object, node.args));
} else if (node.type === "subroutineCall") {
if (node.args.length === 0) node.args = null;
this.stack.push(new Subroutine(node.functor, node.thisArg, node.args));
} else if (node.type === "subroutineMethodCall") {
if (node.args.length === 0) node.args = null;
this.stack.push(new Subroutine(node.object[node.prop], node.object, node.args));
} else if (node.type === "sysCall") {
if (!(sysCall in this.sysCalls)) throw new ReferenceError("ABORTING CYCLE: Requested SysCall \"" + node.callName + "\" was not found.");
this.sysCalls[node.callName](node.args);
}
} catch (error) {
console.group(error);
console.log("\n");
console.error(`Current Task:\n{\n\t${Object.keys(this.task).map(key => `${key}: ${((typeof this.task[key]) === "function" ? "[function "+ this.task[key].name + "]" : this.task[key])}`).join(",\n\t")}\n}\n`);
console.error("Dispatcher Stack:\n" + this.stack.map(function (node) {
return `{\n\t${Object.keys(node).map(key => `${key}: ${((typeof node[key]) === "function" ? "[function "+ node[key].name + "]" : node[key])}`).join(",\n\t")}\n}`;
}).join(",\n"));
console.groupEnd();
this.running = false;
}
};
Dispatcher.prototype.enqueue = function (functor, thisArg, ...args) {
this.stack.push(new Coroutine(functor, thisArg, ...args));
};
Dispatcher.prototype.runSync = function () {
this.running = true;
while (this.running) this.cycle();
};
Dispatcher.prototype.runAsync = function (quantum = 0) {
this.running = true;
return new Promise(function (resolve, reject) {
const asyncRunner = function () {
this.cycle();
if (!this.running) return resolve();
setTimeout(asyncRunner, quantum);
};
});
};
Dispatcher.prototype.runIterator = function* () {
this.running = true;
while (this.running) yield this.cycle();
};
Dispatcher.prototype.stop = function () {
this.running = false;
};
module.exports = Dispatcher;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment