Cleanup code and API: provenance and feedback support

This commit is contained in:
Fedor Isakov 2024-08-02 18:13:46 +02:00
parent 4a142a257b
commit 06a2e2c6e1
5 changed files with 53 additions and 45 deletions

View File

@ -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>) : Feedback() {
private val severity: Severity = maxSeverity(elements)
override fun handle(currentRuleMatch: RuleMatch, feedbackKey: Any, feedbackBasis: List<Any>, 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<Rule>): 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<Any>) {
if (!alreadyHandled()) {
for (feedback in elements) {
feedback.report(supervisor, ruleMatch, provenance)
}
setHandled()
}
}

View File

@ -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<Any>, supervisor: Supervisor): Boolean {
if (!alreadyHandled() && supervisor.handleFeedback(currentRuleMatch, feedbackKey, feedbackBasis, this)) {
setHandled()
open fun handle(supervisor: Supervisor, ruleMatch: RuleMatch, provenance: List<Rule>): 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<Any>) {
if (!alreadyHandled()) {
supervisor.receiveFeedback(this, ruleMatch, provenance)
setHandled()
}
}

View File

@ -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

View File

@ -84,7 +84,12 @@ interface MatchJournal : EvidenceSource {
*/
fun storeView(): StoreView
fun basisRuleTags(chunk: Chunk): List<Any>
/**
* 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<Rule>
// NB: returns same collection of justifications, not copy
fun justifications() = this.currentPos().chunk.justifications()

View File

@ -178,18 +178,18 @@ internal open class MatchJournalImpl(
override fun storeView(): StoreView = StoreViewImpl(allOccurrences())
override fun basisRuleTags(chunk: Chunk): List<Any> {
val ptags = mutableListOf<Any>()
override fun provenanceRules(chunk: Chunk): List<Rule> {
val rules = mutableListOf<Rule>()
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<Occurrence> {