Decompose controller's processing of constraints for better separation of concerns.
Restructure the code in controller to enable being called from an external component.
This commit is contained in:
parent
bfc4b641bb
commit
11a9505ee9
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package jetbrains.mps.logic.reactor.core
|
package jetbrains.mps.logic.reactor.core
|
||||||
|
|
||||||
|
import jetbrains.mps.logic.reactor.core.internal.FeedbackStatus
|
||||||
|
import jetbrains.mps.logic.reactor.core.internal.RuleMatchImpl
|
||||||
import jetbrains.mps.logic.reactor.evaluation.PredicateInvocation
|
import jetbrains.mps.logic.reactor.evaluation.PredicateInvocation
|
||||||
import jetbrains.mps.logic.reactor.evaluation.StoreView
|
import jetbrains.mps.logic.reactor.evaluation.StoreView
|
||||||
|
|
||||||
|
|
@ -40,4 +42,19 @@ interface Controller {
|
||||||
/** For tests only */
|
/** For tests only */
|
||||||
fun storeView(): StoreView
|
fun storeView(): StoreView
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies necessary predicates implied by the match.
|
||||||
|
* Checks the guard.
|
||||||
|
* The returned status indicates whether the match can be processed.
|
||||||
|
*/
|
||||||
|
fun offerMatch(match: RuleMatchEx, inStatus: FeedbackStatus): FeedbackStatus
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates the rule's body.
|
||||||
|
* The caller must ensure that [offerMatch] has been called before and
|
||||||
|
* its status allowed the operation.
|
||||||
|
* The returned status captures the result of evaluating the rule's body.
|
||||||
|
*/
|
||||||
|
fun processBody(match: RuleMatchEx, inStatus: FeedbackStatus): FeedbackStatus
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,4 +31,6 @@ interface RuleMatchEx : RuleMatch {
|
||||||
*/
|
*/
|
||||||
fun signature(): ArrayList<Id<Occurrence>?>
|
fun signature(): ArrayList<Id<Occurrence>?>
|
||||||
|
|
||||||
|
// TODO better be an inline extension fun
|
||||||
|
fun forEachReplaced(action: (Occurrence) -> Unit)
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +57,9 @@ internal class ControllerImpl (
|
||||||
fun activate(constraint: Constraint) : FeedbackStatus {
|
fun activate(constraint: Constraint) : FeedbackStatus {
|
||||||
// FIXME noLogicalContext
|
// FIXME noLogicalContext
|
||||||
val context = Context(NORMAL(), noLogicalContext)
|
val context = Context(NORMAL(), noLogicalContext)
|
||||||
|
|
||||||
activateConstraint(constraint, context)
|
activateConstraint(constraint, context)
|
||||||
|
|
||||||
return context.currentStatus()
|
return context.currentStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,98 +81,70 @@ internal class ControllerImpl (
|
||||||
override fun reactivate(occ: Occurrence) {
|
override fun reactivate(occ: Occurrence) {
|
||||||
// FIXME propagate the status further up the call stack
|
// FIXME propagate the status further up the call stack
|
||||||
// TODO: introduce status to solver API?
|
// TODO: introduce status to solver API?
|
||||||
|
// TODO update the stack
|
||||||
|
|
||||||
val status = process(occ, NORMAL())
|
val status = process(occ, NORMAL())
|
||||||
|
|
||||||
if (status is FAILED) {
|
if (status is FAILED) {
|
||||||
throw status.failure.failureCause()
|
throw status.failure.failureCause()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun process(active: Occurrence, inStatus: FeedbackStatus) : FeedbackStatus {
|
private fun processMatch(match: RuleMatchEx, inStatus: FeedbackStatus) : FeedbackStatus =
|
||||||
assert(active.alive)
|
offerMatch(match, inStatus)
|
||||||
|
.let { when (it) {
|
||||||
|
is ABORTED -> { // guard is not satisfied
|
||||||
|
trace.reject(match)
|
||||||
|
return it.recover() // return from the enclosing method
|
||||||
|
|
||||||
return profiler.profile<FeedbackStatus>("process_${active.constraint().symbol()}") {
|
} is FAILED -> { // guard failed
|
||||||
|
trace.feedback(it.failure)
|
||||||
|
return it.recover() // return from the enclosing method
|
||||||
|
|
||||||
if (!active.stored) {
|
} else -> it
|
||||||
frameStack.current.store.store(active)
|
} }
|
||||||
trace.activate(active)
|
.also { consumeMatch(match) }
|
||||||
} else {
|
.also { trace.trigger(match) }
|
||||||
trace.reactivate(active)
|
.also { processDiscarded(match) }
|
||||||
}
|
.then { processBody(match, it) }
|
||||||
|
.also { trace.finish(match) }
|
||||||
|
|
||||||
val activatedFront = dispatchFront.expand(active)
|
override fun offerMatch(match: RuleMatchEx, inStatus: FeedbackStatus) : FeedbackStatus =
|
||||||
this.dispatchFront = activatedFront
|
inStatus.then { checkMatchPreconditions(match, it) }
|
||||||
|
.also { trace.trying(match) }
|
||||||
|
.then { processGuard(match, it) }
|
||||||
|
|
||||||
val outStatus = activatedFront.matches().toList().fold(inStatus) { status, match ->
|
private fun checkMatchPreconditions(match: RuleMatchEx, inStatus: FeedbackStatus) : FeedbackStatus {
|
||||||
// TODO: paranoid check. should be isAlive() instead
|
|
||||||
// FIXME: move this check elsewhere
|
|
||||||
if (status.operational && active.stored && match.allStored())
|
|
||||||
processMatch(status, match as RuleMatchImpl)
|
|
||||||
else
|
|
||||||
status
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: should be isAlive()
|
|
||||||
if (active.stored) {
|
|
||||||
trace.suspend(active)
|
|
||||||
}
|
|
||||||
|
|
||||||
outStatus
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun processMatch(inStatus: FeedbackStatus, match: RuleMatchImpl) : FeedbackStatus {
|
|
||||||
val context = Context(inStatus, match.logicalContext())
|
val context = Context(inStatus, match.logicalContext())
|
||||||
|
|
||||||
// invoke matched pattern predicates
|
// invoke matched pattern predicates
|
||||||
for (prd in match.patternPredicates()) {
|
for (prd in match.patternPredicates()) {
|
||||||
|
// normally these are safe, but unification can fail
|
||||||
if (!tellPredicate(prd, context)) break
|
if (!tellPredicate(prd, context)) break
|
||||||
}
|
}
|
||||||
|
|
||||||
trace.trying(match)
|
return context.currentStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processGuard(match: RuleMatchEx, inStatus: FeedbackStatus) : FeedbackStatus {
|
||||||
|
val context = Context(inStatus, match.logicalContext())
|
||||||
|
|
||||||
// check guard
|
// check guard
|
||||||
for (gprd in match.rule().guard()) {
|
for (gprd in match.rule().guard()) {
|
||||||
if (!askPredicate(gprd, context)) break
|
if (!askPredicate(gprd, context)) break
|
||||||
}
|
}
|
||||||
|
|
||||||
context.updateStatus { status ->
|
|
||||||
when (status) {
|
|
||||||
is ABORTED -> { // guard is not satisfied
|
|
||||||
trace.reject(match)
|
|
||||||
return status.recover()
|
|
||||||
|
|
||||||
}
|
|
||||||
is FAILED -> { // guard failed
|
|
||||||
trace.feedback(status.failure)
|
|
||||||
return status.recover()
|
|
||||||
|
|
||||||
}
|
|
||||||
else -> status
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dispatchFront = dispatchFront.consume(match)
|
|
||||||
trace.trigger(match)
|
|
||||||
|
|
||||||
match.forEachReplaced {occ ->
|
|
||||||
this.dispatchFront = dispatchFront.contract(occ)
|
|
||||||
frameStack.current.store.discard(occ)
|
|
||||||
trace.discard(occ)
|
|
||||||
}
|
|
||||||
|
|
||||||
processBody(match, context)
|
|
||||||
|
|
||||||
trace.finish(match)
|
|
||||||
|
|
||||||
return context.currentStatus()
|
return context.currentStatus()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processBody(match: RuleMatchImpl, context: Context) {
|
override fun processBody(match: RuleMatchEx, inStatus: FeedbackStatus) : FeedbackStatus {
|
||||||
|
val context = Context(inStatus, match.logicalContext())
|
||||||
|
|
||||||
val altIt = match.rule().bodyAlternation().iterator()
|
val altIt = match.rule().bodyAlternation().iterator()
|
||||||
while (altIt.hasNext()) {
|
while (altIt.hasNext()) {
|
||||||
val body = altIt.next()
|
val body = altIt.next()
|
||||||
|
|
||||||
context.updateStatus { status ->
|
context.eval { status ->
|
||||||
if (status is FAILED) {
|
if (status is FAILED) {
|
||||||
trace.retry(match)
|
trace.retry(match)
|
||||||
status.recover()
|
status.recover()
|
||||||
|
|
@ -203,7 +177,7 @@ internal class ControllerImpl (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val altOk = context.updateStatus { status ->
|
val altOk = context.eval { status ->
|
||||||
if (status is FAILED) {
|
if (status is FAILED) {
|
||||||
trace.feedback(status.failure)
|
trace.feedback(status.failure)
|
||||||
|
|
||||||
|
|
@ -234,13 +208,65 @@ internal class ControllerImpl (
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return context.currentStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun process(active: Occurrence, inStatus: FeedbackStatus) : FeedbackStatus {
|
||||||
|
assert(active.alive)
|
||||||
|
|
||||||
|
return profiler.profile<FeedbackStatus>("process_${active.constraint().symbol()}") {
|
||||||
|
|
||||||
|
if (!active.stored) {
|
||||||
|
frameStack.current.store.store(active)
|
||||||
|
trace.activate(active)
|
||||||
|
} else {
|
||||||
|
trace.reactivate(active)
|
||||||
|
}
|
||||||
|
|
||||||
|
val activatedFront = dispatchFront.expand(active)
|
||||||
|
this.dispatchFront = activatedFront
|
||||||
|
|
||||||
|
val outStatus = activatedFront.matches().toList().fold(inStatus) { status, match ->
|
||||||
|
// TODO: paranoid check. should be isAlive() instead
|
||||||
|
// FIXME: move this check elsewhere
|
||||||
|
if (status.operational && active.stored && match.allStored())
|
||||||
|
processMatch(match as RuleMatchImpl, status)
|
||||||
|
else
|
||||||
|
status
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: should be isAlive()
|
||||||
|
if (active.stored) {
|
||||||
|
trace.suspend(active)
|
||||||
|
}
|
||||||
|
|
||||||
|
outStatus
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun consumeMatch(match: RuleMatchEx) {
|
||||||
|
this.dispatchFront = dispatchFront.consume(match)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun processDiscarded (match: RuleMatchEx) {
|
||||||
|
match.forEachReplaced { occ ->
|
||||||
|
this.dispatchFront = dispatchFront.contract(occ)
|
||||||
|
|
||||||
|
frameStack.current.store.discard(occ)
|
||||||
|
|
||||||
|
trace.discard(occ)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun activateConstraint(constraint: Constraint, context: Context) : Boolean {
|
private fun activateConstraint(constraint: Constraint, context: Context) : Boolean {
|
||||||
val args = supervisor.instantiateArguments(constraint.arguments(), context.logicalContext, context)
|
val args = supervisor.instantiateArguments(constraint.arguments(), context.logicalContext, context)
|
||||||
return context.updateStatus { status ->
|
return context.eval { status ->
|
||||||
|
|
||||||
|
// TODO update the state stack
|
||||||
val active = constraint.occurrence(this, args, context.logicalContext)
|
val active = constraint.occurrence(this, args, context.logicalContext)
|
||||||
process(active, status)
|
process(active, status)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,6 +297,9 @@ internal class ControllerImpl (
|
||||||
override fun <V : Any> variable(metaLogical: MetaLogical<V>): Logical<V>? = null
|
override fun <V : Any> variable(metaLogical: MetaLogical<V>): Logical<V>? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inline fun FeedbackStatus.then(action: (FeedbackStatus) -> FeedbackStatus) : FeedbackStatus =
|
||||||
|
if (operational) action(this) else this
|
||||||
|
|
||||||
private fun RuleMatch.patternPredicates() =
|
private fun RuleMatch.patternPredicates() =
|
||||||
(rule().headKept() + rule().headReplaced()).zip(matchHeadKept() + matchHeadReplaced()).flatMap {
|
(rule().headKept() + rule().headReplaced()).zip(matchHeadKept() + matchHeadReplaced()).flatMap {
|
||||||
it.first.patternPredicates(it.second.arguments())
|
it.first.patternPredicates(it.second.arguments())
|
||||||
|
|
@ -296,7 +325,7 @@ internal class ControllerImpl (
|
||||||
block.invoke(status)
|
block.invoke(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun updateStatus(block: (FeedbackStatus) -> FeedbackStatus) : Boolean {
|
inline fun eval(block: (FeedbackStatus) -> FeedbackStatus) : Boolean {
|
||||||
this.status = block.invoke(status)
|
this.status = block.invoke(status)
|
||||||
return status.operational
|
return status.operational
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ internal class RuleMatchImpl(private val rule: Rule,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fun forEachReplaced(action: (Occurrence) -> Unit) =
|
override fun forEachReplaced(action: (Occurrence) -> Unit) =
|
||||||
headReplaced.forEach(action)
|
headReplaced.forEach(action)
|
||||||
|
|
||||||
override fun signature(): ArrayList<Id<Occurrence>?> =
|
override fun signature(): ArrayList<Id<Occurrence>?> =
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import jetbrains.mps.logic.reactor.core.*
|
import jetbrains.mps.logic.reactor.core.*
|
||||||
|
import jetbrains.mps.logic.reactor.core.internal.FeedbackStatus
|
||||||
import jetbrains.mps.logic.reactor.evaluation.PredicateInvocation
|
import jetbrains.mps.logic.reactor.evaluation.PredicateInvocation
|
||||||
import jetbrains.mps.logic.reactor.evaluation.StoreView
|
import jetbrains.mps.logic.reactor.evaluation.StoreView
|
||||||
import jetbrains.mps.logic.reactor.logical.Logical
|
import jetbrains.mps.logic.reactor.logical.Logical
|
||||||
|
|
@ -159,6 +160,14 @@ class MockController : Controller {
|
||||||
override fun storeView(): StoreView {
|
override fun storeView(): StoreView {
|
||||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun offerMatch(match: RuleMatchEx, inStatus: FeedbackStatus): FeedbackStatus {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun processBody(match: RuleMatchEx, inStatus: FeedbackStatus): FeedbackStatus {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ConjBuilder(val type: Class<out AndItem>) {
|
class ConjBuilder(val type: Class<out AndItem>) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue