Fix two major subtraction-related bugs in Route union and intersect.
Moved a bunch of lookup logic into rlookupWild, which let me delete a lot of special-purpose and flawed code. It is clearly heading toward being properly-refactored, like the Racket implementation (and the ESOP2016 paper's presentation) is. The performance problems may interrupt this gradual evolution before it is complete, though: hopefully I will be able to move to an explicitly memory-managed scheme soon.
This commit is contained in:
parent
df0ff273b1
commit
49d11b1a73
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
var unclampedPatch =
|
||||
Patch.retract(["fieldContents", __, __])
|
||||
.andThen(Patch.assert(["fieldContents", "initial", 7])));
|
||||
.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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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']);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue