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 704d25a8..a95b5c12 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 @@ -51,7 +51,10 @@ interface MatchJournal : MutableIterable, EvidenceSource { fun initialChunk(): MatchChunk /** - * Current position at the journal. + * Returns current [Chunk] position in the journal. + * NB: returned [Pos] isn't granular: it doesn't take into account + * replayed position *inside* [Chunk], that is, [Pos.entriesCount] + * will return the count of all [Occurrence]s. */ fun currentPos(): Pos @@ -82,8 +85,9 @@ interface MatchJournal : MutableIterable, EvidenceSource { fun dropDescendantsWhile(ancestor: Chunk, dropIf: (Chunk) -> Boolean) /** - * Replay activated and discarded occurrences logged in journal between current and provided positions. - * Advances journal position to specified position. + * Replay activated and discarded occurrences logged in journal between current + * and provided positions. Advances journal position to specified position. + * Idempotent operation (can be called multiple times on same [Pos]]). * @throws IllegalStateException when position is not from the future (relative to current pos). */ fun replay(futurePos: Pos) @@ -140,54 +144,71 @@ interface MatchJournal : MutableIterable, EvidenceSource { */ data class View(val chunks: List, val evidenceSeed: Evidence) : MatchJournalView { override fun getStoreView(): StoreView = StoreViewImpl( - chunks.flatMap { it.entriesLog() }.allOccurrences().asSequence() + chunks.flatMap { it.entries() }.allOccurrences().asSequence() ) } + /** + * [MatchJournal] operates on [Chunk]s -- basic [Justified] entities. + * Each [Chunk] contains a number of entries, corresponding to events + * of activated and discarded [Occurrence]s, which happened before + * the next [Chunk] was logged. + */ interface Chunk : Justified { - - // fixme: hide rm-mutability - var entries: MutableList - fun entriesLog(): List = entries - + /** + * Corresponds to event of activated or discarded [Occurrence] + */ data class Entry(val occ: Occurrence, val discarded: Boolean = false) { override fun toString() = (if (discarded) '-' else '+') + occ.toString() } - fun isDescendantOf(chunk: Chunk): Boolean = this.justifiedBy(chunk) - fun isTopLevel(): Boolean = justifications().size() <= 1 // this condition implies that there're no ancestor chunks + /** + * Returns historically ordered list of activated and discarded [Occurrence]s. + */ + fun entries(): List - fun activatedLog(): List = entriesLog().filter { !it.discarded }.map { it.occ } - fun discardedLog(): List = entriesLog().filter { it.discarded }.map { it.occ } + /** + * Check whether this [Chunk] depends on another, which is specified by justifications. + */ + fun isDescendantOf(chunk: Chunk): Boolean = this.justifiedBy(chunk) + + /** + * 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 } /** * Returns the resulting collection of activated occurrences * without discarded (only in this chunk!) occurrences. */ - fun activated(): List = entriesLog().allOccurrences() + fun activated(): List = entries().allOccurrences() - fun toPos(): Pos = Pos(this, entriesLog().size) + fun toPos(): Pos = Pos(this, entries().size) } - class MatchChunk(override val evidence: Evidence, val match: RuleMatch) : Chunk { - private val justifications = match.justifications().apply { add(evidence) } - - override fun justifications(): Justifications = justifications - - override var entries: MutableList = mutableListOf() - - override fun entriesLog(): List = entries - - override fun toString() = "(id=$evidence, ${justifications()}, ${match.rule().tag()}, $entries)" - + /** + * [Chunk] corresponding to a [RuleMatch] of a principal [Rule]. + */ + interface MatchChunk : Chunk { + val match: RuleMatch val ruleUniqueTag: Any get() = match.rule().uniqueTag() } - class OccChunk(val occ: Occurrence) : Chunk, Justified by occ { - - override var entries: MutableList = mutableListOf() - - override fun toString() = "(id=$evidence, ${justifications()}, activation of $occ, $entries)" + /** + * [Chunk] corresponding to an activation of a principal [Occurrence]. + */ + interface OccChunk : Chunk { + val occ: Occurrence } open class Pos(val chunk: Chunk, val entriesCount: Int) { 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 6eeb26ad..5a27f206 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 @@ -34,8 +34,26 @@ internal open class MatchJournalImpl( view: MatchJournal.View? = null ) : MatchJournal { + private abstract class ChunkImpl : Chunk { + var entries: MutableList = mutableListOf() + override fun entries(): List = entries + } + + private class MatchChunkImpl(override val evidence: Evidence, override val match: RuleMatch) : ChunkImpl(), MatchChunk { + override fun toString() = "(id=$evidence, ${justifications()}, ${match.rule().tag()}, $entries)" + + override fun justifications(): Justifications = justifications + + private val justifications = match.justifications().apply { add(evidence) } + } + + private class OccChunkImpl(override val occ: Occurrence) : ChunkImpl(), Justified by occ, OccChunk { + override fun toString() = "(id=$evidence, ${justifications()}, activation of $occ, $entries)" + } + + // invariant: never empty - private val hist: IteratorMutableList + private val hist: IteratorMutableList private var evidenceSeed: Evidence @@ -45,14 +63,14 @@ internal open class MatchJournalImpl( init { if (view == null) { evidenceSeed = 0 - val initChunk = MatchChunk(nextEvidence(), InitRuleMatch) - hist = IteratorMutableList(LinkedList().apply { add(initChunk) }) + val initChunk = MatchChunkImpl(nextEvidence(), InitRuleMatch) + hist = IteratorMutableList(LinkedList().apply { add(initChunk) }) } else { // assert that initial chunk is present with (view.chunks.first()) { assert(this is MatchChunk && match is InitRuleMatch) } - hist = IteratorMutableList(LinkedList(view.chunks as List)) + hist = IteratorMutableList(LinkedList(view.chunks as List)) evidenceSeed = view.evidenceSeed } } @@ -61,9 +79,9 @@ internal open class MatchJournalImpl( // pointer to current position in history where logging (chunk additions) and log erasing (chunk removals) happens - private var posPtr: MutableListIterator = hist.listIterator() + private var posPtr: MutableListIterator = hist.listIterator() // invariant: always contains valid chunk - private var current: Chunk = posPtr.next() // take the initial chunk, move posPtr + private var current: ChunkImpl = posPtr.next() // take the initial chunk, move posPtr override fun iterator(): MutableIterator = hist.iterator() @@ -73,7 +91,7 @@ internal open class MatchJournalImpl( var added: MatchChunk? = null if (ispec.isPrincipal(match.rule())) { - added = MatchChunk(nextEvidence(), match) + added = MatchChunkImpl(nextEvidence(), match) current = added posPtr.add(current) } @@ -89,7 +107,7 @@ internal open class MatchJournalImpl( var added: OccChunk? = null if (ispec.isPrincipal(occ.constraint)) { - added = OccChunk(occ) + added = OccChunkImpl(occ) current = added posPtr.add(current) } @@ -128,7 +146,7 @@ internal open class MatchJournalImpl( posPtr = hist.listIterator(hist.size) while (posPtr.hasPrevious()) { current = posPtr.previous() - resetOccurrences(current.entries) + resetOccurrences(current.entries()) } } @@ -242,7 +260,7 @@ internal open class MatchJournalImpl( val set = HashSet>() for (chunk in hist) { // initial chunk is counted too - chunk.entriesLog().forEach { + chunk.entries().forEach { val idOcc = Id(it.occ) if (it.discarded) set.remove(idOcc) else set.add(idOcc) } diff --git a/reactor/Test/test/TestIncrementalProgram.kt b/reactor/Test/test/TestIncrementalProgram.kt index a771938d..cc1f0fdb 100644 --- a/reactor/Test/test/TestIncrementalProgram.kt +++ b/reactor/Test/test/TestIncrementalProgram.kt @@ -80,7 +80,7 @@ class TestIncrementalProgram { (this.journalView as MatchJournal.View).chunks private fun EvaluationResult.chunksSymbolView() = this.token().chunks().map { - it.entriesLog().map { entry -> !entry.discarded to entry.occ.constraint().symbol() } + it.entries().map { entry -> !entry.discarded to entry.occ.constraint().symbol() } } private fun EvaluationResult.lastChunk() = this.token().chunks().last() as MatchJournal.Chunk