diff --git a/js/compiler/compiler.js b/js/compiler/compiler.js
index 3d507bf..0e76a82 100644
--- a/js/compiler/compiler.js
+++ b/js/compiler/compiler.js
@@ -122,24 +122,8 @@ var modifiedSourceActions = {
var label = maybeLabel.numChildren === 1
? maybeLabel.children[0].interval.contents
: JSON.stringify(typeName.interval.contents);
- var fragments = [];
- fragments.push(
- 'var ' + typeName.asES5 + ' = (function() {',
- ' var $SyndicateMeta$ = {',
- ' label: ' + label + ',',
- ' arguments: ' + JSON.stringify(formals),
- ' };',
- ' return function ' + typeName.asES5 + '(' + formalsRaw.asES5 + ') {',
- ' return {');
- formals.forEach(function(f) {
- fragments.push(' ' + JSON.stringify(f) + ': ' + f + ',');
- });
- fragments.push(
- ' "$SyndicateMeta$": $SyndicateMeta$',
- ' };',
- ' };',
- '})();');
- return fragments.join('\n');
+ return 'var ' + typeName.asES5 + ' = Syndicate.Route.makeStructureConstructor(' +
+ label + ', ' + JSON.stringify(formals) + ');';
},
SendMessageStatement: function(_colons, expr, sc) {
diff --git a/js/examples/button/index.expanded.js b/js/examples/button/index.expanded.js
index 1d21de6..3dfe4ee 100644
--- a/js/examples/button/index.expanded.js
+++ b/js/examples/button/index.expanded.js
@@ -1,32 +1,6 @@
"use strict";
-var DOM = (function() {
- var $SyndicateMeta$ = {
- label: "DOM",
- arguments: ["containerSelector","fragmentClass","spec"]
- };
- return function DOM(containerSelector, fragmentClass, spec) {
- return {
- "containerSelector": containerSelector,
- "fragmentClass": fragmentClass,
- "spec": spec,
- "$SyndicateMeta$": $SyndicateMeta$
- };
- };
-})();
-var jQuery = (function() {
- var $SyndicateMeta$ = {
- label: "jQuery",
- arguments: ["selector","eventType","event"]
- };
- return function jQuery(selector, eventType, event) {
- return {
- "selector": selector,
- "eventType": eventType,
- "event": event,
- "$SyndicateMeta$": $SyndicateMeta$
- };
- };
-})();
+var DOM = Syndicate.DOM.DOM;
+var jQueryEvent = Syndicate.JQuery.jQueryEvent;
$(document).ready(function() {
new Syndicate.Ground(function () {
@@ -37,7 +11,7 @@ $(document).ready(function() {
this.counter = 0;
Syndicate.Actor.createFacet()
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(DOM('#button-label','',Syndicate.seal(this.counter)), 0); }))
-.onEvent(false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(jQuery('#counter','click',_), 0); }), (function() { var _ = Syndicate.__; return { assertion: jQuery('#counter','click',_), metalevel: 0 }; }), (function() {
+.onEvent(false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(jQueryEvent('#counter','click',_), 0); }), (function() { var _ = Syndicate.__; return { assertion: jQueryEvent('#counter','click',_), metalevel: 0 }; }), (function() {
this.counter++;
})).completeBuild();
});
diff --git a/js/examples/button/index.html b/js/examples/button/index.html
index 184d544..1387309 100644
--- a/js/examples/button/index.html
+++ b/js/examples/button/index.html
@@ -3,7 +3,6 @@
Syndicate: Button Example
-
diff --git a/js/examples/button/index.js b/js/examples/button/index.js
index fc85acf..7b6bfee 100644
--- a/js/examples/button/index.js
+++ b/js/examples/button/index.js
@@ -1,5 +1,5 @@
-assertion type DOM(containerSelector, fragmentClass, spec);
-assertion type jQuery(selector, eventType, event);
+var DOM = Syndicate.DOM.DOM;
+var jQueryEvent = Syndicate.JQuery.jQueryEvent;
$(document).ready(function() {
ground dataspace {
@@ -10,7 +10,7 @@ $(document).ready(function() {
this.counter = 0;
forever {
assert DOM('#button-label', '', Syndicate.seal(this.counter));
- on message jQuery('#counter', 'click', _) {
+ on message jQueryEvent('#counter', 'click', _) {
this.counter++;
}
}
diff --git a/js/examples/dom/index.js b/js/examples/dom/index.js
index 9f9122c..c66babe 100644
--- a/js/examples/dom/index.js
+++ b/js/examples/dom/index.js
@@ -12,15 +12,18 @@ $(document).ready(function () {
console.log('starting ground boot');
Syndicate.DOM.spawnDOMDriver();
+ var DOM = Syndicate.DOM.DOM;
+ var jQueryEvent = Syndicate.JQuery.jQueryEvent;
Dataspace.spawn({
boot: function () {
- return assert(["DOM", "#clicker-holder", "clicker",
- seal(["button", ["span", [["style", "font-style: italic"]], "Click me!"]])])
- .andThen(sub(["jQuery", "button.clicker", "click", __]));
+ return assert(DOM("#clicker-holder", "clicker",
+ seal(["button", ["span", [["style", "font-style: italic"]],
+ "Click me!"]])))
+ .andThen(sub(jQueryEvent("button.clicker", "click", __)));
},
handleEvent: function (e) {
- if (e.type === "message" && e.message[0] === "jQuery") {
+ if (e.type === "message" && jQueryEvent.isClassOf(e.message)) {
Dataspace.send("bump_count");
}
}
@@ -33,11 +36,11 @@ $(document).ready(function () {
return sub("bump_count");
},
updateState: function () {
- Dataspace.stateChange(retract(["DOM", __, __, __])
- .andThen(assert(["DOM", "#counter-holder", "counter",
- seal(["div",
- ["p", "The current count is: ",
- this.counter]])])));
+ Dataspace.stateChange(retract(DOM.pattern)
+ .andThen(assert(DOM("#counter-holder", "counter",
+ seal(["div",
+ ["p", "The current count is: ",
+ this.counter]])))));
},
handleEvent: function (e) {
if (e.type === "message" && e.message === "bump_count") {
diff --git a/js/examples/iot/index.js b/js/examples/iot/index.js
index 3d25e34..b6d56de 100644
--- a/js/examples/iot/index.js
+++ b/js/examples/iot/index.js
@@ -6,8 +6,8 @@ assertion type tvAlert(text);
assertion type switchAction(on);
assertion type componentPresent(name);
-assertion type DOM(containerSelector, fragmentClass, spec);
-assertion type jQuery(selector, eventType, event);
+var DOM = Syndicate.DOM.DOM;
+var jQueryEvent = Syndicate.JQuery.jQueryEvent;
///////////////////////////////////////////////////////////////////////////
// TV
@@ -48,7 +48,7 @@ function spawnRemoteControl() {
actor {
forever {
assert componentPresent('remote control');
- on message jQuery('#remote-control', 'click', _) {
+ on message jQueryEvent('#remote-control', 'click', _) {
:: remoteClick();
}
}
@@ -92,14 +92,14 @@ function spawnStoveSwitch() {
"img/stove-coil-element-" +
(this.powerOn ? "hot" : "cold") + ".jpg"]]]));
- on message jQuery('#stove-switch-on', 'click', _) { this.powerOn = true; }
- on message jQuery('#stove-switch-off', 'click', _) { this.powerOn = false; }
+ on message jQueryEvent('#stove-switch-on', 'click', _) { this.powerOn = true; }
+ on message jQueryEvent('#stove-switch-off', 'click', _) { this.powerOn = false; }
on message switchAction($newState) {
this.powerOn = newState;
}
} until {
- case message jQuery('#kill-stove-switch', 'click', _);
+ case message jQueryEvent('#kill-stove-switch', 'click', _);
}
}
}
@@ -120,7 +120,7 @@ function spawnPowerDrawMonitor() {
this.watts = on ? 1500 : 0;
}
} until {
- case message jQuery('#kill-power-draw-monitor', 'click', _);
+ case message jQueryEvent('#kill-power-draw-monitor', 'click', _);
}
}
}
@@ -187,10 +187,10 @@ function spawnFailureMonitor() {
function spawnChaosMonkey() {
actor {
forever {
- on message jQuery('#spawn-power-draw-monitor', 'click', _) {
+ on message jQueryEvent('#spawn-power-draw-monitor', 'click', _) {
spawnPowerDrawMonitor();
}
- on message jQuery('#spawn-stove-switch', 'click', _) {
+ on message jQueryEvent('#spawn-stove-switch', 'click', _) {
spawnStoveSwitch();
}
}
diff --git a/js/examples/jquery/index.js b/js/examples/jquery/index.js
index f9436b7..d65ab5a 100644
--- a/js/examples/jquery/index.js
+++ b/js/examples/jquery/index.js
@@ -14,10 +14,13 @@ $(document).ready(function () {
Dataspace.spawn({
boot: function () {
- return sub(['jQuery', '#clicker', 'click', __]);
+ return sub(Syndicate.JQuery.jQueryEvent('#clicker', 'click', __));
},
handleEvent: function (e) {
- if (e.type === 'message' && e.message[0] === 'jQuery' && e.message[1] === '#clicker') {
+ if (e.type === 'message'
+ && Syndicate.JQuery.jQueryEvent.isClassOf(e.message)
+ && e.message.selector === '#clicker')
+ {
var r = $('#result');
r.html(Number(r.html()) + 1);
}
diff --git a/js/examples/smoketest-dsl/index.js b/js/examples/smoketest-dsl/index.js
index 221f226..24d279a 100644
--- a/js/examples/smoketest-dsl/index.js
+++ b/js/examples/smoketest-dsl/index.js
@@ -23,8 +23,8 @@ ground dataspace {
actor {
forever {
- on message $m {
- console.log("Got message:", m);
+ on message beep($counter) {
+ console.log("beep!", counter);
}
}
}
diff --git a/js/examples/smoketest/index.js b/js/examples/smoketest/index.js
index fa688d8..e67da12 100644
--- a/js/examples/smoketest/index.js
+++ b/js/examples/smoketest/index.js
@@ -1,5 +1,7 @@
"use strict";
+var beep = Syndicate.Route.makeStructureConstructor('beep', ['counter']);
+
var G;
$(document).ready(function () {
var Dataspace = Syndicate.Dataspace;
@@ -15,16 +17,16 @@ $(document).ready(function () {
boot: function () {},
handleEvent: function (e) {},
step: function () {
- Dataspace.send(["beep", this.counter++]);
+ Dataspace.send(beep(this.counter++));
return this.counter <= 10;
}
});
Dataspace.spawn({
- boot: function () { return sub(["beep", __]); },
+ boot: function () { return sub(beep.pattern); },
handleEvent: function (e) {
if (e.type === 'message') {
- console.log("beep!", e.message[1]);
+ console.log("beep!", e.message.counter);
}
}
});
diff --git a/js/examples/textfield-dsl/index.js b/js/examples/textfield-dsl/index.js
index 52c5f07..4effb36 100644
--- a/js/examples/textfield-dsl/index.js
+++ b/js/examples/textfield-dsl/index.js
@@ -1,7 +1,7 @@
///////////////////////////////////////////////////////////////////////////
// GUI
-assertion type jQuery(selector, eventType, event);
+var jQueryEvent = Syndicate.JQuery.jQueryEvent;
assertion type fieldCommand(detail);
assertion type fieldContents(text, pos);
assertion type highlight(state);
@@ -34,8 +34,8 @@ function spawnGui() {
var text = this.text;
var pos = this.pos;
var highlight = this.highlightState;
- var hLeft = highlight ? highlight.get(0) : 0;
- var hRight = highlight ? highlight.get(1) : 0;
+ var hLeft = highlight ? highlight[0] : 0;
+ var hRight = highlight ? highlight[1] : 0;
$("#fieldContents")[0].innerHTML = highlight
? piece(text, pos, 0, hLeft, "normal") +
piece(text, pos, hLeft, hRight, "highlight") +
@@ -44,7 +44,7 @@ function spawnGui() {
};
forever {
- on message jQuery("#inputRow", "+keypress", $event) {
+ on message jQueryEvent("#inputRow", "+keypress", $event) {
var keycode = event.keyCode;
var character = String.fromCharCode(event.charCode);
if (keycode === 37 /* left */) {
@@ -79,11 +79,11 @@ function spawnGui() {
function spawnModel() {
actor {
- this.fieldContents = "initial";
- this.cursorPos = this.fieldContents.length; /* positions address gaps between characters */
+ this.fieldValue = "initial";
+ this.cursorPos = this.fieldValue.length; /* positions address gaps between characters */
forever {
- assert fieldContents(this.fieldContents, this.cursorPos);
+ assert fieldContents(this.fieldValue, this.cursorPos);
on message fieldCommand("cursorLeft") {
this.cursorPos--;
@@ -93,24 +93,24 @@ function spawnModel() {
on message fieldCommand("cursorRight") {
this.cursorPos++;
- if (this.cursorPos > this.fieldContents.length)
- this.cursorPos = this.fieldContents.length;
+ if (this.cursorPos > this.fieldValue.length)
+ this.cursorPos = this.fieldValue.length;
}
on message fieldCommand("backspace") {
if (this.cursorPos > 0) {
- this.fieldContents =
- this.fieldContents.substring(0, this.cursorPos - 1) +
- this.fieldContents.substring(this.cursorPos);
+ this.fieldValue =
+ this.fieldValue.substring(0, this.cursorPos - 1) +
+ this.fieldValue.substring(this.cursorPos);
this.cursorPos--;
}
}
on message fieldCommand(["insert", $newText]) {
- this.fieldContents =
- this.fieldContents.substring(0, this.cursorPos) +
+ this.fieldValue =
+ this.fieldValue.substring(0, this.cursorPos) +
newText +
- this.fieldContents.substring(this.cursorPos);
+ this.fieldValue.substring(this.cursorPos);
this.cursorPos += newText.length;
}
}
@@ -122,13 +122,13 @@ function spawnModel() {
function spawnSearch() {
actor {
- this.fieldContents = "";
+ this.fieldValue = "";
this.highlight = false;
this.search = function () {
var searchtext = $("#searchBox")[0].value;
if (searchtext) {
- var pos = this.fieldContents.indexOf(searchtext);
+ var pos = this.fieldValue.indexOf(searchtext);
this.highlight = (pos !== -1) && [pos, pos + searchtext.length];
} else {
this.highlight = false;
@@ -138,12 +138,12 @@ function spawnSearch() {
forever {
assert highlight(this.highlight);
- on message jQuery("#searchBox", "input", $event) {
+ on message jQueryEvent("#searchBox", "input", $event) {
this.search();
}
on asserted fieldContents($text, _) {
- this.fieldContents = text;
+ this.fieldValue = text;
this.search();
}
}
diff --git a/js/examples/textfield/index.js b/js/examples/textfield/index.js
index 2fcdfd2..077eba6 100644
--- a/js/examples/textfield/index.js
+++ b/js/examples/textfield/index.js
@@ -7,6 +7,11 @@ var Patch = Syndicate.Patch;
var __ = Syndicate.__;
var _$ = Syndicate._$;
+var jQueryEvent = Syndicate.JQuery.jQueryEvent;
+var fieldContents = Route.makeStructureConstructor('fieldContents', ['text', 'pos']);
+var highlight = Route.makeStructureConstructor('highlight', ['state']);
+var fieldCommand = Route.makeStructureConstructor('fieldCommand', ['detail']);
+
function escapeText(text) {
text = text.replace(/&/g, '&');
text = text.replace(/ this.fieldContents.length)
- this.cursorPos = this.fieldContents.length;
+ if (this.cursorPos > this.fieldValue.length)
+ this.cursorPos = this.fieldValue.length;
} else if (command === "backspace" && this.cursorPos > 0) {
- this.fieldContents =
- this.fieldContents.substring(0, this.cursorPos - 1) +
- this.fieldContents.substring(this.cursorPos);
+ this.fieldValue =
+ this.fieldValue.substring(0, this.cursorPos - 1) +
+ this.fieldValue.substring(this.cursorPos);
this.cursorPos--;
} else if (command.constructor === Array && command[0] === "insert") {
var newText = command[1];
- this.fieldContents =
- this.fieldContents.substring(0, this.cursorPos) +
+ this.fieldValue =
+ this.fieldValue.substring(0, this.cursorPos) +
newText +
- this.fieldContents.substring(this.cursorPos);
+ this.fieldValue.substring(this.cursorPos);
this.cursorPos += newText.length;
}
this.publishState();
@@ -128,8 +133,8 @@ function spawnModel() {
publishState: function () {
Dataspace.stateChange(
- Patch.retract(["fieldContents", __, __])
- .andThen(Patch.assert(["fieldContents", this.fieldContents, this.cursorPos])));
+ Patch.retract(fieldContents.pattern)
+ .andThen(Patch.assert(fieldContents(this.fieldValue, this.cursorPos))));
}
});
}
@@ -139,24 +144,24 @@ function spawnModel() {
function spawnSearch() {
Dataspace.spawn({
- fieldContents: "",
+ fieldValue: "",
highlight: false,
boot: function () {
this.publishState();
- return Patch.sub(["jQuery", "#searchBox", "input", __])
- .andThen(Patch.sub(["fieldContents", __, __]));
+ return Patch.sub(jQueryEvent("#searchBox", "input", __))
+ .andThen(Patch.sub(fieldContents.pattern));
},
- fieldContentsProjection: Route.compileProjection(["fieldContents", _$("text"), _$("pos")]),
+ fieldContentsProjection: fieldContents(_$("text"), _$("pos")),
handleEvent: function (e) {
var self = this;
- if (e.type === "message" && e.message[0] === "jQuery") {
+ if (jQueryEvent.isClassOf(e.message)) {
this.search();
}
if (e.type === "stateChange") {
Route.projectObjects(e.patch.added, this.fieldContentsProjection).forEach(function (c) {
- self.fieldContents = c.text;
+ self.fieldValue = c.text;
});
this.search();
}
@@ -164,15 +169,15 @@ function spawnSearch() {
publishState: function () {
Dataspace.stateChange(
- Patch.retract(["highlight", __])
- .andThen(Patch.assert(["highlight", this.highlight])));
+ Patch.retract(highlight.pattern)
+ .andThen(Patch.assert(highlight(this.highlight))));
},
search: function () {
var searchtext = $("#searchBox")[0].value;
var oldHighlight = this.highlight;
if (searchtext) {
- var pos = this.fieldContents.indexOf(searchtext);
+ var pos = this.fieldValue.indexOf(searchtext);
this.highlight = (pos !== -1) && [pos, pos + searchtext.length];
} else {
this.highlight = false;
diff --git a/js/src/ack.js b/js/src/ack.js
index dd83ac9..01f0df0 100644
--- a/js/src/ack.js
+++ b/js/src/ack.js
@@ -5,7 +5,7 @@ var Dataspace = require('./dataspace.js').Dataspace;
var Route = require('./route.js');
var Patch = require('./patch.js');
-var $Ack = new Route.$Special('ack');
+var ack = Route.makeStructureConstructor('ack', ['id']);
function Ack(metaLevel, id) {
this.metaLevel = metaLevel || 0;
@@ -14,19 +14,19 @@ function Ack(metaLevel, id) {
}
Ack.prototype.arm = function () {
- Dataspace.stateChange(Patch.sub([$Ack, this.id], this.metaLevel));
- Dataspace.send([$Ack, this.id], this.metaLevel);
+ Dataspace.stateChange(Patch.sub(ack(this.id), this.metaLevel));
+ Dataspace.send(ack(this.id), this.metaLevel);
};
Ack.prototype.disarm = function () {
- Dataspace.stateChange(Patch.unsub([$Ack, this.id], this.metaLevel));
+ Dataspace.stateChange(Patch.unsub(ack(this.id), this.metaLevel));
};
Ack.prototype.check = function (e) {
if (!this.done) {
if (e.type === 'message') {
var m = Patch.stripAtMeta(e.message, this.metaLevel);
- if (m && m[0] === $Ack && m[1] === this.id) {
+ if (ack.isClassOf(m) && m.id === this.id) {
this.disarm();
this.done = true;
}
@@ -37,5 +37,5 @@ Ack.prototype.check = function (e) {
///////////////////////////////////////////////////////////////////////////
-module.exports.$Ack = $Ack;
+module.exports.ack = ack;
module.exports.Ack = Ack;
diff --git a/js/src/actor.js b/js/src/actor.js
index 92239a2..e614108 100644
--- a/js/src/actor.js
+++ b/js/src/actor.js
@@ -94,11 +94,10 @@ Facet.prototype.onEvent = function(isTerminal, eventType, subscriptionFn, projec
if (e.type === 'stateChange') {
var proj = projectionFn.call(facet.actor.state);
var spec = Patch.prependAtMeta(proj.assertion, proj.metalevel);
- var compiledSpec = Route.compileProjection(spec);
var objects = Route.projectObjects(eventType === 'asserted'
? e.patch.added
: e.patch.removed,
- compiledSpec);
+ spec);
if (objects && objects.size > 0) {
// console.log(objects.toArray());
if (isTerminal) { facet.terminate(); }
diff --git a/js/src/dataspace.js b/js/src/dataspace.js
index 4466bdd..f9121d1 100644
--- a/js/src/dataspace.js
+++ b/js/src/dataspace.js
@@ -229,11 +229,11 @@ Dataspace.prototype.interpretAction = function (pid, action) {
return true;
case 'message':
- if (Patch.isObserve(action.message)) {
+ if (Patch.observe.isClassOf(action.message)) {
console.warn('Process ' + pid + ' send message containing query', action.message);
}
- if (pid !== 'meta' && Patch.isAtMeta(action.message)) {
- Dataspace.send(action.message[1]);
+ if (pid !== 'meta' && Patch.atMeta.isClassOf(action.message)) {
+ Dataspace.send(action.message.assertion);
} else {
this.mux.routeMessage(action.message).forEach(function (pid) {
self.deliverEvent(pid, action);
diff --git a/js/src/demand-matcher.js b/js/src/demand-matcher.js
index d5bba33..dedb0a5 100644
--- a/js/src/demand-matcher.js
+++ b/js/src/demand-matcher.js
@@ -20,8 +20,10 @@ function DemandMatcher(demandSpec, supplySpec, options) {
this.supplySpec = supplySpec;
this.demandPattern = Route.projectionToPattern(demandSpec);
this.supplyPattern = Route.projectionToPattern(supplySpec);
- this.demandProjection = Route.compileProjection(Patch.prependAtMeta(demandSpec, this.metaLevel));
- this.supplyProjection = Route.compileProjection(Patch.prependAtMeta(supplySpec, this.metaLevel));
+ this.demandProjection = Patch.prependAtMeta(demandSpec, this.metaLevel);
+ this.supplyProjection = Patch.prependAtMeta(supplySpec, this.metaLevel);
+ this.demandProjectionNames = Route.projectionNames(this.demandProjection);
+ this.supplyProjectionNames = Route.projectionNames(this.supplyProjection);
this.currentDemand = Immutable.Set();
this.currentSupply = Immutable.Set();
}
@@ -40,10 +42,12 @@ DemandMatcher.prototype.handleEvent = function (e) {
DemandMatcher.prototype.handlePatch = function (p) {
var self = this;
- var addedDemand = Route.trieKeys(Route.project(p.added, self.demandProjection));
- var removedDemand = Route.trieKeys(Route.project(p.removed, self.demandProjection));
- var addedSupply = Route.trieKeys(Route.project(p.added, self.supplyProjection));
- var removedSupply = Route.trieKeys(Route.project(p.removed, self.supplyProjection));
+ var dN = self.demandProjectionNames.length;
+ var sN = self.supplyProjectionNames.length;
+ var addedDemand = Route.trieKeys(Route.project(p.added, self.demandProjection), dN);
+ var removedDemand = Route.trieKeys(Route.project(p.removed, self.demandProjection), dN);
+ var addedSupply = Route.trieKeys(Route.project(p.added, self.supplyProjection), sN);
+ var removedSupply = Route.trieKeys(Route.project(p.removed, self.supplyProjection), sN);
if (addedDemand === null) {
throw new Error("Syndicate: wildcard demand detected:\n" +
@@ -61,12 +65,12 @@ DemandMatcher.prototype.handlePatch = function (p) {
removedSupply.forEach(function (captures) {
if (self.currentDemand.has(captures)) {
- self.onSupplyDecrease(Route.captureToObject(captures, self.supplyProjection));
+ self.onSupplyDecrease(Route.captureToObject(captures, self.supplyProjectionNames));
}
});
addedDemand.forEach(function (captures) {
if (!self.currentSupply.has(captures)) {
- self.onDemandIncrease(Route.captureToObject(captures, self.demandProjection));
+ self.onDemandIncrease(Route.captureToObject(captures, self.demandProjectionNames));
}
});
diff --git a/js/src/dom-driver.js b/js/src/dom-driver.js
index 6b21767..3ba661c 100644
--- a/js/src/dom-driver.js
+++ b/js/src/dom-driver.js
@@ -1,6 +1,7 @@
// DOM fragment display driver
var Patch = require("./patch.js");
var DemandMatcher = require('./demand-matcher.js').DemandMatcher;
+var Route = require('./route.js');
var Ack = require('./ack.js').Ack;
var Seal = require('./seal.js').Seal;
@@ -9,27 +10,25 @@ var Dataspace = Dataspace_.Dataspace;
var __ = Dataspace_.__;
var _$ = Dataspace_._$;
+var DOM = Route.makeStructureConstructor('DOM', ['selector', 'fragmentClass', 'fragmentSpec']);
+
function spawnDOMDriver(domWrapFunction, jQueryWrapFunction) {
- domWrapFunction = domWrapFunction || defaultWrapFunction;
+ domWrapFunction = domWrapFunction || DOM;
var spec = domWrapFunction(_$('selector'), _$('fragmentClass'), _$('fragmentSpec'));
Dataspace.spawn(
new DemandMatcher(spec,
- Patch.advertise(spec),
+ Patch.advertise(spec), // TODO: are the embedded captures problematic here? If not, why not?
{
onDemandIncrease: function (c) {
Dataspace.spawn(new DOMFragment(c.selector,
- c.fragmentClass,
- c.fragmentSpec,
- domWrapFunction,
- jQueryWrapFunction));
+ c.fragmentClass,
+ c.fragmentSpec,
+ domWrapFunction,
+ jQueryWrapFunction));
}
}));
}
-function defaultWrapFunction(selector, fragmentClass, fragmentSpec) {
- return ["DOM", selector, fragmentClass, fragmentSpec];
-}
-
function DOMFragment(selector, fragmentClass, fragmentSpec, domWrapFunction, jQueryWrapFunction) {
this.selector = selector;
this.fragmentClass = fragmentClass;
@@ -138,4 +137,4 @@ DOMFragment.prototype.buildNodes = function () {
///////////////////////////////////////////////////////////////////////////
module.exports.spawnDOMDriver = spawnDOMDriver;
-module.exports.defaultWrapFunction = defaultWrapFunction;
+module.exports.DOM = DOM;
diff --git a/js/src/jquery-driver.js b/js/src/jquery-driver.js
index aca370f..81f8ca6 100644
--- a/js/src/jquery-driver.js
+++ b/js/src/jquery-driver.js
@@ -1,15 +1,18 @@
// JQuery event driver
var Patch = require("./patch.js");
var DemandMatcher = require('./demand-matcher.js').DemandMatcher;
+var Route = require('./route.js');
var Dataspace_ = require("./dataspace.js");
var Dataspace = Dataspace_.Dataspace;
var __ = Dataspace_.__;
var _$ = Dataspace_._$;
+var jQueryEvent = Route.makeStructureConstructor('jQueryEvent', ['selector', 'eventName', 'eventValue']);
+
function spawnJQueryDriver(baseSelector, metaLevel, wrapFunction) {
metaLevel = metaLevel || 0;
- wrapFunction = wrapFunction || defaultWrapFunction;
+ wrapFunction = wrapFunction || jQueryEvent;
Dataspace.spawn(
new DemandMatcher(Patch.observe(wrapFunction(_$('selector'), _$('eventName'), __)),
Patch.advertise(wrapFunction(_$('selector'), _$('eventName'), __)),
@@ -17,25 +20,21 @@ function spawnJQueryDriver(baseSelector, metaLevel, wrapFunction) {
metaLevel: metaLevel,
onDemandIncrease: function (c) {
Dataspace.spawn(new JQueryEventRouter(baseSelector,
- c.selector,
- c.eventName,
- metaLevel,
- wrapFunction));
+ c.selector,
+ c.eventName,
+ metaLevel,
+ wrapFunction));
}
}));
}
-function defaultWrapFunction(selector, eventName, eventValue) {
- return ["jQuery", selector, eventName, eventValue];
-}
-
function JQueryEventRouter(baseSelector, selector, eventName, metaLevel, wrapFunction) {
var self = this;
this.baseSelector = baseSelector || null;
this.selector = selector;
this.eventName = eventName;
this.metaLevel = metaLevel || 0;
- this.wrapFunction = wrapFunction || defaultWrapFunction;
+ this.wrapFunction = wrapFunction || jQueryEvent;
this.preventDefault = (this.eventName.charAt(0) !== "+");
this.handler =
Dataspace.wrap(function (e) {
@@ -88,4 +87,4 @@ function simplifyDOMEvent(e) {
module.exports.spawnJQueryDriver = spawnJQueryDriver;
module.exports.simplifyDOMEvent = simplifyDOMEvent;
-module.exports.defaultWrapFunction = defaultWrapFunction;
+module.exports.jQueryEvent = jQueryEvent;
diff --git a/js/src/main.js b/js/src/main.js
index 09edd73..71674b1 100644
--- a/js/src/main.js
+++ b/js/src/main.js
@@ -12,7 +12,7 @@ module.exports.Route = require("./route.js");
copyKeys(['__', '_$', '$Capture', '$Special',
'is_emptyTrie', 'emptyTrie',
'embeddedTrie', 'compilePattern',
- 'compileProjection', 'project', 'projectObjects',
+ 'project', 'projectObjects',
'prettyTrie'],
module.exports,
module.exports.Route);
@@ -35,7 +35,6 @@ module.exports.Reflect = require("./reflect.js");
module.exports.Patch = require("./patch.js");
copyKeys(['emptyPatch',
'observe', 'atMeta', 'advertise',
- 'isObserve', 'isAtMeta', 'isAdvertise',
'assert', 'retract', 'sub', 'unsub', 'pub', 'unpub',
'patchSeq'],
module.exports,
diff --git a/js/src/mux.js b/js/src/mux.js
index 70c274c..9089706 100644
--- a/js/src/mux.js
+++ b/js/src/mux.js
@@ -45,12 +45,12 @@ Mux.prototype.updateStream = function (pid, unclampedPatch) {
};
var atMetaEverything = Route.compilePattern(true, Patch.atMeta(Route.__));
-var atMetaBranchKeys = Immutable.List([Route.SOA, Patch.$AtMeta]);
-var onlyMeta = Immutable.Set.of("meta");
+var atMetaBranchKeys = Immutable.List([[Patch.atMeta.meta.arguments.length, Patch.atMeta.meta]]);
+var onlyMeta = Route.trieSuccess(Immutable.Set.of("meta"));
function echoCancelledTrie(t) {
return Route.subtract(t, atMetaEverything, function (v1, v2) {
- return v1.has("meta") ? onlyMeta : null;
+ return v1.has("meta") ? onlyMeta : Route.emptyTrie;
});
}
@@ -95,12 +95,10 @@ function computeEvents(oldMux, newMux, updateStreamResult) {
function computeAffectedPids(routingTable, delta) {
var cover = Route._union(delta.added, delta.removed);
- routingTable = Route.trieStep(routingTable, Route.SOA);
- routingTable = Route.trieStep(routingTable, Patch.$Observe);
+ routingTable =
+ Route.trieStep(routingTable, Patch.observe.meta.arguments.length, Patch.observe.meta);
return Route.matchTrie(cover, routingTable, Immutable.Set(),
- function (v, r, acc) {
- return acc.union(Route.trieStep(r, Route.EOA).value);
- });
+ function (v1, v2, acc) { return acc.union(v2); });
}
Mux.prototype.routeMessage = function (body) {
diff --git a/js/src/patch.js b/js/src/patch.js
index 93ed59f..52680ca 100644
--- a/js/src/patch.js
+++ b/js/src/patch.js
@@ -13,18 +13,11 @@ function Patch(added, removed) {
var emptyPatch = new Patch(Route.emptyTrie, Route.emptyTrie);
var removeEverythingPatch = new Patch(Route.emptyTrie, Route.compilePattern(true, __));
+var trueLabel = Route.trieSuccess(true);
-var $Observe = new Route.$Special("$Observe");
-var $AtMeta = new Route.$Special("$AtMeta");
-var $Advertise = new Route.$Special("$Advertise");
-
-function observe(p) { return [$Observe, p]; }
-function atMeta(p) { return [$AtMeta, p]; }
-function advertise(p) { return [$Advertise, p]; }
-
-function isObserve(p) { return p[0] === $Observe; }
-function isAtMeta(p) { return p[0] === $AtMeta; }
-function isAdvertise(p) { return p[0] === $Advertise; }
+var observe = Route.makeStructureConstructor('observe', ['assertion']);
+var atMeta = Route.makeStructureConstructor('atMeta', ['assertion']);
+var advertise = Route.makeStructureConstructor('advertise', ['assertion']);
function prependAtMeta(p, level) {
while (level--) {
@@ -35,8 +28,8 @@ function prependAtMeta(p, level) {
function stripAtMeta(p, level) {
while (level--) {
- if (p.length === 2 && p[0] === $AtMeta) {
- p = p[1];
+ if (atMeta.isClassOf(p)) {
+ p = p.assertion;
} else {
return null;
}
@@ -115,7 +108,7 @@ Patch.prototype.lift = function () {
Route.compilePattern(true, atMeta(Route.embeddedTrie(this.removed))));
};
-var atMetaProj = Route.compileProjection(atMeta(_$));
+var atMetaProj = atMeta(_$);
Patch.prototype.drop = function () {
return new Patch(Route.project(this.added, atMetaProj),
Route.project(this.removed, atMetaProj));
@@ -132,8 +125,9 @@ Patch.prototype.label = function (labelValue) {
};
Patch.prototype.limit = function (bound) {
- return new Patch(Route.subtract(this.added, bound, function (v1, v2) { return null; }),
- Route.intersect(this.removed, bound, function (v1, v2) { return v1; }));
+ return new Patch(Route.subtract(this.added, bound, function (v1, v2) { return Route.emptyTrie; }),
+ Route.intersect(this.removed, bound,
+ function (v1, v2) { return Route.trieSuccess(v1); }));
};
var metaLabelSet = Immutable.Set(["meta"]);
@@ -143,20 +137,20 @@ Patch.prototype.computeAggregate = function (label, base, removeMeta /* optional
function addCombiner(v1, v2) {
if (removeMeta && Immutable.is(v2, metaLabelSet)) {
- return v1;
+ return Route.trieSuccess(v1);
} else {
- return null;
+ return Route.emptyTrie;
}
}
function removeCombiner(v1, v2) {
if (v2.size === 1) {
- return v1;
+ return Route.trieSuccess(v1);
} else {
if (removeMeta && v2.size === 2 && v2.has("meta")) {
- return v1;
+ return Route.trieSuccess(v1);
} else {
- return null;
+ return Route.emptyTrie;
}
}
}
@@ -167,9 +161,11 @@ Patch.prototype.applyTo = function (base) {
};
Patch.prototype.updateInterests = function (base) {
- return Route._union(Route.subtract(base, this.removed, function (v1, v2) { return null; }),
+ return Route._union(Route.subtract(base,
+ this.removed,
+ function (v1, v2) { return Route.emptyTrie; }),
this.added,
- function (v1, v2) { return true; });
+ function (v1, v2) { return trueLabel; });
};
Patch.prototype.unapplyTo = function (base) {
@@ -180,9 +176,9 @@ Patch.prototype.andThen = function (nextPatch) {
return new Patch(nextPatch.updateInterests(this.added),
Route._union(Route.subtract(this.removed,
nextPatch.added,
- function (v1, v2) { return null; }),
+ function (v1, v2) { return Route.emptyTrie; }),
nextPatch.removed,
- function (v1, v2) { return true; }));
+ function (v1, v2) { return trueLabel; }));
};
function patchSeq(/* patch, patch, ... */) {
@@ -199,11 +195,8 @@ function computePatch(oldBase, newBase) {
}
function biasedIntersection(object, subject) {
- subject = Route.trieStep(subject, Route.SOA);
- subject = Route.trieStep(subject, $Observe);
- return Route.intersect(object, subject,
- function (v1, v2) { return true; },
- function (v, r) { return Route.trieStep(r, Route.EOA); });
+ subject = Route.trieStep(subject, observe.meta.arguments.length, observe.meta);
+ return Route.intersect(object, subject, function (v1, v2) { return Route.trieSuccess(v1); });
}
Patch.prototype.viewFrom = function (interests) {
@@ -240,15 +233,9 @@ module.exports.Patch = Patch;
module.exports.emptyPatch = emptyPatch;
module.exports.removeEverythingPatch = removeEverythingPatch;
-module.exports.$Observe = $Observe;
-module.exports.$AtMeta = $AtMeta;
-module.exports.$Advertise = $Advertise;
module.exports.observe = observe;
module.exports.atMeta = atMeta;
module.exports.advertise = advertise;
-module.exports.isObserve = isObserve;
-module.exports.isAtMeta = isAtMeta;
-module.exports.isAdvertise = isAdvertise;
module.exports.prependAtMeta = prependAtMeta;
module.exports.stripAtMeta = stripAtMeta;
diff --git a/js/src/route.js b/js/src/route.js
index efdb218..c9e109d 100644
--- a/js/src/route.js
+++ b/js/src/route.js
@@ -2,26 +2,45 @@
var Immutable = require("immutable");
+///////////////////////////////////////////////////////////////////////////
+// "Structures": Simple named-tuple-like records.
+// TODO: shore up $SyndicateMeta$, making it a proper object
+
+function instantiateStructure($SyndicateMeta$, argvals) {
+ var result = {"$SyndicateMeta$": $SyndicateMeta$};
+ var argnames = $SyndicateMeta$.arguments;
+ for (var i = 0; i < argnames.length; i++) {
+ result[argnames[i]] = argvals[i];
+ }
+ return result;
+}
+
function makeStructureConstructor(label, argumentNames) {
var $SyndicateMeta$ = {
label: label,
arguments: argumentNames
};
- return function() {
- var result = {"$SyndicateMeta$": $SyndicateMeta$};
- for (var i = 0; i < argumentNames.length; i++) {
- result[argumentNames[i]] = arguments[i];
- }
- return result;
+ var ctor = function() {
+ return instantiateStructure($SyndicateMeta$, arguments);
};
+ ctor.meta = $SyndicateMeta$;
+ ctor.isClassOf = function (v) { return v && v.$SyndicateMeta$ === $SyndicateMeta$; };
+ ctor.pattern = ctor.apply(null, Immutable.Repeat(__, argumentNames.length).toArray());
+ return ctor;
+}
+
+function isSyndicateMeta(m) {
+ // TODO: include more structure in $SyndicateMeta$ objects to make
+ // this judgement less sloppy.
+ return m && m.label && Array.isArray(m.arguments);
}
function isStructure(s) {
return (s !== null) && (typeof s === 'object') && ("$SyndicateMeta$" in s);
}
-function structureToArray(s) {
- var result = [s.$SyndicateMeta$.label];
+function structureToArray(s, excludeLabel) {
+ var result = excludeLabel ? [] : [s.$SyndicateMeta$.label];
var args = s.$SyndicateMeta$.arguments;
for (var i = 0; i < args.length; i++) {
result.push(s[args[i]]);
@@ -29,25 +48,72 @@ function structureToArray(s) {
return result;
}
+///////////////////////////////////////////////////////////////////////////
+// $Special: Builder of singletons.
+
function $Special(name) {
this.name = name;
}
-var __ = new $Special("wildcard"); /* wildcard marker */
-
-var SOA = new $Special("["); // start of array
-var EOA = new $Special("]"); // end of array
+///////////////////////////////////////////////////////////////////////////
+// Misc. utilities
function die(message) {
throw new Error(message);
}
-function $Embedded(trie) {
+///////////////////////////////////////////////////////////////////////////
+// Trie representations
+//
+// A Trie is one of
+// - emptyTrie
+// - an instance of $Success
+// - an instance of $Branch
+
+var emptyTrie = new $Special("Mt");
+
+function is_emptyTrie(m) {
+ return m === emptyTrie;
+}
+
+function $Success(value) {
+ this.value = value;
+}
+
+$Success.prototype.equals = function (other) {
+ if (!(other instanceof $Success)) return false;
+ return Immutable.is(this.value, other.value);
+};
+
+function $Branch(wild, edges, count) {
+ this.wild = wild || emptyTrie; // Trie
+ this.edges = edges || Immutable.Map(); // Map from arity to Map from key-like-thing to Trie
+ // (Sigmas are 0-ary.)
+ this.count = count || 0;
+}
+
+$Branch.prototype.equals = function (other) {
+ if (!(other instanceof $Branch)) return false;
+ return (this.count === other.count) && Immutable.is(this.wild, other.wild) && Immutable.is(this.edges, other.edges);
+};
+
+///////////////////////////////////////////////////////////////////////////
+// Patterns, projections and captures
+
+var __ = new $Special("wildcard"); /* wildcard marker */
+var SOA = new $Special("array"); /* key for start-of-array */
+
+function $Embedded(trie, arrayLength) {
this.trie = trie;
+ this.arrayLength = arrayLength;
}
function embeddedTrie(trie) {
- return new $Embedded(trie);
+ return new $Embedded(trie, null);
+}
+
+function embeddedTrieArray(trie, arrayLength) {
+ return new $Embedded(trie, arrayLength);
}
// The name argument should be a string or null; it defaults to null.
@@ -66,92 +132,129 @@ function isCapture(x) { return x instanceof $Capture || x === _$; }
function captureName(x) { return x instanceof $Capture ? x.name : null; }
function capturePattern(x) { return x instanceof $Capture ? x.pattern : __; }
-var SOC = new $Special("{"); // start of capture
-var EOC = new $Special("}"); // end of capture
-
-function $Success(value) {
- this.value = value;
-}
-
-$Success.prototype.equals = function (other) {
- if (!(other instanceof $Success)) return false;
- return Immutable.is(this.value, other.value);
-};
-
-function $WildcardSequence(trie) {
- this.trie = trie;
-}
-
-$WildcardSequence.prototype.equals = function (other) {
- if (!(other instanceof $WildcardSequence)) return false;
- return Immutable.is(this.trie, other.trie);
-};
-
-function is_emptyTrie(m) {
- return Immutable.is(m, emptyTrie);
-}
-
///////////////////////////////////////////////////////////////////////////
// Constructors
-var emptyTrie = Immutable.Map();
-
function rsuccess(v) {
- return (v === null) ? emptyTrie : new $Success(v);
+ return new $Success(v);
}
-function rseq(e, r) {
- if (r === emptyTrie) return emptyTrie;
- return emptyTrie.set(e, r);
+function rseq(arity, key, r) {
+ if (is_emptyTrie(r)) return emptyTrie;
+ return new $Branch(emptyTrie, Immutable.Map.of(arity, Immutable.Map.of(key, r)), 1);
}
function rwild(r) {
- return rseq(__, r);
+ if (is_emptyTrie(r)) return emptyTrie;
+ return new $Branch(r, Immutable.Map(), 0);
}
-function rwildseq(r) {
- return (r === emptyTrie) ? emptyTrie : new $WildcardSequence(r);
+function rcopybranch(r) {
+ return new $Branch(r.wild, r.edges, r.count);
+}
+
+function prepend_wilds(n, r) {
+ while (n-- > 0) { r = rwild(r); }
+ return r;
+}
+
+// true iff r1 could have been the output of prepend_wilds(n, r2).
+function equal_upto_wilds(n, r1, r2) {
+ while (true) {
+ if (n === 0 || is_emptyTrie(r1)) {
+ return Immutable.is(r1, r2);
+ }
+ if (!(r1 instanceof $Branch) || r1.count > 0) {
+ return false;
+ }
+ n = n - 1;
+ r1 = r1.wild;
+ }
+}
+
+function rupdate_inplace(r, arity, key, k) {
+ if (equal_upto_wilds(arity, k, r.wild)) {
+ var m = r.edges.get(arity);
+ if (!m) return;
+ if (m.has(key)) r.count--;
+ m = m.remove(key);
+ r.edges = m.isEmpty() ? r.edges.remove(arity) : r.edges.set(arity, m);
+ } else {
+ var m = r.edges.get(arity) || Immutable.Map();
+ if (!m.has(key)) r.count++;
+ r.edges = r.edges.set(arity, m.set(key, k));
+ }
+}
+
+function rlookup(r, arity, key) {
+ var m = r.edges.get(arity);
+ m = m && m.get(key);
+ return m || prepend_wilds(arity, r.wild);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+function collapse(r) {
+ if ((r instanceof $Branch) && is_emptyTrie(r.wild) && (r.count === 0)) {
+ return emptyTrie;
+ } else {
+ return r;
+ }
+}
+
+// use with extreme care
+function newEmptyBranch() {
+ return new $Branch(emptyTrie, Immutable.Map(), 0);
+}
+
+var canonicalExpandedEmpty = newEmptyBranch();
+function expand(r) {
+ if (is_emptyTrie(r)) {
+ return canonicalExpandedEmpty;
+ } else {
+ return r;
+ }
}
///////////////////////////////////////////////////////////////////////////
function compilePattern(v, p) {
if (!p) die("compilePattern: missing pattern");
- return walk(p, rseq(EOA, rsuccess(v)));
+ return walk(p, rsuccess(v));
function walk(p, acc) {
if (p === __) return rwild(acc);
if (Array.isArray(p)) {
- acc = rseq(EOA, acc);
for (var i = p.length - 1; i >= 0; i--) {
acc = walk(p[i], acc);
}
- return rseq(SOA, acc);
+ return rseq(p.length, SOA, acc);
}
if (Immutable.List.isList(p)) {
- acc = rseq(EOA, acc);
p.reverse().forEach(function (element) {
acc = walk(element, acc);
});
- return rseq(SOA, acc);
+ return rseq(p.size, SOA, acc);
}
if (isStructure(p)) {
var args = p.$SyndicateMeta$.arguments;
- acc = rseq(EOA, acc);
for (var i = args.length - 1; i >= 0; i--) {
acc = walk(p[args[i]], acc);
}
- acc = rseq(p.$SyndicateMeta$.label, acc);
- return rseq(SOA, acc);
+ return rseq(args.length, p.$SyndicateMeta$, acc);
}
if (p instanceof $Embedded) {
- return appendTrie(p.trie, function (v) { return acc; });
+ acc = appendTrie(p.trie, function (v) { return acc; });
+ if (p.arrayLength !== null) {
+ acc = rseq(p.arrayLength, SOA, acc);
+ }
+ return acc;
} else {
- return rseq(p, acc);
+ return rseq(0, p, acc);
}
}
}
@@ -173,8 +276,13 @@ function matchPattern(v, p) {
if (p === __) return;
- if (isStructure(p)) { p = structureToArray(p); }
- if (isStructure(v)) { v = structureToArray(v); }
+ if (isStructure(p) && isStructure(v) && (p.$SyndicateMeta$ === v.$SyndicateMeta$)) {
+ var args = p.$SyndicateMeta$.arguments;
+ for (var i = 0; i < args.length; i++) {
+ walk(v[args[i]], p[args[i]]);
+ }
+ return;
+ }
if (Array.isArray(p) && Array.isArray(v) && p.length === v.length) {
for (var i = 0; i < p.length; i++) {
@@ -198,126 +306,95 @@ function matchPattern(v, p) {
}
}
-function rupdate(r, key, k) {
- var oldWild = r.get(__, emptyTrie);
- if (Immutable.is(k, oldWild)) {
- return r.remove(key);
- } else {
- return r.set(key, k);
+///////////////////////////////////////////////////////////////////////////
+
+function combine(combineSuccess, leftEmpty, rightEmpty, leftBase, rightBase, r1, r2) {
+ return walk(r1, r2);
+
+ function walk(r1, r2) {
+ if (is_emptyTrie(r1)) return collapse(leftEmpty(r2));
+ if (is_emptyTrie(r2)) return collapse(rightEmpty(r1));
+
+ if ((r1 instanceof $Success) || (r2 instanceof $Success)) {
+ return collapse(combineSuccess(r1, r2));
+ }
+
+ if (!(r1 instanceof $Branch) || !(r2 instanceof $Branch)) {
+ die("Invalid trie given to combine");
+ }
+
+ /* fold-over-keys */
+ var w = walk(r1.wild, r2.wild);
+
+ var acc;
+ if (!is_emptyTrie(r1.wild) && !is_emptyTrie(r2.wild)) {
+ acc = rcopybranch(expand(rwild(w)));
+ var seen = Immutable.Map();
+ r1.edges.forEach(function (keymap, arity) {
+ keymap.forEach(function (r1v, key) {
+ var r2v = rlookup(r2, arity, key);
+ rupdate_inplace(acc, arity, key, walk(r1v, r2v));
+ seen = seen.set(arity, (seen.get(arity) || Immutable.Set()).add(key));
+ });
+ });
+ r2.edges.forEach(function (keymap, arity) {
+ keymap.forEach(function (r2v, key) {
+ var r1v = rlookup(r1, arity, key);
+ var s = seen.get(arity);
+ s = s && s.has(key);
+ if (!s) rupdate_inplace(acc, arity, key, walk(r1v, r2v));
+ });
+ });
+ } else if (!is_emptyTrie(r1.wild) || (is_emptyTrie(r2.wild) && (r1.count > r2.count))) {
+ acc = rcopybranch(expand(leftBase(r1)));
+ acc.wild = w;
+ r2.edges.forEach(function (keymap, arity) {
+ keymap.forEach(function (r2v, key) {
+ var r1v = rlookup(r1, arity, key);
+ rupdate_inplace(acc, arity, key, walk(r1v, r2v));
+ });
+ });
+ } else {
+ acc = rcopybranch(expand(rightBase(r2)));
+ acc.wild = w;
+ r1.edges.forEach(function (keymap, arity) {
+ keymap.forEach(function (r1v, key) {
+ var r2v = rlookup(r2, arity, key);
+ rupdate_inplace(acc, arity, key, walk(r1v, r2v));
+ });
+ });
+ }
+
+ return collapse(acc);
}
}
-function rlookup(r, key) {
- return r.get(key, emptyTrie);
-}
-
-function rlookupWild(r, key) {
- var result = r.get(key, false);
- if (result) return result;
- var wildEdge = rlookup(r, __);
- if (is_keyOpen(key)) return rwildseq(wildEdge);
- if (is_keyClose(key)) return (wildEdge instanceof $WildcardSequence) ? wildEdge.trie : emptyTrie;
- return wildEdge;
-}
-
-function is_keyOpen(k) {
- return k === SOA;
-}
-
-function is_keyClose(k) {
- return k === EOA;
-}
-
-function is_keyNormal(k) {
- return !(is_keyOpen(k) || is_keyClose(k));
+function asymmetricTrieError(r1, r2) {
+ die("Asymmetric tries: " + r1 + ", " + r2);
}
///////////////////////////////////////////////////////////////////////////
var unionSuccessesDefault = function (v1, v2) {
- if (v1 === true) return v2;
- if (v2 === true) return v1;
- return v1.union(v2);
+ return rsuccess(v1.union(v2));
};
-var intersectSuccessesDefault = function (v1, v2) {
- return v1;
-};
-
-var subtractSuccessesDefault = function (v1, v2) {
- var r = v1.subtract(v2);
- if (r.isEmpty()) return null;
- return r;
-};
-
-var matchTrieSuccesses = function (v1, v2, acc) {
- return acc.union(v2);
-};
-
-var projectSuccess = function (v) {
- return v;
-};
-
-///////////////////////////////////////////////////////////////////////////
-
-function expandWildseq(r) {
- return union(rwild(rwildseq(r)), rseq(EOA, r));
-}
-
function union(o1, o2, unionSuccessesOpt) {
var unionSuccesses = unionSuccessesOpt || unionSuccessesDefault;
- return merge(o1, o2);
+ return combine(unionCombiner,
+ function (x) { return x; },
+ function (x) { return x; },
+ function (x) { return x; },
+ function (x) { return x; },
+ o1,
+ o2);
- function merge(o1, o2) {
- if (is_emptyTrie(o1)) return o2;
- if (is_emptyTrie(o2)) return o1;
- return walk(o1, o2);
- }
-
- function walk(r1, r2) {
- if (r1 instanceof $WildcardSequence) {
- if (r2 instanceof $WildcardSequence) {
- return rwildseq(walk(r1.trie, r2.trie));
- }
- r1 = expandWildseq(r1.trie);
- } else if (r2 instanceof $WildcardSequence) {
- r2 = expandWildseq(r2.trie);
- }
-
- if (r1 instanceof $Success) {
- if (r2 instanceof $Success) {
- return rsuccess(unionSuccesses(r1.value, r2.value));
- } else {
- die("Route.union: left short!");
- }
- } else if (r2 instanceof $Success) {
- die("Route.union: right short!");
- }
-
- var w = merge(rlookup(r1, __), rlookup(r2, __));
- var target;
-
- function examineKey(key) {
- if ((key !== __) && !target.has(key)) {
- target = rupdate(target, key, merge(rlookupWild(r1, key), rlookupWild(r2, key)));
- }
- }
-
- if (is_emptyTrie(w)) {
- var smaller = r1.size < r2.size ? r1 : r2;
- var larger = r1.size < r2.size ? r2 : r1;
- target = larger;
- smaller.forEach(function (val, key) {
- var k = merge(rlookup(smaller, key), rlookup(larger, key));
- target = rupdate(target, key, k);
- });
- } else {
- target = rwild(w);
- r1.forEach(function (val, key) { examineKey(key) });
- r2.forEach(function (val, key) { examineKey(key) });
- }
-
- return target;
+ function unionCombiner(r1, r2) {
+ if ((r1 instanceof $Success) && (r2 instanceof $Success))
+ return unionSuccesses(r1.value, r2.value);
+ if (is_emptyTrie(r1)) return r2;
+ if (is_emptyTrie(r2)) return r1;
+ asymmetricTrieError(r1, r2);
}
}
@@ -329,200 +406,71 @@ function unionN() {
return acc;
}
-function intersect(o1, o2, intersectSuccessesOpt, leftShortOpt) {
+///////////////////////////////////////////////////////////////////////////
+
+var intersectSuccessesDefault = unionSuccessesDefault;
+
+function intersect(o1, o2, intersectSuccessesOpt) {
var intersectSuccesses = intersectSuccessesOpt || intersectSuccessesDefault;
- var leftShort = leftShortOpt || function (v, r) {
- die("Route.intersect: left side short!");
- };
- return walk(o1, o2);
+ return combine(intersectCombiner,
+ function (x) { return emptyTrie; },
+ function (x) { return emptyTrie; },
+ function (x) { return emptyTrie; },
+ function (x) { return emptyTrie; },
+ o1,
+ o2);
- function walkFlipped(r2, r1) { return walk(r1, r2); }
-
- function walk(r1, r2) {
- // INVARIANT: r1 is a part of the original o1, and
- // likewise for r2. This is so that the first arg to
- // intersectSuccesses always comes from r1, and the second
- // from r2.
+ function intersectCombiner(r1, r2) {
+ if ((r1 instanceof $Success) && (r2 instanceof $Success))
+ return intersectSuccesses(r1.value, r2.value);
if (is_emptyTrie(r1)) return emptyTrie;
if (is_emptyTrie(r2)) return emptyTrie;
-
- if (r1 instanceof $WildcardSequence) {
- if (r2 instanceof $WildcardSequence) {
- return rwildseq(walk(r1.trie, r2.trie));
- }
- r1 = expandWildseq(r1.trie);
- } else if (r2 instanceof $WildcardSequence) {
- r2 = expandWildseq(r2.trie);
- }
-
- if (r1 instanceof $Success) {
- if (r2 instanceof $Success) {
- return rsuccess(intersectSuccesses(r1.value, r2.value));
- } else {
- return leftShort(r1.value, r2);
- }
- }
-
- var w1 = rlookup(r1, __);
- var w2 = rlookup(r2, __);
- var w = walk(w1, w2);
-
- var target = emptyTrie;
-
- function examineKey(key) {
- if ((key !== __) && !target.has(key)) {
- target = rupdate(target, key, walk(rlookupWild(r1, key), rlookupWild(r2, key)));
- }
- }
-
- if (is_emptyTrie(w1)) {
- if (is_emptyTrie(w2)) {
- (r1.size < r2.size ? r1 : r2).forEach(function (val, key) { examineKey(key) });
- } else {
- r1.forEach(function (val, key) { examineKey(key) });
- }
- } else {
- if (is_emptyTrie(w2)) {
- r2.forEach(function (val, key) { examineKey(key) });
- } else {
- target = rupdate(target, __, w);
- r1.forEach(function (val, key) { examineKey(key) });
- r2.forEach(function (val, key) { examineKey(key) });
- }
- }
- return target;
+ asymmetricTrieError(r1, r2);
}
}
-// The subtractSuccesses function should return null to signal "no
-// remaining success values".
+///////////////////////////////////////////////////////////////////////////
+
+var subtractSuccessesDefault = function (v1, v2) {
+ var r = v1.subtract(v2);
+ if (r.isEmpty()) return emptyTrie;
+ return rsuccess(r);
+};
+
function subtract(o1, o2, subtractSuccessesOpt) {
var subtractSuccesses = subtractSuccessesOpt || subtractSuccessesDefault;
- return walk(o1, o2);
+ return combine(subtractCombiner,
+ function (x) { return emptyTrie; },
+ function (x) { return x; },
+ function (x) { return x; },
+ function (x) { return emptyTrie; },
+ o1,
+ o2);
- function walkFlipped(r2, r1) { return walk(r1, r2); }
-
- function walk(r1, r2) {
- if (is_emptyTrie(r1)) {
- return emptyTrie;
- } else {
- if (is_emptyTrie(r2)) {
- return r1;
- }
- }
-
- if (r1 instanceof $WildcardSequence) {
- if (r2 instanceof $WildcardSequence) {
- return rwildseq(walk(r1.trie, r2.trie));
- }
- r1 = expandWildseq(r1.trie);
- } else if (r2 instanceof $WildcardSequence) {
- r2 = expandWildseq(r2.trie);
- }
-
- if (r1 instanceof $Success && r2 instanceof $Success) {
- return rsuccess(subtractSuccesses(r1.value, r2.value));
- }
-
- var w1 = rlookup(r1, __);
- var w2 = rlookup(r2, __);
- var w = walk(w1, w2);
- var target;
-
- function examineKey(key) {
- if (key !== __) {
- var k1 = rlookupWild(r1, key);
- var k2 = rlookupWild(r2, key);
- var updatedK = walk(k1, k2);
-
- // Here we ensure a "minimal" remainder in cases
- // where after an erasure, a particular key's
- // continuation is the same as the wildcard's
- // continuation. TODO: the equals check may
- // be expensive. If so, how can it be made
- // cheaper?
- if (is_keyOpen(key)) {
- target = rupdate(target, key,
- ((updatedK instanceof $WildcardSequence) &&
- Immutable.is(updatedK.trie, w))
- ? w
- : updatedK);
- } else {
- target = rupdate(target, key, updatedK);
- }
- }
- }
-
- if (is_emptyTrie(w2)) {
- target = r1;
- r2.forEach(function (val, key) { examineKey(key) });
- } else {
- target = emptyTrie;
- target = rupdate(target, __, w);
- r1.forEach(function (val, key) { examineKey(key) });
- r2.forEach(function (val, key) { examineKey(key) });
- }
-
- return collapseWildcardSequences(target);
+ function subtractCombiner(r1, r2) {
+ if ((r1 instanceof $Success) && (r2 instanceof $Success))
+ return subtractSuccesses(r1.value, r2.value);
+ if (is_emptyTrie(r1)) return emptyTrie;
+ if (is_emptyTrie(r2)) return r1;
+ asymmetricTrieError(r1, r2);
}
}
-function collapseWildcardSequences(target) {
- // Here, the target is complete. If it has only two keys,
- // one wild and one is_keyClose, and wild's continuation
- // is a $WildcardSequence and the other continuation is
- // identical to the sequence's continuation, then replace
- // the whole thing with a nested $WildcardSequence.
- // (We know w === rlookup(target, __) from before.)
- //
- // TODO: I suspect actually this applies even if there are
- // more than two keys, so long as all their continuations
- // are identical and there's at least one is_keyClose
- // alongside a wild.
- if (target.size === 2) {
- var finalW = rlookup(target, __);
- if (finalW instanceof $WildcardSequence) {
- target.forEach(function (k, key) {
- if ((key !== __) && is_keyClose(key)) {
- if (Immutable.is(k, finalW.trie)) {
- target = finalW;
- return false; // terminate the iteration early
- }
- }
- });
- }
- }
- return target;
-}
+///////////////////////////////////////////////////////////////////////////
-// Returns null on failed match, otherwise the appropriate success
+// Returns failureResult on failed match, otherwise the appropriate success
// value contained in the trie r.
-function matchValue(r, v) {
- var failureResult = null;
+function matchValue(r, v, failureResultOpt) {
+ var failureResult = failureResultOpt || null;
var vs = Immutable.List.of(v);
- var stack = [Immutable.List()];
while (!is_emptyTrie(r)) {
- if (r instanceof $WildcardSequence) {
- if (stack.length === 0) return failureResult;
- vs = stack.pop();
- r = r.trie;
- continue;
- }
-
if (r instanceof $Success) {
- if (vs.size === 0 && stack.length === 0) return r.value;
- return failureResult;
- }
-
- if (vs.size === 0) {
- if (stack.length === 0) return failureResult;
- vs = stack.pop();
- r = rlookup(r, EOA);
- continue;
+ return vs.isEmpty() ? r.value : failureResult;
}
+ if (vs.isEmpty()) return failureResult;
var v = vs.first();
vs = vs.shift();
@@ -531,124 +479,59 @@ function matchValue(r, v) {
}
if (Array.isArray(v)) {
- if (r.has(SOA)) {
- r = rlookup(r, SOA);
- stack.push(vs);
- vs = Immutable.List(v);
- } else {
- r = rlookup(r, __);
- }
+ r = rlookup(r, v.length, SOA);
+ vs = Immutable.List(v).concat(vs);
} else if (isStructure(v)) {
- if (r.has(SOA)) {
- r = rlookup(r, SOA);
- stack.push(vs);
- vs = Immutable.List.of(v.$SyndicateMeta$.label);
- var args = v.$SyndicateMeta$.arguments;
- for (var i = 0; i < args.length; i++) {
- vs = vs.push(v[args[i]]);
- }
- } else {
- r = rlookup(r, __);
- }
+ r = rlookup(r, v.$SyndicateMeta$.arguments.length, v.$SyndicateMeta$);
+ vs = Immutable.List(structureToArray(v, true)).concat(vs);
} else {
- if (r.has(v)) {
- r = rlookup(r, v);
- } else {
- r = rlookup(r, __);
- }
+ r = rlookup(r, 0, v);
}
}
return failureResult;
}
-function matchTrie(o1, o2, seed, leftShortOpt) {
+function matchTrie(o1, o2, seed, combiner) {
var acc = typeof seed === 'undefined' ? Immutable.Set() : seed; // variable updated imperatively
- var leftShort = leftShortOpt || function (v, r, acc) {
- die("Route.matchTrie: left side short!");
- };
walk(o1, o2);
return acc;
- function walkFlipped(r2, r1) { return walk(r1, r2); }
-
function walk(r1, r2) {
if (is_emptyTrie(r1) || is_emptyTrie(r2)) return;
- if (r1 instanceof $WildcardSequence) {
- if (r2 instanceof $WildcardSequence) {
- walk(r1.trie, r2.trie);
- return;
- }
- r1 = expandWildseq(r1.trie);
- } else if (r2 instanceof $WildcardSequence) {
- r2 = expandWildseq(r2.trie);
- }
-
- if (r1 instanceof $Success) {
- if (r2 instanceof $Success) {
- acc = matchTrieSuccesses(r1.value, r2.value, acc);
- } else {
- acc = leftShort(r1.value, r2, acc);
- }
+ if ((r1 instanceof $Success) && (r2 instanceof $Success)) {
+ acc = combiner(r1.value, r2.value, acc);
return;
- } else if (r2 instanceof $Success) {
- die("Route.matchTrie: right side short!");
}
- var w1 = rlookup(r1, __);
- var w2 = rlookup(r2, __);
- walk(w1, w2);
-
- function examineKey(key) {
- if (key !== __) {
- var k1 = rlookup(r1, key);
- var k2 = rlookup(r2, key);
- if (is_emptyTrie(k1)) {
- if (is_emptyTrie(k2)) {
- return;
- } else {
- walkWild(walk, w1, key, k2);
- }
- } else {
- if (is_emptyTrie(k2)) {
- walkWild(walkFlipped, w2, key, k1);
- } else {
- walk(k1, k2);
- }
- }
- }
+ if (!(r1 instanceof $Branch) || !(r2 instanceof $Branch)) {
+ asymmetricTrieError(r1, r2);
}
- // Optimize similarly to intersect().
- if (is_emptyTrie(w1)) {
- if (is_emptyTrie(w2)) {
- (r1.size < r2.size ? r1 : r2).forEach(function (val, key) { examineKey(key) });
+ walk(r1.wild, r2.wild);
+
+ function examineKeys(keymap, arity) {
+ keymap.forEach(function (_val, key) {
+ walk(rlookup(r1, arity, key), rlookup(r2, arity, key));
+ });
+ }
+
+ if (is_emptyTrie(r1.wild)) {
+ if (is_emptyTrie(r2.wild)) {
+ (r1.count < r2.count ? r1 : r2).edges.forEach(examineKeys);
} else {
- r1.forEach(function (val, key) { examineKey(key) });
+ r1.edges.forEach(examineKeys);
}
} else {
- if (is_emptyTrie(w2)) {
- r2.forEach(function (val, key) { examineKey(key) });
+ if (is_emptyTrie(r2.wild)) {
+ r2.edges.forEach(examineKeys);
} else {
- r1.forEach(function (val, key) { examineKey(key) });
- r2.forEach(function (val, key) { examineKey(key) });
+ r1.edges.forEach(examineKeys);
+ r2.edges.forEach(examineKeys);
}
}
}
-
- function walkWild(walker, w, key, k) {
- if (is_emptyTrie(w)) return;
- if (is_keyOpen(key)) {
- walker(rwildseq(w), k);
- return;
- }
- if (is_keyClose(key)) {
- if (w instanceof $WildcardSequence) walker(w.trie, k);
- return;
- }
- walker(w, k);
- }
}
function appendTrie(m, mTailFn) {
@@ -656,104 +539,79 @@ function appendTrie(m, mTailFn) {
function walk(m) {
if (is_emptyTrie(m)) return emptyTrie;
- if (m instanceof $WildcardSequence) return rwildseq(walk(m.trie));
- if (m instanceof $Success) die("Ill-formed trie");
+ if (m instanceof $Success) return mTailFn(m.value);
- var target = emptyTrie;
- m.forEach(function (k, key) {
- if (is_keyClose(key) && (k instanceof $Success)) {
- target = union(target, mTailFn(k.value));
- } else {
- target = rupdate(target, key, walk(k));
- }
+ var target = newEmptyBranch();
+ target.wild = walk(m.wild);
+ m.edges.forEach(function (keymap, arity) {
+ keymap.forEach(function (k, key) {
+ rupdate_inplace(target, arity, key, walk(k));
+ });
});
- return target;
+ return collapse(target);
}
}
-function triePruneBranch(m, keys) {
- if (keys.isEmpty()) return emptyTrie;
-
- if (is_emptyTrie(m)) return emptyTrie;
- if (m instanceof $WildcardSequence) {
- return collapseWildcardSequences(triePruneBranch(expandWildseq(m.trie), keys));
- }
- if (m instanceof $Success) return m;
-
- var key = keys.first();
- var rest = keys.shift();
- return rupdate(m, key, triePruneBranch(rlookupWild(m, key), rest));
+function triePruneBranch(m, arityKeys) {
+ if (arityKeys.isEmpty()) return emptyTrie;
+ if (!(m instanceof $Branch)) return m;
+ var arityKey = arityKeys.first();
+ var rest = arityKeys.shift();
+ var arity = arityKey[0];
+ var key = arityKey[1];
+ m = rcopybranch(m);
+ rupdate_inplace(m, arity, key, triePruneBranch(rlookup(m, arity, key), rest));
+ return collapse(m);
}
-function trieStep(m, key) {
+function trieStep(m, arity, key) {
+ if (typeof key === 'undefined') {
+ // Cope with API change which would otherwise silently cause problems
+ die("trieStep: missing 'key' argument");
+ }
if (is_emptyTrie(m)) return emptyTrie;
- if (m instanceof $WildcardSequence) return (is_keyClose(key) ? m.trie : m);
if (m instanceof $Success) return emptyTrie;
- return rlookupWild(m, key);
+ return rlookup(m, arity, key);
}
function relabel(m, f) {
- return walk(m);
-
- function walk(m) {
- if (is_emptyTrie(m)) return emptyTrie;
- if (m instanceof $WildcardSequence) return rwildseq(walk(m.trie));
- if (m instanceof $Success) return rsuccess(f(m.value));
-
- var target = emptyTrie;
- m.forEach(function (k, key) {
- target = rupdate(target, key, walk(k));
- });
- return target;
- }
+ return appendTrie(m, function (v) {
+ var v1 = f(v);
+ return v1 ? rsuccess(v1) : emptyTrie;
+ });
}
-function compileProjection(/* projection, projection, ... */) {
+///////////////////////////////////////////////////////////////////////////
+
+function projectionNames(p) {
var names = [];
- var acc = [];
- for (var i = 0; i < arguments.length; i++) {
- walk(arguments[i]);
- }
- acc.push(EOA);
- return {names: names, spec: acc};
+ walk(p);
+ return names;
function walk(p) {
if (isCapture(p)) {
names.push(captureName(p));
- acc.push(SOC);
walk(capturePattern(p));
- acc.push(EOC);
return;
}
if (Array.isArray(p)) {
- acc.push(SOA);
- for (var i = 0; i < p.length; i++) {
- walk(p[i]);
- }
- acc.push(EOA);
+ for (var i = 0; i < p.length; i++) walk(p[i]);
return;
}
if (isStructure(p)) {
- acc.push(SOA);
- acc.push(p.$SyndicateMeta$.label);
var args = p.$SyndicateMeta$.arguments;
- for (var i = 0; i < args.length; i++) {
- walk(p[args[i]]);
- }
- acc.push(EOA);
+ for (var i = 0; i < args.length; i++) walk(p[args[i]]);
return;
}
-
- if (p instanceof $Embedded) {
- die("Cannot embed trie in projection");
- } else {
- acc.push(p);
- }
}
}
+function projectionArity(p) {
+ return projectionNames(p).length;
+}
+
function projectionToPattern(p) {
return walk(p);
@@ -777,253 +635,176 @@ function projectionToPattern(p) {
return result;
}
- if (p instanceof $Embedded) {
- return p.trie;
- } else {
- return p;
- }
+ return p;
}
}
-function project(m, compiledProjection) {
- var spec = compiledProjection.spec;
- return walk(false, m, 0);
+function project(t, wholeSpec, projectSuccessOpt, combinerOpt) {
+ return projectMany(t, Immutable.List.of(wholeSpec), projectSuccessOpt, combinerOpt);
+}
- function walk(isCapturing, m, specIndex) {
- if (specIndex >= spec.length) {
- if (isCapturing) die("Bad specification: unclosed capture");
- if (m instanceof $Success) {
- return rseq(EOA, rsuccess(projectSuccess(m.value)));
- } else {
- return emptyTrie;
- }
+function projectMany(t, wholeSpecs, projectSuccessOpt, combinerOpt) {
+ var projectSuccess = projectSuccessOpt || rsuccess;
+ var combiner = combinerOpt || unionSuccessesDefault;
+
+ return walk(false, t, Immutable.List(wholeSpecs), function (t) {
+ if (t instanceof $Success) {
+ return projectSuccess(t.value);
+ } else {
+ return emptyTrie;
}
+ });
- if (is_emptyTrie(m)) return emptyTrie;
+ function walk(isCapturing, t, specs, kont) {
+ if (specs.isEmpty()) return kont(t);
+ if (!(t instanceof $Branch)) return emptyTrie;
- var item = spec[specIndex];
- var nextIndex = specIndex + 1;
+ var spec = specs.first();
+ var specsRest = specs.rest();
- if (item === EOC) {
- if (!isCapturing) die("Bad specification: unexpected EOC");
- return walk(false, m, nextIndex);
- }
-
- if (item === SOC) {
- if (isCapturing) die("Bad specification: nested capture");
- return walk(true, m, nextIndex);
- }
-
- if (item === __) {
- if (m instanceof $WildcardSequence) {
- if (isCapturing) {
- return rwild(walk(isCapturing, m, nextIndex));
- } else {
- return walk(isCapturing, m, nextIndex);
- }
- }
-
- if (m instanceof $Success) {
- return emptyTrie;
- }
-
- var target;
+ if (isCapture(spec)) {
if (isCapturing) {
- target = emptyTrie;
- target = rupdate(target, __, walk(isCapturing, rlookup(m, __), nextIndex));
- m.forEach(function (mk, key) {
- if (key !== __) {
- if (is_keyOpen(key)) {
- target = rupdate(target, key, captureNested(mk, function (mk2) {
- return walk(isCapturing, mk2, nextIndex);
- }));
- } else if (is_keyClose(key)) {
- // do nothing
- } else {
- target = rupdate(target, key, walk(isCapturing, mk, nextIndex));
- }
- }
- });
+ die("projectMany: nested capture in projection: " + wholeSpecs);
+ }
+ return walk(true, t, Immutable.List.of(capturePattern(spec)), function (intermediate) {
+ return walk(false, intermediate, specsRest, kont);
+ });
+ }
+
+ if (spec === __) {
+ if (isCapturing) {
+ var target = newEmptyBranch();
+ target.wild = walk(isCapturing, t.wild, specsRest, kont);
+ t.edges.forEach(function (keymap, arity) {
+ var innerSpecs = Immutable.Repeat(__, arity);
+ keymap.forEach(function (k, key) {
+ rupdate_inplace(target, arity, key,
+ walk(isCapturing, k, innerSpecs, function (intermediate) {
+ return walk(isCapturing, intermediate, specsRest, kont);
+ }));
+ });
+ });
+ return collapse(target);
} else {
- target = walk(isCapturing, rlookup(m, __), nextIndex);
- m.forEach(function (mk, key) {
- if (key !== __) {
- if (is_keyOpen(key)) {
- target = union(target, skipNested(mk, function (mk2) {
- return walk(isCapturing, mk2, nextIndex);
- }));
- } else if (is_keyClose(key)) {
- // do nothing
- } else {
- target = union(target, walk(isCapturing, mk, nextIndex));
- }
- }
- });
+ var seed = walk(isCapturing, t.wild, specsRest, kont);
+ t.edges.forEach(function (keymap, arity) {
+ var innerSpecs = Immutable.Repeat(__, arity);
+ keymap.forEach(function (k, key) {
+ seed = union(seed,
+ walk(isCapturing, k, innerSpecs, function (intermediate) {
+ return walk(isCapturing, intermediate, specsRest, kont);
+ }),
+ combiner);
+ });
+ });
+ return seed;
}
return target;
}
- var result;
- if (m instanceof $WildcardSequence) {
- if (is_keyOpen(item)) {
- result = walk(isCapturing, rwildseq(m), nextIndex);
- } else if (is_keyClose(item)) {
- result = walk(isCapturing, m.trie, nextIndex);
- } else {
- result = walk(isCapturing, m, nextIndex);
- }
- } else if (m instanceof $Success) {
- result = emptyTrie;
- } else {
- if (is_keyOpen(item)) {
- result = walk(isCapturing, rwildseq(rlookup(m, __)), nextIndex);
- } else if (is_keyClose(item)) {
- result = emptyTrie;
- } else {
- result = walk(isCapturing, rlookup(m, __), nextIndex);
- }
- result = union(result, walk(isCapturing, rlookup(m, item), nextIndex));
+ if (isStructure(spec)) {
+ var arity = spec.$SyndicateMeta$.arguments.length;
+ var key = spec.$SyndicateMeta$;
+ var intermediate = walk(isCapturing,
+ rlookup(t, arity, key),
+ Immutable.List(structureToArray(spec, true)),
+ function (intermediate) {
+ return walk(isCapturing, intermediate, specsRest, kont);
+ });
+ return isCapturing ? rseq(arity, key, intermediate) : intermediate;
}
- if (isCapturing) {
- result = rseq(item, result);
+
+ if (Array.isArray(spec)) {
+ var intermediate = walk(isCapturing,
+ rlookup(t, spec.length, SOA),
+ Immutable.List(spec),
+ function (intermediate) {
+ return walk(isCapturing, intermediate, specsRest, kont);
+ });
+ return isCapturing ? rseq(spec.length, SOA, intermediate) : intermediate;
}
+
+ if (spec instanceof $Embedded) {
+ die("$Embedded patterns not supported in projectMany()");
+ }
+
+ /* It is a normal atom */
+ var intermediate = walk(isCapturing, rlookup(t, 0, spec), specsRest, kont);
+ return isCapturing ? rseq(0, spec, intermediate) : intermediate;
+ }
+}
+
+function reconstructSequence(key, items) {
+ if (key === SOA) {
+ return items.toArray();
+ } else {
+ return instantiateStructure(key, items);
+ }
+}
+
+function trieKeys(m, takeCount0) {
+ if (typeof takeCount0 !== 'number') {
+ // Cope with API change which would otherwise silently cause problems
+ die("Missing mandatory argument 'takeCount' to Route.trieKeys");
+ }
+
+ if (is_emptyTrie(m)) return Immutable.Set();
+ return walk(m, takeCount0, Immutable.List(),
+ function (items, tail) {
+ if (is_emptyTrie(tail)) return Immutable.Set();
+ if (tail instanceof $Success) return Immutable.Set.of(items);
+ die("Trie contains more than the requested "+takeCount0+" items");
+ });
+
+ function walk(m, takeCount, valsRev, kont) {
+ if (takeCount === 0) return kont(valsRev.reverse(), m);
+
+ if (is_emptyTrie(m)) return Immutable.Set();
+ if (m instanceof $Success) {
+ die("Trie contains fewer than the requested "+takeCount0+" items");
+ }
+
+ if (!is_emptyTrie(m.wild)) return false;
+
+ var result = Immutable.Set();
+ m.edges.forEach(function (keymap, arity) {
+ if (result === false) return false; // break out of iteration
+ keymap.forEach(function (k, key) {
+ if (result === false) return false; // break out of iteration
+
+ var piece;
+ if (isSyndicateMeta(key) || key === SOA) { // TODO: this is sloppy
+ piece = walk(k, arity, Immutable.List(), function (items, m1) {
+ var item = reconstructSequence(key, items);
+ return walk(m1, takeCount - 1, valsRev.unshift(item), kont);
+ });
+ } else {
+ piece = walk(k, takeCount - 1, valsRev.unshift(key), kont);
+ }
+
+ result = (piece === false) ? false : result.union(piece);
+ });
+ });
return result;
}
-
- function captureNested(m, cont) {
- if (m instanceof $WildcardSequence) {
- return rwildseq(cont(m.trie));
- }
-
- if (is_emptyTrie(m) || (m instanceof $Success)) {
- return emptyTrie;
- }
-
- var target = emptyTrie;
- target = rupdate(target, __, captureNested(rlookup(m, __), cont));
- m.forEach(function (mk, key) {
- if (key !== __) {
- if (is_keyOpen(key)) {
- target = rupdate(target, key, captureNested(mk, function (mk2) {
- return captureNested(mk2, cont);
- }));
- } else if (is_keyClose(key)) {
- target = rupdate(target, key, cont(mk));
- } else {
- target = rupdate(target, key, captureNested(mk, cont));
- }
- }
- });
- return target;
- }
-
- function skipNested(m, cont) {
- if (m instanceof $WildcardSequence) {
- return cont(m.trie);
- }
-
- if (is_emptyTrie(m) || (m instanceof $Success)) {
- return emptyTrie;
- }
-
- var target = skipNested(rlookup(m, __), cont);
- m.forEach(function (mk, key) {
- if (key !== __) {
- if (is_keyOpen(key)) {
- target = union(target, skipNested(mk, function (mk2) {
- return skipNested(mk2, cont)
- }));
- } else if (is_keyClose(key)) {
- target = union(target, cont(mk));
- } else {
- target = union(target, skipNested(mk, cont));
- }
- }
- });
- return target;
- }
}
-function trieKeys(m) {
- if (is_emptyTrie(m)) return Immutable.Set();
- var result = walkSeq(m, function (vss, vsk) { return vss; });
- if (result === null) return null;
- return Immutable.Set(result);
-
- function walk(m, k) {
- if (m instanceof $WildcardSequence) return null;
- if (m instanceof $Success) return [];
- if (m.has(__)) return null;
- var acc = [];
- m.forEach(function (mk, key) {
- var piece;
- if (is_keyOpen(key)) {
- piece = walkSeq(mk, function (vss, vsk) {
- var acc = [];
- for (var i = 0; i < vss.length; i++) {
- var vs = vss[i];
- acc = acc.concat(k(transformSeqs(vs, key), vsk));
- }
- return acc;
- });
- } else if (is_keyClose(key)) {
- die("trieKeys: internal error: unexpected key-close");
- } else {
- piece = k(key, mk);
- }
- if (piece === null) return null;
- acc = acc.concat(piece);
- });
- return acc;
- }
-
- function walkSeq(m, k) {
- if (m instanceof $WildcardSequence) return null;
- if (m instanceof $Success) return k([], emptyTrie); // TODO: ??
- if (m.has(__)) return null;
- var acc = [];
- m.forEach(function (mk, key) {
- var piece;
- if (is_keyClose(key)) {
- piece = k([Immutable.List()], mk);
- } else {
- piece = walk(rseq(key, mk), function (v, vk) {
- return walkSeq(vk, function (vss, vsk) {
- var acc = [];
- for (var i = 0; i < vss.length; i++) {
- acc.push(vss[i].unshift(v));
- }
- return k(acc, vsk);
- });
- });
- }
- if (piece === null) return null;
- acc = acc.concat(piece);
- });
- return acc;
- }
-
- function transformSeqs(vs, opener) {
- if (opener === SOA) return vs;
- die("Internal error: unknown opener " + opener);
- }
-}
-
-function captureToObject(captures, compiledProjection) {
+function captureToObject(captures, names) {
var d = {};
captures.forEach(function (key, index) {
- d[compiledProjection.names[index] || ('$' + index)] = key;
+ d[names[index] || ('$' + index)] = key;
});
return d;
}
-function trieKeysToObjects(trieKeysResult, compiledProjection) {
- if (trieKeysResult === null) return null;
- return trieKeysResult.toList().map(function (e) { return captureToObject(e, compiledProjection); });
+function trieKeysToObjects(trieKeysResult, names) {
+ if (trieKeysResult === false) return false;
+ return trieKeysResult.toList().map(function (e) { return captureToObject(e, names); });
}
-function projectObjects(m, compiledProjection) {
- return trieKeysToObjects(trieKeys(project(m, compiledProjection)), compiledProjection);
+function projectObjects(m, projection) {
+ var names = projectionNames(projection);
+ return trieKeysToObjects(trieKeys(project(m, projection), names.length), names);
}
function prettyTrie(m, initialIndent) {
@@ -1032,41 +813,49 @@ function prettyTrie(m, initialIndent) {
return acc.join('');
function walk(i, m) {
- if (m instanceof $WildcardSequence) {
- acc.push("...>");
- walk(i + 4, m.trie);
- return;
- }
if (m instanceof $Success) {
var v = m.value;
if (Immutable.Set.isSet(v)) { v = v.toArray(); }
- acc.push("{" + JSON.stringify(v) + "}");
+ acc.push(" {" + JSON.stringify(v) + "}");
return;
}
- if (m.size === 0) {
- acc.push("::: nothing");
+ if (is_emptyTrie(m)) {
+ acc.push(" ::: nothing");
return;
}
var needSep = false;
- m.toOrderedMap()
- .sortBy(function (k, key) { return key })
- .forEach(function (k, key) {
- if (needSep) {
- acc.push("\n");
- acc.push(indentStr(i));
- } else {
- needSep = true;
- }
- acc.push(" ");
- if (key === __) key = '★';
- else if (key === SOA) key = '<';
- else if (key === EOA) key = '>';
- else if (key instanceof $Special) key = key.name;
- else key = JSON.stringify(key);
- acc.push(key);
- walk(i + key.length + 1, k);
+ if (!is_emptyTrie(m.wild)) {
+ var key = "★";
+ needSep = true;
+ acc.push(" ");
+ acc.push(key);
+ walk(i + key.length + 1, m.wild);
+ }
+ m.edges
+ .toOrderedMap()
+ .sortBy(function (keymap, arity) { return arity })
+ .forEach(function (keymap, arity) {
+ keymap
+ .toOrderedMap()
+ .sortBy(function (k, key) { return key })
+ .forEach(function (k, key) {
+ if (needSep) {
+ acc.push("\n");
+ acc.push(indentStr(i));
+ } else {
+ needSep = true;
+ }
+ acc.push(" ");
+ if (key === SOA) key = '<' + arity + '>';
+ else if (isSyndicateMeta(key)) key = key.label + '<' + arity + '>';
+ else if (key instanceof $Special) key = key.name;
+ else if (typeof key === 'undefined') key = 'undefined';
+ else key = JSON.stringify(key);
+ acc.push(key);
+ walk(i + key.length + 1, k);
+ });
});
}
@@ -1079,7 +868,6 @@ function prettyTrie(m, initialIndent) {
module.exports.__ = __;
module.exports.SOA = SOA;
-module.exports.EOA = EOA;
module.exports.$Capture = $Capture;
module.exports.$Special = $Special;
module.exports.makeStructureConstructor = makeStructureConstructor;
@@ -1087,6 +875,7 @@ module.exports._$ = _$;
module.exports.is_emptyTrie = is_emptyTrie;
module.exports.emptyTrie = emptyTrie;
module.exports.embeddedTrie = embeddedTrie;
+module.exports.embeddedTrieArray = embeddedTrieArray;
module.exports.compilePattern = compilePattern;
module.exports.matchPattern = matchPattern;
module.exports._union = union;
@@ -1098,10 +887,13 @@ module.exports.matchTrie = matchTrie;
module.exports.appendTrie = appendTrie;
module.exports.triePruneBranch = triePruneBranch;
module.exports.trieStep = trieStep;
+module.exports.trieSuccess = rsuccess;
module.exports.relabel = relabel;
-module.exports.compileProjection = compileProjection;
+module.exports.projectionNames = projectionNames;
+module.exports.projectionArity = projectionArity;
module.exports.projectionToPattern = projectionToPattern;
module.exports.project = project;
+module.exports.projectMany = projectMany;
module.exports.trieKeys = trieKeys;
module.exports.captureToObject = captureToObject;
module.exports.trieKeysToObjects = trieKeysToObjects;
@@ -1112,6 +904,5 @@ module.exports.prettyTrie = prettyTrie;
module.exports._testing = {
rsuccess: rsuccess,
rseq: rseq,
- rwild: rwild,
- rwildseq: rwildseq
+ rwild: rwild
};
diff --git a/js/test/test-mux.js b/js/test/test-mux.js
index 24eb348..23ed3cd 100644
--- a/js/test/test-mux.js
+++ b/js/test/test-mux.js
@@ -32,13 +32,13 @@ describe('mux stream', function () {
describe('addition', function () {
it('should union interests appropriately', function () {
var m = getM();
- checkPrettyTrie(m.routingTable, [' 1 >{[0]}',
- ' 2 >{[0,1]}',
- ' 3 >{[1]}']);
- checkPrettyTrie(m.interestsOf(0), [' 1 >{[0]}',
- ' 2 >{[0]}']);
- checkPrettyTrie(m.interestsOf(1), [' 2 >{[1]}',
- ' 3 >{[1]}']);
+ checkPrettyTrie(m.routingTable, [' 1 {[0]}',
+ ' 2 {[0,1]}',
+ ' 3 {[1]}']);
+ checkPrettyTrie(m.interestsOf(0), [' 1 {[0]}',
+ ' 2 {[0]}']);
+ checkPrettyTrie(m.interestsOf(1), [' 2 {[1]}',
+ ' 3 {[1]}']);
});
});
@@ -51,30 +51,30 @@ describe('mux stream', function () {
.andThen(Patch.assert(4))
.andThen(Patch.retract(99));
checkPrettyPatch(rawPatch,
- [' 1 >{true}',
- ' 4 >{true}'],
- [' 2 >{true}',
- ' 3 >{true}',
- ' 99 >{true}']);
+ [' 1 {true}',
+ ' 4 {true}'],
+ [' 2 {true}',
+ ' 3 {true}',
+ ' 99 {true}']);
var m = getM();
var updateStreamResult = m.updateStream(1, rawPatch);
expect(updateStreamResult.pid).to.equal(1);
checkPrettyPatch(updateStreamResult.delta,
- [' 1 >{[1]}',
- ' 4 >{[1]}'],
- [' 2 >{[1]}',
- ' 3 >{[1]}']);
- checkPrettyTrie(m.routingTable, [' 1 >{[0,1]}',
- ' 2 >{[0]}',
- ' 4 >{[1]}']);
- checkPrettyTrie(m.interestsOf(0), [' 1 >{[0]}',
- ' 2 >{[0]}']);
- checkPrettyTrie(m.interestsOf(1), [' 1 >{[1]}',
- ' 4 >{[1]}']);
+ [' 1 {[1]}',
+ ' 4 {[1]}'],
+ [' 2 {[1]}',
+ ' 3 {[1]}']);
+ checkPrettyTrie(m.routingTable, [' 1 {[0,1]}',
+ ' 2 {[0]}',
+ ' 4 {[1]}']);
+ checkPrettyTrie(m.interestsOf(0), [' 1 {[0]}',
+ ' 2 {[0]}']);
+ checkPrettyTrie(m.interestsOf(1), [' 1 {[1]}',
+ ' 4 {[1]}']);
checkPrettyPatch(updateStreamResult.deltaAggregate,
- [' 4 >{[1]}'],
- [' 3 >{[1]}']);
+ [' 4 {[1]}'],
+ [' 3 {[1]}']);
});
});
@@ -84,17 +84,17 @@ describe('mux stream', function () {
var updateStreamResult = m.removeStream(1);
expect(updateStreamResult.pid).to.equal(1);
checkPrettyPatch(updateStreamResult.delta,
- ['::: nothing'],
- [' 2 >{[1]}',
- ' 3 >{[1]}']);
- checkPrettyTrie(m.routingTable, [' 1 >{[0]}',
- ' 2 >{[0]}']);
- checkPrettyTrie(m.interestsOf(0), [' 1 >{[0]}',
- ' 2 >{[0]}']);
- checkPrettyTrie(m.interestsOf(1), ['::: nothing']);
+ [' ::: nothing'],
+ [' 2 {[1]}',
+ ' 3 {[1]}']);
+ checkPrettyTrie(m.routingTable, [' 1 {[0]}',
+ ' 2 {[0]}']);
+ checkPrettyTrie(m.interestsOf(0), [' 1 {[0]}',
+ ' 2 {[0]}']);
+ checkPrettyTrie(m.interestsOf(1), [' ::: nothing']);
checkPrettyPatch(updateStreamResult.deltaAggregate,
- ['::: nothing'],
- [' 3 >{[1]}']);
+ [' ::: nothing'],
+ [' 3 {[1]}']);
});
});
});
diff --git a/js/test/test-patch.js b/js/test/test-patch.js
index e622487..9c06d70 100644
--- a/js/test/test-patch.js
+++ b/js/test/test-patch.js
@@ -19,76 +19,76 @@ function checkPrettyPatch(p, expectedAdded, expectedRemoved) {
describe('basic patch compilation', function () {
it('should print as expected', function () {
checkPrettyPatch(Patch.assert([1, 2]),
- [' < 1 2 > >{true}'],
- ['::: nothing']);
+ [' <2> 1 2 {true}'],
+ [' ::: nothing']);
checkPrettyPatch(Patch.assert(__),
- [' ★ >{true}'],
- ['::: nothing']);
+ [' ★ {true}'],
+ [' ::: nothing']);
checkPrettyPatch(Patch.sub(__),
- [' < $Observe ★ > >{true}'],
- ['::: nothing']);
+ [' observe<1> ★ {true}'],
+ [' ::: nothing']);
checkPrettyPatch(Patch.sub([1, 2]),
- [' < $Observe < 1 2 > > >{true}'],
- ['::: nothing']);
+ [' observe<1> <2> 1 2 {true}'],
+ [' ::: nothing']);
checkPrettyPatch(Patch.pub('x'),
- [' < $Advertise "x" > >{true}'],
- ['::: nothing']);
+ [' advertise<1> "x" {true}'],
+ [' ::: nothing']);
});
it('should work at nonzero metalevel', function () {
checkPrettyPatch(Patch.assert([1, 2], 0),
- [' < 1 2 > >{true}'],
- ['::: nothing']);
+ [' <2> 1 2 {true}'],
+ [' ::: nothing']);
checkPrettyPatch(Patch.assert([1, 2], 1),
- [' < $AtMeta < 1 2 > > >{true}'],
- ['::: nothing']);
+ [' atMeta<1> <2> 1 2 {true}'],
+ [' ::: nothing']);
checkPrettyPatch(Patch.assert([1, 2], 2),
- [' < $AtMeta < $AtMeta < 1 2 > > > >{true}'],
- ['::: nothing']);
+ [' atMeta<1> atMeta<1> <2> 1 2 {true}'],
+ [' ::: nothing']);
checkPrettyPatch(Patch.sub([1, 2], 0),
- [' < $Observe < 1 2 > > >{true}'],
- ['::: nothing']);
+ [' observe<1> <2> 1 2 {true}'],
+ [' ::: nothing']);
checkPrettyPatch(Patch.sub([1, 2], 1),
- [' < $AtMeta < $Observe < 1 2 > > > >{true}',
- ' $Observe < $AtMeta < 1 2 > > > >{true}'],
- ['::: nothing']);
+ [' atMeta<1> observe<1> <2> 1 2 {true}',
+ ' observe<1> atMeta<1> <2> 1 2 {true}'],
+ [' ::: nothing']);
checkPrettyPatch(Patch.sub([1, 2], 2),
- [' < $AtMeta < $AtMeta < $Observe < 1 2 > > > > >{true}',
- ' $Observe < $AtMeta < 1 2 > > > > >{true}',
- ' $Observe < $AtMeta < $AtMeta < 1 2 > > > > >{true}'],
- ['::: nothing']);
+ [' atMeta<1> atMeta<1> observe<1> <2> 1 2 {true}',
+ ' observe<1> atMeta<1> <2> 1 2 {true}',
+ ' observe<1> atMeta<1> atMeta<1> <2> 1 2 {true}'],
+ [' ::: nothing']);
});
});
describe('patch sequencing', function () {
it('should do the right thing in simple cases', function () {
checkPrettyPatch(Patch.assert(__).andThen(Patch.retract(3)),
- [' ★ >{true}',
- ' 3::: nothing'],
- [' 3 >{true}']);
+ [' ★ {true}',
+ ' 3 ::: nothing'],
+ [' 3 {true}']);
checkPrettyPatch(Patch.assert(3).andThen(Patch.retract(__)),
- ['::: nothing'],
- [' ★ >{true}']);
+ [' ::: nothing'],
+ [' ★ {true}']);
checkPrettyPatch(Patch.assert(__).andThen(Patch.retract(__)),
- ['::: nothing'],
- [' ★ >{true}']);
+ [' ::: nothing'],
+ [' ★ {true}']);
checkPrettyPatch(Patch.assert(3).andThen(Patch.retract(3)),
- ['::: nothing'],
- [' 3 >{true}']);
+ [' ::: nothing'],
+ [' 3 {true}']);
checkPrettyPatch(Patch.sub([1, __]).andThen(Patch.unsub([1, 2])),
- [' < $Observe < 1 ★ > > >{true}',
- ' 2::: nothing'],
- [' < $Observe < 1 2 > > >{true}']);
+ [' observe<1> <2> 1 ★ {true}',
+ ' 2 ::: nothing'],
+ [' observe<1> <2> 1 2 {true}']);
checkPrettyPatch(Patch.sub([__, 2]).andThen(Patch.unsub([1, 2])),
- [' < $Observe < ★ 2 > > >{true}',
- ' 1::: nothing'],
- [' < $Observe < 1 2 > > >{true}']);
+ [' observe<1> <2> ★ 2 {true}',
+ ' 1 ::: nothing'],
+ [' observe<1> <2> 1 2 {true}']);
checkPrettyPatch(Patch.sub([__, __]).andThen(Patch.unsub([1, 2])),
- [' < $Observe < ★ ★ > > >{true}',
- ' 1 ★ > > >{true}',
- ' 2::: nothing'],
- [' < $Observe < 1 2 > > >{true}']);
+ [' observe<1> <2> ★ ★ {true}',
+ ' 1 ★ {true}',
+ ' 2 ::: nothing'],
+ [' observe<1> <2> 1 2 {true}']);
});
it('works for longer chains of asserts and retracts', function () {
@@ -99,11 +99,11 @@ describe('patch sequencing', function () {
.andThen(Patch.assert(4))
.andThen(Patch.retract(99));
checkPrettyPatch(rawPatch,
- [' 1 >{true}',
- ' 4 >{true}'],
- [' 2 >{true}',
- ' 3 >{true}',
- ' 99 >{true}']);
+ [' 1 {true}',
+ ' 4 {true}'],
+ [' 2 {true}',
+ ' 3 {true}',
+ ' 99 {true}']);
});
});
@@ -111,31 +111,31 @@ describe('patch sequencing', function () {
describe('patch lifting', function () {
it('should basically work', function () {
checkPrettyPatch(Patch.assert([1, 2]).lift(),
- [' < $AtMeta < 1 2 > > >{true}'],
- ['::: nothing']);
+ [' atMeta<1> <2> 1 2 {true}'],
+ [' ::: nothing']);
checkPrettyPatch(Patch.sub([1, 2]).lift(),
- [' < $AtMeta < $Observe < 1 2 > > > >{true}'],
- ['::: nothing']);
+ [' atMeta<1> observe<1> <2> 1 2 {true}'],
+ [' ::: nothing']);
checkPrettyPatch(Patch.assert([1, 2]).andThen(Patch.assert(Patch.atMeta([1, 2]))).lift(),
- [' < $AtMeta < $AtMeta < 1 2 > > > >{true}',
- ' 1 2 > > >{true}'],
- ['::: nothing']);
+ [' atMeta<1> atMeta<1> <2> 1 2 {true}',
+ ' <2> 1 2 {true}'],
+ [' ::: nothing']);
});
});
describe('patch dropping', function () {
it('should basically work', function () {
checkPrettyPatch(Patch.assert([1, 2]).drop(),
- ['::: nothing'],
- ['::: nothing']);
+ [' ::: nothing'],
+ [' ::: nothing']);
checkPrettyPatch(Patch.sub([1, 2]).drop(),
- ['::: nothing'],
- ['::: nothing']);
+ [' ::: nothing'],
+ [' ::: nothing']);
checkPrettyPatch(Patch.sub([1, 2], 1).drop(),
- [' < $Observe < 1 2 > > >{true}'],
- ['::: nothing']);
+ [' observe<1> <2> 1 2 {true}'],
+ [' ::: nothing']);
checkPrettyPatch(Patch.assert([1, 2]).andThen(Patch.assert(Patch.atMeta([1, 2]))).drop(),
- [' < 1 2 > >{true}'],
- ['::: nothing']);
+ [' <2> 1 2 {true}'],
+ [' ::: nothing']);
});
});
diff --git a/js/test/test-route.js b/js/test/test-route.js
index 569a6ca..0776d4f 100644
--- a/js/test/test-route.js
+++ b/js/test/test-route.js
@@ -10,7 +10,20 @@ function checkPrettyTrie(m, expected) {
}
function checkTrieKeys(actual, expected) {
- expect(actual.equals(Immutable.Set(expected).map(Immutable.List))).to.be(true);
+ expect(Immutable.is(actual, Immutable.Set(expected).map(Immutable.List))).to.be(true);
+}
+
+function checkProjectedObjects(actual, expected) {
+ actual = Immutable.Set(actual);
+ expected = Immutable.Set(expected);
+ actual.forEach(function (e) { c(expected, e) });
+ expected.forEach(function (e) { c(actual, e) });
+ function c(s, e1) {
+ if (!s.find(function (e2) { return expect.eql(e1, e2) })) {
+ throw new Error("Comparison failed: actual " + JSON.stringify(actual.toArray()) +
+ "; expected " + JSON.stringify(expected.toArray()));
+ }
+ }
}
describe("basic pattern compilation", function () {
@@ -20,8 +33,8 @@ describe("basic pattern compilation", function () {
var mAAny = r.compilePattern(sAAny, ['A', r.__]);
it("should print as expected", function () {
- checkPrettyTrie(mAny, [' ★ >{["mAny"]}']);
- checkPrettyTrie(mAAny, [' < "A" ★ > >{["mAAny"]}']);
+ checkPrettyTrie(mAny, [' ★ {["mAny"]}']);
+ checkPrettyTrie(mAAny, [' <2> "A" ★ {["mAAny"]}']);
});
describe("of wildcard", function () {
@@ -53,81 +66,78 @@ describe("unions", function () {
it("should collapse common prefix wildcard", function () {
checkPrettyTrie(r.union(r.compilePattern(Immutable.Set(['A']), [r.__, 'A']),
r.compilePattern(Immutable.Set(['B']), [r.__, 'B'])),
- [' < ★ "A" > >{["A"]}',
- ' "B" > >{["B"]}']);
+ [' <2> ★ "A" {["A"]}',
+ ' "B" {["B"]}']);
});
it("should unroll wildcard unioned with nonwildcard", function () {
checkPrettyTrie(r.union(r.compilePattern(Immutable.Set(['A']), [r.__, 'A']),
r.compilePattern(Immutable.Set(['W']), r.__)),
- [' ★ >{["W"]}',
- ' < ★ "A" ★...> >{["W"]}',
- ' > >{["W","A"]}',
- ' ★...> >{["W"]}',
- ' > >{["W"]}',
- ' > >{["W"]}']);
+ [' ★ {["W"]}',
+ ' <2> ★ ★ {["W"]}',
+ ' "A" {["A","W"]}']);
});
it("should properly multiply out", function () {
checkPrettyTrie(r.union(r.compilePattern(Immutable.Set(['A']), [r.__, 2]),
r.compilePattern(Immutable.Set(['C']), [1, 3]),
r.compilePattern(Immutable.Set(['B']), [3, 4])),
- [' < ★ 2 > >{["A"]}',
- ' 1 2 > >{["A"]}',
- ' 3 > >{["C"]}',
- ' 3 2 > >{["A"]}',
- ' 4 > >{["B"]}']);
+ [' <2> ★ 2 {["A"]}',
+ ' 1 2 {["A"]}',
+ ' 3 {["C"]}',
+ ' 3 2 {["A"]}',
+ ' 4 {["B"]}']);
checkPrettyTrie(r.union(r.compilePattern(Immutable.Set(['C']), [1, 3]),
r.compilePattern(Immutable.Set(['B']), [3, 4])),
- [' < 1 3 > >{["C"]}',
- ' 3 4 > >{["B"]}']);
+ [' <2> 1 3 {["C"]}',
+ ' 3 4 {["B"]}']);
checkPrettyTrie(r.union(r.compilePattern(Immutable.Set(['A']), [r.__, 2]),
r.compilePattern(Immutable.Set(['C']), [1, 3])),
- [' < ★ 2 > >{["A"]}',
- ' 1 2 > >{["A"]}',
- ' 3 > >{["C"]}']);
+ [' <2> ★ 2 {["A"]}',
+ ' 1 2 {["A"]}',
+ ' 3 {["C"]}']);
checkPrettyTrie(r.union(r.compilePattern(Immutable.Set(['A']), [r.__, 2]),
r.compilePattern(Immutable.Set(['B']), [3, 4])),
- [' < ★ 2 > >{["A"]}',
- ' 3 2 > >{["A"]}',
- ' 4 > >{["B"]}']);
+ [' <2> ★ 2 {["A"]}',
+ ' 3 2 {["A"]}',
+ ' 4 {["B"]}']);
});
it("should correctly construct intermediate values", function () {
var MU = r.emptyTrie;
MU = r.union(MU, r.compilePattern(Immutable.Set(['A']), [r.__, 2]));
- checkPrettyTrie(MU, [' < ★ 2 > >{["A"]}']);
+ checkPrettyTrie(MU, [' <2> ★ 2 {["A"]}']);
MU = r.union(MU, r.compilePattern(Immutable.Set(['C']), [1, 3]));
- checkPrettyTrie(MU, [' < ★ 2 > >{["A"]}',
- ' 1 2 > >{["A"]}',
- ' 3 > >{["C"]}']);
+ checkPrettyTrie(MU, [' <2> ★ 2 {["A"]}',
+ ' 1 2 {["A"]}',
+ ' 3 {["C"]}']);
MU = r.union(MU, r.compilePattern(Immutable.Set(['B']), [3, 4]));
- checkPrettyTrie(MU, [' < ★ 2 > >{["A"]}',
- ' 1 2 > >{["A"]}',
- ' 3 > >{["C"]}',
- ' 3 2 > >{["A"]}',
- ' 4 > >{["B"]}']);
+ checkPrettyTrie(MU, [' <2> ★ 2 {["A"]}',
+ ' 1 2 {["A"]}',
+ ' 3 {["C"]}',
+ ' 3 2 {["A"]}',
+ ' 4 {["B"]}']);
});
it("should handle identical patterns with different pids", function () {
var m = r.union(r.compilePattern(Immutable.Set('B'), [2]),
r.compilePattern(Immutable.Set('C'), [3]));
- checkPrettyTrie(m, [' < 2 > >{["B"]}',
- ' 3 > >{["C"]}']);
+ checkPrettyTrie(m, [' <1> 2 {["B"]}',
+ ' 3 {["C"]}']);
m = r.union(r.compilePattern(Immutable.Set('A'), [2]), m);
- checkPrettyTrie(m, [' < 2 > >{["A","B"]}',
- ' 3 > >{["C"]}']);
+ checkPrettyTrie(m, [' <1> 2 {["A","B"]}',
+ ' 3 {["C"]}']);
});
it('should work with subtraction and wildcards', function () {
var x = r.compilePattern(Immutable.Set(["A"]), [r.__]);
var y = r.compilePattern(Immutable.Set(["A"]), ["Y"]);
var z = r.compilePattern(Immutable.Set(["A"]), ["Z"]);
- var expected = [' < "Y"::: nothing',
- ' ★ > >{["A"]}'];
+ var expected = [' <1> ★ {["A"]}',
+ ' "Y" ::: nothing'];
checkPrettyTrie(r.subtract(r.union(x, z), y), expected);
checkPrettyTrie(r.union(r.subtract(x, y), z), expected);
});
@@ -135,42 +145,42 @@ describe("unions", function () {
describe("projections", function () {
describe("with picky structure", function () {
- var proj = r.compileProjection(r._$("v", [[r.__]]));
+ var proj = r._$("v", [[r.__]]);
it("should include things that match as well as wildcards", function () {
checkPrettyTrie(r.project(r.union(r.compilePattern(Immutable.Set(['A']), r.__),
r.compilePattern(Immutable.Set(['B']), [['b']])),
proj),
- [' < < ★ > > >{["A"]}',
- ' "b" > > >{["A","B"]}']);
+ [' <1> <1> ★ {["A"]}',
+ ' "b" {["A","B"]}']);
});
it("should exclude things that lack the required structure", function () {
checkPrettyTrie(r.project(r.union(r.compilePattern(Immutable.Set(['A']), r.__),
r.compilePattern(Immutable.Set(['B']), ['b'])),
proj),
- [' < < ★ > > >{["A"]}']);
+ [' <1> <1> ★ {["A"]}']);
});
});
describe("simple positional", function () {
- var proj = r.compileProjection([r._$, r._$]);
+ var proj = [r._$, r._$];
it("should collapse common prefixes", function () {
checkPrettyTrie(r.project(r.union(r.compilePattern(Immutable.Set(['A']), [1, 2]),
r.compilePattern(Immutable.Set(['C']), [1, 3]),
r.compilePattern(Immutable.Set(['B']), [3, 4])),
proj),
- [' 1 2 >{["A"]}',
- ' 3 >{["C"]}',
- ' 3 4 >{["B"]}']);
+ [' 1 2 {["A"]}',
+ ' 3 {["C"]}',
+ ' 3 4 {["B"]}']);
});
it("should yield a correct set of results", function () {
var u = r.union(r.compilePattern(Immutable.Set(['A']), [1, 2]),
r.compilePattern(Immutable.Set(['C']), [1, 3]),
r.compilePattern(Immutable.Set(['B']), [3, 4]));
- checkTrieKeys(r.trieKeys(r.project(u, proj)), [[1, 2], [1, 3], [3, 4]]);
+ checkTrieKeys(r.trieKeys(r.project(u, proj), 2), [[1, 2], [1, 3], [3, 4]]);
});
});
});
@@ -179,29 +189,27 @@ describe("subtraction", function () {
it("should basically work", function () {
checkPrettyTrie(r.subtract(r.compilePattern(true, r.__),
r.compilePattern(true, 3),
- function (v1, v2) { return null; }),
- [" ★ >{true}",
- " 3::: nothing"]);
+ function (v1, v2) { return r.emptyTrie; }),
+ [" ★ {true}",
+ " 3 ::: nothing"]);
checkPrettyTrie(r.subtract(r.compilePattern(true, r.__),
r.compilePattern(true, [3]),
- function (v1, v2) { return null; }),
- [" ★ >{true}",
- " < ★...> >{true}",
- " > >{true}",
- " 3 ★...> >{true}",
- " >::: nothing"]);
+ function (v1, v2) { return r.emptyTrie; }),
+ [" ★ {true}",
+ " <1> ★ {true}",
+ " 3 ::: nothing"]);
});
it("should be idempotent if the subtrahend doesn't overlap the minuend", function () {
checkPrettyTrie(r.compilePattern(true, 1),
- [' 1 >{true}']);
+ [' 1 {true}']);
checkPrettyTrie(r.subtract(r.compilePattern(true, 1),
r.compilePattern(true, 2)),
- [' 1 >{true}']);
+ [' 1 {true}']);
checkPrettyTrie(r.subtract(r.compilePattern(true, 1),
r.compilePattern(true, 2),
function (v1, v2) { return null; }),
- [' 1 >{true}']);
+ [' 1 {true}']);
});
});
@@ -211,11 +219,11 @@ describe("subtract after union", function () {
var R12 = r.union(R1, R2);
it("should have sane preconditions", function () { // Am I doing this right?
- checkPrettyTrie(R1, [' < ★ "B" > >{["A"]}']);
- checkPrettyTrie(R2, [' < "A" ★ > >{["B"]}']);
- checkPrettyTrie(R12, [' < "A" "B" > >{["B","A"]}',
- ' ★ > >{["B"]}',
- ' ★ "B" > >{["A"]}']);
+ checkPrettyTrie(R1, [' <2> ★ "B" {["A"]}']);
+ checkPrettyTrie(R2, [' <2> "A" ★ {["B"]}']);
+ checkPrettyTrie(R12, [' <2> ★ "B" {["A"]}',
+ ' "A" ★ {["B"]}',
+ ' "B" {["A","B"]}']);
});
it("should yield the remaining ingredients of the union", function () {
@@ -248,28 +256,30 @@ describe("trieKeys on wild tries", function () {
r.compilePattern(Immutable.Set(['C']), [1, 3]),
r.compilePattern(Immutable.Set(['B']), [3, 4]));
- it("should yield null to signal an infinite result", function () {
- expect(r.trieKeys(r.project(M, r.compileProjection([r._$, r._$])))).to.be(null);
+ it("should yield false to signal an infinite result", function () {
+ expect(r.trieKeys(r.project(M, [r._$, r._$]), 1)).to.be(false);
});
it("should extract just the second array element successfully", function () {
- checkTrieKeys(r.trieKeys(r.project(M, r.compileProjection([r.__, r._$]))),
+ checkTrieKeys(r.trieKeys(r.project(M, [r.__, r._$]), 1),
[[2],[3],[4]]);
});
- var M2 = r.project(M, r.compileProjection([r._$, r._$]));
+ var M2 = r.project(M, [r._$, r._$]);
it("should survive double-projection", function () {
- checkTrieKeys(r.trieKeys(r.project(M2, r.compileProjection(r.__, r._$))),
+ checkTrieKeys(r.trieKeys(r.projectMany(M2, [r.__, r._$]), 1),
[[2],[3],[4]]);
});
it("should survive embedding and reprojection", function () {
- checkTrieKeys(r.trieKeys(r.project(r.compilePattern(true, [r.embeddedTrie(M2)]),
- r.compileProjection([r.__, r._$]))),
+ checkTrieKeys(r.trieKeys(r.project(r.compilePattern(Immutable.Set(['A']),
+ r.embeddedTrieArray(M2, 2)),
+ [r.__, r._$]), 1),
[[2],[3],[4]]);
- checkTrieKeys(r.trieKeys(r.project(r.compilePattern(true, [[r.embeddedTrie(M2)]]),
- r.compileProjection([[r.__, r._$]]))),
+ checkTrieKeys(r.trieKeys(r.project(r.compilePattern(Immutable.Set(['A']),
+ [r.embeddedTrieArray(M2, 2)]),
+ [[r.__, r._$]]), 1),
[[2],[3],[4]]);
});
});
@@ -278,23 +288,24 @@ describe("trieKeys using multiple-values in projections", function () {
var M = r.union(r.compilePattern(Immutable.Set(['A']), [1, 2]),
r.compilePattern(Immutable.Set(['C']), [1, 3]),
r.compilePattern(Immutable.Set(['B']), [3, 4]));
- var proj = r.compileProjection([r._$, r._$]);
+ var proj = [r._$, r._$];
var M2 = r.project(M, proj);
it("should be able to extract ordinary values", function () {
- checkTrieKeys(r.trieKeys(M2), [[1,2],[1,3],[3,4]]);
+ checkTrieKeys(r.trieKeys(M2, 2), [[1,2],[1,3],[3,4]]);
});
it("should be able to be reprojected as a sequence of more than one value", function () {
- checkTrieKeys(r.trieKeys(r.project(M2, r.compileProjection(r._$, r._$))),
+ checkTrieKeys(r.trieKeys(r.projectMany(M2, [r._$, r._$]), 2),
[[1,2],[1,3],[3,4]]);
});
it("should be convertible into objects with $-indexed fields", function () {
- expect(r.trieKeysToObjects(r.trieKeys(M2), proj).toArray())
- .to.eql([{'$0': 3, '$1': 4}, {'$0': 1, '$1': 2}, {'$0': 1, '$1': 3}]);
- expect(r.projectObjects(M, proj).toArray())
- .to.eql([{'$0': 3, '$1': 4}, {'$0': 1, '$1': 2}, {'$0': 1, '$1': 3}]);
+ var names = r.projectionNames(proj);
+ checkProjectedObjects(r.trieKeysToObjects(r.trieKeys(M2, names.length), names).toArray(),
+ [{'$0': 3, '$1': 4}, {'$0': 1, '$1': 2}, {'$0': 1, '$1': 3}]);
+ checkProjectedObjects(r.projectObjects(M, proj).toArray(),
+ [{'$0': 3, '$1': 4}, {'$0': 1, '$1': 2}, {'$0': 1, '$1': 3}]);
});
});
@@ -304,15 +315,15 @@ describe("trieKeys using multiple-values in projections, with names", function (
r.compilePattern(Immutable.Set(['B']), [3, 4]));
it("should yield named fields", function () {
- expect(r.projectObjects(M, r.compileProjection([r._$("fst"), r._$("snd")])).toArray())
- .to.eql([{'fst': 3, 'snd': 4}, {'fst': 1, 'snd': 2}, {'fst': 1, 'snd': 3}]);
+ checkProjectedObjects(r.projectObjects(M, [r._$("fst"), r._$("snd")]).toArray(),
+ [{'fst': 3, 'snd': 4}, {'fst': 1, 'snd': 2}, {'fst': 1, 'snd': 3}]);
});
it("should yield numbered fields where names are missing", function () {
- expect(r.projectObjects(M, r.compileProjection([r._$, r._$("snd")])).toArray())
- .to.eql([{'$0': 3, 'snd': 4}, {'$0': 1, 'snd': 2}, {'$0': 1, 'snd': 3}]);
- expect(r.projectObjects(M, r.compileProjection([r._$("fst"), r._$])).toArray())
- .to.eql([{'fst': 3, '$1': 4}, {'fst': 1, '$1': 2}, {'fst': 1, '$1': 3}]);
+ checkProjectedObjects(r.projectObjects(M, [r._$, r._$("snd")]).toArray(),
+ [{'$0': 3, 'snd': 4}, {'$0': 1, 'snd': 2}, {'$0': 1, 'snd': 3}]);
+ checkProjectedObjects(r.projectObjects(M, [r._$("fst"), r._$]).toArray(),
+ [{'fst': 3, '$1': 4}, {'fst': 1, '$1': 2}, {'fst': 1, '$1': 3}]);
});
});
@@ -393,47 +404,51 @@ describe("calls to matchPattern", function () {
var ctor = r.makeStructureConstructor('foo', ['bar', 'zot']);
expect(r.matchPattern(ctor(123, 234), ctor(r._$("bar"), r._$("zot"))))
.to.eql({ bar: 123, zot: 234, length: 2 });
+ // Previously, structures were roughly the same as arrays:
expect(r.matchPattern(["foo", 123, 234], ctor(r._$("bar"), r._$("zot"))))
- .to.eql({ bar: 123, zot: 234, length: 2 });
+ .to.be(null);
expect(r.matchPattern(ctor(123, 234), ["foo", r._$("bar"), r._$("zot")]))
- .to.eql({ bar: 123, zot: 234, length: 2 });
+ .to.be(null);
});
});
describe("Projection with no captures", function () {
it("should yield the empty sequence when there's a match", function () {
- var emptySequence = [' >{["A"]}'];
+ var emptySequence = [' {["A"]}'];
- checkPrettyTrie(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]),
- r.compileProjection(r.__)),
+ checkPrettyTrie(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]), r.__),
emptySequence);
- checkPrettyTrie(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]),
- r.compileProjection([r.__, r.__])),
+ checkPrettyTrie(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]), [r.__, r.__]),
emptySequence);
- checkPrettyTrie(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]),
- r.compileProjection(["X", r.__])),
+ checkPrettyTrie(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]), ["X", r.__]),
emptySequence);
});
it("should yield the empty trie when there's no match", function () {
expect(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]),
- r.compileProjection(["Y", r.__]))).to.be(r.emptyTrie);
+ ["Y", r.__])).to.be(r.emptyTrie);
});
it("should yield nonempty sequences when there are captures after all", function () {
checkPrettyTrie(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]),
- r.compileProjection([r.__, r._$])),
- [' ★ >{["A"]}']);
+ [r.__, r._$]),
+ [' ★ {["A"]}']);
checkPrettyTrie(r.project(r.compilePattern(Immutable.Set(['A']), ["X", r.__]),
- r.compileProjection([r._$, r._$])),
- [' "X" ★ >{["A"]}']);
+ [r._$, r._$]),
+ [' "X" ★ {["A"]}']);
});
});
describe('trieStep', function () {
it('should expand wildcard when given SOA', function () {
- expect(Immutable.is(r.trieStep(r.compilePattern(true, r.__), r.SOA),
- r._testing.rwildseq(r._testing.rseq(r.EOA, r._testing.rsuccess(true)))))
+ expect(Immutable.is(r.trieStep(r.compilePattern(true, r.__), 0, r.SOA),
+ r._testing.rsuccess(true)))
+ .to.be(true);
+ expect(Immutable.is(r.trieStep(r.compilePattern(true, r.__), 1, r.SOA),
+ r._testing.rwild(r._testing.rsuccess(true))))
+ .to.be(true);
+ expect(Immutable.is(r.trieStep(r.compilePattern(true, r.__), 2, r.SOA),
+ r._testing.rwild(r._testing.rwild(r._testing.rsuccess(true)))))
.to.be(true);
});
});
@@ -443,60 +458,61 @@ describe('intersect', function () {
var x = r.compilePattern(Immutable.Set([0]), ["fieldContents", r.__, r.__]);
var y = r.compilePattern(Immutable.Set([0]), ["fieldContents", "initial", 7]);
checkPrettyTrie(r.subtract(x, y), [
- ' < "fieldContents" ★ ★ > >{[0]}',
- ' "initial" ★ > >{[0]}',
- ' 7::: nothing']);
- checkPrettyTrie(r.intersect(r.subtract(x, y), y), ['::: nothing']);
+ ' <3> "fieldContents" ★ ★ {[0]}',
+ ' "initial" ★ {[0]}',
+ ' 7 ::: nothing']);
+ checkPrettyTrie(r.intersect(r.subtract(x, y), y), [' ::: nothing']);
});
});
describe('triePruneBranch', function () {
it('should not affect empty trie', function () {
- checkPrettyTrie(r.triePruneBranch(r.emptyTrie, Immutable.List([])), ['::: nothing']);
- checkPrettyTrie(r.triePruneBranch(r.emptyTrie, Immutable.List([r.SOA])), ['::: nothing']);
- checkPrettyTrie(r.triePruneBranch(r.emptyTrie, Immutable.List(["x"])), ['::: nothing']);
- checkPrettyTrie(r.triePruneBranch(r.emptyTrie, Immutable.List([r.SOA, "x"])), ['::: nothing']);
+ checkPrettyTrie(r.triePruneBranch(r.emptyTrie, Immutable.List([])), [' ::: nothing']);
+ checkPrettyTrie(r.triePruneBranch(r.emptyTrie, Immutable.List([r.SOA])), [' ::: nothing']);
+ checkPrettyTrie(r.triePruneBranch(r.emptyTrie, Immutable.List(["x"])), [' ::: nothing']);
+ checkPrettyTrie(r.triePruneBranch(r.emptyTrie, Immutable.List([r.SOA, "x"])), [' ::: nothing']);
});
it('should leave a hole in a full trie', function () {
var full = r.compilePattern(true, r.__);
- checkPrettyTrie(r.triePruneBranch(full, Immutable.List([])), ['::: nothing']);
- checkPrettyTrie(r.triePruneBranch(full, Immutable.List([r.SOA])),
- [' ★ >{true}',
- ' <::: nothing']);
- checkPrettyTrie(r.triePruneBranch(full, Immutable.List(["x"])),
- [' ★ >{true}',
- ' "x"::: nothing']);
- checkPrettyTrie(r.triePruneBranch(full, Immutable.List([r.SOA, "x"])),
- [' ★ >{true}',
- ' < ★...> >{true}',
- ' > >{true}',
- ' "x"::: nothing']);
+ checkPrettyTrie(r.triePruneBranch(full, Immutable.List([])), [' ::: nothing']);
+ checkPrettyTrie(r.triePruneBranch(full, Immutable.List([[0, r.SOA]])),
+ [' ★ {true}',
+ ' <0> ::: nothing']);
+ checkPrettyTrie(r.triePruneBranch(full, Immutable.List([[0, "x"]])),
+ [' ★ {true}',
+ ' "x" ::: nothing']);
+ checkPrettyTrie(r.triePruneBranch(full, Immutable.List([[2, r.SOA], [0, "x"]])),
+ [' ★ {true}',
+ ' <2> ★ ★ {true}',
+ ' "x" ::: nothing']);
});
it('should prune in a finite tree and leave the rest alone', function () {
var A = r.compilePattern(true, ["y"])
var B = r.union(r.compilePattern(true, ["x"]), A);
var C = r.union(r.compilePattern(true, "z"), B);
- checkPrettyTrie(r.triePruneBranch(A, Immutable.List([])), ['::: nothing']);
- checkPrettyTrie(r.triePruneBranch(B, Immutable.List([])), ['::: nothing']);
- checkPrettyTrie(r.triePruneBranch(C, Immutable.List([])), ['::: nothing']);
- checkPrettyTrie(r.triePruneBranch(A, Immutable.List(["z"])), [' < "y" > >{true}']);
- checkPrettyTrie(r.triePruneBranch(B, Immutable.List(["z"])), [' < "x" > >{true}',
- ' "y" > >{true}']);
- checkPrettyTrie(r.triePruneBranch(C, Immutable.List(["z"])), [' < "x" > >{true}',
- ' "y" > >{true}']);
- checkPrettyTrie(r.triePruneBranch(A, Immutable.List([r.SOA])), ['::: nothing']);
- checkPrettyTrie(r.triePruneBranch(B, Immutable.List([r.SOA])), ['::: nothing']);
- checkPrettyTrie(r.triePruneBranch(C, Immutable.List([r.SOA])), [' "z" >{true}']);
- checkPrettyTrie(r.triePruneBranch(A, Immutable.List([r.SOA, "x"])), [' < "y" > >{true}']);
- checkPrettyTrie(r.triePruneBranch(B, Immutable.List([r.SOA, "x"])), [' < "y" > >{true}']);
- checkPrettyTrie(r.triePruneBranch(C, Immutable.List([r.SOA, "x"])), [' < "y" > >{true}',
- ' "z" >{true}']);
- checkPrettyTrie(r.triePruneBranch(A, Immutable.List([r.SOA, "y"])), ['::: nothing']);
- checkPrettyTrie(r.triePruneBranch(B, Immutable.List([r.SOA, "y"])), [' < "x" > >{true}']);
- checkPrettyTrie(r.triePruneBranch(C, Immutable.List([r.SOA, "y"])), [' < "x" > >{true}',
- ' "z" >{true}']);
+ checkPrettyTrie(r.triePruneBranch(A, Immutable.List([])), [' ::: nothing']);
+ checkPrettyTrie(r.triePruneBranch(B, Immutable.List([])), [' ::: nothing']);
+ checkPrettyTrie(r.triePruneBranch(C, Immutable.List([])), [' ::: nothing']);
+ checkPrettyTrie(r.triePruneBranch(A, Immutable.List([[0, "z"]])), [' <1> "y" {true}']);
+ checkPrettyTrie(r.triePruneBranch(B, Immutable.List([[0, "z"]])), [' <1> "x" {true}',
+ ' "y" {true}']);
+ checkPrettyTrie(r.triePruneBranch(C, Immutable.List([[0, "z"]])), [' <1> "x" {true}',
+ ' "y" {true}']);
+ checkPrettyTrie(r.triePruneBranch(A, Immutable.List([[1, r.SOA]])), [' ::: nothing']);
+ checkPrettyTrie(r.triePruneBranch(B, Immutable.List([[1, r.SOA]])), [' ::: nothing']);
+ checkPrettyTrie(r.triePruneBranch(C, Immutable.List([[1, r.SOA]])), [' "z" {true}']);
+ var px = [[1, r.SOA], [0, "x"]];
+ checkPrettyTrie(r.triePruneBranch(A, Immutable.List(px)), [' <1> "y" {true}']);
+ checkPrettyTrie(r.triePruneBranch(B, Immutable.List(px)), [' <1> "y" {true}']);
+ checkPrettyTrie(r.triePruneBranch(C, Immutable.List(px)), [' "z" {true}',
+ ' <1> "y" {true}']);
+ var py = [[1, r.SOA], [0, "y"]];
+ checkPrettyTrie(r.triePruneBranch(A, Immutable.List(py)), [' ::: nothing']);
+ checkPrettyTrie(r.triePruneBranch(B, Immutable.List(py)), [' <1> "x" {true}']);
+ checkPrettyTrie(r.triePruneBranch(C, Immutable.List(py)), [' "z" {true}',
+ ' <1> "x" {true}']);
});
});
@@ -520,7 +536,7 @@ describe('makeStructureConstructor', function () {
var ctor = r.makeStructureConstructor('foo', ['bar', 'zot']);
var inst = ctor(123, 234);
var x = r.compilePattern(sA, ctor(123, r.__));
- checkPrettyTrie(x, [' < "foo" 123 ★ > >{["A"]}']);
+ checkPrettyTrie(x, [' foo<2> 123 ★ {["A"]}']);
expect(r.matchValue(x, ctor(123, 234))).to.eql(sA);
expect(r.matchValue(x, ctor(234, 123))).to.eql(null);
});
diff --git a/js/test/test-syndicate.js b/js/test/test-syndicate.js
index 252ab51..91b5edb 100644
--- a/js/test/test-syndicate.js
+++ b/js/test/test-syndicate.js
@@ -60,10 +60,9 @@ describe("configurationTrace", function() {
Dataspace.send(123);
Dataspace.send(234);
}, ['<<<<<<<< Removed:\n'+
- '::: nothing\n'+
+ ' ::: nothing\n'+
'======== Added:\n'+
- ' < $Observe ★ > >{[0]}\n'
- +' >::: nothing\n'+
+ ' observe<1> ★ {[0]}\n'+
'>>>>>>>>',
Syndicate.message(123),
Syndicate.message(234)]);
@@ -84,9 +83,9 @@ describe("nonempty initial routes", function () {
handleEvent: traceEvent(trace)
});
}, ['<<<<<<<< Removed:\n'+
- '::: nothing\n'+
+ ' ::: nothing\n'+
'======== Added:\n'+
- ' < "A" ★ > >{[1]}\n'+
+ ' <2> "A" ★ {[0]}\n'+
'>>>>>>>>']);
});
});
@@ -104,14 +103,14 @@ describe("nested actor with an echoey protocol", function () {
});
}));
}, ['<<<<<<<< Removed:\n'+
- '::: nothing\n'+
+ ' ::: nothing\n'+
'======== Added:\n'+
- ' < $AtMeta "X" > >{[0]}\n'+
+ ' atMeta<1> "X" {["meta"]}\n'+
'>>>>>>>>',
'<<<<<<<< Removed:\n'+
- ' < $AtMeta "X" > >{[0]}\n'+
+ ' atMeta<1> "X" {["meta"]}\n'+
'======== Added:\n'+
- '::: nothing\n'+
+ ' ::: nothing\n'+
'>>>>>>>>']);
})
it("shouldn't see an echoed message", function () {
@@ -120,7 +119,7 @@ describe("nested actor with an echoey protocol", function () {
Dataspace.spawn({
boot: function () {
Dataspace.send("X", 1); // happens after subs on next line!
- return Patch.sub("X", 1);
+ return Patch.sub("X", 1);
},
handleEvent: traceEvent(trace)
});