Prepare before merging StoreAwareJournal & ProcessingStateImpl: extract FrameStack from ProcState, inherit StoreAwareJ from MatchJ by delegation

This commit is contained in:
Grigorii Kirgizov 2019-05-20 21:07:05 +03:00
parent 799b57c74e
commit 77bc7538a4
5 changed files with 236 additions and 207 deletions

View File

@ -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)
}
}

View File

@ -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<MatchJournal.Chunk> {
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<Chunk>, 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<Entry> = 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<MatchJournal.Chunk>
protected var nextChunkId: Int
init {
if (view == null) {
hist = LinkedList<MatchJournal.Chunk>()
nextChunkId = 0
} else {
hist = LinkedList<MatchJournal.Chunk>(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<MatchJournal.Chunk> = 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<MatchJournal.Chunk.Entry>
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<MatchJournal.Chunk.Entry>) =
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<Occurrence> {
// the following lop doesn't handle this case of starting pos, when 'current' isn't valid
if (!pos.hasPrevious())
return emptySequence()
val set = HashSet<Id<Occurrence>>()
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<Occurrence>) : StoreView {
val allOccurrences = occurrences.toSet()
val allSymbols = allOccurrences.map { co -> co.constraint().symbol() }.toSet()
override fun constraintSymbols(): Iterable<ConstraintSymbol> = allSymbols
override fun allOccurrences(): Iterable<ConstraintOccurrence> = allOccurrences
override fun occurrences(symbol: ConstraintSymbol): Iterable<ConstraintOccurrence> =
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
}

View File

@ -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<StateFrame>()
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.

View File

@ -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<MatchJournal.Chunk> {
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<Chunk>, 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<Entry> = 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<MatchJournal.Chunk>
protected var nextChunkId: Int
init {
if (view == null) {
hist = LinkedList<MatchJournal.Chunk>()
nextChunkId = 0
} else {
hist = LinkedList<MatchJournal.Chunk>(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<MatchJournal.Chunk> = 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<MatchJournal.Chunk.Entry>
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<MatchJournal.Chunk.Entry>) =
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<Occurrence> {
// the following lop doesn't handle this case of starting pos, when 'current' isn't valid
if (!pos.hasPrevious())
return emptySequence()
val set = HashSet<Id<Occurrence>>()
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<Occurrence>) : StoreView {
val allOccurrences = occurrences.toSet()
val allSymbols = allOccurrences.map { co -> co.constraint().symbol() }.toSet()
override fun constraintSymbols(): Iterable<ConstraintSymbol> = allSymbols
override fun allOccurrences(): Iterable<ConstraintOccurrence> = allOccurrences
override fun occurrences(symbol: ConstraintSymbol): Iterable<ConstraintOccurrence> =
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
}
}

View File

@ -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