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 9fa56ba1..4bca421a 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/CompositeFeedback.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/CompositeFeedback.kt @@ -18,6 +18,7 @@ package jetbrains.mps.logic.reactor.core import jetbrains.mps.logic.reactor.evaluation.RuleMatch import jetbrains.mps.logic.reactor.evaluation.Supervisor +import jetbrains.mps.logic.reactor.program.Rule import java.util.ArrayList @@ -27,25 +28,27 @@ import java.util.ArrayList class CompositeFeedback private constructor(private val elements: List) : Feedback() { private val severity: Severity = maxSeverity(elements) - 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, feedbackBasis, feedback)) { - feedback.setHandled() - unhandled -= 1 + override fun handle(supervisor: Supervisor, ruleMatch: RuleMatch, provenance: List): Boolean { + var unhandled = false + if (!alreadyHandled()) { + for (feedback in elements) { + if (!feedback.handle(supervisor, ruleMatch, provenance)) { + unhandled = true } } } - return unhandled == 0 + if (!unhandled) { + setHandled() + } + return alreadyHandled() } - override fun handle(ruleMatch: RuleMatch) { - for (feedback in elements) { - if (!feedback.alreadyHandled()) { - feedback.setHandled() + override fun report(supervisor: Supervisor, ruleMatch: RuleMatch, provenance: List) { + if (!alreadyHandled()) { + for (feedback in elements) { + feedback.report(supervisor, ruleMatch, provenance) } + setHandled() } } 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 d2b1a84e..9f20b4de 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Feedback.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Feedback.kt @@ -19,6 +19,8 @@ package jetbrains.mps.logic.reactor.core import jetbrains.mps.logic.reactor.evaluation.EvaluationFeedback import jetbrains.mps.logic.reactor.evaluation.RuleMatch import jetbrains.mps.logic.reactor.evaluation.Supervisor +import jetbrains.mps.logic.reactor.evaluation.Supervisor.HandleResult +import jetbrains.mps.logic.reactor.program.Rule /** * @author Fedor Isakov @@ -40,15 +42,24 @@ abstract class Feedback : EvaluationFeedback() { /** * Returns true if the feedback has been handled. */ - open fun handle(currentRuleMatch: RuleMatch, feedbackKey: Any, feedbackBasis: List, supervisor: Supervisor): Boolean { - if (!alreadyHandled() && supervisor.handleFeedback(currentRuleMatch, feedbackKey, feedbackBasis, this)) { - setHandled() + open fun handle(supervisor: Supervisor, ruleMatch: RuleMatch, provenance: List): Boolean { + if (!alreadyHandled()) { + when (supervisor.handleFeedback(this, ruleMatch, provenance)) { + HandleResult.DROP -> { setHandled() } + HandleResult.PROPAGATE -> { /*NOP*/ } + HandleResult.ESCALATE -> { /*NOP*/ } + null -> throw NullPointerException() + } } return alreadyHandled() } - open fun handle(ruleMatch: RuleMatch) { + /** + * Receive the feedback and mark as handled unconditionally. + */ + open fun report(supervisor: Supervisor, ruleMatch: RuleMatch, provenance: List) { if (!alreadyHandled()) { + supervisor.receiveFeedback(this, ruleMatch, provenance) setHandled() } } 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 582963fd..604c95fd 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 @@ -171,12 +171,8 @@ internal class ControllerImpl ( if (itemOk) { context.withStatus { status -> - if (status.feedback?.alreadyHandled() == false) { - status.feedback.handle(match, - newParent.match.feedbackKey, - processing.basisRuleTags(newParent), - supervisor) - } + // FIXME support ESCALATE option + status.feedback?.handle(supervisor, match, processing.provenanceRules(newParent)) } } else { @@ -187,27 +183,20 @@ internal class ControllerImpl ( val altOk = context.eval { status -> if (status is FAILED) { - // if there's alternative body branch then try it + // if there's alternative body branch then try it before failing if (altIt.hasNext()) { - // clear the failure handled status - // the supervisor is NOT notified here - status.failure.handle(match) + // report the feedback and suppress the failure + status.failure.report(supervisor, match, processing.provenanceRules(newParent)) status - // if failure can be handled here then recover - } else if (status.feedback?.alreadyHandled() == false - && status.failure.handle(match, - newParent.match.feedbackKey, - processing.basisRuleTags(newParent), - supervisor)) { + // if failure can be not handled here, then propagate further up the stack + // FIXME support ESCALATE option + } else if (!status.failure.handle(supervisor, match, processing.provenanceRules(newParent))) { + status - status.recover() - - // else propagate further up the stack } else { - status + status.recover() } - } else { status } @@ -303,7 +292,7 @@ internal class ControllerImpl ( logicalContext: LogicalContext, rule: Rule? = null, trace: EvaluationTrace = EvaluationTrace.NULL) : - this(inStatus, false, logicalContext, rule, trace) + this(inStatus, false, logicalContext, rule, trace) fun currentStatus(): FeedbackStatus = 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 5ba1cf16..e77fb169 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 @@ -84,7 +84,12 @@ interface MatchJournal : EvidenceSource { */ fun storeView(): StoreView - fun basisRuleTags(chunk: Chunk): List + /** + * Returns the list of rules that serve as the provenance for the given chunk. + * Provenance can be used to determine the location(s) in the source code + * to mark with the feedback message. + */ + fun provenanceRules(chunk: Chunk): List // NB: returns same collection of justifications, not copy fun justifications() = this.currentPos().chunk.justifications() 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 ce6a2cd9..c1bb11ba 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 @@ -178,18 +178,18 @@ internal open class MatchJournalImpl( override fun storeView(): StoreView = StoreViewImpl(allOccurrences()) - override fun basisRuleTags(chunk: Chunk): List { - val ptags = mutableListOf() + override fun provenanceRules(chunk: Chunk): List { + val rules = mutableListOf() chunk.justifications().forEach { jn -> // hist is sequential, random access can be expensive (lookupChunkByEvidence(jn) as? MatchChunk)?.let { - if (it.match.rule().isBasis) { - ptags.add(it.ruleUniqueTag) + if (it.match.rule().isProvenance) { + rules.add(it.match.rule()) } } true } - return ptags + return rules } private fun allOccurrences(): Sequence {