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) {
|
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) {
|
function is_keyOpen(k) {
|
||||||
|
@ -252,20 +257,9 @@ function union(o1, o2, unionSuccessesOpt) {
|
||||||
var w = merge(rlookup(r1, __), rlookup(r2, __));
|
var w = merge(rlookup(r1, __), rlookup(r2, __));
|
||||||
var target;
|
var target;
|
||||||
|
|
||||||
function examineKey(rA, key, rB) {
|
function examineKey(key) {
|
||||||
if ((key !== __) && !target.has(key)) {
|
if ((key !== __) && !target.has(key)) {
|
||||||
var k = merge(rlookup(rA, key), rlookup(rB, key));
|
target = rupdate(target, key, merge(rlookupWild(r1, key), rlookupWild(r2, 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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,8 +273,8 @@ function union(o1, o2, unionSuccessesOpt) {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
target = rwild(w);
|
target = rwild(w);
|
||||||
r1.forEach(function (val, key) { examineKey(r1, key, r2) });
|
r1.forEach(function (val, key) { examineKey(key) });
|
||||||
r2.forEach(function (val, key) { examineKey(r2, key, r1) });
|
r2.forEach(function (val, key) { examineKey(key) });
|
||||||
}
|
}
|
||||||
|
|
||||||
return target;
|
return target;
|
||||||
|
@ -337,21 +331,7 @@ function intersect(o1, o2, intersectSuccessesOpt, leftShortOpt) {
|
||||||
|
|
||||||
function examineKey(key) {
|
function examineKey(key) {
|
||||||
if ((key !== __) && !target.has(key)) {
|
if ((key !== __) && !target.has(key)) {
|
||||||
var k1 = rlookup(r1, key);
|
target = rupdate(target, key, walk(rlookupWild(r1, key), rlookupWild(r2, 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -372,16 +352,6 @@ function intersect(o1, o2, intersectSuccessesOpt, leftShortOpt) {
|
||||||
}
|
}
|
||||||
return target;
|
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
|
// The subtractSuccesses function should return null to signal "no
|
||||||
|
@ -423,27 +393,8 @@ function subtract(o1, o2, subtractSuccessesOpt) {
|
||||||
if (key !== __) {
|
if (key !== __) {
|
||||||
var k1 = rlookupWild(r1, key);
|
var k1 = rlookupWild(r1, key);
|
||||||
var k2 = rlookupWild(r2, key);
|
var k2 = rlookupWild(r2, key);
|
||||||
var updatedK;
|
var updatedK = walk(k1, k2);
|
||||||
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);
|
|
||||||
}
|
|
||||||
// Here we ensure a "minimal" remainder in cases
|
// Here we ensure a "minimal" remainder in cases
|
||||||
// where after an erasure, a particular key's
|
// where after an erasure, a particular key's
|
||||||
// continuation is the same as the wildcard's
|
// continuation is the same as the wildcard's
|
||||||
|
@ -669,12 +620,7 @@ function trieStep(m, key) {
|
||||||
if (is_emptyTrie(m)) return emptyTrie;
|
if (is_emptyTrie(m)) return emptyTrie;
|
||||||
if (m instanceof $WildcardSequence) return (is_keyClose(key) ? m.trie : m);
|
if (m instanceof $WildcardSequence) return (is_keyClose(key) ? m.trie : m);
|
||||||
if (m instanceof $Success) return emptyTrie;
|
if (m instanceof $Success) return emptyTrie;
|
||||||
var result = rlookupWild(m, key);
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function relabel(m, f) {
|
function relabel(m, f) {
|
||||||
|
|
|
@ -262,18 +262,14 @@ describe('computeEvents', function () {
|
||||||
var pid1 = oldM.addStream(Patch.assert(["fieldContents", "initial", 7])).pid;
|
var pid1 = oldM.addStream(Patch.assert(["fieldContents", "initial", 7])).pid;
|
||||||
var pid2 = oldM.addStream(Patch.sub(["fieldContents", __, __])).pid;
|
var pid2 = oldM.addStream(Patch.sub(["fieldContents", __, __])).pid;
|
||||||
var newM = oldM.shallowCopy();
|
var newM = oldM.shallowCopy();
|
||||||
var updateStreamResult =
|
var unclampedPatch =
|
||||||
newM.updateStream(pid1,
|
Patch.retract(["fieldContents", __, __])
|
||||||
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);
|
var events = Mux.computeEvents(oldM, newM, updateStreamResult);
|
||||||
|
|
||||||
it('should send an empty patch to the subscriber', function () {
|
it('should send no patch to the subscriber', function () {
|
||||||
expect(events.eventMap.size).to.be(1);
|
expect(events.eventMap.size).to.be(0);
|
||||||
expect(events.eventMap.has(pid2)).to.be(true);
|
|
||||||
checkPrettyPatch(events.eventMap.get(pid2).strip(),
|
|
||||||
['::: nothing'],
|
|
||||||
['::: nothing']);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -62,7 +62,7 @@ describe("unions", function () {
|
||||||
r.compilePattern(Immutable.Set(['W']), r.__)),
|
r.compilePattern(Immutable.Set(['W']), r.__)),
|
||||||
[' ★ >{["W"]}',
|
[' ★ >{["W"]}',
|
||||||
' < ★ "A" ★...> >{["W"]}',
|
' < ★ "A" ★...> >{["W"]}',
|
||||||
' > >{["A","W"]}',
|
' > >{["W","A"]}',
|
||||||
' ★...> >{["W"]}',
|
' ★...> >{["W"]}',
|
||||||
' > >{["W"]}',
|
' > >{["W"]}',
|
||||||
' > >{["W"]}']);
|
' > >{["W"]}']);
|
||||||
|
@ -121,6 +121,16 @@ describe("unions", function () {
|
||||||
checkPrettyTrie(m, [' < 2 > >{["A","B"]}',
|
checkPrettyTrie(m, [' < 2 > >{["A","B"]}',
|
||||||
' 3 > >{["C"]}']);
|
' 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 () {
|
describe("projections", function () {
|
||||||
|
@ -132,7 +142,7 @@ describe("projections", function () {
|
||||||
r.compilePattern(Immutable.Set(['B']), [['b']])),
|
r.compilePattern(Immutable.Set(['B']), [['b']])),
|
||||||
proj),
|
proj),
|
||||||
[' < < ★ > > >{["A"]}',
|
[' < < ★ > > >{["A"]}',
|
||||||
' "b" > > >{["B","A"]}']);
|
' "b" > > >{["A","B"]}']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should exclude things that lack the required structure", function () {
|
it("should exclude things that lack the required structure", function () {
|
||||||
|
@ -417,3 +427,15 @@ describe('trieStep', function () {
|
||||||
.to.be(true);
|
.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