Add MatchJournal.Index for additional queries and operations involving ordering of occurrences

This commit is contained in:
Grigorii Kirgizov 2019-06-04 19:44:56 +03:00
parent eec343f902
commit 3e50f47f73
2 changed files with 62 additions and 22 deletions

View File

@ -25,7 +25,9 @@ import jetbrains.mps.logic.reactor.logical.LogicalContext
import jetbrains.mps.logic.reactor.logical.MetaLogical
import jetbrains.mps.logic.reactor.program.*
import jetbrains.mps.logic.reactor.util.Id
import org.jetbrains.kotlin.utils.mapToIndex
import java.util.*
import kotlin.Comparator
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
@ -35,8 +37,6 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
fun logMatch(match: RuleMatch)
fun logActivation(occ: Occurrence)
fun activatingChunkOf(occ: Occurrence): Chunk?
fun currentPos(): Pos
fun resetPos()
fun reset(pastPos: Pos)
@ -44,7 +44,11 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
fun view(): View
fun storeView(): StoreView
fun index(): Index
interface Index : Comparator<Pos> {
fun activatingChunkOf(occ: Id<Occurrence>): Chunk?
}
data class View(val chunks: List<Chunk>, val nextChunkId: Int)
@ -115,8 +119,6 @@ internal open class MatchJournalImpl(
private var pos: MutableListIterator<ChunkImpl> = hist.listIterator()
private var current: ChunkImpl = pos.next() // take the initial chunk, move pos
private val parentChunksIndex = HashMap<Occurrence, MatchJournal.Chunk>()
override fun iterator(): MutableIterator<MatchJournal.Chunk> = hist.iterator()
@ -147,19 +149,6 @@ internal open class MatchJournalImpl(
}
override fun activatingChunkOf(occ: Occurrence): MatchJournal.Chunk? {
if (ispec.isPrincipal(occ.constraint)) {
//todo: maintain index and find from index
// maintain, build from view, anything else?
// how to handle removed chunks? when to invalidate index?
return parentChunksIndex[occ]
}
// todo: for non-principal find manually... need it?
return null
}
override fun currentPos(): MatchJournal.Pos = current
// fixme: unclear, whether this makes sense here, in "pure" journal without store? along with reset and replay?
@ -198,6 +187,8 @@ internal open class MatchJournalImpl(
override fun storeView(): StoreView = StoreViewImpl(allOccurrences())
override fun index(): MatchJournal.Index = IndexImpl(ispec, hist)
private fun allOccurrences(): Sequence<Occurrence> {
// the following loop doesn't handle this case of starting pos, when 'current' isn't valid (e.g. just right after resetPos())
if (!pos.hasPrevious())
@ -216,6 +207,7 @@ internal open class MatchJournalImpl(
}
// todo: remove ispec from there
private class ChunkImpl(val ispec: IncrementalProgramSpec, match: RuleMatch, id: Int, justifications: Justs) : MatchJournal.Chunk(match, id, justifications)
{
var occurrences: MutableList<MatchJournal.Chunk.Entry> = mutableListOf()
@ -228,8 +220,8 @@ internal open class MatchJournalImpl(
override fun principalConstraint(): Constraint? {
try {
val body = match.rule().bodyAlternation().first()
val princProds = body.filter { it is Constraint && it.isPrincipal() }
return princProds.first() as Constraint
val pProds = body.filter { it is Constraint && it.isPrincipal() }
return pProds.first() as Constraint
} catch (e: NoSuchElementException) {
return null
}
@ -237,6 +229,49 @@ internal open class MatchJournalImpl(
}
class IndexImpl(ispec: IncrementalProgramSpec, chunks: Iterable<MatchJournal.Chunk>): MatchJournal.Index
{
private val chunkOrder: Map<Int, Int>
// only for principal constraints
private val parentChunks: Map<Id<Occurrence>, MatchJournal.Chunk>
init {
chunkOrder = chunks.map { it.id }.mapToIndex()
val m = HashMap<Id<Occurrence>, MatchJournal.Chunk>()
chunks.forEach {chunk ->
chunk.activatedLog().filter { occ ->
ispec.isPrincipal(occ.constraint)
}.forEach {occ ->
m[Id(occ)] = chunk
}
}
parentChunks = m
}
// todo: for non-principal find manually, walk through the whole journal... need it?
override fun activatingChunkOf(occ: Id<Occurrence>): MatchJournal.Chunk? = parentChunks[occ]
// 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
}
// 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)
}
}
private class StoreViewImpl(occurrences: Sequence<Occurrence>) : StoreView {
val allOccurrences = occurrences.toSet()

View File

@ -22,6 +22,7 @@ import jetbrains.mps.logic.reactor.evaluation.RuleMatch
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.program.Constraint
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
@ -110,11 +111,15 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
: StoreAwareJournalImpl(journal)
{
private val ruleOrder: Map<Any, Int> = ruleIndex.map { it.uniqueTag() }.mapToIndex()
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 data class ExecPos(val pos: MatchJournal.Pos, val activeOcc: Occurrence)
// fixme: wtf, why idea's compiler complains???
override fun index(): MatchJournal.Index = journalIndex
// only for tests
fun pushActivateFirstOccOf(ctr: Constraint): Boolean {
@ -154,7 +159,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
// todo: do need to reactivate only the main, matching~activating match?
// (i.e. don't reactivate additional, inactive heads that only completed the match?)
execQueue.addAll(validOccs.map { occ ->
val chunkPos = activatingChunkOf(occ)
val chunkPos = journalIndex.activatingChunkOf(Id(occ))
if (chunkPos != null) ExecPos(chunkPos, occ) else null
}.filterNotNull())
@ -205,7 +210,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
// Does this chunk have principal occurrence and can activate anything at all?
if (pCtr != null) {
for (rule in headedRules) {
// Can this rule be matched by 'pp'?
// Can this rule be matched by principal occurrence?
// fixme: maybe use RuleIndex here?
if (rule.headKept().contains(pCtr) || rule.headReplaced().contains(pCtr)) {
@ -381,4 +386,4 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
private fun RuleMatch.allHeads() = matchHeadKept() + matchHeadReplaced()
private fun RuleMatch.allStored() = allHeads().all { co -> (co as Occurrence).stored }
}
}