Compare commits
3 Commits
main
...
webrtc_tmp
Author | SHA1 | Date |
---|---|---|
Tony Garnock-Jones | a7e832b59f | |
Tony Garnock-Jones | d8c9209017 | |
Tony Garnock-Jones | 52e8fab9f3 |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -76,7 +76,7 @@ $(document).ready(function () {
|
||||||
// Monitor connection, notifying connectivity changes
|
// Monitor connection, notifying connectivity changes
|
||||||
state: "crashed", // start with this to avoid spurious initial message print
|
state: "crashed", // start with this to avoid spurious initial message print
|
||||||
boot: function () {
|
boot: function () {
|
||||||
return [sub(["broker_state", __], 0, 1)];
|
World.updateRoutes([sub(["broker_state", __], 0, 1)]);
|
||||||
},
|
},
|
||||||
handleEvent: function (e) {
|
handleEvent: function (e) {
|
||||||
if (e.type === "routes") {
|
if (e.type === "routes") {
|
||||||
|
@ -94,7 +94,7 @@ $(document).ready(function () {
|
||||||
World.spawn({
|
World.spawn({
|
||||||
// Actual chat functionality
|
// Actual chat functionality
|
||||||
boot: function () {
|
boot: function () {
|
||||||
return this.subscriptions();
|
World.updateRoutes(this.subscriptions());
|
||||||
},
|
},
|
||||||
nym: function () { return $("#nym").val(); },
|
nym: function () { return $("#nym").val(); },
|
||||||
currentStatus: function () { return $("#status").val(); },
|
currentStatus: function () { return $("#status").val(); },
|
||||||
|
|
|
@ -13,18 +13,15 @@ $(document).ready(function () {
|
||||||
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
|
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
|
||||||
|
|
||||||
World.spawn({
|
World.spawn({
|
||||||
boot: function () {
|
|
||||||
return [pub(["DOM", "#clicker-holder", "clicker",
|
|
||||||
["button", ["span", [["style", "font-style: italic"]], "Click me!"]]]),
|
|
||||||
pub("bump_count"),
|
|
||||||
sub(["jQuery", "button.clicker", "click", __])];
|
|
||||||
},
|
|
||||||
handleEvent: function (e) {
|
handleEvent: function (e) {
|
||||||
if (e.type === "message" && e.message[0] === "jQuery") {
|
if (e.type === "message" && e.message[0] === "jQuery") {
|
||||||
World.send("bump_count");
|
World.send("bump_count");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, [pub(["DOM", "#clicker-holder", "clicker",
|
||||||
|
["button", ["span", [["style", "font-style: italic"]], "Click me!"]]]),
|
||||||
|
pub("bump_count"),
|
||||||
|
sub(["jQuery", "button.clicker", "click", __])]);
|
||||||
|
|
||||||
World.spawn({
|
World.spawn({
|
||||||
counter: 0,
|
counter: 0,
|
||||||
|
|
|
@ -29,13 +29,6 @@ $(document).ready(function () {
|
||||||
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy"); // local
|
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy"); // local
|
||||||
|
|
||||||
World.spawn({
|
World.spawn({
|
||||||
boot: function () {
|
|
||||||
return [pub(domWrap("#clicker-holder", localId + "-clicker",
|
|
||||||
["button", ["span", [["style", "font-style: italic"]],
|
|
||||||
"Click me! (" + localId + ")"]])),
|
|
||||||
pub("bump_count"),
|
|
||||||
sub(jQueryWrap("button."+localId+"-clicker", "click", __))];
|
|
||||||
},
|
|
||||||
handleEvent: function (e) {
|
handleEvent: function (e) {
|
||||||
console.log(JSON.stringify(e));
|
console.log(JSON.stringify(e));
|
||||||
if (e.type === "message"
|
if (e.type === "message"
|
||||||
|
@ -47,7 +40,11 @@ $(document).ready(function () {
|
||||||
World.send("bump_count");
|
World.send("bump_count");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, [pub(domWrap("#clicker-holder", localId + "-clicker",
|
||||||
|
["button", ["span", [["style", "font-style: italic"]],
|
||||||
|
"Click me! (" + localId + ")"]])),
|
||||||
|
pub("bump_count"),
|
||||||
|
sub(jQueryWrap("button."+localId+"-clicker", "click", __))]);
|
||||||
|
|
||||||
World.spawn({
|
World.spawn({
|
||||||
counter: 0,
|
counter: 0,
|
||||||
|
|
|
@ -12,9 +12,6 @@ $(document).ready(function () {
|
||||||
|
|
||||||
World.spawn({
|
World.spawn({
|
||||||
name: 'GestaltDisplay',
|
name: 'GestaltDisplay',
|
||||||
boot: function () {
|
|
||||||
return [sub(__, 0, 10), pub(__, 0, 10)];
|
|
||||||
},
|
|
||||||
handleEvent: function (e) {
|
handleEvent: function (e) {
|
||||||
if (e.type === "routes") {
|
if (e.type === "routes") {
|
||||||
var gd = document.getElementById('gestalt-display');
|
var gd = document.getElementById('gestalt-display');
|
||||||
|
@ -24,7 +21,7 @@ $(document).ready(function () {
|
||||||
gd.appendChild(t);
|
gd.appendChild(t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, [sub(__, 0, 10), pub(__, 0, 10)]);
|
||||||
|
|
||||||
World.spawn(new Actor(function () {
|
World.spawn(new Actor(function () {
|
||||||
this.counter = 0;
|
this.counter = 0;
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>RTCPeerConnection</title>
|
||||||
|
<meta charset=utf8>
|
||||||
|
<script src="../../third-party/jquery-2.0.3.min.js"></script>
|
||||||
|
<script src="../../dist/minimart.js"></script>
|
||||||
|
<script src="index.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>RTCPeerConnection</h1>
|
||||||
|
<pre id="output"></pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,217 @@
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function chatEvent(nym, status, utterance, stamp) {
|
||||||
|
return ["chatEvent", nym, status, utterance, stamp || +(new Date())];
|
||||||
|
}
|
||||||
|
|
||||||
|
function halfConnection(fromNym, toNym, sdp, ice) {
|
||||||
|
return ["route", fromNym, toNym, sdp, ice];
|
||||||
|
}
|
||||||
|
|
||||||
|
function remoteValue(x, metaLevel) {
|
||||||
|
return ["broker", metaLevel || 0, x];
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
var G;
|
||||||
|
$(document).ready(function () {
|
||||||
|
var World = Minimart.World;
|
||||||
|
var Actor = Minimart.Actor;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
|
var rtcConfig = {"iceServers": [{"url": "stun:stun.l.google.com:19302"}]};
|
||||||
|
|
||||||
|
G = new Minimart.Ground(function () {
|
||||||
|
var wsconn = new Minimart.WebSocket.WebSocketConnection("broker", "ws://server.minimart.leastfixedpoint.com:8000/", true);
|
||||||
|
World.spawn(wsconn);
|
||||||
|
|
||||||
|
var localNym = "user-" + Math.floor(Math.random() * 1048576);
|
||||||
|
console.log("localNym", localNym);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Advertise our presence.
|
||||||
|
//
|
||||||
|
|
||||||
|
World.spawn(new Actor(function () {
|
||||||
|
Actor.advertise(
|
||||||
|
function () { return remoteValue(chatEvent(localNym, "", __, __)) });
|
||||||
|
}));
|
||||||
|
|
||||||
|
//
|
||||||
|
// DemandMatcher waits for people to appear, and when they do, offers an SDP to them.
|
||||||
|
//
|
||||||
|
|
||||||
|
var dm = new Minimart.DemandMatcher(remoteValue(chatEvent(_$, __, __, __)), 0, {
|
||||||
|
supplyProjection: remoteValue(halfConnection(__, _$, __, __))
|
||||||
|
});
|
||||||
|
dm.onDemandIncrease = function (captures) {
|
||||||
|
spawnRoute(captures[0]);
|
||||||
|
};
|
||||||
|
World.spawn(dm);
|
||||||
|
|
||||||
|
//
|
||||||
|
// spawnRoute spawns an actor responsible for managing an
|
||||||
|
// RTCPeerConnection to a specific remote peer.
|
||||||
|
//
|
||||||
|
|
||||||
|
function spawnRoute(remoteNym) {
|
||||||
|
var RTCPeerConnection = (window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection);
|
||||||
|
var RTCSessionDescription = (window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription);
|
||||||
|
var RTCIceCandidate = (window.RTCIceCandidate || window.mozRTCIceCandidate || window.webkitRTCIceCandidate);
|
||||||
|
|
||||||
|
console.log("spawnRoute", remoteNym);
|
||||||
|
|
||||||
|
World.spawn(new Actor(function () {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.peerSeen = false;
|
||||||
|
|
||||||
|
if (localNym !== remoteNym) {
|
||||||
|
self.pc = new RTCPeerConnection(rtcConfig);
|
||||||
|
self.ch = self.pc.createDataChannel('x'); // TODO -- label
|
||||||
|
self.localSdp = null;
|
||||||
|
self.localIce = [];
|
||||||
|
self.remoteSdp = null;
|
||||||
|
self.remoteIce = [];
|
||||||
|
|
||||||
|
// WebRTC likes to pretend UDP is like a phone call.
|
||||||
|
self.isOriginator = localNym < remoteNym;
|
||||||
|
|
||||||
|
var errback = World.wrap(function (err) { // TODO - make a standard World utility?
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
|
||||||
|
function flushLocalIce() {
|
||||||
|
if (!self.remoteSdp) { return; }
|
||||||
|
while (self.localIce.length) {
|
||||||
|
World.send(remoteValue(halfConnection(localNym,
|
||||||
|
remoteNym,
|
||||||
|
self.localSdp,
|
||||||
|
self.localIce.shift())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.pc.onicecandidate = World.wrap(function (e) {
|
||||||
|
if (e && e.candidate) {
|
||||||
|
var ice = [e.candidate.candidate, e.candidate.sdpMLineIndex, e.candidate.sdpMid];
|
||||||
|
console.log("LOCAL CANDIDATE", JSON.stringify(ice));
|
||||||
|
self.localIce.push(ice);
|
||||||
|
flushLocalIce();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (self.isOriginator) {
|
||||||
|
self.pc.createOffer(function (offer) {
|
||||||
|
self.pc.setLocalDescription(new RTCSessionDescription(offer), function () {
|
||||||
|
self.localSdp = self.pc.localDescription.sdp;
|
||||||
|
self.updateRoutes();
|
||||||
|
}, errback);
|
||||||
|
}, errback);
|
||||||
|
}
|
||||||
|
|
||||||
|
Actor.advertise(
|
||||||
|
function () { return remoteValue(halfConnection(localNym,
|
||||||
|
remoteNym,
|
||||||
|
self.localSdp,
|
||||||
|
__)) },
|
||||||
|
{ when: function () { return self.local.sdp } });
|
||||||
|
|
||||||
|
Actor.subscribe(
|
||||||
|
function () { return remoteValue(halfConnection(remoteNym,
|
||||||
|
localNym,
|
||||||
|
__,
|
||||||
|
_$("ice"))) },
|
||||||
|
function (ice) {
|
||||||
|
console.log("REMOTE CANDIDATE", JSON.stringify(ice));
|
||||||
|
var candidate = new RTCIceCandidate({ candidate: ice[0],
|
||||||
|
sdpMLineIndex: ice[1],
|
||||||
|
sdpMid: ice[2] });
|
||||||
|
self.pc.addIceCandidate(candidate);
|
||||||
|
});
|
||||||
|
|
||||||
|
Actor.observeAdvertisers(
|
||||||
|
function () { return remoteValue(halfConnection(remoteNym,
|
||||||
|
localNym,
|
||||||
|
_$("sdp"),
|
||||||
|
__)) },
|
||||||
|
{ singleton: "remote" },
|
||||||
|
function () {
|
||||||
|
if (self.local.sdp && self.remote) {
|
||||||
|
var remoteSessionDescription = new RTCSessionDescription({
|
||||||
|
type: "offer",
|
||||||
|
sdp: self.remote.sdp
|
||||||
|
});
|
||||||
|
// console.log("Local SDP is", self.localSdp);
|
||||||
|
// console.log("Remote SDP is", params);
|
||||||
|
self.pc.setRemoteDescription(new RTCSessionDescription(params), function () {}, errback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Actor.observeAdvertisers(
|
||||||
|
function () { return remoteValue(chatEvent(remoteNym, __, __, __)) },
|
||||||
|
{ presence: "peerPresent" },
|
||||||
|
function () {
|
||||||
|
self.peerSeen = self.peerSeen || self.peerPresent;
|
||||||
|
if (self.peerSeen && !self.peerPresent) {
|
||||||
|
console.log("Peer went missing", remoteNym);
|
||||||
|
World.exit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// World.spawn(new Actor(function () {
|
||||||
|
// var self = this;
|
||||||
|
// self.pc = new (window.mozRTCPeerConnection || window.webkitRTCPeerConnection)(rtcConfig);
|
||||||
|
// x = self.pc;
|
||||||
|
// self.ch = self.pc.createDataChannel('x'); // TODO -- label
|
||||||
|
|
||||||
|
// self.pc.onicecandidate = World.wrap(function (e) {
|
||||||
|
// self.offerSdp = self.pc.localDescription.sdp;
|
||||||
|
// console.log("onicecandidate", self.offerSdp);
|
||||||
|
// self.updateRoutes();
|
||||||
|
// });
|
||||||
|
|
||||||
|
// Actor.observeAdvertisers(
|
||||||
|
// function () { return ["broker_state", _$("state")] },
|
||||||
|
// { name: "broker_states",
|
||||||
|
// set: function (o) { return o.state; } },
|
||||||
|
// function () {
|
||||||
|
// if (self.broker_states[0] === "connected" && !self.pc.localDescription) {
|
||||||
|
// var errback = World.wrap(function (err) {
|
||||||
|
// throw new Error(err);
|
||||||
|
// });
|
||||||
|
// self.pc.createOffer(function (offer) {
|
||||||
|
// self.pc.setLocalDescription(offer, function () {}, errback);
|
||||||
|
// }, errback);
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// Actor.advertise(
|
||||||
|
// function () { return ["broker", 0, ["offerSdp", self.offerSdp]] },
|
||||||
|
// { when: function () { return self.offerSdp } });
|
||||||
|
|
||||||
|
// Actor.observeAdvertisers(
|
||||||
|
// function () { return ["broker", 0, ["offerSdp", _$("offerSdp")]] },
|
||||||
|
// { name: "offers",
|
||||||
|
// set: function (o) { return o.offerSdp; } },
|
||||||
|
// function () {
|
||||||
|
// var elt = document.getElementById('output');
|
||||||
|
// elt.innerHTML = '';
|
||||||
|
// for (var i = 0; i < self.offers.length; i++) {
|
||||||
|
// // console.log(self.offers[i]);
|
||||||
|
// elt.appendChild(document.createTextNode('\n' + self.offers[i]));
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// }));
|
||||||
|
});
|
||||||
|
G.startStepping();
|
||||||
|
});
|
|
@ -10,7 +10,6 @@
|
||||||
"build-min": "browserify src/main.js -s Minimart -o dist/_minimart.js && uglifyjs dist/_minimart.js -o dist/minimart.min.js && rm dist/_minimart.js",
|
"build-min": "browserify src/main.js -s Minimart -o dist/_minimart.js && uglifyjs dist/_minimart.js -o dist/minimart.min.js && rm dist/_minimart.js",
|
||||||
"build": "npm run build-debug && npm run build-min",
|
"build": "npm run build-debug && npm run build-min",
|
||||||
"watch": "watchify src/main.js -d -s Minimart -o dist/minimart.js",
|
"watch": "watchify src/main.js -d -s Minimart -o dist/minimart.js",
|
||||||
"test": "mocha",
|
|
||||||
"prepublish": "npm run build"
|
"prepublish": "npm run build"
|
||||||
},
|
},
|
||||||
"author": "Tony Garnock-Jones <tonyg@ccs.neu.edu>",
|
"author": "Tony Garnock-Jones <tonyg@ccs.neu.edu>",
|
||||||
|
@ -18,7 +17,6 @@
|
||||||
"watchify": "^0.6.1",
|
"watchify": "^0.6.1",
|
||||||
"uglify-js": "^2.4.12",
|
"uglify-js": "^2.4.12",
|
||||||
"browserify": "^3.30.4",
|
"browserify": "^3.30.4",
|
||||||
"mocha": "^1.17.1",
|
"mocha": "^1.17.1"
|
||||||
"expect.js": "^0.3.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
30
src/actor.js
30
src/actor.js
|
@ -1,5 +1,5 @@
|
||||||
|
var Reflect = require("./reflect.js");
|
||||||
var Minimart = require("./minimart.js");
|
var Minimart = require("./minimart.js");
|
||||||
var util = require("./util.js");
|
|
||||||
var World = Minimart.World;
|
var World = Minimart.World;
|
||||||
var Route = Minimart.Route;
|
var Route = Minimart.Route;
|
||||||
|
|
||||||
|
@ -13,9 +13,8 @@ function Actor(bootfn) {
|
||||||
try {
|
try {
|
||||||
Actor._chunks = [];
|
Actor._chunks = [];
|
||||||
bootfn.call(this);
|
bootfn.call(this);
|
||||||
var initialGestalt = finalizeActor(this, Actor._chunks);
|
finalizeActor(this, Actor._chunks);
|
||||||
Actor._chunks = oldChunks;
|
Actor._chunks = oldChunks;
|
||||||
return [initialGestalt];
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Actor._chunks = oldChunks;
|
Actor._chunks = oldChunks;
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -124,7 +123,7 @@ function finalizeActor(behavior, chunks) {
|
||||||
var compiledProjections = {};
|
var compiledProjections = {};
|
||||||
var previousObjs = {};
|
var previousObjs = {};
|
||||||
|
|
||||||
behavior._computeRoutes = function () {
|
behavior.updateRoutes = function () {
|
||||||
var newRoutes = Route.emptyGestalt;
|
var newRoutes = Route.emptyGestalt;
|
||||||
for (var i = 0; i < chunks.length; i++) {
|
for (var i = 0; i < chunks.length; i++) {
|
||||||
var chunk = chunks[i];
|
var chunk = chunks[i];
|
||||||
|
@ -160,11 +159,7 @@ function finalizeActor(behavior, chunks) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newRoutes;
|
World.updateRoutes([newRoutes]);
|
||||||
};
|
|
||||||
|
|
||||||
behavior.updateRoutes = function () {
|
|
||||||
World.updateRoutes([this._computeRoutes()]);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
behavior.handleEvent = function (e) {
|
behavior.handleEvent = function (e) {
|
||||||
|
@ -183,7 +178,7 @@ function finalizeActor(behavior, chunks) {
|
||||||
{
|
{
|
||||||
var matchResult = Route.matchPattern(e.message, projections[i]);
|
var matchResult = Route.matchPattern(e.message, projections[i]);
|
||||||
if (matchResult) {
|
if (matchResult) {
|
||||||
util.kwApply(chunk.handler, this, matchResult);
|
kwApply(chunk.handler, this, matchResult);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -259,10 +254,23 @@ function finalizeActor(behavior, chunks) {
|
||||||
if (chunk.options.removed) { behavior[chunk.options.removed] = []; }
|
if (chunk.options.removed) { behavior[chunk.options.removed] = []; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
behavior.updateRoutes();
|
||||||
|
}
|
||||||
|
|
||||||
return behavior._computeRoutes();
|
function kwApply(f, thisArg, args) {
|
||||||
|
var formals = Reflect.formalParameters(f);
|
||||||
|
var actuals = []
|
||||||
|
for (var i = 0; i < formals.length; i++) {
|
||||||
|
var formal = formals[i];
|
||||||
|
if (!(formal in args)) {
|
||||||
|
throw new Error("Function parameter '"+formal+"' not present in args");
|
||||||
|
}
|
||||||
|
actuals.push(args[formal]);
|
||||||
|
}
|
||||||
|
return f.apply(thisArg, actuals);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
module.exports.Actor = Actor;
|
module.exports.Actor = Actor;
|
||||||
|
module.exports.kwApply = kwApply;
|
||||||
|
|
|
@ -17,7 +17,9 @@ function spawnDOMDriver(domWrapFunction, jQueryWrapFunction) {
|
||||||
fragmentClass,
|
fragmentClass,
|
||||||
fragmentSpec,
|
fragmentSpec,
|
||||||
domWrapFunction,
|
domWrapFunction,
|
||||||
jQueryWrapFunction));
|
jQueryWrapFunction),
|
||||||
|
[sub(domWrapFunction(selector, fragmentClass, fragmentSpec)),
|
||||||
|
sub(domWrapFunction(selector, fragmentClass, fragmentSpec), 0, 1)]);
|
||||||
};
|
};
|
||||||
World.spawn(d);
|
World.spawn(d);
|
||||||
}
|
}
|
||||||
|
@ -38,14 +40,12 @@ function DOMFragment(selector, fragmentClass, fragmentSpec, domWrapFunction, jQu
|
||||||
DOMFragment.prototype.boot = function () {
|
DOMFragment.prototype.boot = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var monitoring =
|
var monitoring =
|
||||||
sub(self.domWrapFunction(self.selector, self.fragmentClass, self.fragmentSpec), 1, 2);
|
sub(this.domWrapFunction(self.selector, self.fragmentClass, self.fragmentSpec), 1, 2);
|
||||||
|
|
||||||
World.spawn(new World(function () {
|
World.spawn(new World(function () {
|
||||||
Minimart.JQuery.spawnJQueryDriver(self.selector+" > ."+self.fragmentClass,
|
Minimart.JQuery.spawnJQueryDriver(self.selector+" > ."+self.fragmentClass,
|
||||||
1,
|
1,
|
||||||
self.jQueryWrapFunction);
|
self.jQueryWrapFunction);
|
||||||
World.spawn({
|
World.spawn({
|
||||||
boot: function () { return [monitoring] },
|
|
||||||
handleEvent: function (e) {
|
handleEvent: function (e) {
|
||||||
if (e.type === "routes") {
|
if (e.type === "routes") {
|
||||||
var level = e.gestalt.getLevel(1, 0); // find participant peers
|
var level = e.gestalt.getLevel(1, 0); // find participant peers
|
||||||
|
@ -54,11 +54,8 @@ DOMFragment.prototype.boot = function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, [monitoring]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return [sub(self.domWrapFunction(self.selector, self.fragmentClass, self.fragmentSpec)),
|
|
||||||
sub(self.domWrapFunction(self.selector, self.fragmentClass, self.fragmentSpec), 0, 1)]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DOMFragment.prototype.handleEvent = function (e) {
|
DOMFragment.prototype.handleEvent = function (e) {
|
||||||
|
|
|
@ -18,7 +18,9 @@ function spawnJQueryDriver(baseSelector, metaLevel, wrapFunction) {
|
||||||
selector,
|
selector,
|
||||||
eventName,
|
eventName,
|
||||||
metaLevel,
|
metaLevel,
|
||||||
wrapFunction));
|
wrapFunction),
|
||||||
|
[pub(wrapFunction(selector, eventName, __), metaLevel),
|
||||||
|
pub(wrapFunction(selector, eventName, __), metaLevel, 1)]);
|
||||||
};
|
};
|
||||||
World.spawn(d);
|
World.spawn(d);
|
||||||
}
|
}
|
||||||
|
@ -45,11 +47,6 @@ function JQueryEventRouter(baseSelector, selector, eventName, metaLevel, wrapFun
|
||||||
this.handler);
|
this.handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
JQueryEventRouter.prototype.boot = function () {
|
|
||||||
return [pub(this.wrapFunction(this.selector, this.eventName, __), this.metaLevel),
|
|
||||||
pub(this.wrapFunction(this.selector, this.eventName, __), this.metaLevel, 1)];
|
|
||||||
};
|
|
||||||
|
|
||||||
JQueryEventRouter.prototype.handleEvent = function (e) {
|
JQueryEventRouter.prototype.handleEvent = function (e) {
|
||||||
if (e.type === "routes" && e.gestalt.isEmpty()) {
|
if (e.type === "routes" && e.gestalt.isEmpty()) {
|
||||||
this.computeNodes().off(this.eventName, this.handler);
|
this.computeNodes().off(this.eventName, this.handler);
|
||||||
|
|
|
@ -19,8 +19,10 @@ function pub(pattern, metaLevel, level) {
|
||||||
return Route.simpleGestalt(true, pattern, metaLevel, level);
|
return Route.simpleGestalt(true, pattern, metaLevel, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
function spawn(behavior) {
|
function spawn(behavior, initialGestalts) {
|
||||||
return { type: "spawn", behavior: behavior };
|
return { type: "spawn",
|
||||||
|
behavior: behavior,
|
||||||
|
initialGestalt: Route.gestaltUnion(initialGestalts || []) };
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateRoutes(gestalts) {
|
function updateRoutes(gestalts) {
|
||||||
|
@ -83,8 +85,8 @@ World.updateRoutes = function (gestalts) {
|
||||||
World.current().enqueueAction(World.activePid(), updateRoutes(gestalts));
|
World.current().enqueueAction(World.activePid(), updateRoutes(gestalts));
|
||||||
};
|
};
|
||||||
|
|
||||||
World.spawn = function (behavior) {
|
World.spawn = function (behavior, initialGestalts) {
|
||||||
World.current().enqueueAction(World.activePid(), spawn(behavior));
|
World.current().enqueueAction(World.activePid(), spawn(behavior, initialGestalts));
|
||||||
};
|
};
|
||||||
|
|
||||||
World.exit = function (exn) {
|
World.exit = function (exn) {
|
||||||
|
@ -224,18 +226,13 @@ World.prototype.performAction = function (pid, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case "spawn":
|
case "spawn":
|
||||||
var pid = World.nextPid++;
|
var pid = World.nextPid++;
|
||||||
var entry = { gestalt: Route.emptyGestalt, behavior: action.behavior };
|
var newGestalt = action.initialGestalt.label(pid);
|
||||||
this.processTable[pid] = entry;
|
this.processTable[pid] = { gestalt: newGestalt, behavior: action.behavior };
|
||||||
if (entry.behavior.boot) {
|
if (action.behavior.boot) {
|
||||||
var initialGestalts = this.asChild(pid, function () { return entry.behavior.boot() });
|
this.asChild(pid, function () { action.behavior.boot() });
|
||||||
if (initialGestalts) {
|
|
||||||
entry.gestalt = Route.gestaltUnion(initialGestalts).label(pid);
|
|
||||||
}
|
|
||||||
this.markPidRunnable(pid);
|
this.markPidRunnable(pid);
|
||||||
}
|
}
|
||||||
if (!Route.emptyGestalt.equals(entry.gestalt)) {
|
this.applyAndIssueRoutingUpdate(Route.emptyGestalt, newGestalt, pid);
|
||||||
this.applyAndIssueRoutingUpdate(Route.emptyGestalt, entry.gestalt, pid);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case "routes":
|
case "routes":
|
||||||
if (pid in this.processTable) {
|
if (pid in this.processTable) {
|
||||||
|
@ -318,11 +315,6 @@ World.prototype.dispatchEvent = function (e) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
World.prototype.boot = function () {
|
|
||||||
// Needed in order for the new World to be marked as "runnable", so
|
|
||||||
// its initial actions get performed.
|
|
||||||
};
|
|
||||||
|
|
||||||
World.prototype.handleEvent = function (e) {
|
World.prototype.handleEvent = function (e) {
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
case "routes":
|
case "routes":
|
||||||
|
@ -444,8 +436,8 @@ DemandMatcher.prototype.debugState = function () {
|
||||||
|
|
||||||
DemandMatcher.prototype.boot = function () {
|
DemandMatcher.prototype.boot = function () {
|
||||||
var observerLevel = 1 + Math.max(this.demandLevel, this.supplyLevel);
|
var observerLevel = 1 + Math.max(this.demandLevel, this.supplyLevel);
|
||||||
return [sub(this.demandPattern, this.metaLevel, observerLevel),
|
World.updateRoutes([sub(this.demandPattern, this.metaLevel, observerLevel),
|
||||||
pub(this.supplyPattern, this.metaLevel, observerLevel)];
|
pub(this.supplyPattern, this.metaLevel, observerLevel)]);
|
||||||
};
|
};
|
||||||
|
|
||||||
DemandMatcher.prototype.handleEvent = function (e) {
|
DemandMatcher.prototype.handleEvent = function (e) {
|
||||||
|
|
|
@ -12,7 +12,7 @@ function Spy(label, useJson, observationLevel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Spy.prototype.boot = function () {
|
Spy.prototype.boot = function () {
|
||||||
return [sub(__, 0, this.observationLevel), pub(__, 0, this.observationLevel)];
|
World.updateRoutes([sub(__, 0, this.observationLevel), pub(__, 0, this.observationLevel)]);
|
||||||
};
|
};
|
||||||
|
|
||||||
Spy.prototype.handleEvent = function (e) {
|
Spy.prototype.handleEvent = function (e) {
|
||||||
|
|
20
src/util.js
20
src/util.js
|
@ -1,23 +1,13 @@
|
||||||
var Reflect = require("./reflect.js");
|
// Minimal jQueryish utilities. Reimplemented because jQuery needs
|
||||||
|
// window to exist, and we want to run in Web Worker context as well.
|
||||||
|
|
||||||
module.exports.extend = function (what, _with) {
|
function extend(what, _with) {
|
||||||
for (var prop in _with) {
|
for (var prop in _with) {
|
||||||
if (_with.hasOwnProperty(prop)) {
|
if (_with.hasOwnProperty(prop)) {
|
||||||
what[prop] = _with[prop];
|
what[prop] = _with[prop];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return what;
|
return what;
|
||||||
};
|
}
|
||||||
|
|
||||||
module.exports.kwApply = function (f, thisArg, args) {
|
module.exports.extend = extend;
|
||||||
var formals = Reflect.formalParameters(f);
|
|
||||||
var actuals = []
|
|
||||||
for (var i = 0; i < formals.length; i++) {
|
|
||||||
var formal = formals[i];
|
|
||||||
if (!(formal in args)) {
|
|
||||||
throw new Error("Function parameter '"+formal+"' not present in args");
|
|
||||||
}
|
|
||||||
actuals.push(args[formal]);
|
|
||||||
}
|
|
||||||
return f.apply(thisArg, actuals);
|
|
||||||
};
|
|
||||||
|
|
|
@ -17,8 +17,8 @@ function WakeDetector(period) {
|
||||||
|
|
||||||
WakeDetector.prototype.boot = function () {
|
WakeDetector.prototype.boot = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
World.updateRoutes([pub(this.message)]);
|
||||||
this.timerId = setInterval(World.wrap(function () { self.trigger(); }), this.period);
|
this.timerId = setInterval(World.wrap(function () { self.trigger(); }), this.period);
|
||||||
return [pub(this.message)];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
WakeDetector.prototype.handleEvent = function (e) {};
|
WakeDetector.prototype.handleEvent = function (e) {};
|
||||||
|
|
|
@ -1,99 +0,0 @@
|
||||||
var expect = require('expect.js');
|
|
||||||
|
|
||||||
var Minimart = require('../src/main.js');
|
|
||||||
|
|
||||||
var World = Minimart.World;
|
|
||||||
var Actor = Minimart.Actor;
|
|
||||||
var sub = Minimart.sub;
|
|
||||||
var pub = Minimart.pub;
|
|
||||||
var __ = Minimart.__;
|
|
||||||
var _$ = Minimart._$;
|
|
||||||
|
|
||||||
function configurationTrace(bootConfiguration) {
|
|
||||||
var eventLog = [];
|
|
||||||
function trace(item) {
|
|
||||||
eventLog.push(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
var G = new Minimart.Ground(function () {
|
|
||||||
bootConfiguration(trace);
|
|
||||||
});
|
|
||||||
|
|
||||||
while (G.step()) {
|
|
||||||
// do nothing until G becomes inert
|
|
||||||
}
|
|
||||||
|
|
||||||
return eventLog;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkTrace(bootConfiguration, expected) {
|
|
||||||
expect(configurationTrace(bootConfiguration)).to.eql(expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("configurationTrace", function() {
|
|
||||||
describe("with an inert configuration", function () {
|
|
||||||
it("should yield an empty trace", function () {
|
|
||||||
checkTrace(function (trace) {}, []);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("with a single trace in an inert configuration", function () {
|
|
||||||
it("should yield that trace", function () {
|
|
||||||
checkTrace(function (trace) { trace(1) }, [1]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("with some traced communication", function () {
|
|
||||||
it("should yield an appropriate trace", function () {
|
|
||||||
checkTrace(function (trace) {
|
|
||||||
World.spawn({
|
|
||||||
boot: function () { return [sub(__)] },
|
|
||||||
handleEvent: function (e) {
|
|
||||||
trace(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
World.send(123);
|
|
||||||
World.send(234);
|
|
||||||
}, [Minimart.updateRoutes([]),
|
|
||||||
Minimart.sendMessage(123),
|
|
||||||
Minimart.sendMessage(234)]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("nonempty initial routes", function () {
|
|
||||||
it("should be immediately signalled to the process", function () {
|
|
||||||
// Specifically, no Minimart.updateRoutes([]) first.
|
|
||||||
checkTrace(function (trace) {
|
|
||||||
World.spawn({
|
|
||||||
boot: function () { return [pub(["A", __])] },
|
|
||||||
handleEvent: function (e) {
|
|
||||||
World.spawn({
|
|
||||||
boot: function () { return [sub(["A", __], 0, 1)] },
|
|
||||||
handleEvent: trace
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [Minimart.updateRoutes([pub(["A", __]).label(1)])]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("actor with nonempty initial routes", function () {
|
|
||||||
it("shouldn't see initial empty conversational context", function () {
|
|
||||||
checkTrace(function (trace) {
|
|
||||||
World.spawn({
|
|
||||||
boot: function () { return [pub(["A", __])] },
|
|
||||||
handleEvent: function (e) {
|
|
||||||
World.spawn(new Actor(function () {
|
|
||||||
Actor.observeAdvertisers(
|
|
||||||
function () { return ["A", __] },
|
|
||||||
{ presence: "isPresent" },
|
|
||||||
function () {
|
|
||||||
trace(["isPresent", this.isPresent]);
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, [["isPresent", true]]);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,544 +0,0 @@
|
||||||
var expect = require('expect.js');
|
|
||||||
var util = require('util');
|
|
||||||
var r = require("../src/route.js");
|
|
||||||
|
|
||||||
function checkPrettyMatcher(m, expected) {
|
|
||||||
expect(r.prettyMatcher(m)).to.equal(expected.join('\n'));
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkPrettyGestalt(g, expected) {
|
|
||||||
expect(g.pretty()).to.equal(expected.join('\n') + '\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("basic pattern compilation", function () {
|
|
||||||
var sAny = r.arrayToSet(['mAny']);
|
|
||||||
var sAAny = r.arrayToSet(['mAAny']);
|
|
||||||
var mAny = r.compilePattern(sAny, r.__);
|
|
||||||
var mAAny = r.compilePattern(sAAny, ['A', r.__]);
|
|
||||||
|
|
||||||
it("should print as expected", function () {
|
|
||||||
checkPrettyMatcher(mAny, [' ★ >{["mAny"]}']);
|
|
||||||
checkPrettyMatcher(mAAny, [' < "A" ★ > >{["mAAny"]}']);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("of wildcard", function () {
|
|
||||||
it("should match anything", function () {
|
|
||||||
expect(r.matchValue(mAny, 'hi')).to.eql(sAny);
|
|
||||||
expect(r.matchValue(mAny, ['A', 'hi'])).to.eql(sAny);
|
|
||||||
expect(r.matchValue(mAny, ['B', 'hi'])).to.eql(sAny);
|
|
||||||
expect(r.matchValue(mAny, ['A', [['hi']]])).to.eql(sAny);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("of A followed by wildcard", function () {
|
|
||||||
it("should match A followed by anything", function () {
|
|
||||||
expect(r.matchValue(mAAny, 'hi')).to.be(null);
|
|
||||||
expect(r.matchValue(mAAny, ['A', 'hi'])).to.eql(sAAny);
|
|
||||||
expect(r.matchValue(mAAny, ['B', 'hi'])).to.be(null);
|
|
||||||
expect(r.matchValue(mAAny, ['A', [['hi']]])).to.eql(sAAny);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should observe basic (in)equivalences", function () {
|
|
||||||
expect(r.matcherEquals(mAny, mAAny)).to.be(false);
|
|
||||||
expect(r.matcherEquals(mAny, mAny)).to.be(true);
|
|
||||||
expect(r.matcherEquals(mAAny, mAAny)).to.be(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("unions", function () {
|
|
||||||
it("should collapse common prefix wildcard", function () {
|
|
||||||
checkPrettyMatcher(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B'])),
|
|
||||||
[' < ★ "A" > >{["A"]}',
|
|
||||||
' "B" > >{["B"]}']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should unroll wildcard unioned with nonwildcard", function () {
|
|
||||||
checkPrettyMatcher(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
|
||||||
r.compilePattern(r.arrayToSet(['W']), r.__)),
|
|
||||||
[' ★ >{["W"]}',
|
|
||||||
' < ★ "A" ★...> >{["W"]}',
|
|
||||||
' > >{["A","W"]}',
|
|
||||||
' ★...> >{["W"]}',
|
|
||||||
' > >{["W"]}',
|
|
||||||
' > >{["W"]}']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should properly multiply out", function () {
|
|
||||||
checkPrettyMatcher(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4])),
|
|
||||||
[' < 1 2 > >{["A"]}',
|
|
||||||
' 3 > >{["C"]}',
|
|
||||||
' 3 2 > >{["A"]}',
|
|
||||||
' 4 > >{["B"]}',
|
|
||||||
' ★ 2 > >{["A"]}']);
|
|
||||||
|
|
||||||
checkPrettyMatcher(r.union(r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4])),
|
|
||||||
[' < 1 3 > >{["C"]}',
|
|
||||||
' 3 4 > >{["B"]}']);
|
|
||||||
|
|
||||||
checkPrettyMatcher(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3])),
|
|
||||||
[' < 1 2 > >{["A"]}',
|
|
||||||
' 3 > >{["C"]}',
|
|
||||||
' ★ 2 > >{["A"]}']);
|
|
||||||
|
|
||||||
checkPrettyMatcher(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4])),
|
|
||||||
[' < 3 2 > >{["A"]}',
|
|
||||||
' 4 > >{["B"]}',
|
|
||||||
' ★ 2 > >{["A"]}']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should correctly construct intermediate values", function () {
|
|
||||||
var MU = r.emptyMatcher;
|
|
||||||
MU = r.union(MU, r.compilePattern(r.arrayToSet(['A']), [r.__, 2]));
|
|
||||||
checkPrettyMatcher(MU, [' < ★ 2 > >{["A"]}']);
|
|
||||||
MU = r.union(MU, r.compilePattern(r.arrayToSet(['C']), [1, 3]));
|
|
||||||
checkPrettyMatcher(MU, [' < 1 2 > >{["A"]}',
|
|
||||||
' 3 > >{["C"]}',
|
|
||||||
' ★ 2 > >{["A"]}']);
|
|
||||||
MU = r.union(MU, r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
|
||||||
checkPrettyMatcher(MU, [' < 1 2 > >{["A"]}',
|
|
||||||
' 3 > >{["C"]}',
|
|
||||||
' 3 2 > >{["A"]}',
|
|
||||||
' 4 > >{["B"]}',
|
|
||||||
' ★ 2 > >{["A"]}']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle identical patterns with different pids", function () {
|
|
||||||
var m = r.union(r.compilePattern(r.arrayToSet('B'), [2]),
|
|
||||||
r.compilePattern(r.arrayToSet('C'), [3]));
|
|
||||||
checkPrettyMatcher(m, [' < 2 > >{["B"]}',
|
|
||||||
' 3 > >{["C"]}']);
|
|
||||||
m = r.union(r.compilePattern(r.arrayToSet('A'), [2]), m);
|
|
||||||
checkPrettyMatcher(m, [' < 2 > >{["A","B"]}',
|
|
||||||
' 3 > >{["C"]}']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("projections", function () {
|
|
||||||
describe("with picky structure", function () {
|
|
||||||
var proj = r.compileProjection(r._$("v", [[r.__]]));
|
|
||||||
|
|
||||||
it("should include things that match as well as wildcards", function () {
|
|
||||||
checkPrettyMatcher(r.project(r.union(r.compilePattern(r.arrayToSet(['A']), r.__),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [['b']])),
|
|
||||||
proj),
|
|
||||||
[' < < "b" > > >{["B","A"]}',
|
|
||||||
' ★ > > >{["A"]}']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should exclude things that lack the required structure", function () {
|
|
||||||
checkPrettyMatcher(r.project(r.union(r.compilePattern(r.arrayToSet(['A']), r.__),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), ['b'])),
|
|
||||||
proj),
|
|
||||||
[' < < ★ > > >{["A"]}']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("simple positional", function () {
|
|
||||||
var proj = r.compileProjection([r._$, r._$]);
|
|
||||||
|
|
||||||
it("should collapse common prefixes", function () {
|
|
||||||
checkPrettyMatcher(r.project(r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4])),
|
|
||||||
proj),
|
|
||||||
[' 1 2 >{["A"]}',
|
|
||||||
' 3 >{["C"]}',
|
|
||||||
' 3 4 >{["B"]}']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should yield a correct set of results", function () {
|
|
||||||
expect(r.matcherKeys(r.project(r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4])),
|
|
||||||
proj))).to.eql([[1, 2], [1, 3], [3, 4]]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("erasePath after union", function () {
|
|
||||||
var R1 = r.compilePattern(r.arrayToSet(['A']), [r.__, "B"]);
|
|
||||||
var R2 = r.compilePattern(r.arrayToSet(['B']), ["A", r.__]);
|
|
||||||
var R12 = r.union(R1, R2);
|
|
||||||
|
|
||||||
it("should have sane preconditions", function () { // Am I doing this right?
|
|
||||||
checkPrettyMatcher(R1, [' < ★ "B" > >{["A"]}']);
|
|
||||||
checkPrettyMatcher(R2, [' < "A" ★ > >{["B"]}']);
|
|
||||||
checkPrettyMatcher(R12, [' < "A" "B" > >{["B","A"]}',
|
|
||||||
' ★ > >{["B"]}',
|
|
||||||
' ★ "B" > >{["A"]}']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should yield the remaining ingredients of the union", function () {
|
|
||||||
expect(r.matcherEquals(r.erasePath(R12, R1), R2)).to.be(true);
|
|
||||||
expect(r.matcherEquals(r.erasePath(R12, R2), R1)).to.be(true);
|
|
||||||
expect(r.matcherEquals(r.erasePath(R12, R1), R1)).to.be(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("basic gestalt construction", function () {
|
|
||||||
it("should print as expected", function () {
|
|
||||||
checkPrettyGestalt(r.simpleGestalt(false, "A", 0, 0),
|
|
||||||
['GESTALT metalevel 0 level 0:',
|
|
||||||
' - subs: "A" >{true}']);
|
|
||||||
checkPrettyGestalt(r.simpleGestalt(true, "B", 0, 0),
|
|
||||||
['GESTALT metalevel 0 level 0:',
|
|
||||||
' - advs: "B" >{true}']);
|
|
||||||
checkPrettyGestalt(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0)),
|
|
||||||
['GESTALT metalevel 0 level 0:',
|
|
||||||
' - subs: "A" >{true}',
|
|
||||||
' - advs: "B" >{true}']);
|
|
||||||
|
|
||||||
checkPrettyGestalt(r.simpleGestalt(false, "A", 2, 2),
|
|
||||||
['GESTALT metalevel 2 level 2:',
|
|
||||||
' - subs: "A" >{true}']);
|
|
||||||
checkPrettyGestalt(r.simpleGestalt(true, "B", 2, 2),
|
|
||||||
['GESTALT metalevel 2 level 2:',
|
|
||||||
' - advs: "B" >{true}']);
|
|
||||||
checkPrettyGestalt(r.simpleGestalt(false, "A", 2, 2).union(r.simpleGestalt(true, "B", 2, 2)),
|
|
||||||
['GESTALT metalevel 2 level 2:',
|
|
||||||
' - subs: "A" >{true}',
|
|
||||||
' - advs: "B" >{true}']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("matching", function () {
|
|
||||||
function check1(gMetalevel, level, mMetalevel) {
|
|
||||||
var g = r.simpleGestalt(false, "A", gMetalevel, level).label(123);
|
|
||||||
var result = g.matchValue("A", mMetalevel, false);
|
|
||||||
if (gMetalevel === mMetalevel) {
|
|
||||||
it("should match at level "+level, function () {
|
|
||||||
expect(result).to.eql([123]);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
it("should not match at level "+level, function () {
|
|
||||||
expect(result).to.eql([]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function gMetaLevelCheck(gMetalevel, mMetalevel) {
|
|
||||||
describe("at gestalt metalevel "+gMetalevel+", message metalevel "+mMetalevel, function () {
|
|
||||||
check1(gMetalevel, 0, mMetalevel);
|
|
||||||
check1(gMetalevel, 1, mMetalevel);
|
|
||||||
check1(gMetalevel, 2, mMetalevel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function mMetaLevelCheck(mMetalevel) {
|
|
||||||
gMetaLevelCheck(0, mMetalevel);
|
|
||||||
gMetaLevelCheck(2, mMetalevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
mMetaLevelCheck(0);
|
|
||||||
mMetaLevelCheck(1);
|
|
||||||
mMetaLevelCheck(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("gestalt filtering", function () {
|
|
||||||
function check1(metalevel, observedLevel, observerLevel) {
|
|
||||||
var observer = r.simpleGestalt(true, r.__, metalevel, observerLevel).label("observer");
|
|
||||||
var observed = r.simpleGestalt(false, "A", metalevel, observedLevel).label("observed");
|
|
||||||
var resultM = observed.filter(observer);
|
|
||||||
var resultL = observed.match(observer);
|
|
||||||
if (observedLevel < observerLevel) {
|
|
||||||
it("should be able to see gestalt at level "+observedLevel, function () {
|
|
||||||
expect(resultM.isEmpty()).to.be(false);
|
|
||||||
expect(resultL).to.eql(["observer"]);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
it("should not be able to see gestalt at level "+observedLevel, function () {
|
|
||||||
expect(resultM.isEmpty()).to.be(true);
|
|
||||||
expect(resultL).to.eql([]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function metalevelCheck(metalevel, observerLevel) {
|
|
||||||
describe("observer at level "+observerLevel+" in metalevel "+metalevel, function () {
|
|
||||||
check1(metalevel, 0, observerLevel);
|
|
||||||
check1(metalevel, 1, observerLevel);
|
|
||||||
check1(metalevel, 2, observerLevel);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function levelCheck(observerLevel) {
|
|
||||||
metalevelCheck(0, observerLevel);
|
|
||||||
metalevelCheck(2, observerLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
levelCheck(0);
|
|
||||||
levelCheck(1);
|
|
||||||
levelCheck(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("matcher equality", function () {
|
|
||||||
it("should not rely on object identity", function () {
|
|
||||||
expect(r.matcherEquals(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B'])),
|
|
||||||
r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B']))))
|
|
||||||
.to.be(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should respect commutativity of union", function () {
|
|
||||||
expect(r.matcherEquals(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B'])),
|
|
||||||
r.union(r.compilePattern(r.arrayToSet(['B']), [r.__, 'B']),
|
|
||||||
r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']))))
|
|
||||||
.to.be(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("gestalt equality", function () {
|
|
||||||
it("should distinguish emptyGestalt reliably", function () {
|
|
||||||
expect(r.simpleGestalt(false, r.__, 0, 10)
|
|
||||||
.union(r.simpleGestalt(true, r.__, 0, 10))
|
|
||||||
.equals(r.emptyGestalt))
|
|
||||||
.to.be(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should not rely on object identity", function () {
|
|
||||||
expect(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))
|
|
||||||
.equals(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))))
|
|
||||||
.to.be(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should respect commutativity of union", function () {
|
|
||||||
expect(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))
|
|
||||||
.equals(r.simpleGestalt(true, "B", 0, 0).union(r.simpleGestalt(false, "A", 0, 0))))
|
|
||||||
.to.be(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should discriminate between advs and subs", function () {
|
|
||||||
expect(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))
|
|
||||||
.equals(r.simpleGestalt(false, "B", 0, 0).union(r.simpleGestalt(true, "A", 0, 0))))
|
|
||||||
.to.be(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("matcherKeys on wild matchers", function () {
|
|
||||||
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
|
||||||
|
|
||||||
it("should yield null to signal an infinite result", function () {
|
|
||||||
expect(r.matcherKeys(r.project(M, r.compileProjection([r._$, r._$])))).to.be(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should extract just the second array element successfully", function () {
|
|
||||||
expect(r.matcherKeys(r.project(M, r.compileProjection([r.__, r._$])))).to.eql([[2],[3],[4]]);
|
|
||||||
});
|
|
||||||
|
|
||||||
var M2 = r.project(M, r.compileProjection([r._$, r._$]));
|
|
||||||
|
|
||||||
it("should survive double-projection", function () {
|
|
||||||
expect(r.matcherKeys(r.project(M2, r.compileProjection(r.__, r._$)))).to.eql([[2],[3],[4]]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should survive embedding and reprojection", function () {
|
|
||||||
expect(r.matcherKeys(r.project(r.compilePattern(true, [r.embeddedMatcher(M2)]),
|
|
||||||
r.compileProjection([r.__, r._$])))).to.eql([[2],[3],[4]]);
|
|
||||||
expect(r.matcherKeys(r.project(r.compilePattern(true, [[r.embeddedMatcher(M2)]]),
|
|
||||||
r.compileProjection([[r.__, r._$]])))).to.eql([[2],[3],[4]]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("matcherKeys using multiple-values in projections", function () {
|
|
||||||
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
|
||||||
var proj = r.compileProjection([r._$, r._$]);
|
|
||||||
var M2 = r.project(M, proj);
|
|
||||||
|
|
||||||
it("should be able to extract ordinary values", function () {
|
|
||||||
expect(r.matcherKeys(M2))
|
|
||||||
.to.eql([[1,2],[1,3],[3,4]]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be able to be reprojected as a sequence of more than one value", function () {
|
|
||||||
expect(r.matcherKeys(r.project(M2, r.compileProjection(r._$, r._$))))
|
|
||||||
.to.eql([[1,2],[1,3],[3,4]]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should be convertible into objects with $-indexed fields", function () {
|
|
||||||
expect(r.matcherKeysToObjects(r.matcherKeys(M2), proj))
|
|
||||||
.to.eql([{'$0': 1, '$1': 2}, {'$0': 1, '$1': 3}, {'$0': 3, '$1': 4}]);
|
|
||||||
expect(r.projectObjects(M, proj))
|
|
||||||
.to.eql([{'$0': 1, '$1': 2}, {'$0': 1, '$1': 3}, {'$0': 3, '$1': 4}]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("matcherKeys using multiple-values in projections, with names", function () {
|
|
||||||
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
|
||||||
|
|
||||||
it("should yield named fields", function () {
|
|
||||||
expect(r.projectObjects(M, r.compileProjection([r._$("fst"), r._$("snd")])))
|
|
||||||
.to.eql([{'fst': 1, 'snd': 2}, {'fst': 1, 'snd': 3}, {'fst': 3, 'snd': 4}]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should yield numbered fields where names are missing", function () {
|
|
||||||
expect(r.projectObjects(M, r.compileProjection([r._$, r._$("snd")])))
|
|
||||||
.to.eql([{'$0': 1, 'snd': 2}, {'$0': 1, 'snd': 3}, {'$0': 3, 'snd': 4}]);
|
|
||||||
expect(r.projectObjects(M, r.compileProjection([r._$("fst"), r._$])))
|
|
||||||
.to.eql([{'fst': 1, '$1': 2}, {'fst': 1, '$1': 3}, {'fst': 3, '$1': 4}]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("serializeMatcher", function () {
|
|
||||||
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['D']), [r.__, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
|
||||||
var S = r.serializeMatcher(M, r.setToArray);
|
|
||||||
|
|
||||||
it("should basically work", function () {
|
|
||||||
expect(S).to.eql(
|
|
||||||
[ [ [ '(' ],
|
|
||||||
[ [ 1,
|
|
||||||
[ [ 2, [ [ [ ')' ], [ [ [ ')' ], [ '', [ 'A' ] ] ] ] ] ] ],
|
|
||||||
[ 3, [ [ [ ')' ], [ [ [ ')' ], [ '', [ 'C', 'D' ] ] ] ] ] ] ] ] ],
|
|
||||||
[ 3,
|
|
||||||
[ [ 2, [ [ [ ')' ], [ [ [ ')' ], [ '', [ 'A' ] ] ] ] ] ] ],
|
|
||||||
[ 3, [ [ [ ')' ], [ [ [ ')' ], [ '', [ 'D' ] ] ] ] ] ] ],
|
|
||||||
[ 4, [ [ [ ')' ], [ [ [ ')' ], [ '', [ 'B' ] ] ] ] ] ] ] ] ],
|
|
||||||
[ [ '__' ],
|
|
||||||
[ [ 2, [ [ [ ')' ], [ [ [ ')' ], [ '', [ 'A' ] ] ] ] ] ] ],
|
|
||||||
[ 3, [ [ [ ')' ], [ [ [ ')' ], [ '', [ 'D' ] ] ] ] ] ] ] ] ] ] ] ]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should deserialize to something equivalent", function () {
|
|
||||||
expect(r.matcherEquals(M, r.deserializeMatcher(S, r.arrayToSet))).to.be(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("serialize Gestalts", function () {
|
|
||||||
var G = r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 2, 2));
|
|
||||||
var S = G.serialize();
|
|
||||||
|
|
||||||
it("should basically work", function () {
|
|
||||||
expect(S).to.eql(
|
|
||||||
[ 'gestalt',
|
|
||||||
[ [ [ [ [ 'A', [ [ [ ')' ], [ '', true ] ] ] ] ], [] ] ],
|
|
||||||
[],
|
|
||||||
[ [ [], [] ],
|
|
||||||
[ [], [] ],
|
|
||||||
[ [], [ [ 'B', [ [ [ ')' ], [ '', true ] ] ] ] ] ] ] ] ]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should deserialize to something equivalent", function () {
|
|
||||||
expect(G.equals(r.deserializeGestalt(S))).to.be(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("complex erasure", function () {
|
|
||||||
var A = r.compilePattern(r.arrayToSet(['A']), r.__);
|
|
||||||
var B = r.union(r.compilePattern(r.arrayToSet(['B']), [[[["foo"]]]]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [[[["bar"]]]]));
|
|
||||||
describe("after a union", function () {
|
|
||||||
var R0 = r.union(A, B);
|
|
||||||
var R1a = r.erasePath(R0, B);
|
|
||||||
var R1b = r.erasePath(R0, A);
|
|
||||||
|
|
||||||
it("should yield the other parts of the union", function () {
|
|
||||||
expect(r.matcherEquals(R1a, A)).to.be(true);
|
|
||||||
expect(r.matcherEquals(R1b, B)).to.be(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("embedding matchers in patterns", function () {
|
|
||||||
var M1a =
|
|
||||||
r.compilePattern(r.arrayToSet(['A']),
|
|
||||||
[1, r.embeddedMatcher(r.compilePattern(r.arrayToSet(['B']), [2, 3])), 4]);
|
|
||||||
var M1b =
|
|
||||||
r.compilePattern(r.arrayToSet(['A']), [1, [2, 3], 4]);
|
|
||||||
var M2a =
|
|
||||||
r.compilePattern(r.arrayToSet(['A']),
|
|
||||||
[r.embeddedMatcher(r.compilePattern(r.arrayToSet(['B']), [1, 2])),
|
|
||||||
r.embeddedMatcher(r.compilePattern(r.arrayToSet(['C']), [3, 4]))]);
|
|
||||||
var M2b =
|
|
||||||
r.compilePattern(r.arrayToSet(['A']), [[1, 2], [3, 4]]);
|
|
||||||
|
|
||||||
it("should yield matchers equivalent to the original patterns", function () {
|
|
||||||
expect(r.matcherEquals(M1a, M1b)).to.be(true);
|
|
||||||
expect(r.matcherEquals(M2a, M2b)).to.be(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("calls to matchPattern", function () {
|
|
||||||
it("should yield appropriately-named/-numbered fields", function () {
|
|
||||||
expect(r.matchPattern([1, 2, 3], [r.__, 2, r._$])).to.eql({'$0': 3, 'length': 1});
|
|
||||||
expect(r.matchPattern([1, 2, 3], [r.__, 2, r._$("three")])).to.eql({'three': 3, 'length': 1});
|
|
||||||
expect(r.matchPattern([1, 2, 3], [r._$, 2, r._$("three")]))
|
|
||||||
.to.eql({'$0': 1, 'three': 3, 'length': 2});
|
|
||||||
expect(r.matchPattern([1, 2, 3], [r._$("one"), 2, r._$]))
|
|
||||||
.to.eql({'one': 1, '$1': 3, 'length': 2});
|
|
||||||
expect(r.matchPattern([1, 2, 3], [r._$("one"), 2, r._$("three")]))
|
|
||||||
.to.eql({'one': 1, 'three': 3, 'length': 2});
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should fail on value mismatch", function () {
|
|
||||||
expect(r.matchPattern([1, 2, 3], [r.__, 999, r._$("three")])).to.be(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should fail on array length mismatch", function () {
|
|
||||||
expect(r.matchPattern([1, 2, 3], [r.__, 2, r._$("three"), 4])).to.be(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("matches substructure", function () {
|
|
||||||
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$(null, [2, r.__]), r._$("three")]))
|
|
||||||
.to.eql({ one: 1, '$1': [ 2, 999 ], three: 3, length: 3 });
|
|
||||||
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$("two", [2, r.__]), r._$("three")]))
|
|
||||||
.to.eql({ one: 1, two: [ 2, 999 ], three: 3, length: 3 });
|
|
||||||
expect(r.matchPattern([1, [999, 2], 3], [r._$("one"), r._$(null, [2, r.__]), r._$("three")]))
|
|
||||||
.to.be(null);
|
|
||||||
expect(r.matchPattern([1, [999, 2], 3], [r._$("one"), r._$("two", [2, r.__]), r._$("three")]))
|
|
||||||
.to.be(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("matches nested captures", function () {
|
|
||||||
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$(null, [2, r._$]), r._$("three")]))
|
|
||||||
.to.eql({ one: 1, '$2': 999, '$1': [ 2, 999 ], three: 3, length: 4 });
|
|
||||||
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$("two", [2, r._$]), r._$("three")]))
|
|
||||||
.to.eql({ one: 1, '$2': 999, two: [ 2, 999 ], three: 3, length: 4 });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("Projection with no captures", function () {
|
|
||||||
it("should yield the empty sequence when there's a match", function () {
|
|
||||||
var emptySequence = [' >{["A"]}'];
|
|
||||||
|
|
||||||
checkPrettyMatcher(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
|
||||||
r.compileProjection(r.__)),
|
|
||||||
emptySequence);
|
|
||||||
checkPrettyMatcher(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
|
||||||
r.compileProjection([r.__, r.__])),
|
|
||||||
emptySequence);
|
|
||||||
checkPrettyMatcher(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
|
||||||
r.compileProjection(["X", r.__])),
|
|
||||||
emptySequence);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should yield null when there's no match", function () {
|
|
||||||
expect(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
|
||||||
r.compileProjection(["Y", r.__]))).to.be(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should yield nonempty sequences when there are captures after all", function () {
|
|
||||||
checkPrettyMatcher(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
|
||||||
r.compileProjection([r.__, r._$])),
|
|
||||||
[' ★ >{["A"]}']);
|
|
||||||
checkPrettyMatcher(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
|
||||||
r.compileProjection([r._$, r._$])),
|
|
||||||
[' "X" ★ >{["A"]}']);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,309 @@
|
||||||
|
var util = require('util');
|
||||||
|
var r = require("../src/route.js");
|
||||||
|
|
||||||
|
function dump(x) {
|
||||||
|
console.log(util.inspect(x, { depth: null }));
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dumpM(m) {
|
||||||
|
console.log(r.prettyMatcher(m));
|
||||||
|
console.log();
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dumpG(g) {
|
||||||
|
console.log(g.pretty());
|
||||||
|
console.log();
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
mAny = r.compilePattern(r.arrayToSet(['mAny']), r.__);
|
||||||
|
mAAny = r.compilePattern(r.arrayToSet(['mAAny']), ['A', r.__]);
|
||||||
|
dumpM(mAny);
|
||||||
|
dumpM(mAAny);
|
||||||
|
|
||||||
|
dump("mAny:");
|
||||||
|
dump(r.matchValue(mAny, 'hi'));
|
||||||
|
dump(r.matchValue(mAny, ['A', 'hi']));
|
||||||
|
dump(r.matchValue(mAny, ['B', 'hi']));
|
||||||
|
dump(r.matchValue(mAny, ['A', [['hi']]]));
|
||||||
|
|
||||||
|
dump("mAAny:");
|
||||||
|
dump(r.matchValue(mAAny, 'hi'));
|
||||||
|
dump(r.matchValue(mAAny, ['A', 'hi']));
|
||||||
|
dump(r.matchValue(mAAny, ['B', 'hi']));
|
||||||
|
dump(r.matchValue(mAAny, ['A', [['hi']]]));
|
||||||
|
|
||||||
|
console.log("unions");
|
||||||
|
|
||||||
|
dumpM(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B'])));
|
||||||
|
|
||||||
|
dumpM(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
||||||
|
r.compilePattern(r.arrayToSet(['W']), r.__)));
|
||||||
|
|
||||||
|
console.log("projections");
|
||||||
|
|
||||||
|
dumpM(r.project(r.union(r.compilePattern(r.arrayToSet(['A']), r.__),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), ['b'])),
|
||||||
|
r.compileProjection(r._$("v", [[r.__]]))));
|
||||||
|
|
||||||
|
dumpM(r.project(r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4])),
|
||||||
|
r.compileProjection([r._$, r._$])));
|
||||||
|
|
||||||
|
dump(r.matcherKeys(r.project(r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4])),
|
||||||
|
r.compileProjection([r._$, r._$]))));
|
||||||
|
|
||||||
|
var R1 = r.compilePattern(r.arrayToSet(['A']), [r.__, "B"]);
|
||||||
|
var R2 = r.compilePattern(r.arrayToSet(['B']), ["A", r.__]);
|
||||||
|
var R12 = r.union(R1, R2);
|
||||||
|
dumpM(R1);
|
||||||
|
dumpM(R2);
|
||||||
|
dumpM(R12);
|
||||||
|
dumpM(r.erasePath(R12, R1));
|
||||||
|
dumpM(r.erasePath(R12, R2));
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
dumpG(r.simpleGestalt(false, "A", 0, 0));
|
||||||
|
dumpG(r.simpleGestalt(true, "B", 0, 0));
|
||||||
|
dumpG(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0)));
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
dumpG(r.simpleGestalt(false, "A", 2, 2));
|
||||||
|
dumpG(r.simpleGestalt(true, "B", 2, 2));
|
||||||
|
dumpG(r.simpleGestalt(false, "A", 2, 2).union(r.simpleGestalt(true, "B", 2, 2)));
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
function check1(i, j, n) {
|
||||||
|
var result = r.simpleGestalt(false, "A", i, j).label(123).matchValue("A", n, false);
|
||||||
|
dump([i === n ? result.length === 1 && result[0] === 123 : result.length === 0,
|
||||||
|
i, j, n, result]);
|
||||||
|
}
|
||||||
|
function metaLevelCheck(n) {
|
||||||
|
console.log("Checking message matching at metaLevel " + n);
|
||||||
|
check1(0, 0, n);
|
||||||
|
check1(0, 1, n);
|
||||||
|
check1(0, 2, n);
|
||||||
|
check1(2, 0, n);
|
||||||
|
check1(2, 1, n);
|
||||||
|
check1(2, 2, n);
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
metaLevelCheck(0);
|
||||||
|
metaLevelCheck(1);
|
||||||
|
metaLevelCheck(2);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
function check1(i, j, n) {
|
||||||
|
var observer = r.simpleGestalt(true, r.__, i, n).label("observer");
|
||||||
|
var observed = r.simpleGestalt(false, "A", i, j).label("observed");
|
||||||
|
var resultM = observed.filter(observer);
|
||||||
|
var resultL = observed.match(observer);
|
||||||
|
dump([ (j < n
|
||||||
|
? !resultM.isEmpty() && resultL.length === 1 && resultL[0] === "observer"
|
||||||
|
: resultM.isEmpty() && resultL.length === 0),
|
||||||
|
i, j, n, resultL]);
|
||||||
|
}
|
||||||
|
function levelCheck(n) {
|
||||||
|
console.log("Checking gestalt filtering at level " + n);
|
||||||
|
check1(0, 0, n);
|
||||||
|
check1(0, 1, n);
|
||||||
|
check1(0, 2, n);
|
||||||
|
check1(2, 0, n);
|
||||||
|
check1(2, 1, n);
|
||||||
|
check1(2, 2, n);
|
||||||
|
console.log();
|
||||||
|
}
|
||||||
|
levelCheck(0);
|
||||||
|
levelCheck(1);
|
||||||
|
levelCheck(2);
|
||||||
|
})();
|
||||||
|
|
||||||
|
console.log("Checking matcher equality");
|
||||||
|
dump(r.matcherEquals(mAny, mAAny) === false);
|
||||||
|
dump(r.matcherEquals(mAny, mAny) === true);
|
||||||
|
dump(r.matcherEquals(mAAny, mAAny) === true);
|
||||||
|
dump(r.matcherEquals(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B'])),
|
||||||
|
r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B'])))
|
||||||
|
=== true);
|
||||||
|
dump(r.matcherEquals(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B'])),
|
||||||
|
r.union(r.compilePattern(r.arrayToSet(['B']), [r.__, 'B']),
|
||||||
|
r.compilePattern(r.arrayToSet(['A']), [r.__, 'A'])))
|
||||||
|
=== true);
|
||||||
|
|
||||||
|
dump(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))
|
||||||
|
.equals(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0)))
|
||||||
|
=== true);
|
||||||
|
dump(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))
|
||||||
|
.equals(r.simpleGestalt(true, "B", 0, 0).union(r.simpleGestalt(false, "A", 0, 0)))
|
||||||
|
=== true);
|
||||||
|
|
||||||
|
dump(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))
|
||||||
|
.equals(r.simpleGestalt(false, "B", 0, 0).union(r.simpleGestalt(true, "A", 0, 0)))
|
||||||
|
=== false);
|
||||||
|
|
||||||
|
|
||||||
|
console.log("debugging unions (1)");
|
||||||
|
dumpM(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4])));
|
||||||
|
|
||||||
|
dumpM(r.union(r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4])));
|
||||||
|
|
||||||
|
dumpM(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3])));
|
||||||
|
|
||||||
|
dumpM(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4])));
|
||||||
|
|
||||||
|
console.log("debugging unions (2)");
|
||||||
|
var MU = r.emptyMatcher;
|
||||||
|
MU = r.union(MU, r.compilePattern(r.arrayToSet(['A']), [r.__, 2]));
|
||||||
|
dumpM(MU);
|
||||||
|
MU = r.union(MU, r.compilePattern(r.arrayToSet(['C']), [1, 3]));
|
||||||
|
dumpM(MU);
|
||||||
|
MU = r.union(MU, r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
||||||
|
dumpM(MU);
|
||||||
|
|
||||||
|
console.log("debugging unions (3)");
|
||||||
|
dumpM(r.union(r.compilePattern(r.arrayToSet('A'), [2]),
|
||||||
|
dumpM(r.union(r.compilePattern(r.arrayToSet('B'), [2]),
|
||||||
|
r.compilePattern(r.arrayToSet('C'), [3])))));
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
console.log("matcherKeys on wild matchers");
|
||||||
|
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
||||||
|
dump(r.matcherKeys(r.project(M, r.compileProjection([r._$, r._$]))));
|
||||||
|
dump(r.matcherKeys(r.project(M, r.compileProjection([r.__, r._$]))));
|
||||||
|
var M2 = r.project(M, r.compileProjection([r._$, r._$]));
|
||||||
|
dump(r.matcherKeys(r.project(M2,
|
||||||
|
r.compileProjection(r.__, r._$))));
|
||||||
|
dump(r.matcherKeys(r.project(r.compilePattern(true, [r.embeddedMatcher(M2)]),
|
||||||
|
r.compileProjection([r.__, r._$]))));
|
||||||
|
dump(r.matcherKeys(r.project(r.compilePattern(true, [[r.embeddedMatcher(M2)]]),
|
||||||
|
r.compileProjection([[r.__, r._$]]))));
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
console.log("matcherKeys using multiple-values in projections");
|
||||||
|
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
||||||
|
var proj = r.compileProjection([r._$, r._$]);
|
||||||
|
var M2 = r.project(M, proj);
|
||||||
|
dump(r.matcherKeys(M2));
|
||||||
|
dump(r.matcherKeys(r.project(M2, r.compileProjection(r._$, r._$))));
|
||||||
|
dump(r.matcherKeysToObjects(r.matcherKeys(M2), proj));
|
||||||
|
dump(r.projectObjects(M, proj));
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
console.log("matcherKeys using multiple-values in projections, with names");
|
||||||
|
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
||||||
|
dump(r.projectObjects(M, r.compileProjection([r._$("fst"), r._$("snd")])));
|
||||||
|
dump(r.projectObjects(M, r.compileProjection([r._$, r._$("snd")])));
|
||||||
|
dump(r.projectObjects(M, r.compileProjection([r._$("fst"), r._$])));
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
console.log("serializeMatcher");
|
||||||
|
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['D']), [r.__, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
||||||
|
var S = r.serializeMatcher(M, r.setToArray);
|
||||||
|
dump(S);
|
||||||
|
console.log(JSON.stringify(S));
|
||||||
|
dumpM(r.deserializeMatcher(S, r.arrayToSet));
|
||||||
|
dump(r.matcherEquals(M, r.deserializeMatcher(S, r.arrayToSet)) === true);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
console.log("serialize Gestalts");
|
||||||
|
var G = dumpG(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 2, 2)));
|
||||||
|
var S = G.serialize();
|
||||||
|
dump(S);
|
||||||
|
console.log(JSON.stringify(S));
|
||||||
|
dumpG(r.deserializeGestalt(S));
|
||||||
|
dump(G.equals(r.deserializeGestalt(S)) === true);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
console.log("complex erasure");
|
||||||
|
var A = r.compilePattern(r.arrayToSet(['A']), r.__);
|
||||||
|
var B = r.union(r.compilePattern(r.arrayToSet(['B']), [[[["foo"]]]]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [[[["bar"]]]]));
|
||||||
|
var R0 = r.union(A, B);
|
||||||
|
var R1a = r.erasePath(R0, B);
|
||||||
|
var R1b = r.erasePath(R0, A);
|
||||||
|
dumpM(R0);
|
||||||
|
dumpM(R1a);
|
||||||
|
dumpM(R1b);
|
||||||
|
dump(r.matcherEquals(R1a, A) === true);
|
||||||
|
dump(r.matcherEquals(R1b, B) === true);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
console.log("Embedding matchers in patterns");
|
||||||
|
var M1a =
|
||||||
|
r.compilePattern(r.arrayToSet(['A']),
|
||||||
|
[1, r.embeddedMatcher(r.compilePattern(r.arrayToSet(['B']), [2, 3])), 4]);
|
||||||
|
var M1b =
|
||||||
|
r.compilePattern(r.arrayToSet(['A']), [1, [2, 3], 4]);
|
||||||
|
var M2a =
|
||||||
|
r.compilePattern(r.arrayToSet(['A']),
|
||||||
|
[r.embeddedMatcher(r.compilePattern(r.arrayToSet(['B']), [1, 2])),
|
||||||
|
r.embeddedMatcher(r.compilePattern(r.arrayToSet(['C']), [3, 4]))]);
|
||||||
|
var M2b =
|
||||||
|
r.compilePattern(r.arrayToSet(['A']), [[1, 2], [3, 4]]);
|
||||||
|
dumpM(M1a);
|
||||||
|
dumpM(M2a);
|
||||||
|
dump(r.matcherEquals(M1a, M1b) === true);
|
||||||
|
dump(r.matcherEquals(M2a, M2b) === true);
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
console.log("Calls to matchPattern");
|
||||||
|
dump(r.matchPattern([1, 2, 3], [r.__, 2, r._$]));
|
||||||
|
dump(r.matchPattern([1, 2, 3], [r.__, 2, r._$("three")]));
|
||||||
|
dump(r.matchPattern([1, 2, 3], [r._$, 2, r._$("three")]));
|
||||||
|
dump(r.matchPattern([1, 2, 3], [r._$("one"), 2, r._$]));
|
||||||
|
dump(r.matchPattern([1, 2, 3], [r._$("one"), 2, r._$("three")]));
|
||||||
|
dump(r.matchPattern([1, 2, 3], [r.__, 999, r._$("three")]) === null);
|
||||||
|
dump(r.matchPattern([1, 2, 3], [r.__, 2, r._$("three"), 4]) === null);
|
||||||
|
|
||||||
|
dump(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$(null, [2, r.__]), r._$("three")]));
|
||||||
|
dump(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$("two", [2, r.__]), r._$("three")]));
|
||||||
|
dump(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$(null, [2, r._$]), r._$("three")]));
|
||||||
|
dump(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$("two", [2, r._$]), r._$("three")]));
|
||||||
|
})();
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
console.log("Projection with no captures");
|
||||||
|
dumpM(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
||||||
|
r.compileProjection(r.__)));
|
||||||
|
dumpM(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
||||||
|
r.compileProjection([r.__, r.__])));
|
||||||
|
dumpM(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
||||||
|
r.compileProjection(["X", r.__])));
|
||||||
|
dumpM(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
||||||
|
r.compileProjection(["Y", r.__])));
|
||||||
|
dumpM(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
||||||
|
r.compileProjection([r.__, r._$])));
|
||||||
|
dumpM(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
||||||
|
r.compileProjection([r._$, r._$])));
|
||||||
|
})();
|
Loading…
Reference in New Issue