diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt index 05a6ef8f..254a7a80 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt @@ -98,13 +98,13 @@ interface MatchJournal : EvidenceSource { */ fun replay(futurePos: Pos) - /** * Returns snapshot of the journal. */ fun view(): View /** + * For tests only. * Returns [StoreView] of the journal from the beginning to its current position. */ fun storeView(): StoreView @@ -116,8 +116,6 @@ interface MatchJournal : EvidenceSource { */ data class View(val chunks: List, val evidenceSeed: Evidence) : MatchJournalView { - constructor() : this(emptyList(), 0) - override fun getStoreView(): StoreView = StoreViewImpl( chunks.flatMap { it.entries() }.allOccurrences().asSequence() ) @@ -143,20 +141,6 @@ interface MatchJournal : EvidenceSource { */ fun entries(): List - /** - * Checks whether this [Chunk] has no ancestors (not counting [MatchJournal.initialChunk]) - */ - fun isTopLevel(): Boolean = justifications().size() <= 1 - - /** - * Same as [entries], but returns only activated [Occurrence]s. - */ - fun activatedLog(): List = entries().filter { !it.discarded }.map { it.occ } - - /** - * Same as [entries], but returns only discarded [Occurrence]s. - */ - fun discardedLog(): List = entries().filter { it.discarded }.map { it.occ } fun toPos(): Pos = Pos(this, entries().size) } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt index 536d3da9..b9e950fc 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt @@ -51,7 +51,7 @@ internal open class MatchJournalImpl( override fun toString() = "(id=$evidence, ${justifications()}, activation of $occ, $entries)" } - private object CornerChunk: ChunkImpl() { + private object CornerChunk : ChunkImpl() { override val evidence: Evidence = -1 override fun justifications(): Justifications = emptyJustifications() } @@ -179,24 +179,22 @@ internal open class MatchJournalImpl( override fun currentPos(): MatchJournal.Pos = __cursor.current.toPos() override fun reset(pastPos: MatchJournal.Pos) { - reset(pastPos, true) + __cursor.moveToPastRemoving(pastPos) { + popAncestor() + resetOccurrences(it.entries()) + it !== pastPos.chunk + } + // drop occurrences at final chunk + with(pastPos.chunk as ChunkImpl) { + entries = entries.subList(0, pastPos.entriesCount) + } replay(pastPos) } override fun resetCursor(pastPos: Pos) { - reset(pastPos, false) - } - - // TBR - private fun reset(pastPos: MatchJournal.Pos, removing: Boolean) { - __cursor.moveToPastRemoving(pastPos) { + __cursor.moveToPastApplying(pastPos) { popAncestor() resetOccurrences(it.entries()) - removing && it !== pastPos.chunk - } - // drop occurrences at final chunk - if (removing) with(pastPos.chunk as ChunkImpl) { - entries = entries.subList(0, pastPos.entriesCount) } } @@ -204,12 +202,9 @@ internal open class MatchJournalImpl( if (__cursor at ancestorChunksStack.peek()) ancestorChunksStack.pop() } - override fun replay(futurePos: Pos) = replayWith(futurePos) {} - - - private inline fun replayWith(futurePos: Pos, action: (Chunk) -> Unit) { + override fun replay(futurePos: Pos) { while (!(__cursor at futurePos || __cursor.atEnd())) { - action(__cursor.next()) + __cursor.next() } __cursor assertAt futurePos // Chunks are replayed fully by ptr, this partial replay unneeded @@ -258,8 +253,7 @@ internal open class MatchJournalImpl( * Provides [MatchJournal] look-ahead traverse. */ private open class JournalIteratorImpl(private val it: ListIterator) - : JournalIterator, Iterator by it - { + : JournalIterator, Iterator by it { protected var __next: ChunkImpl protected var __current: ChunkImpl @@ -284,7 +278,7 @@ internal open class MatchJournalImpl( protected fun updateNext() { - assert(it.hasNext()) {"journal iterator must always have next chunk (maybe the final)"} + assert(it.hasNext()) { "journal iterator must always have next chunk (maybe the final)" } __next = it.next() it.previous() // iterator always points between stored chunks } @@ -309,8 +303,7 @@ internal open class MatchJournalImpl( * [Cursor] points at position between [current] & [next]. */ private inner class Cursor(private val it: MutableListIterator) - : MutableJournalIterator, JournalIteratorImpl(it) - { + : MutableJournalIterator, JournalIteratorImpl(it) { /** * Replays [Chunk]s while iterating (see [replayChunk]) */ @@ -368,6 +361,25 @@ internal open class MatchJournalImpl( this assertAt pastPos } + /** + * Walk in journal in inverse order ([pastPos] <- [current]) while + * applying [action] to each [Chunk] (including [pastPos]). + * + * Contract: after the call [__next] points to [pastPos]. + */ + inline fun moveToPastApplying(pastPos: Pos, action: (Chunk) -> Unit) { + while (!atStart()) { + __current = it.previous() + action(__current) + if (this at pastPos) { + __current = it.previous() + it.next() // make it point right after __current + updateNext() + return + } + } + this assertAt pastPos + } private fun replayChunk(chunk: Chunk) { if (!atStart()) trackAncestor(chunk) @@ -382,11 +394,11 @@ internal open class MatchJournalImpl( private class IteratorMutableList(private val l: MutableList) : List by l { override fun iterator(): MutableIterator = l.iterator() override fun listIterator(): MutableListIterator = l.listIterator() - override fun listIterator(index: Int): MutableListIterator = l.listIterator(index) + override fun listIterator(index: Int): MutableListIterator = l.listIterator(index) val last: E get() = if (l is LinkedList) l.last else l.last() } - + /** * Mock RuleMatch for use only in initial Chunk */ diff --git a/reactor/Test/test/TestIncrementalProgram.kt b/reactor/Test/test/TestIncrementalProgram.kt index 9de460f4..d97e5c42 100644 --- a/reactor/Test/test/TestIncrementalProgram.kt +++ b/reactor/Test/test/TestIncrementalProgram.kt @@ -49,8 +49,7 @@ class TestIncrementalProgram { } private fun Builder.launch(name: String, incrSpec: IncrementalSpec, resultHandler: (EvaluationResult) -> Unit) - : Pair - { + : Pair { val result = EvaluationSession.newSession(program(name)) .withParameter(EvaluationSession.ParameterKey.of("main", Constraint::class.java), MockConstraint(ConstraintSymbol("main", 0))) .withIncrSpec(incrSpec) @@ -60,9 +59,8 @@ class TestIncrementalProgram { return this to result } - private fun Builder.relaunch(name: String, incrSpec: IncrementalSpec, sessionToken: SessionToken, resultHandler: (EvaluationResult) -> Unit ) - : Pair - { + private fun Builder.relaunch(name: String, incrSpec: IncrementalSpec, sessionToken: SessionToken, resultHandler: (EvaluationResult) -> Unit) + : Pair { val prog = program(name) val result = EvaluationSession.newSession(prog) .withParameter(EvaluationSession.ParameterKey.of("main", Constraint::class.java), MockConstraint(ConstraintSymbol("main", 0))) @@ -81,14 +79,18 @@ class TestIncrementalProgram { it.entries().map { entry -> !entry.discarded to entry.occ.constraint().symbol() } } -// private fun EvaluationResult.lastChunk() = this.token().chunks().last() - private fun EvaluationResult.lastChunk() = this.token().chunks().let { it[it.size-2] } + // private fun EvaluationResult.lastChunk() = this.token().chunks().last() + private fun EvaluationResult.lastChunk() = this.token().chunks().let { it[it.size - 2] } private fun EvaluationResult.countChunks() = this.token().chunks().size private fun Iterable.constraintSymbols() = this.map { it.constraint.symbol() } - private fun EvaluationResult.lastChunkSymbols() = this.lastChunk().activatedLog().constraintSymbols() + private fun EvaluationResult.lastChunkSymbols() = lastChunk() + .entries() + .filter { !it.discarded } + .map { it.occ } + .constraintSymbols() private fun ConstraintOccurrence.firstValue(): Any? = (arguments().first() as? Logical<*>)?.value() @@ -143,17 +145,17 @@ class TestIncrementalProgram { headReplaced( pconstraint("bar") ), - body( )) + body()) ).launch("addRule", progSpec) { result -> storeSnapshot = result.storeView().constraintSymbols() storeSnapshot shouldBe setOf(sym0("foo")) }.also { (builder, evalRes) -> builder - .removeRules(listOf("bar")) - .relaunch("no changes", progSpec, evalRes.token()) { result -> - result.storeView().constraintSymbols() shouldBe storeSnapshot - } + .removeRules(listOf("bar")) + .relaunch("no changes", progSpec, evalRes.token()) { result -> + result.storeView().constraintSymbols() shouldBe storeSnapshot + } } } @@ -313,7 +315,7 @@ class TestIncrementalProgram { ).relaunch("atStart", progSpec, evalRes.token()) { result -> result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("main"), sym0("bar")) - result.countChunks() shouldBe nPrincipalMatches+1 + result.countChunks() shouldBe nPrincipalMatches + 1 result.lastChunkSymbols() shouldBe listOf(sym0("bar")) } } @@ -860,10 +862,10 @@ class TestIncrementalProgram { builder.removeRules(listOf("baz.qux")) .relaunch("removed", progSpec, evalRes.token()) { result -> - result.storeView().constraintSymbols() shouldBe setOf(sym0("baz")) - result.lastChunkSymbols() shouldBe listOf(sym0("baz")) - result.countChunks() shouldBe (nPrincipalMatches - 1) - } + result.storeView().constraintSymbols() shouldBe setOf(sym0("baz")) + result.lastChunkSymbols() shouldBe listOf(sym0("baz")) + result.countChunks() shouldBe (nPrincipalMatches - 1) + } } } @@ -1326,7 +1328,7 @@ class TestIncrementalProgram { ).launch("launch", progSpec) { result -> result.storeView().constraintSymbols() shouldBe setOf(sym0("important")) - assertTrue( result.lastChunk().let { it is MatchJournal.MatchChunk && it.match.rule().uniqueTag().toString() == "foo" } ) + assertTrue(result.lastChunk().let { it is MatchJournal.MatchChunk && it.match.rule().uniqueTag().toString() == "foo" }) }.also { (builder, evalRes) -> @@ -1341,7 +1343,7 @@ class TestIncrementalProgram { listOf("bar") ).relaunch("invalidate bar", progSpec, evalRes.token()) { result -> - result.storeView().constraintSymbols() shouldBe setOf( sym0("bar"), sym0("expected") ) + result.storeView().constraintSymbols() shouldBe setOf(sym0("bar"), sym0("expected")) } } } @@ -1465,7 +1467,7 @@ class TestIncrementalProgram { listOf("bar") ).relaunch("invalidate bar", progSpec, evalRes.token()) { result -> - result.storeView().constraintSymbols() shouldBe setOf( sym0("bar"), sym0("expected"), sym0("expected2") ) + result.storeView().constraintSymbols() shouldBe setOf(sym0("bar"), sym0("expected"), sym0("expected2")) val occInstances2 = result.storeView().allOccurrences().filter { it.constraint().symbol() == sym0("expected2") } // instance of occurrence from the second run must be different --- // if it's same it means that "fooChild" rule wasn't invalidated! @@ -1571,7 +1573,7 @@ class TestIncrementalProgram { pconstraint("foo", X) ), body( - statement({ x, y -> eq(x,y) }, X, Y), + statement({ x, y -> eq(x, y) }, X, Y), constraint("hasBound", Y) )) @@ -1625,7 +1627,7 @@ class TestIncrementalProgram { expression({ x -> !x.isBound }, X) ), body( - constraint("expected" ) + constraint("expected") )) ) .relaunch("relaunch", progSpec, evalRes.token()) { result -> @@ -1664,7 +1666,7 @@ class TestIncrementalProgram { expression({ x -> !x.isBound }, X) ), body( - constraint("unexpected" ) + constraint("unexpected") )) ).launch("normal run", progSpec) { result -> @@ -1712,7 +1714,7 @@ class TestIncrementalProgram { pconstraint("foo", X) ), body( - statement({ x, y -> eq(x,y) }, X, Y), + statement({ x, y -> eq(x, y) }, X, Y), constraint("hasBound", Y) )), rule("bindVar", @@ -1739,7 +1741,7 @@ class TestIncrementalProgram { expression({ x -> !x.isBound }, X) ), body( - constraint("expected" ) + constraint("expected") )) ) .relaunch("relaunch", progSpec, evalRes.token()) { result -> @@ -1792,7 +1794,7 @@ class TestIncrementalProgram { expression({ x -> !x.isBound }, X) ), body( - constraint("expected" ) + constraint("expected") )) ) .relaunch("relaunch", progSpec, evalRes.token()) { result -> @@ -1831,14 +1833,14 @@ class TestIncrementalProgram { expression({ x -> !x.isBound }, X) ), body( - constraint("expected" ) + constraint("expected") )), rule("produceBound", headKept( pconstraint("foo", X) ), body( - statement({ x, y -> eq(x,y) }, X, Y), + statement({ x, y -> eq(x, y) }, X, Y), constraint("hasBound", Y) )), rule("bindVar", @@ -1924,7 +1926,7 @@ class TestIncrementalProgram { pconstraint("bar", Y) ), body( - statement({ x, y -> eq(x,y) }, X, Y) + statement({ x, y -> eq(x, y) }, X, Y) )) ) .relaunch("relaunch", progSpec, evalRes.token()) { result -> @@ -1937,7 +1939,6 @@ class TestIncrementalProgram { } - @Test fun expectTypeGenericNonprincipal() { val progSpec = MockIncrProgSpec( @@ -2063,13 +2064,13 @@ class TestIncrementalProgram { }.also { (builder, evalRes) -> builder - .removeRules(listOf("process")) - .relaunch("removed write", progSpec, evalRes.token()) { result -> + .removeRules(listOf("process")) + .relaunch("removed write", progSpec, evalRes.token()) { result -> - // without 'write' resource isn't exhausted - result.storeView().constraintSymbols() shouldBe setOf(sym0("hasRead"), sym0("resource"), sym0("doProcess")) - result.storeView().occurrences(sym0("hasRead")).count() shouldBe 3 - } + // without 'write' resource isn't exhausted + result.storeView().constraintSymbols() shouldBe setOf(sym0("hasRead"), sym0("resource"), sym0("doProcess")) + result.storeView().occurrences(sym0("hasRead")).count() shouldBe 3 + } } }