Add initial version of logic for handling future matches in processActivated()

This commit is contained in:
Grigorii Kirgizov 2019-06-05 18:39:49 +03:00
parent dffab2016c
commit 50fcfd0d8a
2 changed files with 77 additions and 19 deletions

View File

@ -38,6 +38,8 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
fun logActivation(occ: Occurrence)
fun currentPos(): Pos
fun isCurrent(): Boolean = currentPos().chunk() == this.last()
fun resetPos()
fun reset(pastPos: Pos)
fun replay(controller: Controller, futurePos: Pos)
@ -89,6 +91,25 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
&& other.chunk() === chunk()
&& other.entriesInChunk() == entriesInChunk()
}
data class OccurrencePos(val occ: Occurrence, private val chunk: MatchJournal.Chunk, private val offset: Int): MatchJournal.Pos()
{
companion object {
fun get(index: MatchJournal.Index, occ: Occurrence): OccurrencePos? {
val idOcc = Id(occ)
val chunk = index.activatingChunkOf(idOcc) ?: return null
val i = chunk.entriesLog().indexOfFirst { entry ->
Id(entry.occ) == idOcc && !entry.isDiscarded
}
val offset = i + 1
return if (offset >= 0) OccurrencePos(occ, chunk, offset) else null
}
}
override fun chunk(): MatchJournal.Chunk = chunk
override fun entriesInChunk(): Int = offset
}
}
@ -228,7 +249,6 @@ internal open class MatchJournalImpl(
}
}
class IndexImpl(ispec: IncrementalProgramSpec, chunks: Iterable<MatchJournal.Chunk>): MatchJournal.Index
{
private val chunkOrder: Map<Int, Int>
@ -257,18 +277,6 @@ internal open class MatchJournalImpl(
val co = compareBy<MatchJournal.Pos>{ chunkOrder[it.chunk().id] }.compare(lhs, rhs)
return if (co == 0) lhs.entriesInChunk().compareTo(rhs.entriesInChunk()) else co
}
// Compare occurrences by their activating chunk
// todo: test case for two principal occurrences activated in the same chunk???
fun compare(lhs: Occurrence, rhs: Occurrence): Int {
val cl = activatingChunkOf(Id(lhs))
val cr = activatingChunkOf(Id(rhs))
if (cl == null && cr == null) return 0
if (cl == null) return -1
if (cr == null) return 1
return this.compare(cl, cr)
}
}

View File

@ -25,7 +25,6 @@ import jetbrains.mps.logic.reactor.program.Rule
import jetbrains.mps.logic.reactor.util.Id
import jetbrains.mps.logic.reactor.util.Profiler
import jetbrains.mps.logic.reactor.util.profile
import org.jetbrains.kotlin.utils.mapToIndex
import java.util.*
@ -110,11 +109,18 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
val profiler: Profiler? = null)
: StoreAwareJournalImpl(journal)
{
private val ruleOrder: Map<Any, Int> = ruleIndex.map { it.uniqueTag() }.mapToIndex()
private val ruleOrder: Map<Any, Int> = HashMap<Any, Int>().apply {
ruleIndex.forEachIndexed { index, rule -> put(rule.uniqueTag(), index) }
}
private val journalIndex: MatchJournal.Index = journal.index()
private val execQueue: Queue<ExecPos> = LinkedList<ExecPos>()
// private val execQueue: Queue<ExecPos> = PriorityQueue<ExecPos>(this.count() / 3) { lhs, rhs -> journalIndex.compare(lhs.pos, rhs.pos) }
private val postponedMatches: MutableMap<Id<Occurrence>, List<RuleMatchEx>> = HashMap()
private val execQueue: Queue<ExecPos> =
PriorityQueue<ExecPos>(1 + this.count() / 2) {
lhs, rhs -> journalIndex.compare(lhs.pos, rhs.pos)
}
private data class ExecPos(val pos: MatchJournal.Pos, val activeOcc: Occurrence)
@ -319,7 +325,6 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
active.stored = true
trace.activate(active)
// todo: need doing it at reactivation?
logActivation(active)
active.revive(controller)
@ -329,7 +334,20 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
this.dispatchingFront = dispatchingFront.expand(active)
val outStatus = dispatchingFront.matches().toList().fold(inStatus) { status, match ->
val matches = dispatchingFront.matches().toList()
val currentMatches =
// todo: assert that there can be no future matches on non-principal occurrences?
// if (isCurrent() || !active.isPrincipal()) {
if (true) {
matches
} else {
val newCurrentMatches = handleFutureMatches(matches)
val allMatches = newCurrentMatches + (postponedMatches.remove(Id(active)) ?: emptyList())
// Sort according to rule priorities
allMatches.sortedBy { ruleOrder[it.rule().uniqueTag()] }
}
val outStatus = currentMatches.fold(inStatus) { status, match ->
// TODO: paranoid check. should be isAlive() instead
// FIXME: move this check elsewhere
if (status.operational && active.stored && match.allStored())
@ -347,6 +365,38 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
}
}
// TODO: provide ispec to ProcessingStateImpl and use it
private fun Occurrence.isPrincipal() = this.constraint().isPrincipal()
// Determines, filters out and enqueues (to execution queue) future matches. Returns only current matches.
private fun handleFutureMatches(matches: List<RuleMatchEx>): List<RuleMatchEx> {
val currentPos = currentPos()
val currentMatches = mutableListOf<RuleMatchEx>()
for (m in matches) {
latestMatchedOccurrence(m)?.let { pos ->
// if it is a future match
if (journalIndex.compare(currentPos, pos) > 0) {
val idOcc = Id(pos.occ)
postponedMatches[idOcc] = (postponedMatches[idOcc] ?: emptyList()) + listOf(m)
execQueue.offer(ExecPos(pos, pos.occ))
} else {
currentMatches.add(m)
}
}
}
return currentMatches
}
// The latest matched occurrence from match's head is (by definition) the occurrence which activated this match.
private fun latestMatchedOccurrence(match: RuleMatchEx): MatchJournal.OccurrencePos? =
match.signature().mapNotNull { occSig ->
occSig?.wrapped?.let { occ ->
MatchJournal.OccurrencePos.get(journalIndex, occ)
}
}.maxWith(journalIndex) // compare positions: find latest
private inline fun FeedbackStatus.then(action: (FeedbackStatus) -> FeedbackStatus) : FeedbackStatus =
if (operational) action(this) else this