diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt index 328ea966..73b89ce6 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt @@ -17,6 +17,7 @@ package jetbrains.mps.logic.reactor.core import jetbrains.mps.logic.reactor.core.internal.FeedbackStatus +import jetbrains.mps.logic.reactor.core.internal.MatchJournal import jetbrains.mps.logic.reactor.evaluation.PredicateInvocation import jetbrains.mps.logic.reactor.evaluation.StoreView @@ -54,6 +55,6 @@ interface Controller { * its status allowed the operation. * The returned status captures the result of evaluating the rule's body. */ - fun processBody(match: RuleMatchEx, inStatus: FeedbackStatus): FeedbackStatus + fun processBody(match: RuleMatchEx, parent: MatchJournal.MatchChunk, inStatus: FeedbackStatus): FeedbackStatus } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleMatchEx.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleMatchEx.kt index 3aedca25..8397aa93 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleMatchEx.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleMatchEx.kt @@ -35,4 +35,9 @@ interface RuleMatchEx : RuleMatch { // TODO better be an inline extension fun fun forEachReplaced(action: (Occurrence) -> Unit) -} \ No newline at end of file +} + + +fun RuleMatch.allHeads() = (matchHeadKept().asSequence() + matchHeadReplaced().asSequence()) as Sequence + +fun RuleMatch.allStored() = allHeads().all { it.stored } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt index 6820543e..b79a3f4e 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ConstraintsProcessing.kt @@ -58,7 +58,7 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di private data class MatchCandidate(val rule: Rule, val occChunk: MatchJournal.OccChunk) - fun reactivate(controller: Controller, activeOcc: Occurrence): FeedbackStatus { + fun reactivate(controller: Controller, activeOcc: Occurrence, parent: MatchJournal.MatchChunk): FeedbackStatus { assert(activeOcc.stored) // Forget that occ was seen. Otherwise it will be @@ -68,7 +68,7 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di trace.reactivateIncremental(activeOcc) - return processActivated(controller, activeOcc, FeedbackStatus.NORMAL()) + return processActivated(controller, activeOcc, parent, FeedbackStatus.NORMAL()) } /** @@ -118,11 +118,11 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di // We removed the match, so need to reactivate all still valid occurrences from the head // by definition of Chunk and principal rule, all occurrences from the head are principal - val matchedOccs = chunk.match.allHeads() as Iterable + val matchedOccs = chunk.match.allHeads().asIterable() val validOccs = matchedOccs.filter { occ -> !occ.justifiedByAny(justificationRoots) } -// assert(matchedOccs.all { it.isPrincipal() }) + assert(matchedOccs.all { it.isPrincipal() }) execQueue.offerAll(validOccs) } @@ -213,7 +213,7 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di * Calls the controller to process matches (if any) that were triggered. * This method may be called at most once for a fresh state frame. */ - fun processActivated(controller: Controller, active: Occurrence, inStatus: FeedbackStatus) : FeedbackStatus { + fun processActivated(controller: Controller, active: Occurrence, parent: MatchJournal.MatchChunk, inStatus: FeedbackStatus) : FeedbackStatus { push() assert(active.alive) @@ -237,7 +237,7 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di if (isFront() || !active.isPrincipal()) { matches } else { - assert( matches.all { ispec.isPrincipal(it.rule()) } ) + assert( matches.all { it.isPrincipal() } ) execQueue.postponeFutureMatches(matches) } @@ -252,7 +252,7 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di // TODO: paranoid check. should be isAlive() instead // FIXME: move this check elsewhere if (status.operational && active.stored && match.allStored()) - processMatch(controller, match, status) + processMatch(controller, match, parent, status) else status } @@ -279,7 +279,7 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di private inline fun FeedbackStatus.then(action: (FeedbackStatus) -> FeedbackStatus) : FeedbackStatus = if (operational) action(this) else this - private fun processMatch(controller: Controller, match: RuleMatchEx, inStatus: FeedbackStatus) : FeedbackStatus = + private fun processMatch(controller: Controller, match: RuleMatchEx, parent: MatchJournal.MatchChunk, inStatus: FeedbackStatus) : FeedbackStatus = controller.offerMatch(match, inStatus) .let { when (it) { @@ -300,7 +300,7 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di accept(controller, match) } .then { - controller.processBody(match, it) + controller.processBody(match, parent, it) } .also { trace.finish(match) } @@ -341,9 +341,7 @@ internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.Di } + private fun RuleMatch.isPrincipal() = ispec.isPrincipal(this.rule()) + private fun Occurrence.isPrincipal() = ispec.isPrincipal(this.constraint()) - - private fun RuleMatch.allHeads() = matchHeadKept() + matchHeadReplaced() - - private fun RuleMatch.allStored() = allHeads().all { co -> (co as Occurrence).stored } } 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 72d795ba..c8cc4173 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 @@ -47,7 +47,7 @@ internal class ControllerImpl ( }) trace.activate(occ) - val status = processing.processActivated(this, active, NORMAL()) + val status = processing.processActivated(this, active, processing.initialChunk(), NORMAL()) if (status is FAILED) { throw status.failure.failureCause() } @@ -77,7 +77,7 @@ internal class ControllerImpl ( // FIXME noLogicalContext val context = Context(NORMAL(), noLogicalContext, null, trace) - activateConstraint(constraint, processing.justifications(), context) + activateConstraint(constraint, processing.initialChunk(), justsCopy(processing.justifications()), context) return context.currentStatus() } @@ -102,7 +102,8 @@ internal class ControllerImpl ( profiler.profile("reactivate_${occ.constraint.symbol()}") { trace.reactivate(occ) - processing.processActivated(this, occ, NORMAL()) + // FIXME: propagate parent MatchChunk through call to reactivate() + processing.processActivated(this, occ, processing.parentChunk(), NORMAL()) } @@ -135,7 +136,7 @@ internal class ControllerImpl ( return context.currentStatus() } - override fun processBody(match: RuleMatchEx, inStatus: FeedbackStatus) : FeedbackStatus { + override fun processBody(match: RuleMatchEx, parent: MatchJournal.MatchChunk, inStatus: FeedbackStatus) : FeedbackStatus { val context = Context(inStatus, match.logicalContext(), match.rule().uniqueTag(), trace) val altIt = match.rule().bodyAlternation().iterator() @@ -153,15 +154,22 @@ internal class ControllerImpl ( } val savedPos = processing.currentPos() + var newParent: MatchJournal.MatchChunk = parent - val currentJusts = processing.justifications() + if (match.isPrincipal()) { + // This match corresponds to the last added chunk + assert( (savedPos.chunk as? MatchJournal.MatchChunk)?.match === match ) + newParent = savedPos.chunk as MatchJournal.MatchChunk + } + + val justifications = processing.justifications() for (item in body) { val itemOk = when (item) { is Constraint -> { // track justifications only for principal constraints - val justs = if (ispec.isPrincipal(item)) currentJusts else emptyJustifications() - activateConstraint(item, justs, context) + val js = if (item.isPrincipal()) justsCopy(justifications) else emptyJustifications() + activateConstraint(item, newParent, js, context) } is Predicate -> tellPredicate(item, context) else -> throw IllegalArgumentException("unknown item ${item}") @@ -170,7 +178,7 @@ internal class ControllerImpl ( if (itemOk) { context.withStatus { status -> if (status.feedback?.alreadyHandled() == false) { - status.feedback.handle(match, processing.ancestorMatch().match, supervisor) + status.feedback.handle(match, newParent.match, supervisor) } } @@ -191,7 +199,7 @@ internal class ControllerImpl ( // if failure can be handled here then recover } else if (status.feedback?.alreadyHandled() == false - && status.failure.handle(match, processing.ancestorMatch().match, supervisor)) { + && status.failure.handle(match, newParent.match, supervisor)) { status.recover() @@ -218,15 +226,15 @@ internal class ControllerImpl ( return context.currentStatus() } - private fun activateConstraint(constraint: Constraint, justifications: Justifications, context: Context) : Boolean { + private fun activateConstraint(constraint: Constraint, parent: MatchJournal.MatchChunk, justifications: Justifications, context: Context) : Boolean { val args = supervisor.instantiateArguments(constraint.arguments(), context.logicalContext, context) return context.eval { status -> profiler.profile("activate_${constraint.symbol()}") { - constraint.occurrence(logicalStateObservable(), args, processing.nextEvidence(), justsCopy(justifications), context.logicalContext, context.ruleUniqueTag).let { occ -> + constraint.occurrence(logicalStateObservable(), args, processing.nextEvidence(), justifications, context.logicalContext, context.ruleUniqueTag).let { occ -> trace.activate(occ) - processing.processActivated(this, occ, status) + processing.processActivated(this, occ, parent, status) } } @@ -268,7 +276,10 @@ internal class ControllerImpl ( it.first.patternPredicates(it.second.arguments()) }.toList() - private fun RuleMatch.allStored() = (matchHeadKept() + matchHeadReplaced()).all { co -> (co as Occurrence).stored } + + private fun RuleMatch.isPrincipal() = ispec.isPrincipal(this.rule()) + + private fun Occurrence.isPrincipal() = ispec.isPrincipal(this.constraint()) inner private class Context(inStatus: FeedbackStatus, diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ExecutionQueue.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ExecutionQueue.kt index e39fc689..36cf2950 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ExecutionQueue.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ExecutionQueue.kt @@ -49,7 +49,7 @@ internal class ExecutionQueue( private val postponedMatches: MutableMap, List> = HashMap() private val execQueue: Queue = - PriorityQueue(1 + journalIndex.size / 2) { // just an estimate + PriorityQueue(1 + journalIndex.size / 8) { // just an estimate lhs, rhs -> journalIndex.compare(lhs.pos, rhs.pos) } @@ -74,7 +74,8 @@ internal class ExecutionQueue( // If the occurrence is still in the store after replay (i.e. if it's valid to activate it) if (execPos.activeOcc.stored) { - status = processing.reactivate(controller, execPos.activeOcc) + // fixme: parentChunk can also be tracked + status = processing.reactivate(controller, execPos.activeOcc, processing.parentChunk()) // Leave journal processing as it was at the point of failure if (!status.operational) return status } 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 2377172c..41f0245a 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 @@ -39,11 +39,16 @@ interface MatchJournal : MutableIterable, EvidenceSource { fun logActivation(occ: Occurrence): OccChunk? /** - * Returns nearest MatchChunk which justifies current chunk. + * Returns nearest [MatchChunk] which justifies current chunk. * If current chunk is [MatchChunk], then it is returned. - * May return initial chunk. For initial chunk case returns it. + * May return initial chunk. */ - fun ancestorMatch(): MatchChunk + fun parentChunk(): MatchChunk + + /** + * Returns the initial chunk which is always present in history + */ + fun initialChunk(): MatchChunk /** * Current position at the journal. diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt index 957f4a35..cbc8be52 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchJournalImpl.kt @@ -99,7 +99,9 @@ internal open class MatchJournalImpl( return added } - override fun ancestorMatch(): MatchChunk { + override fun initialChunk(): MatchChunk = this.first() as MatchChunk + + override fun parentChunk(): MatchChunk { if (current is MatchChunk) { return current as MatchChunk } @@ -111,7 +113,7 @@ internal open class MatchJournalImpl( return prev } } - return hist.first() as MatchChunk // initial chunk + return initialChunk() } override fun currentPos(): MatchJournal.Pos = current.toPos() @@ -333,9 +335,6 @@ internal open class MatchJournalImpl( fun MatchJournal.justifications() = this.currentPos().chunk.justifications() -fun RuleMatch.justifications(): Justifications { - val res: Justifications = justsOf() - this.matchHeadKept().forEach { res.addAll( (it as Occurrence).justifications() ) } - this.matchHeadReplaced().forEach { res.addAll( (it as Occurrence).justifications() ) } - return res -} \ No newline at end of file +fun RuleMatch.justifications(): Justifications = justsOf().also { allJss -> + this.allHeads().forEach { allJss.addAll(it.justifications()) } +} diff --git a/reactor/Test/test/RulesHelper.kt b/reactor/Test/test/RulesHelper.kt index f27d3653..7756f2bf 100644 --- a/reactor/Test/test/RulesHelper.kt +++ b/reactor/Test/test/RulesHelper.kt @@ -1,5 +1,6 @@ import jetbrains.mps.logic.reactor.core.* import jetbrains.mps.logic.reactor.core.internal.FeedbackStatus +import jetbrains.mps.logic.reactor.core.internal.MatchJournal import jetbrains.mps.logic.reactor.evaluation.PredicateInvocation import jetbrains.mps.logic.reactor.evaluation.StoreView import jetbrains.mps.logic.reactor.logical.Logical @@ -237,7 +238,7 @@ class MockController : Controller { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } - override fun processBody(match: RuleMatchEx, inStatus: FeedbackStatus): FeedbackStatus { + override fun processBody(match: RuleMatchEx, parent: MatchJournal.MatchChunk, inStatus: FeedbackStatus): FeedbackStatus { TODO("not implemented") //To change body of created functions use File | Settings | File Templates. } }