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 index cddb81b6..f836b8f1 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournal.kt @@ -54,6 +54,9 @@ interface MatchJournal : MutableIterable { override fun toString() = (if (isDiscarded) '-' else '+') + occ.toString() } + fun isDescendantOf(chunkId: Int): Boolean = justifications.contains(chunkId) + fun isTopLevel(): Boolean = justifications.size() <= 1 // this condition implies that there're no ancestor chunks + abstract fun entriesLog(): List fun activatedLog(): List = entriesLog().filter { !it.isDiscarded }.map { it.occ } fun discardedLog(): List = entriesLog().filter { it.isDiscarded }.map { it.occ } @@ -148,6 +151,7 @@ internal open class MatchJournalImpl( 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] } 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 eadf623d..e6b6608b 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 @@ -150,7 +150,6 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp val (invalidatedOccs, validOccs) = matchedOccs.partition { occ -> occ.justifications().intersects(justificationRoots) } - assert(invalidatedOccs.isEmpty()) // todo: do need to reactivate only the main, matching~activating match? // (i.e. don't reactivate additional, inactive heads that only completed the match?) @@ -190,23 +189,27 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp // only rules that can match on principal occurrences will pass through this method to the queue // so, no non-principal rule should pass it. fun addRuleMatches(rules: Iterable) { + val activationCandidates = mutableListOf() + val (headedRules, headlessRules) = rules.partition { rule -> + rule.headKept().count() != 0 || rule.headReplaced().count() != 0 + } val it = this.iterator() var prevChunk = it.next() // skip initial chunk while (it.hasNext()) { // note: if there's only an initial chunk, we have nothing to do val chunk = it.next() - val pp = chunk.principalConstraint() + val pCtr = chunk.principalConstraint() // Does this chunk have principal occurrence and can activate anything at all? - if (pp != null) { - for (rule in rules) { + if (pCtr != null) { + for (rule in headedRules) { // Can this rule be matched by 'pp'? // fixme: maybe use RuleIndex here? - if (rule.headKept().contains(pp) || rule.headReplaced().contains(pp)) { + if (rule.headKept().contains(pCtr) || rule.headReplaced().contains(pCtr)) { - val princOcc = chunk.findOccurrence(pp) + val princOcc = chunk.findOccurrence(pCtr) ?: throw IllegalStateException("Chunk with principal occurrence must have it in activated occurrences!") // Then we will need to find the place among existing child chunks @@ -227,18 +230,12 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp // Place to activate candidate: // either as the last one, after all existing activations // or according to the ordering between rules. - - val chunkRule = chunk.match.rule() - val currRuleOrder = ruleOrder[chunkRule.uniqueTag()] - ?: throw(IllegalStateException("There can be no chunks with rules not in rule index!")) - val candRuleOrder = ruleOrder[candRule.uniqueTag()] - ?: throw(IllegalStateException("Match candidate rule must be present in the rule index!")) - - val childChunksEnded = !chunk.justifications.contains(parentId) + val placeToInsertFound = compareRuleOrders(chunk.match.rule(), candRule) > 0 + val childChunksEnded = !chunk.isDescendantOf(parentId) val pos = // We need the previous chunk as pos here (i.e. adding after it). - if (childChunksEnded || currRuleOrder > candRuleOrder) prevChunk + if (childChunksEnded || placeToInsertFound) prevChunk // Case when adding at the very end else if (!it.hasNext()) chunk else continue @@ -248,10 +245,32 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp aIt.remove() } + // Case for headless rules, which are activated at the 'top' level according to rule order in RuleIndex + if (chunk.isTopLevel()) { + for (candRule in headlessRules) { + val placeToInsertFound = compareRuleOrders(chunk.match.rule(), candRule) > 0 + if (placeToInsertFound) { + // TODO: how 'at start' rules get activated? +// execQueue.offer(ExecPos(prevChunk, candOcc)) + // todo: remove added rule from headlessRules + } + } + } + prevChunk = chunk } } + private fun compareRuleOrders(lhs: Rule, rhs: Rule): Int { + val lhsRuleOrder = ruleOrder[lhs.uniqueTag()] + val rhsRuleOrder = ruleOrder[rhs.uniqueTag()] + + if (lhsRuleOrder == null || rhsRuleOrder == null) { + throw(IllegalStateException("Rules compared must be present in the rule index!")) + } + return lhsRuleOrder.compareTo(rhsRuleOrder) + } + fun launchQueue(controller: Controller): FeedbackStatus.NORMAL { if (execQueue.isNotEmpty()) { resetStore() @@ -261,7 +280,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp 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 (!) then execPos. + // Then shouldn't replay, because currentPos is valid and more recent (!) than execPos. if (execPos != prevPos) { replay(controller, execPos.pos) }