Refactor MatchJournal.Pos. Make RulesDiff usable in coderules mps code. Extend EvaluationTrace.
This commit is contained in:
parent
49aa913556
commit
ed93865359
|
|
@ -20,8 +20,10 @@ import jetbrains.mps.logic.reactor.program.Rule
|
|||
|
||||
data class RulesDiff(val added: Iterable<Rule>, val removed: Set<Any>) {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun emptyDiff() = RulesDiff(emptyList(), emptySet())
|
||||
|
||||
@JvmStatic
|
||||
fun findDiff(old: Iterable<Any>, new: Iterable<Rule>): RulesDiff {
|
||||
val oldSet = old.toHashSet()
|
||||
val newSet = new.toHashSet()
|
||||
|
|
|
|||
|
|
@ -103,21 +103,21 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
|
|||
* Returns position of the first principal occurrence activated in provided chunk.
|
||||
* Returns null if the chunk activates no principal occurrences.
|
||||
*/
|
||||
fun principalOccurrenceOf(chunk: Chunk): OccurrencePos?
|
||||
fun principalOccurrenceOf(chunk: Chunk): Pos?
|
||||
|
||||
/**
|
||||
* Find [OccurrencePos] by occurrence.
|
||||
* Find [Pos] by occurrence.
|
||||
*/
|
||||
fun principalPos(occ: Id<Occurrence>): OccurrencePos? =
|
||||
fun principalPos(occ: Id<Occurrence>): Pos? =
|
||||
// just a composition of two operations
|
||||
activatingChunkOf(occ)?.let { principalOccurrenceOf(it) }
|
||||
|
||||
/**
|
||||
* Find [OccurrencePos] at which provided match was triggered.
|
||||
* Find [Pos] at which provided match was triggered.
|
||||
* Returned position specifies [Occurrence] which triggered the match.
|
||||
* Works not for any match, must work for matches of principal rules.
|
||||
*/
|
||||
fun activationPos(match: RuleMatchEx): OccurrencePos? =
|
||||
fun activationPos(match: RuleMatchEx): Pos? =
|
||||
// The latest matched occurrence from match's head is(by definition)
|
||||
// the occurrence which activated this match.
|
||||
match.signature().mapNotNull { occSig ->
|
||||
|
|
@ -136,14 +136,14 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
|
|||
)
|
||||
}
|
||||
|
||||
abstract class Chunk(val match: RuleMatch, val id: Int, val justifications: Justs) : MatchJournalChunk, Pos()
|
||||
abstract class Chunk(val match: RuleMatch, val id: Int, val justifications: Justs) : MatchJournalChunk
|
||||
{
|
||||
override fun match(): RuleMatch = match
|
||||
override fun id(): Int = id
|
||||
override fun justifications(): TIntSet = justifications
|
||||
|
||||
data class Entry(val occ: Occurrence, val discarded: Boolean = false) : MatchJournalChunk.Entry {
|
||||
override fun occ(): ConstraintOccurrence = occ
|
||||
override fun occ(): Occurrence = occ
|
||||
override fun discarded(): Boolean = discarded
|
||||
override fun toString() = (if (discarded) '-' else '+') + occ.toString()
|
||||
}
|
||||
|
|
@ -151,6 +151,7 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
|
|||
fun isDescendantOf(chunkId: Int): Boolean = justifications().contains(chunkId)
|
||||
fun isTopLevel(): Boolean = justifications().size() <= 1 // this condition implies that there're no ancestor chunks
|
||||
|
||||
abstract override fun entriesLog(): List<Entry>
|
||||
fun activatedLog(): List<Occurrence> = entriesLog().filter { !it.discarded() }.map { it.occ() as Occurrence }
|
||||
fun discardedLog(): List<Occurrence> = entriesLog().filter { it.discarded() }.map { it.occ() as Occurrence }
|
||||
|
||||
|
|
@ -162,31 +163,19 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
|
|||
|
||||
override fun toString() = "(id=${id()}, ${justifications()}, ${match().rule().uniqueTag()}, ${entriesLog()})"
|
||||
|
||||
// fixme: just provide toPos() instead of override?
|
||||
override val chunk: Chunk
|
||||
get() = this
|
||||
override val entriesInChunk: Int
|
||||
get() = entriesLog().size
|
||||
fun toPos(): Pos = Pos(this, entriesLog().size)
|
||||
}
|
||||
|
||||
abstract class Pos {
|
||||
abstract val chunk: Chunk
|
||||
abstract val entriesInChunk: Int
|
||||
open class Pos(val chunk: Chunk, val entriesCount: Int) {
|
||||
val occ: Occurrence
|
||||
get() = chunk.entriesLog()[entriesCount - 1].occ()
|
||||
|
||||
override fun equals(other: Any?) =
|
||||
other is Pos
|
||||
&& other.chunk === chunk
|
||||
&& other.entriesInChunk == entriesInChunk
|
||||
}
|
||||
&& other.entriesCount == entriesCount
|
||||
|
||||
data class OccurrencePos(
|
||||
val occ: Occurrence,
|
||||
override val chunk: Chunk,
|
||||
private val offset: Int): MatchJournal.Pos()
|
||||
{
|
||||
// fixme: remove 'entriesInChunk' everywhere, use just offset in Pos
|
||||
override val entriesInChunk: Int
|
||||
get() = offset + 1
|
||||
override fun hashCode(): Int = 31 * chunk.hashCode() + entriesCount
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ internal open class MatchJournalImpl(
|
|||
}
|
||||
|
||||
|
||||
override fun currentPos(): MatchJournal.Pos = current
|
||||
override fun currentPos(): MatchJournal.Pos = current.toPos()
|
||||
|
||||
override fun resetPos() {
|
||||
posPtr = hist.listIterator()
|
||||
|
|
@ -100,7 +100,7 @@ internal open class MatchJournalImpl(
|
|||
override fun reset(pastPos: MatchJournal.Pos) {
|
||||
while (posPtr.hasPrevious()) {
|
||||
if (current === pastPos.chunk) {
|
||||
current.entries = current.entries.subList(0, pastPos.entriesInChunk)
|
||||
current.entries = current.entries.subList(0, pastPos.entriesCount)
|
||||
return
|
||||
}
|
||||
current = posPtr.previous()
|
||||
|
|
@ -113,7 +113,7 @@ internal open class MatchJournalImpl(
|
|||
while (posPtr.hasNext()) {
|
||||
current = posPtr.next()
|
||||
if (futurePos.chunk === current) {
|
||||
replayOccurrences(controller, current.entries.take(futurePos.entriesInChunk))
|
||||
replayOccurrences(controller, current.entries.take(futurePos.entriesCount))
|
||||
return
|
||||
}
|
||||
replayOccurrences(controller, current.entries)
|
||||
|
|
@ -181,7 +181,7 @@ internal open class MatchJournalImpl(
|
|||
// only for principal constraints
|
||||
private val parentChunks: Map<Id<Occurrence>, MatchJournal.Chunk>
|
||||
|
||||
private val principalOccurrences: Map<Int, MatchJournal.OccurrencePos>
|
||||
private val principalOccurrences: Map<Int, MatchJournal.Pos>
|
||||
|
||||
init {
|
||||
chunkOrder = HashMap<Int, Int>().apply {
|
||||
|
|
@ -189,13 +189,13 @@ internal open class MatchJournalImpl(
|
|||
}
|
||||
|
||||
val m = HashMap<Id<Occurrence>, MatchJournal.Chunk>()
|
||||
val m2 = HashMap<Int, MatchJournal.OccurrencePos>()
|
||||
val m2 = HashMap<Int, MatchJournal.Pos>()
|
||||
chunks.forEach { chunk ->
|
||||
// actually there should be only a single principal occurrence, 'find' is enough
|
||||
chunk.entriesLog().forEachIndexed { index, e ->
|
||||
if (ispec.isPrincipal(e.occ.constraint()) && !e.discarded()) {
|
||||
m[Id(e.occ)] = chunk
|
||||
m2[chunk.id] = MatchJournal.OccurrencePos(e.occ, chunk, index)
|
||||
m2[chunk.id] = MatchJournal.Pos(chunk, index + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -210,7 +210,7 @@ internal open class MatchJournalImpl(
|
|||
// todo: throw for invalid positions?
|
||||
override fun compare(lhs: MatchJournal.Pos, rhs: MatchJournal.Pos): Int {
|
||||
val co = compareBy<MatchJournal.Pos>{ chunkOrder[it.chunk.id] }.compare(lhs, rhs)
|
||||
return if (co == 0) lhs.entriesInChunk.compareTo(rhs.entriesInChunk) else co
|
||||
return if (co == 0) lhs.entriesCount.compareTo(rhs.entriesCount) else co
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,11 +71,13 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
|
|||
private data class MatchCandidate(val rule: Rule, val occ: Occurrence, val occParentId: Int)
|
||||
|
||||
|
||||
// Invalidation includes several activities:
|
||||
// - removing chunks (i.e. principal matches) corresponding to
|
||||
// removed rules and chunks depending on them from journal
|
||||
// - reactivating occurrences that led to invalidated matches
|
||||
// - pruning invalidated occurrences and matches from Dispatcher's state
|
||||
/**
|
||||
* Invalidation includes several activities:
|
||||
* - removing chunks (i.e. principal matches) corresponding to
|
||||
* removed rules and chunks depending on them from journal
|
||||
* - reactivating occurrences that led to invalidated matches
|
||||
* - pruning invalidated occurrences and matches from Dispatcher's state
|
||||
*/
|
||||
fun invalidateRuleMatches(ruleIds: Set<Any>) {
|
||||
val justificationRoots = mutableListOf<Int>()
|
||||
|
||||
|
|
@ -101,7 +103,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
|
|||
// (i.e. don't reactivate additional, inactive heads that only completed the match?)
|
||||
execQueue.addAll(validOccs.mapNotNull { occ ->
|
||||
journalIndex.activatingChunkOf(Id(occ))?.let { chunkPos ->
|
||||
ExecPos(chunkPos, occ)
|
||||
ExecPos(chunkPos.toPos(), occ)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -111,6 +113,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
|
|||
if (toInvalidate) {
|
||||
// Remove the chunk from the journal
|
||||
it.remove()
|
||||
trace.invalidate(chunk.match())
|
||||
|
||||
// Seems, it's not strictly necessary, because some of its head occurrences are anyway invalidated forever
|
||||
// and storing this invalid consumed match can make no harm, except some memory overhead.
|
||||
|
|
@ -158,7 +161,8 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
|
|||
// Also remember the parent justification of this rule candidate
|
||||
// to drop it from monitoring when child chunks end.
|
||||
activationCandidates.add(MatchCandidate(rule, pOcc, chunk.id))
|
||||
// todo: also use the rule to help Dispatcher in future? i.e. try matching only on the candidate rule
|
||||
// todo: also use the rule to help Dispatcher in future?
|
||||
// i.e. try matching only on the candidate rule
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -175,12 +179,13 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
|
|||
|
||||
val pos =
|
||||
// We need the previous chunk as pos here (i.e. adding after it).
|
||||
if (childChunksEnded || placeToInsertFound) prevChunk
|
||||
if (childChunksEnded || placeToInsertFound) prevChunk.toPos()
|
||||
// Case when adding at the very end
|
||||
else if (!it.hasNext()) chunk
|
||||
else if (!it.hasNext()) chunk.toPos()
|
||||
else continue
|
||||
|
||||
execQueue.offer(ExecPos(pos, candOcc))
|
||||
trace.potentialMatch(candOcc, candRule)
|
||||
// Drop the candidate if appropriate activation place is found.
|
||||
aIt.remove()
|
||||
}
|
||||
|
|
@ -195,17 +200,17 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
|
|||
if (execQueue.isNotEmpty()) {
|
||||
resetStore()
|
||||
|
||||
var prevPos: ExecPos? = null
|
||||
var prevPos: MatchJournal.Pos? = null
|
||||
do {
|
||||
val execPos = execQueue.poll()
|
||||
|
||||
// Handles the case when several matches are added to the same position.
|
||||
// Then shouldn't replay, because currentPos is valid and more recent (!) than execPos.
|
||||
if (execPos != prevPos) {
|
||||
if (execPos.pos != prevPos) {
|
||||
replay(controller, execPos.pos)
|
||||
lastIncrementalRootPos = execPos.pos
|
||||
}
|
||||
prevPos = execPos
|
||||
prevPos = execPos.pos
|
||||
|
||||
// If the occurrence is still in the store after replay (i.e. if it's valid to activate it)
|
||||
if (execPos.activeOcc.stored) {
|
||||
|
|
@ -213,12 +218,13 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
|
|||
// Incremental reactivation isn't like the usual reactivation,
|
||||
// it should proceed more like usual activation.
|
||||
this.dispatchingFront = dispatchingFront.forgetSeen(execPos.activeOcc)
|
||||
trace.reactivateIncremental(execPos.activeOcc)
|
||||
controller.reactivate(execPos.activeOcc)
|
||||
}
|
||||
} while (execQueue.isNotEmpty())
|
||||
}
|
||||
// Also replay to the end after queue is fully executed
|
||||
replay(controller, this.last())
|
||||
replay(controller, this.last().toPos())
|
||||
// fixme: get FeedbackStatus out of reactivate()
|
||||
return FeedbackStatus.NORMAL()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,11 +41,11 @@ internal open class StoreAwareJournalImpl(private val journal: MatchJournal, pri
|
|||
: MatchJournal by journal, StoreAwareJournal, ProcessingState by state
|
||||
{
|
||||
|
||||
private class PosImpl(
|
||||
private class FramePos(
|
||||
val frame: StateFrame,
|
||||
override val chunk: MatchJournal.Chunk,
|
||||
override val entriesInChunk: Int = 0
|
||||
) : MatchJournal.Pos()
|
||||
chunk: MatchJournal.Chunk,
|
||||
entriesCount: Int = 0
|
||||
) : MatchJournal.Pos(chunk, entriesCount)
|
||||
|
||||
|
||||
fun push() = state.push()
|
||||
|
|
@ -60,12 +60,12 @@ internal open class StoreAwareJournalImpl(private val journal: MatchJournal, pri
|
|||
|
||||
|
||||
override fun currentPos(): MatchJournal.Pos =
|
||||
PosImpl(state.currentFrame(), journal.currentPos().chunk, journal.currentPos().entriesInChunk)
|
||||
FramePos(state.currentFrame(), journal.currentPos().chunk, journal.currentPos().entriesCount)
|
||||
|
||||
// Throw away recently added chunks and reset store accordingly
|
||||
// NB: not checking that chunks are actually recently added, from this exec session
|
||||
override fun reset(pastPos: MatchJournal.Pos) {
|
||||
if (pastPos is PosImpl) {
|
||||
if (pastPos is FramePos) {
|
||||
state.reset(pastPos.frame)
|
||||
journal.reset(pastPos)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package jetbrains.mps.logic.reactor.evaluation;
|
|||
|
||||
|
||||
import jetbrains.mps.logic.reactor.core.EvaluationFailure;
|
||||
import jetbrains.mps.logic.reactor.program.Rule;
|
||||
|
||||
/**
|
||||
* An interface to be implemented by clients wishing to be notified of the events during evaluation.
|
||||
|
|
@ -51,4 +52,10 @@ public interface EvaluationTrace {
|
|||
default void ask(boolean result, PredicateInvocation invocation) {}
|
||||
|
||||
default void feedback(EvaluationFeedback feedback) {}
|
||||
|
||||
default void invalidate(RuleMatch ruleMatch) {}
|
||||
|
||||
default void reactivateIncremental(ConstraintOccurrence occurrence) {}
|
||||
|
||||
default void potentialMatch(ConstraintOccurrence occurrence, Rule rule) {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -452,7 +452,7 @@ class TestStoreAwareJournal {
|
|||
// continue from the second chunk (match of rule1)
|
||||
val rmIt = iterator()
|
||||
rmIt.next() // skip initial chunk
|
||||
val continueFrom = rmIt.next()
|
||||
val continueFrom = rmIt.next().toPos()
|
||||
rmIt.next()
|
||||
rmIt.remove()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue