From 77bc7538a476183b7306ccf55465553dcb56cede Mon Sep 17 00:00:00 2001 From: Grigorii Kirgizov Date: Mon, 20 May 2019 21:07:05 +0300 Subject: [PATCH] Prepare before merging StoreAwareJournal & ProcessingStateImpl: extract FrameStack from ProcState, inherit StoreAwareJ from MatchJ by delegation --- .../reactor/core/internal/ControllerImpl.kt | 12 +- .../reactor/core/internal/MatchJournal.kt | 199 +++++++++++++++++ .../core/internal/ProcessingStateImpl.kt | 21 +- .../core/internal/StoreAwareJournal.kt | 205 ++---------------- reactor/Test/test/TestStoreAwareJournal.kt | 6 +- 5 files changed, 236 insertions(+), 207 deletions(-) create mode 100644 reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt index 3bd3a000..726f9e34 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt @@ -34,15 +34,18 @@ internal class ControllerImpl ( val trace: EvaluationTrace = EvaluationTrace.NULL, val profiler: Profiler? = null) : Controller { + // fixme +// val journal: StoreAwareJournalImpl = StoreAwareJournalImpl(state, null) /** For tests only */ // override fun storeView(): StoreView = state.storeView() +// override fun storeView(): StoreView = journal.storeView() override fun storeView(): StoreView = TODO() /** For tests only */ override fun evaluate(occ: Occurrence): StoreView { // create the internal occurrence - val active = occ.constraint().occurrence(this, occ.arguments()) + val active = occ.constraint().occurrence(this, occ.arguments(), occ.justifications()) val status = state.processActivated(active, NORMAL()) if (status is FAILED) { @@ -131,6 +134,7 @@ internal class ControllerImpl ( } val savedFrame = state.currentFrame() +// val savedPos = journal.currentPos() for (item in body) { val itemOk = when (item) { @@ -177,6 +181,7 @@ internal class ControllerImpl ( if (!altOk) { // all constraints activated up to a failure are lost state.reset(savedFrame) +// journal.reset(savedPos) } else { // body finished normally @@ -191,8 +196,9 @@ internal class ControllerImpl ( val args = supervisor.instantiateArguments(constraint.arguments(), context.logicalContext, context) return context.eval { status -> - state.processActivated(constraint.occurrence(this, args, justsOf(), context.logicalContext), status) - +// val justs = if (constraint.isPrincipal) journal.justs() else justsOf() + val justs = justsOf() + state.processActivated(constraint.occurrence(this, args, justs, context.logicalContext), status) } } 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 new file mode 100644 index 00000000..f6bb6263 --- /dev/null +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt @@ -0,0 +1,199 @@ +/* + * Copyright 2014-2019 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package jetbrains.mps.logic.reactor.core.internal + +import jetbrains.mps.logic.reactor.core.Justs +import jetbrains.mps.logic.reactor.core.Occurrence +import jetbrains.mps.logic.reactor.core.justsOf +import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence +import jetbrains.mps.logic.reactor.evaluation.RuleMatch +import jetbrains.mps.logic.reactor.evaluation.StoreView +import jetbrains.mps.logic.reactor.program.ConstraintSymbol +import jetbrains.mps.logic.reactor.util.Id +import java.util.* +import kotlin.collections.ArrayList + + +interface MatchJournal : MutableIterable { + + fun logMatch(match: RuleMatch) + fun logActivation(occ: Occurrence) + + fun currentPos(): Pos + fun resetPos() + fun reset(pastPos: Pos) + fun replay(futurePos: Pos) + + fun view(): View + fun storeView(): StoreView + + + data class View(val chunks: List, val nextChunkId: Int) + + data class Chunk(val match: RuleMatch, val id: Int, val justifications: Justs) : Pos { + data class Entry(val occ: Occurrence, val isDiscarded: Boolean = false) { + override fun toString() = (if (isDiscarded) '-' else '+') + occ.toString() + } + + var occurrences: MutableList = mutableListOf() + + override fun toString() = "(id=$id, $justifications, ${match.rule().uniqueTag()}, $occurrences)" + + override fun chunk(): Chunk = this + override fun entriesInChunk(): Int = occurrences.size + } + + interface Pos { + fun chunk(): Chunk + fun entriesInChunk(): Int + } +} + + +internal open class MatchJournalImpl(view: MatchJournal.View?): MatchJournal { + + protected val hist: MutableList + protected var nextChunkId: Int + + init { + if (view == null) { + hist = LinkedList() + nextChunkId = 0 + } else { + hist = LinkedList(view.chunks) + nextChunkId = view.nextChunkId + } + } + + constructor(chunkIdSeed: Int) : this(null) { + nextChunkId = chunkIdSeed + } + + // FIXME: provide initial chunk? possibly initialized from StoreView, with empty justifications + protected lateinit var current: MatchJournal.Chunk + protected var pos: MutableListIterator = hist.listIterator() + + + override fun iterator() = hist.iterator() + + + override fun logMatch(match: RuleMatch) { + val m = match as RuleMatchImpl + // If the set of justifications isn't empty, then we deal with principal constraint + val justs = match.headJustifications() + if (!justs.isEmpty) { + justs.add(nextChunkId) + val newChunk = MatchJournal.Chunk(match, nextChunkId, justs) + pos.add(newChunk) + + ++nextChunkId + current = newChunk + } + + // Log discards + match.forEachReplaced {occ -> + current.occurrences.add(MatchJournal.Chunk.Entry(occ, true)) + occ.terminate() + } + } + + override fun logActivation(occ: Occurrence) { + current.occurrences.add(MatchJournal.Chunk.Entry(occ)) + occ.revive() + } + + + override fun currentPos(): MatchJournal.Pos = current + + // fixme: unclear, whether this makes sense here, in "pure" journal without store? along with reset and replay? + override fun resetPos() { pos = hist.listIterator() } + + override fun reset(pastPos: MatchJournal.Pos) { + while (pos.hasPrevious()) { + current = pos.previous() + if (current === pastPos.chunk()) { + current.occurrences = current.occurrences.take(pastPos.entriesInChunk()) as MutableList + return + } + pos.remove() + } + throw IllegalStateException() + } + + override fun replay(futurePos: MatchJournal.Pos) { + while (pos.hasNext()) { + current = pos.next() + if (futurePos.chunk() === current) { + replayOccurrences(current.occurrences.take(futurePos.entriesInChunk())) + return + } + replayOccurrences(current.occurrences) + } + throw IllegalStateException() + } + + private fun replayOccurrences(occSpecs: Iterable) = + occSpecs.forEach { if (it.isDiscarded) it.occ.terminate() else it.occ.revive() } + + + override fun view() = MatchJournal.View(ArrayList(hist), nextChunkId) + + override fun storeView(): StoreView = StoreViewImpl(allOccurrences()) + + private fun allOccurrences(): Sequence { + // the following lop doesn't handle this case of starting pos, when 'current' isn't valid + if (!pos.hasPrevious()) + return emptySequence() + + val set = HashSet>() + for (chunk in hist) { + chunk.occurrences.forEach { + if (it.isDiscarded) set.remove(Id(it.occ)) else set.add(Id(it.occ)) + } + if (chunk === current) + return set.map { it.wrapped }.asSequence() + } + throw IllegalStateException() + } + + + private class StoreViewImpl(occurrences: Sequence) : StoreView { + + val allOccurrences = occurrences.toSet() + + val allSymbols = allOccurrences.map { co -> co.constraint().symbol() }.toSet() + + override fun constraintSymbols(): Iterable = allSymbols + + override fun allOccurrences(): Iterable = allOccurrences + + override fun occurrences(symbol: ConstraintSymbol): Iterable = + allOccurrences.filter { co -> co.constraint().symbol() == symbol }.toSet() + + } +} + +fun MatchJournal.justs() = this.currentPos().chunk().justifications + + +private fun RuleMatch.headJustifications(): Justs { + val res: Justs = justsOf() + this.matchHeadKept().forEach { it.justifications().let { res.addAll(it) } } + this.matchHeadReplaced().forEach { it.justifications().let { res.addAll(it) } } + return res +} + diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt index 9e1c985f..e877d1df 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingStateImpl.kt @@ -44,19 +44,13 @@ import java.util.* * @author Fedor Isakov */ -internal class ProcessingStateImpl constructor(dispatcher: Dispatcher, - val trace: EvaluationTrace = EvaluationTrace.NULL, - val profiler: Profiler? = null) : - ProcessingState, LogicalObserver + +internal open class StateFrameStack() : ProcessingState, LogicalObserver { - - private var dispatchingFront: Dispatcher.DispatchingFront - // invariant: never empty private val stateFrames = LinkedList() init { - this.dispatchingFront = dispatcher.front() stateFrames.push(StateFrame()) } @@ -106,6 +100,17 @@ internal class ProcessingStateImpl constructor(dispatcher: Dispatcher, currentFrame().parentUpdated(logical) } +} + + +internal class ProcessingStateImpl constructor(dispatcher: Dispatcher, + val trace: EvaluationTrace = EvaluationTrace.NULL, + val profiler: Profiler? = null) : + StateFrameStack() +{ + + private var dispatchingFront: Dispatcher.DispatchingFront = dispatcher.front() + /** * Called to update the state with the currently active constraint occurrence. diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/StoreAwareJournal.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/StoreAwareJournal.kt index 3f9d55a8..516ada6f 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/StoreAwareJournal.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/StoreAwareJournal.kt @@ -16,195 +16,24 @@ package jetbrains.mps.logic.reactor.core.internal -import gnu.trove.set.TIntSet -import gnu.trove.set.hash.TIntHashSet import jetbrains.mps.logic.reactor.core.Dispatcher -import jetbrains.mps.logic.reactor.core.Justs -import jetbrains.mps.logic.reactor.core.Occurrence -import jetbrains.mps.logic.reactor.core.justsOf -import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence -import jetbrains.mps.logic.reactor.evaluation.RuleMatch -import jetbrains.mps.logic.reactor.evaluation.StoreView -import jetbrains.mps.logic.reactor.program.ConstraintSymbol -import jetbrains.mps.logic.reactor.util.Id +import jetbrains.mps.logic.reactor.core.ProcessingState import java.lang.IllegalArgumentException -import java.util.* -import kotlin.collections.ArrayList +abstract class StoreAwareJournal(val journal: MatchJournal): MatchJournal by journal { -interface MatchJournal : MutableIterable { - - fun logMatch(match: RuleMatch) - fun logActivation(occ: Occurrence) - - fun currentPos(): Pos - fun reset(pastPos: Pos) - fun replay(futurePos: Pos) - - fun view(): View - fun storeView(): StoreView - - - data class View(val chunks: List, val nextChunkId: Int) - - data class Chunk(val match: RuleMatch, val id: Int, val justifications: Justs) : Pos { - data class Entry(val occ: Occurrence, val isDiscarded: Boolean = false) { - override fun toString() = (if (isDiscarded) '-' else '+') + occ.toString() - } - - var occurrences: MutableList = mutableListOf() - - override fun toString() = "(id=$id, $justifications, ${match.rule().uniqueTag()}, $occurrences)" - - override fun chunk(): Chunk = this - override fun entriesInChunk(): Int = occurrences.size - } - - interface Pos { - fun chunk(): Chunk - fun entriesInChunk(): Int - } -} - -interface StoreAwareJournal: MatchJournal { - - fun testPush(): Unit - fun resetStore() + abstract fun testPush(): Unit + abstract fun resetStore() companion object { - fun fromSeed(disp: Dispatcher, chunkIdSeed: Int = 0): StoreAwareJournal = StoreAwareJournalImpl(ProcessingStateImpl(disp), chunkIdSeed) - fun fromView(disp: Dispatcher, view: MatchJournal.View): StoreAwareJournal = StoreAwareJournalImpl(ProcessingStateImpl(disp), view) + fun fromSeed(disp: Dispatcher, chunkIdSeed: Int = 0): StoreAwareJournal = StoreAwareJournalImpl(MatchJournalImpl(chunkIdSeed)) + fun fromView(disp: Dispatcher, view: MatchJournal.View): StoreAwareJournal = StoreAwareJournalImpl(MatchJournalImpl(view)) } } - -internal open class MatchJournalImpl(view: MatchJournal.View?): MatchJournal { - - protected val hist: MutableList - protected var nextChunkId: Int - - init { - if (view == null) { - hist = LinkedList() - nextChunkId = 0 - } else { - hist = LinkedList(view.chunks) - nextChunkId = view.nextChunkId - } - } - - constructor(chunkIdSeed: Int) : this(null) { - nextChunkId = chunkIdSeed - } - - // FIXME: provide initial chunk? possibly initialized from StoreView, with empty justifications - protected lateinit var current: MatchJournal.Chunk - protected var pos: MutableListIterator = hist.listIterator() - - - override fun iterator() = hist.iterator() - - - override fun logMatch(match: RuleMatch) { - val m = match as RuleMatchImpl - // If the set of justifications isn't empty, then we deal with principal constraint - val justs = match.headJustifications() - if (!justs.isEmpty) { - justs.add(nextChunkId) - val newChunk = MatchJournal.Chunk(match, nextChunkId, justs) - pos.add(newChunk) - - ++nextChunkId - current = newChunk - } - - // Log discards - match.forEachReplaced {occ -> - current.occurrences.add(MatchJournal.Chunk.Entry(occ, true)) - occ.terminate() - } - } - - override fun logActivation(occ: Occurrence) { - current.occurrences.add(MatchJournal.Chunk.Entry(occ)) - occ.revive() - } - - - override fun currentPos(): MatchJournal.Pos = current - - override fun reset(pastPos: MatchJournal.Pos) { - while (pos.hasPrevious()) { - current = pos.previous() - if (current === pastPos.chunk()) { - current.occurrences = current.occurrences.take(pastPos.entriesInChunk()) as MutableList - return - } - pos.remove() - } - throw IllegalStateException() - } - - override fun replay(futurePos: MatchJournal.Pos) { - while (pos.hasNext()) { - current = pos.next() - if (futurePos.chunk() === current) { - replayOccurrences(current.occurrences.take(futurePos.entriesInChunk())) - return - } - replayOccurrences(current.occurrences) - } - throw IllegalStateException() - } - - private fun replayOccurrences(occSpecs: Iterable) = - occSpecs.forEach { if (it.isDiscarded) it.occ.terminate() else it.occ.revive() } - - - override fun view() = MatchJournal.View(ArrayList(hist), nextChunkId) - - override fun storeView(): StoreView = StoreViewImpl(allOccurrences()) - - private fun allOccurrences(): Sequence { - // the following lop doesn't handle this case of starting pos, when 'current' isn't valid - if (!pos.hasPrevious()) - return emptySequence() - - val set = HashSet>() - for (chunk in hist) { - chunk.occurrences.forEach { - if (it.isDiscarded) set.remove(Id(it.occ)) else set.add(Id(it.occ)) - } - if (chunk === current) - return set.map { it.wrapped }.asSequence() - } - throw IllegalStateException() - } - - - private class StoreViewImpl(occurrences: Sequence) : StoreView { - - val allOccurrences = occurrences.toSet() - - val allSymbols = allOccurrences.map { co -> co.constraint().symbol() }.toSet() - - override fun constraintSymbols(): Iterable = allSymbols - - override fun allOccurrences(): Iterable = allOccurrences - - override fun occurrences(symbol: ConstraintSymbol): Iterable = - allOccurrences.filter { co -> co.constraint().symbol() == symbol }.toSet() - - } -} - - -internal class StoreAwareJournalImpl(val state: ProcessingStateImpl, view: MatchJournal.View?): StoreAwareJournal, MatchJournalImpl(view) { - - constructor(state: ProcessingStateImpl, chunkIdSeed: Int) : this(state, null) { - nextChunkId = chunkIdSeed - } - +internal class StoreAwareJournalImpl(journal: MatchJournal, private val state: StateFrameStack = StateFrameStack()) + : StoreAwareJournal(journal), ProcessingState by state +{ private data class PosImpl(val frame: StateFrame, val chunk: MatchJournal.Chunk, val entriesInChunk: Int = 0) : MatchJournal.Pos { override fun chunk(): MatchJournal.Chunk = chunk @@ -215,7 +44,7 @@ internal class StoreAwareJournalImpl(val state: ProcessingStateImpl, view: Match // Reset only store & history position, don't modify history override fun resetStore() { state.reset() - pos = hist.listIterator() + this.resetPos() } // FrameStack API: currentFrame/currentPos, push, reset @@ -224,7 +53,9 @@ internal class StoreAwareJournalImpl(val state: ProcessingStateImpl, view: Match override fun testPush() { state.push() } fun push() = state.push() - override fun currentPos(): MatchJournal.Pos = PosImpl(state.currentFrame(), current, current.occurrences.size) + + override fun currentPos(): MatchJournal.Pos = + PosImpl(state.currentFrame(), super.currentPos().chunk(), super.currentPos().entriesInChunk()) // Throw away recently added chunks and reset store accordingly // NB: not checking that chunks are actually recently added, from this exec session @@ -236,12 +67,4 @@ internal class StoreAwareJournalImpl(val state: ProcessingStateImpl, view: Match throw IllegalArgumentException() } } -} - -private fun RuleMatch.headJustifications(): Justs { - val res: Justs = justsOf() - this.matchHeadKept().forEach { it.justifications().let { res.addAll(it) } } - this.matchHeadReplaced().forEach { it.justifications().let { res.addAll(it) } } - return res -} - +} \ No newline at end of file diff --git a/reactor/Test/test/TestStoreAwareJournal.kt b/reactor/Test/test/TestStoreAwareJournal.kt index c1b69ea9..1ce09a78 100644 --- a/reactor/Test/test/TestStoreAwareJournal.kt +++ b/reactor/Test/test/TestStoreAwareJournal.kt @@ -1,7 +1,5 @@ -import gnu.trove.set.hash.TIntHashSet import jetbrains.mps.logic.reactor.core.* -import jetbrains.mps.logic.reactor.core.internal.MatchJournal -import jetbrains.mps.logic.reactor.core.internal.StoreAwareJournal +import jetbrains.mps.logic.reactor.core.internal.* import jetbrains.mps.logic.reactor.program.ConstraintSymbol import org.junit.Test import org.junit.Assert.* @@ -23,8 +21,6 @@ import org.junit.Assert.* */ -fun MatchJournal.justs() = this.currentPos().chunk().justifications - class TestStoreAwareJournal { @Test