diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt index 71d6fe61..2affcc3a 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt @@ -62,7 +62,9 @@ internal class ControllerImpl ( // FIXME noLogicalContext val context = Context(NORMAL(), noLogicalContext) - activateConstraint(constraint, emptyJusts(), context) + // fixme: is it valid to always provide current justifications? + // while this method is used only in one place at program kick-off, yes, it's initial justs provided. + activateConstraint(constraint, state.justs(), context) return context.currentStatus() } 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 a3877bb5..43f8d49a 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 @@ -74,7 +74,7 @@ interface MatchJournal : MutableIterable { }.map { it.wrapped } abstract fun findOccurrence(ctr: Constraint): Occurrence? - abstract fun principalConstraint(): Constraint? + abstract fun principalOccurrence(): Occurrence? override fun toString() = "(id=$id, $justifications, ${match.rule().uniqueTag()}, ${entriesLog()})" @@ -126,7 +126,8 @@ internal open class MatchJournalImpl( if (view == null) { hist = LinkedList() nextChunkId = 0 - val initChunk = ChunkImpl(ispec, InitRuleMatch, nextChunkId++, justsOf()) + val initChunk = ChunkImpl(ispec, InitRuleMatch, nextChunkId, justsOf(nextChunkId)) + nextChunkId++ hist.add(initChunk) } else { // fixme: check somehow that initial chunk is present? @@ -201,7 +202,15 @@ internal open class MatchJournalImpl( } private fun replayOccurrences(controller: Controller, occSpecs: Iterable) = - occSpecs.forEach { if (it.isDiscarded) it.occ.terminate(controller) else it.occ.revive(controller) } + occSpecs.forEach { + if (it.isDiscarded) { + it.occ.terminate(controller) + it.occ.stored = false + } else { + it.occ.revive(controller) + it.occ.stored = true + } + } override fun view() = MatchJournal.View(ArrayList(hist), nextChunkId) @@ -236,17 +245,10 @@ internal open class MatchJournalImpl( override fun entriesLog(): List = occurrences override fun findOccurrence(ctr: Constraint): Occurrence? = - occurrences.find { !it.isDiscarded && it.occ.constraint.symbol() == ctr.symbol() }?.occ + activatedLog().find { it.constraint.symbol() == ctr.symbol() } - override fun principalConstraint(): Constraint? { - try { - val body = match.rule().bodyAlternation().first() - val pProds = body.filter { it is Constraint && it.isPrincipal() } - return pProds.first() as Constraint - } catch (e: NoSuchElementException) { - return null - } - } + override fun principalOccurrence(): Occurrence? = + activatedLog().find { ispec.isPrincipal(it.constraint) } } class IndexImpl(ispec: IncrementalProgramSpec, chunks: Iterable): MatchJournal.Index 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 ea3648c1..09fd4061 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 @@ -110,6 +110,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp : StoreAwareJournalImpl(journal) { private val ruleOrder: Map = HashMap().apply { + put(MatchJournalImpl.InitRuleMatch.rule().uniqueTag(), -1) ruleIndex.forEachIndexed { index, rule -> put(rule.uniqueTag(), index) } } @@ -196,39 +197,35 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp private data class MatchCandidate(val rule: Rule, val occ: Occurrence, val occParentId: Int) + private fun canMatch(rule: Rule, occ: Occurrence): Boolean = + (rule.headKept() + rule.headReplaced()).find { it.symbol() == occ.constraint.symbol() } != null + // todo: what if non-principal rules will be passed as input there? // 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 pCtr = chunk.principalConstraint() + var chunk = it.next() // initial chunk + var prevChunk = chunk + while (true) { // Does this chunk have principal occurrence and can activate anything at all? - if (pCtr != null) { - for (rule in headedRules) { + val pOcc = chunk.principalOccurrence() + if (pOcc != null) { + + for (rule in rules) { // Can this rule be matched by principal occurrence? // fixme: maybe use RuleIndex here? - if (rule.headKept().contains(pCtr) || rule.headReplaced().contains(pCtr)) { - - val princOcc = chunk.findOccurrence(pCtr) - ?: throw IllegalStateException("Chunk with principal occurrence must have it in activated occurrences!") + if (canMatch(rule, pOcc)) { // Then we will need to find the place among existing child chunks // (i.e. among some number of following ones) // to activate this occurrence, to (possibly) match this rule. // Also remember the parent justification of this rule candidate // to drop it from monitoring when child chunks end. - activationCandidates.add(MatchCandidate(rule, princOcc, chunk.id)) + 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 } } @@ -256,19 +253,9 @@ 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 - } - } - } - + if (!it.hasNext()) break prevChunk = chunk + chunk = it.next() } } @@ -292,12 +279,13 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp // 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 != prevPos) replay(controller, execPos.pos) - } prevPos = execPos - controller.reactivate(execPos.activeOcc) + // If the occurrence is still in the store after replay (i.e. if it's valid to activate it) + if (execPos.activeOcc.stored) + controller.reactivate(execPos.activeOcc) } while (execQueue.isNotEmpty()) } // Also replay to the end after queue is fully executed diff --git a/reactor/Test/test/TestIncrementalProgram.kt b/reactor/Test/test/TestIncrementalProgram.kt index 6b12b189..2a886f86 100644 --- a/reactor/Test/test/TestIncrementalProgram.kt +++ b/reactor/Test/test/TestIncrementalProgram.kt @@ -203,35 +203,41 @@ class TestIncrementalProgram { } @Test - @Ignore("unresolved yet") fun addAtStartMatch() { val progSpec = MockIncrProgSpec( - setOf("main", "empty-head.bar"), - setOf(sym0("foo")) + setOf("main.foo", "main.bar"), + setOf(sym0("main")) ) programWithRules( - rule("main", - headReplaced( - constraint("main") + // 'at-start' rules are launched with a 'main' occurrence, their heads aren't actually empty + rule("main.foo", + headKept( + princConstraint("main") ), body( - princConstraint("foo") + constraint("foo") )) ).launch("addRule", progSpec) { result -> - result.storeView().constraintSymbols() shouldBe setOf(sym0("foo")) + + println(result.token().journalView) + result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("main")) result.token().journalView.chunks.size shouldBe 2 result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("foo")) }.also { (builder, evalRes) -> builder.programWithRules( - rule("empty-head.bar", - headKept(), + rule("main.bar", + headKept( + princConstraint("main") + ), body( constraint("bar") ) ) ).relaunch("atStart", progSpec, evalRes.token()) { result -> - result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("bar")) + + println(result.token().journalView) + result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("main"), sym0("bar")) result.token().journalView.chunks.size shouldBe 3 result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("bar")) }