diff --git a/js/src/route.js b/js/src/route.js index 4ca8888..92b9de8 100644 --- a/js/src/route.js +++ b/js/src/route.js @@ -172,7 +172,12 @@ function rlookup(r, key) { } function rlookupWild(r, key) { - return r.get(key, false); + 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) { @@ -252,20 +257,9 @@ function union(o1, o2, unionSuccessesOpt) { var w = merge(rlookup(r1, __), rlookup(r2, __)); var target; - function examineKey(rA, key, rB) { + function examineKey(key) { if ((key !== __) && !target.has(key)) { - var k = merge(rlookup(rA, key), rlookup(rB, key)); - if (is_keyOpen(key)) { - target = rupdate(target, key, merge(rwildseq(w), k)); - } else if (is_keyClose(key)) { - if (w instanceof $WildcardSequence) { - target = rupdate(target, key, merge(w.trie, k)); - } else { - target = rupdate(target, key, k); - } - } else { - target = rupdate(target, key, merge(w, k)); - } + target = rupdate(target, key, merge(rlookupWild(r1, key), rlookupWild(r2, key))); } } @@ -279,8 +273,8 @@ function union(o1, o2, unionSuccessesOpt) { }); } else { target = rwild(w); - r1.forEach(function (val, key) { examineKey(r1, key, r2) }); - r2.forEach(function (val, key) { examineKey(r2, key, r1) }); + r1.forEach(function (val, key) { examineKey(key) }); + r2.forEach(function (val, key) { examineKey(key) }); } return target; @@ -337,21 +331,7 @@ function intersect(o1, o2, intersectSuccessesOpt, leftShortOpt) { function examineKey(key) { if ((key !== __) && !target.has(key)) { - var k1 = rlookup(r1, key); - var k2 = rlookup(r2, key); - if (is_emptyTrie(k1)) { - if (is_emptyTrie(k2)) { - target = rupdate(target, key, emptyTrie); - } else { - target = rupdate(target, key, walkWild(walk, w1, key, k2)); - } - } else { - if (is_emptyTrie(k2)) { - target = rupdate(target, key, walkWild(walkFlipped, w2, key, k1)); - } else { - target = rupdate(target, key, walk(k1, k2)); - } - } + target = rupdate(target, key, walk(rlookupWild(r1, key), rlookupWild(r2, key))); } } @@ -372,16 +352,6 @@ function intersect(o1, o2, intersectSuccessesOpt, leftShortOpt) { } return target; } - - function walkWild(walker, w, key, k) { - if (is_emptyTrie(w)) return emptyTrie; - if (is_keyOpen(key)) return walker(rwildseq(w), k); - if (is_keyClose(key)) { - if (w instanceof $WildcardSequence) return walker(w.trie, k); - return emptyTrie; - } - return walker(w, k); - } } // The subtractSuccesses function should return null to signal "no @@ -423,27 +393,8 @@ function subtract(o1, o2, subtractSuccessesOpt) { if (key !== __) { var k1 = rlookupWild(r1, key); var k2 = rlookupWild(r2, key); - var updatedK; - if (!k1) { - if (!k2) { - return; - } - // There is an entry in r2 but not r1 for our key. - updatedK = - is_emptyTrie(w1) ? emptyTrie : - is_keyOpen(key) ? walk(rwildseq(w1), k2) : - is_keyClose(key) ? ((w1 instanceof $WildcardSequence) ? walk(w1.trie, k2) : emptyTrie) : - walk(w1, k2); - } else if (!k2) { - // There is an entry in r1 but not r2 for our key. - updatedK = - is_emptyTrie(w2) ? k1 : - is_keyOpen(key) ? walk(k1, rwildseq(w2)) : - is_keyClose(key) ? ((w2 instanceof $WildcardSequence) ? walk(k1, w2.trie) : k1) : - walk(k1, w2); - } else { - updatedK = walk(k1, k2); - } + 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 @@ -669,12 +620,7 @@ function trieStep(m, key) { if (is_emptyTrie(m)) return emptyTrie; if (m instanceof $WildcardSequence) return (is_keyClose(key) ? m.trie : m); if (m instanceof $Success) return emptyTrie; - var result = rlookupWild(m, key); - if (result) return result; - var wildEdge = rlookup(m, __); - if (is_keyOpen(key)) return rwildseq(wildEdge); - if (is_keyClose(key)) return (wildEdge instanceof $WildcardSequence) ? wildEdge.trie : emptyTrie; - return wildEdge; + return rlookupWild(m, key); } function relabel(m, f) { diff --git a/js/test/test-mux.js b/js/test/test-mux.js index f0644eb..24eb348 100644 --- a/js/test/test-mux.js +++ b/js/test/test-mux.js @@ -262,18 +262,14 @@ describe('computeEvents', function () { var pid1 = oldM.addStream(Patch.assert(["fieldContents", "initial", 7])).pid; var pid2 = oldM.addStream(Patch.sub(["fieldContents", __, __])).pid; var newM = oldM.shallowCopy(); - var updateStreamResult = - newM.updateStream(pid1, - Patch.retract(["fieldContents", __, __]) - .andThen(Patch.assert(["fieldContents", "initial", 7]))); + var unclampedPatch = + Patch.retract(["fieldContents", __, __]) + .andThen(Patch.assert(["fieldContents", "initial", 7])); + var updateStreamResult = newM.updateStream(pid1, unclampedPatch); var events = Mux.computeEvents(oldM, newM, updateStreamResult); - it('should send an empty patch to the subscriber', function () { - expect(events.eventMap.size).to.be(1); - expect(events.eventMap.has(pid2)).to.be(true); - checkPrettyPatch(events.eventMap.get(pid2).strip(), - ['::: nothing'], - ['::: nothing']); + it('should send no patch to the subscriber', function () { + expect(events.eventMap.size).to.be(0); }); }); }); diff --git a/js/test/test-route.js b/js/test/test-route.js index 49b1774..1d99e48 100644 --- a/js/test/test-route.js +++ b/js/test/test-route.js @@ -62,7 +62,7 @@ describe("unions", function () { r.compilePattern(Immutable.Set(['W']), r.__)), [' ★ >{["W"]}', ' < ★ "A" ★...> >{["W"]}', - ' > >{["A","W"]}', + ' > >{["W","A"]}', ' ★...> >{["W"]}', ' > >{["W"]}', ' > >{["W"]}']); @@ -121,6 +121,16 @@ describe("unions", function () { checkPrettyTrie(m, [' < 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"]}']; + checkPrettyTrie(r.subtract(r.union(x, z), y), expected); + checkPrettyTrie(r.union(r.subtract(x, y), z), expected); + }); }); describe("projections", function () { @@ -132,7 +142,7 @@ describe("projections", function () { r.compilePattern(Immutable.Set(['B']), [['b']])), proj), [' < < ★ > > >{["A"]}', - ' "b" > > >{["B","A"]}']); + ' "b" > > >{["A","B"]}']); }); it("should exclude things that lack the required structure", function () { @@ -417,3 +427,15 @@ describe('trieStep', function () { .to.be(true); }); }); + +describe('intersect', function () { + it('should compute no-op patch limits properly', 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']); + }); +});