js-marketplace-2014/marketplace.js

162 lines
3.6 KiB
JavaScript

/*---------------------------------------------------------------------------*/
/* Unification */
var __ = new Object(); /* wildcard marker */
__.__ = "__";
function unificationFailed() {
throw {unificationFailed: true};
}
function unify1(a, b) {
var i;
if (a === __) return b;
if (b === __) return a;
if (a === b) return a;
if (Array.isArray(a) && Array.isArray(b)) {
if (a.length !== b.length) unificationFailed();
var result = new Array(a.length);
for (i = 0; i < a.length; i++) {
result[i] = unify1(a[i], b[i]);
}
return result;
}
if (typeof a === "object" && typeof b === "object") {
/* TODO: consider other kinds of matching. I've chosen to
require any field mentioned by either side to be present in
both. Does that make sense? */
var result = ({});
for (i in a) { if (a.hasOwnProperty(i)) result[i] = true; }
for (i in b) { if (b.hasOwnProperty(i)) result[i] = true; }
for (i in result) {
if (result.hasOwnProperty(i)) {
result[i] = unify1(a[i], b[i]);
}
}
return result;
}
unificationFailed();
}
function unify(a, b) {
try {
return {result: unify1(a, b)};
} catch (e) {
if (e.unificationFailed) return undefined;
throw e;
}
}
function anyUnify(aa, bb) {
for (var i = 0; i < aa.length; i++) {
for (var j = 0; j < bb.length; j++) {
if (unify(aa[i], bb[j])) return true;
}
}
return false;
}
/*---------------------------------------------------------------------------*/
/* Relaying */
function Route(isSubscription, pattern, isMeta, level) {
this.isSubscription = isSubscription;
this.pattern = pattern;
this.isMeta = isMeta ? true : false;
this.level = (level === undefined) ? 0 : level;
}
function sub(pattern, isMeta, level) {
return new Route(true, pattern, isMeta, level);
}
function pub(pattern, isMeta, level) {
return new Route(false, pattern, isMeta, level);
}
function spawn(actor) { /* TODO: initialRoutes */
return { type: "spawn", actor: actor };
}
function updateRoutes(routes) {
return { type: "routes", routes: routes };
}
function sendMessage(m) {
return { type: "send", isMeta: false, message: m };
}
function metaMessage(m) {
return { type: "send", isMeta: true, message: m };
}
function World(bootActions) {
this.nextPid = 0;
this.eventQueue = [];
this.processTable = {};
this.downwardRoutes = [];
this.pendingActions = [];
this.enqueueActions(-1, bootActions);
}
World.prototype.enqueueActions = function (pid, actions) {
for (var i = 0; i < actions.length; i++) {
this.pendingActions.push([pid, actions[i]]);
}
};
World.prototype.boot = function () {
this.performPendingActions();
};
World.prototype.performPendingActions = function () {
var queue = this.pendingActions;
this.pendingActions = [];
var item;
while ((item = queue.shift())) {
this.performAction(item[0], item[1]);
}
};
World.prototype.performAction = function (pid, action) {
switch (action.type) {
case "spawn":
this.doSpawn(action.actor);
break;
case "routes":
this.doRoutes(pid, action.routes);
break;
case "send":
this.doSend(pid, action.isMeta, action.message);
break;
default:
throw { message: "Action type " + action.type + " not understood",
action: action };
}
};
World.prototype.doSpawn = function (actor) {
this.processTable[this.nextPid++] = actor;
};
World.prototype.handleEvent = function (e) {
this.dispatchEvent(e);
this.performPendingActions();
};
World.prototype.dispatchEvent = function (e) {
switch (e.type) {
case "routes":
break;
case "send":
break;
default:
break;
}
};