Handle journal reset for occurrences: upd their status (eg !alive). Add test for one error caused by it.
Occurrences which weren't yet activated (according to current journal position) can't be reactivated. Test program is an example of such case.
This commit is contained in:
parent
ba8d00c224
commit
79c31a44ba
|
|
@ -91,17 +91,25 @@ internal open class MatchJournalImpl(
|
|||
override fun isFront(): Boolean = current == hist.last
|
||||
|
||||
override fun resetPos() {
|
||||
posPtr = hist.listIterator()
|
||||
// walk backwards and reset all occurrences
|
||||
posPtr = hist.listIterator(hist.size)
|
||||
while (posPtr.hasPrevious()) {
|
||||
current = posPtr.previous()
|
||||
resetOccurrences(current.entries)
|
||||
}
|
||||
}
|
||||
|
||||
override fun reset(pastPos: MatchJournal.Pos) {
|
||||
// walk backwards, reset occurrences and remove history from posPtr down to pastPos
|
||||
while (posPtr.hasPrevious()) {
|
||||
current = posPtr.previous()
|
||||
if (current === pastPos.chunk) {
|
||||
resetOccurrences(current.entries.drop(pastPos.entriesCount))
|
||||
current.entries = current.entries.subList(0, pastPos.entriesCount)
|
||||
posPtr.next() // make 'posPtr' to always point right after 'current'
|
||||
return
|
||||
}
|
||||
resetOccurrences(current.entries)
|
||||
posPtr.remove()
|
||||
}
|
||||
if (currentPos() != pastPos) throw IllegalStateException()
|
||||
|
|
@ -119,6 +127,17 @@ internal open class MatchJournalImpl(
|
|||
if (currentPos() != futurePos) throw IllegalStateException()
|
||||
}
|
||||
|
||||
private fun resetOccurrences(occSpecs: Iterable<MatchJournal.Chunk.Entry>) =
|
||||
occSpecs.forEach {
|
||||
if (it.discarded) {
|
||||
it.occ.alive = true
|
||||
it.occ.stored = true
|
||||
} else {
|
||||
it.occ.alive = false
|
||||
it.occ.stored = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun replayOccurrences(observable: LogicalStateObservable, occSpecs: Iterable<MatchJournal.Chunk.Entry>) =
|
||||
occSpecs.forEach {
|
||||
if (it.discarded) {
|
||||
|
|
|
|||
|
|
@ -1158,4 +1158,71 @@ class TestIncrementalProgram {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun observerReactivatesFutureOccurrence() {
|
||||
val progSpec = MockIncrProgSpec(
|
||||
setOf("main", "bindVar", "produceBound", "eliminateBound"),
|
||||
setOf(sym1("foo"))
|
||||
)
|
||||
val X = metaLogical<Int>("X")
|
||||
programWithRules(
|
||||
rule("main",
|
||||
headReplaced(
|
||||
constraint("main")
|
||||
),
|
||||
body(
|
||||
princConstraint("foo", X)
|
||||
)),
|
||||
rule("produceBound",
|
||||
headKept(
|
||||
princConstraint("foo", X)
|
||||
),
|
||||
body(
|
||||
princConstraint("hasBound", X)
|
||||
)),
|
||||
rule("eliminateBound",
|
||||
headReplaced(
|
||||
princConstraint("hasBound", X)
|
||||
),
|
||||
guard(
|
||||
expression({ x -> x.isBound }, X)
|
||||
),
|
||||
body(
|
||||
constraint("unexpected")
|
||||
)
|
||||
)
|
||||
).launch("initial run", progSpec) { result ->
|
||||
|
||||
result.storeView().constraintSymbols() shouldBe setOf(sym1("foo"), sym1("hasBound"))
|
||||
|
||||
}.also { (builder, evalRes) ->
|
||||
// Insert rule before produceBound: test whether
|
||||
// occurrence reactivation due to var bind can occur
|
||||
// before this occurrence is actually activated,
|
||||
// which leads, of course, to inconsistent state.
|
||||
builder.insertRulesAt(1,
|
||||
rule("bindVar",
|
||||
headKept(
|
||||
princConstraint("foo", X)
|
||||
),
|
||||
body(
|
||||
statement({ x -> x.set(42) }, X)
|
||||
))
|
||||
).relaunch("test reactivation", progSpec, evalRes.token()) { result ->
|
||||
|
||||
// Currently incremental processing can't handle such rule dependencies
|
||||
// stemming from incremental changes to logicals,
|
||||
// so "eliminateBound" rule won't be reexecuted.
|
||||
// But reactivation of hasBound shouldn't pass either,
|
||||
// because 'hasBound' isn't in store at the point of tried reactivation.
|
||||
result.storeView().constraintSymbols() shouldBe setOf(sym1("foo"), sym1("hasBound"))
|
||||
|
||||
// NB: this is correct if incremental processing
|
||||
// takes into account rule dependencies due to logicals.
|
||||
// result.storeView().constraintSymbols() shouldBe setOf(sym1("foo"), sym0("unexpected"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue