diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/CompositeFeedback.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/CompositeFeedback.kt index 6b541d5d..537157c4 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/CompositeFeedback.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/CompositeFeedback.kt @@ -33,12 +33,12 @@ class CompositeFeedback private constructor(private val elements: List this.severity = maxSeverity(elements) } - override fun handle(currentRuleMatch: RuleMatch, feedbackKey: Any, supervisor: Supervisor): Boolean { + override fun handle(currentRuleMatch: RuleMatch, feedbackKey: Any, feedbackBasis: List, supervisor: Supervisor): Boolean { var unhandled = 0 for (feedback in elements) { if (!feedback.alreadyHandled()) { unhandled += 1 - if (supervisor.handleFeedback(currentRuleMatch, feedbackKey, feedback)) { + if (supervisor.handleFeedback(currentRuleMatch, feedbackKey, feedbackBasis, feedback)) { feedback.setHandled() unhandled -= 1 } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Feedback.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Feedback.kt index 27bb7008..1c8ae69c 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Feedback.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Feedback.kt @@ -40,8 +40,8 @@ abstract class Feedback : EvaluationFeedback() { /** * Returns true if the feedback has been handled. */ - open fun handle(currentRuleMatch: RuleMatch, feedbackKey: Any, supervisor: Supervisor): Boolean { - if (!alreadyHandled() && supervisor.handleFeedback(currentRuleMatch, feedbackKey, this)) { + open fun handle(currentRuleMatch: RuleMatch, feedbackKey: Any, feedbackBasis: List, supervisor: Supervisor): Boolean { + if (!alreadyHandled() && supervisor.handleFeedback(currentRuleMatch, feedbackKey, feedbackBasis, this)) { setHandled() } return alreadyHandled() diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Justified.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Justified.kt index 962ea0a6..4fcb8a04 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Justified.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Justified.kt @@ -30,7 +30,6 @@ typealias Evidence = Int */ typealias Justifications = TIntSet - fun emptyJustifications(): Justifications = TIntHashSet(1) fun justsOf(vararg elements: Evidence): Justifications = TIntHashSet(elements) fun justsFromCollection(collection: Collection): Justifications = TIntHashSet(collection) 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 0d7c130c..ef2d08ee 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 @@ -167,7 +167,10 @@ internal class ControllerImpl ( if (itemOk) { context.withStatus { status -> if (status.feedback?.alreadyHandled() == false) { - status.feedback.handle(match, newParent.match.feedbackKey, supervisor) + status.feedback.handle(match, + newParent.match.feedbackKey, + processing.principalRuleTags(newParent), + supervisor) } } @@ -188,7 +191,10 @@ internal class ControllerImpl ( // if failure can be handled here then recover } else if (status.feedback?.alreadyHandled() == false - && status.failure.handle(match, newParent.match.feedbackKey, supervisor)) { + && status.failure.handle(match, + newParent.match.feedbackKey, + processing.principalRuleTags(newParent), + supervisor)) { status.recover() 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 f09b4969..471290c4 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 @@ -16,6 +16,7 @@ package jetbrains.mps.logic.reactor.core.internal +import gnu.trove.TIntObjectHashMap import jetbrains.mps.logic.reactor.core.* import jetbrains.mps.logic.reactor.evaluation.* import jetbrains.mps.logic.reactor.program.* @@ -23,8 +24,10 @@ import jetbrains.mps.logic.reactor.util.Id import java.util.* import kotlin.Comparator +typealias ChunkIndex = TIntObjectHashMap -interface MatchJournal : Iterable, EvidenceSource { + +interface MatchJournal : EvidenceSource { /** * Add new [Chunk] for matches of principal rules. @@ -68,7 +71,7 @@ interface MatchJournal : Iterable, EvidenceSource { /** * Returns read-only [JournalIterator] that allows to remove future [Chunk]s. */ - override fun iterator(): JournalIterator + fun iterator(): JournalIterator /** * Returns internal [JournalIterator] that allows to remove future [Chunk]s. @@ -118,6 +121,7 @@ interface MatchJournal : Iterable, EvidenceSource { */ fun index(): Index + fun principalRuleTags(chunk: Chunk): List /** * Simplifies some search operations on [MatchJournal]. @@ -165,9 +169,9 @@ interface MatchJournal : Iterable, EvidenceSource { * Length of the indexed [MatchJournal] */ val size: Int - } + /** * Immutable snapshot of [MatchJournal]. */ @@ -178,7 +182,6 @@ interface MatchJournal : Iterable, EvidenceSource { override fun getStoreView(): StoreView = StoreViewImpl( chunks.flatMap { it.entries() }.allOccurrences().asSequence() ) - override fun getPreamble(info: PreambleInfo): View { val cornerChunk = chunks.first() // corner chunk at beginning val initialChunk = chunks[1] @@ -209,6 +212,7 @@ interface MatchJournal : Iterable, EvidenceSource { * the next [Chunk] was logged. */ interface Chunk : Justified { + /** * Corresponds to event of activated or discarded [Occurrence] */ @@ -235,7 +239,6 @@ interface MatchJournal : Iterable, EvidenceSource { * Same as [entries], but returns only discarded [Occurrence]s. */ fun discardedLog(): List = entries().filter { it.discarded }.map { it.occ } - fun toPos(): Pos = Pos(this, entries().size) } @@ -243,6 +246,7 @@ interface MatchJournal : Iterable, EvidenceSource { * [Chunk] corresponding to a [RuleMatch] of a principal [Rule]. */ interface MatchChunk : Chunk { + /** * Returns true if this [Chunk] depends on changes to specified rule. * Relevant for rules with origin. Doesn't include rule from [match]. @@ -253,7 +257,6 @@ interface MatchJournal : Iterable, EvidenceSource { * [RuleMatch] which defines this [Chunk] */ val match: RuleMatch - val ruleUniqueTag: Any get() = match.rule().uniqueTag() } @@ -265,16 +268,15 @@ interface MatchJournal : Iterable, EvidenceSource { } open class Pos(val chunk: Chunk, val entriesCount: Int) { + override fun equals(other: Any?) = other is Pos && other.chunk === chunk && other.entriesCount == entriesCount override fun hashCode(): Int = 31 * chunk.hashCode() + entriesCount - override fun toString(): String = "($chunk, $entriesCount)" } - } 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 fb8bb066..a1f83ba5 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 @@ -64,6 +64,8 @@ internal open class MatchJournalImpl( // invariant: never empty private val hist: IteratorMutableList + private val chunkIndex = ChunkIndex() + // pointer to current position where logging (chunk additions) and log erasing (chunk removals) happen // initially points at initial chunk private val __cursor: Cursor @@ -133,6 +135,13 @@ internal open class MatchJournalImpl( return added } + private fun indexChunk(chunk: Chunk) { + chunkIndex.put(chunk.evidence, chunk) + } + + private fun lookupChunkByEvidence(evidence: Evidence) = + chunkIndex.get(evidence) + private fun trackAncestor(logEvent: Justified) { while (!logEvent.justifiedBy(parentChunk())) { ancestorChunksStack.pop() @@ -234,6 +243,20 @@ internal open class MatchJournalImpl( override fun index(): MatchJournal.Index = IndexImpl(hist) + override fun principalRuleTags(chunk: Chunk): List { + val ptags = mutableListOf() + chunk.justifications().forEach { jn -> + // hist is sequential, random access can be expensive + (lookupChunkByEvidence(jn) as? MatchChunk)?.let { + if (it.match.isPrincipal) { + ptags.add(it.ruleUniqueTag) + } + } + true + } + return ptags + } + private fun allOccurrences(): Sequence { // the following loop doesn't handle this case of starting posPtr, when 'current' isn't valid (e.g. just right after resetPos()) if (__cursor.atStart()) return emptySequence() @@ -320,6 +343,7 @@ internal open class MatchJournalImpl( override fun add(chunk: Chunk) { it.add(chunk as ChunkImpl) __current = chunk + indexChunk(chunk) } override fun removeNext() { @@ -328,7 +352,6 @@ internal open class MatchJournalImpl( updateNext() } - /** * Walk in journal in direct order ([from] -> [current]) * while applying [action] to each visited [Chunk] @@ -380,7 +403,6 @@ internal open class MatchJournalImpl( } } - /** * Immutable view for MutableList that provides mutability only through its iterators */ @@ -453,7 +475,6 @@ internal open class MatchJournalImpl( } } - /** * Mock RuleMatch for use only in initial Chunk */ diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/Supervisor.java b/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/Supervisor.java index aca896bd..80ce85fa 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/Supervisor.java +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/evaluation/Supervisor.java @@ -34,8 +34,5 @@ public interface Supervisor { * Override this method in order to "handle" the feedback. * Returns true if the method has handled (consumed) the feedback. */ - default boolean handleFeedback(RuleMatch ruleMatch, Object feedbackKey, EvaluationFeedback feedback) { - return false; - } - + boolean handleFeedback(RuleMatch ruleMatch, Object feedbackKey, List feedbackBasis, EvaluationFeedback feedback); } diff --git a/reactor/Test/src/program/MockProgram.kt b/reactor/Test/src/program/MockProgram.kt index d9afbebb..7fc8fdd3 100644 --- a/reactor/Test/src/program/MockProgram.kt +++ b/reactor/Test/src/program/MockProgram.kt @@ -3,7 +3,9 @@ */ import jetbrains.mps.logic.reactor.core.RulesDiff +import jetbrains.mps.logic.reactor.evaluation.EvaluationFeedback import jetbrains.mps.logic.reactor.evaluation.InvocationContext +import jetbrains.mps.logic.reactor.evaluation.RuleMatch import jetbrains.mps.logic.reactor.evaluation.Supervisor import jetbrains.mps.logic.reactor.logical.LogicalContext import jetbrains.mps.logic.reactor.logical.MetaLogical @@ -128,7 +130,7 @@ open class MockSupervisor : Supervisor { else a } -// override fun handleFeedback(rule: Rule?, feedback: EvaluationFeedback?): Boolean = false + override fun handleFeedback(ruleMatch: RuleMatch, feedbackKey: Any, feedbackBasis: MutableList, feedback: EvaluationFeedback): Boolean = false } class MockConstraintRegistry() { diff --git a/reactor/Test/test/TestController.kt b/reactor/Test/test/TestController.kt index 6cb366a4..71db6e2b 100644 --- a/reactor/Test/test/TestController.kt +++ b/reactor/Test/test/TestController.kt @@ -68,7 +68,7 @@ class TestController { vararg occurrences: ConstraintOccurrence): Controller { val program = MockProgram("test", rulesLists, registry = MockConstraintRegistry()) val supervisor = object : MockSupervisor() { - override fun handleFeedback(ruleMatch: RuleMatch, feedbackKey: Any, feedback: EvaluationFeedback): Boolean = + override fun handleFeedback(ruleMatch: RuleMatch, feedbackKey: Any, feedbackBasis: MutableList, feedback: EvaluationFeedback): Boolean = feedbackHandler(ruleMatch, feedback) } MockSession.init(program, supervisor)