Refactor constraint occurrence to depend on controller directly.

Drop creating of a frame stack from a store view.
This commit is contained in:
Fedor Isakov 2019-05-12 19:25:01 +02:00
parent b482de1ce5
commit bfc4b641bb
11 changed files with 142 additions and 148 deletions

View File

@ -16,6 +16,7 @@
package jetbrains.mps.logic.reactor.core
import jetbrains.mps.logic.reactor.evaluation.PredicateInvocation
import jetbrains.mps.logic.reactor.evaluation.StoreView
/**
@ -25,8 +26,14 @@ import jetbrains.mps.logic.reactor.evaluation.StoreView
*/
interface Controller {
fun ask(invocation: PredicateInvocation): Boolean
fun tell(invocation: PredicateInvocation)
fun reactivate(occ: Occurrence)
fun currentFrame(): FrameObservable
/** For tests only */
fun evaluate(occ: Occurrence): StoreView

View File

@ -41,17 +41,10 @@ abstract class EvaluationSessionEx(val program: Program,
@Suppress("UNCHECKED_CAST")
override fun <T : Any> parameter(key: ParameterKey<T>): T? = params ?.get(key) as T
override fun ask(invocation: PredicateInvocation): Boolean {
val solver = invocation.predicate().symbol().solver()
val result = solver.ask(invocation)
trace.ask(result, invocation)
return result
}
override fun ask(invocation: PredicateInvocation): Boolean =
controller().ask(invocation)
override fun tell(invocation: PredicateInvocation) {
val solver = invocation.predicate().symbol().solver()
trace.tell(invocation)
solver.tell(invocation)
}
override fun tell(invocation: PredicateInvocation) =
controller().tell(invocation)
}

View File

@ -18,8 +18,6 @@ package jetbrains.mps.logic.reactor.core
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
import jetbrains.mps.logic.reactor.evaluation.EvaluationSession
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.logical.LogicalContext
import jetbrains.mps.logic.reactor.logical.MetaLogical
@ -30,10 +28,10 @@ import jetbrains.mps.logic.reactor.program.Constraint
*
* @author Fedor Isakov
*/
data class Occurrence (val constraint: Constraint,
data class Occurrence (val controller: Controller,
val constraint: Constraint,
val logicalContext: LogicalContext,
val arguments: List<*>,
val currentFrame: () -> FrameObservable) :
val arguments: List<*>) :
ConstraintOccurrence,
LogicalObserver
{
@ -45,7 +43,7 @@ data class Occurrence (val constraint: Constraint,
init {
for (a in arguments) {
if (a is Logical<*>) {
currentFrame().addObserver(a) { this }
controller.currentFrame().addObserver(a) { this }
}
}
}
@ -58,20 +56,20 @@ data class Occurrence (val constraint: Constraint,
override fun valueUpdated(logical: Logical<*>) {
if (alive) {
EvaluationSession.current(EvaluationSessionEx::class.java).controller().reactivate(this)
controller.reactivate(this)
}
}
override fun parentUpdated(logical: Logical<*>) {
if (alive) {
EvaluationSession.current(EvaluationSessionEx::class.java).controller().reactivate(this)
controller.reactivate(this)
}
}
fun terminate() {
for (a in arguments) {
if (a is Logical<*>) {
currentFrame().removeObserver(a) { this }
controller.currentFrame().removeObserver(a) { this }
}
}
alive = false
@ -81,14 +79,14 @@ data class Occurrence (val constraint: Constraint,
}
fun Constraint.occurrence(arguments: List<*>,
currentFrame: () -> FrameObservable,
logicalContext: LogicalContext): Occurrence =
Occurrence(this, logicalContext, arguments, currentFrame)
fun Constraint.occurrence(controller: Controller,
arguments: List<*>,
logicalContext: LogicalContext): Occurrence =
Occurrence(controller, this, logicalContext, arguments)
fun Constraint.occurrence(arguments: List<*>,
currentFrame: () -> FrameObservable): Occurrence =
Occurrence(this, noLogicalContext, arguments, currentFrame)
fun Constraint.occurrence(controller: Controller,
arguments: List<*>): Occurrence =
Occurrence(controller, this, noLogicalContext, arguments)
private val noLogicalContext: LogicalContext = object: LogicalContext {
override fun <V : Any> variable(metaLogical: MetaLogical<V>): Logical<V>? = null

View File

@ -32,18 +32,13 @@ internal class ControllerImpl (
val supervisor: Supervisor,
val ruleIndex: RuleIndex,
val trace: EvaluationTrace = EvaluationTrace.NULL,
val profiler: Profiler? = null,
val storeView: StoreView? = null) : Controller
val profiler: Profiler? = null) : Controller
{
// FIXME move to parameter
private val session: EvaluationSession = EvaluationSession.current()
private var dispatchFront = Dispatcher(ruleIndex).front()
// FIXME move to context
private val frameStack = FrameStack(storeView)
private val frameStack = FrameStack()
/** For tests only */
override fun storeView(): StoreView = frameStack.current.store.view()
@ -51,7 +46,7 @@ internal class ControllerImpl (
/** For tests only */
override fun evaluate(occ: Occurrence): StoreView {
// create the internal occurrence
val active = occ.constraint().occurrence(occ.arguments(), { frameStack.current })
val active = occ.constraint().occurrence(this, occ.arguments())
val status = process(active, NORMAL())
if (status is FAILED) {
throw status.failure.failureCause()
@ -66,6 +61,21 @@ internal class ControllerImpl (
return context.currentStatus()
}
override fun currentFrame(): FrameObservable = frameStack.current
override fun ask(invocation: PredicateInvocation): Boolean {
val solver = invocation.predicate().symbol().solver()
val result = solver.ask(invocation)
trace.ask(result, invocation)
return result
}
override fun tell(invocation: PredicateInvocation) {
val solver = invocation.predicate().symbol().solver()
trace.tell(invocation)
solver.tell(invocation)
}
override fun reactivate(occ: Occurrence) {
// FIXME propagate the status further up the call stack
// TODO: introduce status to solver API?
@ -148,6 +158,14 @@ internal class ControllerImpl (
trace.discard(occ)
}
processBody(match, context)
trace.finish(match)
return context.currentStatus()
}
private fun processBody(match: RuleMatchImpl, context: Context) {
val altIt = match.rule().bodyAlternation().iterator()
while (altIt.hasNext()) {
val body = altIt.next()
@ -216,16 +234,12 @@ internal class ControllerImpl (
break
}
}
trace.finish(match)
return context.currentStatus()
}
private fun activateConstraint(constraint: Constraint, context: Context) : Boolean {
val args = supervisor.instantiateArguments(constraint.arguments(), context.logicalContext, context)
return context.updateStatus { status ->
val active = constraint.occurrence(args, { frameStack.current }, context.logicalContext)
val active = constraint.occurrence(this, args, context.logicalContext)
process(active, status)
}
}
@ -235,7 +249,7 @@ internal class ControllerImpl (
context.evalSafe { status ->
val args = supervisor.instantiateArguments(predicate.arguments(), context.logicalContext, context)
if (session.ask(predicate.invocation(args, context.logicalContext, context)))
if (ask(predicate.invocation(args, context.logicalContext, context)))
status
else
status.abort(DetailedFeedback("predicate not satisfied"))
@ -248,7 +262,7 @@ internal class ControllerImpl (
context.runSafe {
val args = supervisor.instantiateArguments(predicate.arguments(), context.logicalContext, context)
session.tell(predicate.invocation(args, context.logicalContext, context))
tell(predicate.invocation(args, context.logicalContext, context))
}
}
@ -264,54 +278,54 @@ internal class ControllerImpl (
private fun RuleMatch.allStored() = (matchHeadKept() + matchHeadReplaced()).all { co -> (co as Occurrence).stored }
}
private class Context(inStatus: FeedbackStatus,
val logicalContext: LogicalContext) : InvocationContext
{
private class Context(inStatus: FeedbackStatus,
val logicalContext: LogicalContext) : InvocationContext
{
private var status = inStatus
fun currentStatus(): FeedbackStatus = status
private var status = inStatus
fun currentStatus(): FeedbackStatus = status
override fun report(feedback: EvaluationFeedback) {
when (feedback) {
is EvaluationFailure -> this.status = status.fail(feedback)
is DetailedFeedback -> this.status = status.report(feedback)
}
}
inline fun withStatus(block: (FeedbackStatus) -> Unit) {
block.invoke(status)
}
inline fun updateStatus(block: (FeedbackStatus) -> FeedbackStatus) : Boolean {
this.status = block.invoke(status)
return status.operational
}
inline fun evalSafe(block: (FeedbackStatus) -> FeedbackStatus) : Boolean {
if (status.operational) {
try {
this.status = block.invoke(status)
} catch (ex: EvaluationFailureException) {
this.status = status.fail(EvaluationFailure(ex))
}
}
return status.operational
}
inline fun runSafe(block: () -> Unit) : Boolean {
if (status.operational) {
try {
block()
} catch (ex: EvaluationFailureException) {
this.status = status.fail(EvaluationFailure(ex))
override fun report(feedback: EvaluationFeedback) {
when (feedback) {
is EvaluationFailure -> this.status = status.fail(feedback)
is DetailedFeedback -> this.status = status.report(feedback)
}
}
return status.operational
inline fun withStatus(block: (FeedbackStatus) -> Unit) {
block.invoke(status)
}
inline fun updateStatus(block: (FeedbackStatus) -> FeedbackStatus) : Boolean {
this.status = block.invoke(status)
return status.operational
}
inline fun evalSafe(block: (FeedbackStatus) -> FeedbackStatus) : Boolean {
if (status.operational) {
try {
this.status = block.invoke(status)
} catch (ex: EvaluationFailureException) {
this.status = status.fail(EvaluationFailure(ex))
}
}
return status.operational
}
inline fun runSafe(block: () -> Unit) : Boolean {
if (status.operational) {
try {
block()
} catch (ex: EvaluationFailureException) {
this.status = status.fail(EvaluationFailure(ex))
}
}
return status.operational
}
}
}
@ -323,4 +337,4 @@ fun createController(
trace: EvaluationTrace = EvaluationTrace.NULL,
profiler: Profiler? = null,
storeView: StoreView? = null) : Controller =
ControllerImpl(supervisor, ruleIndex, trace, profiler, storeView)
ControllerImpl(supervisor, ruleIndex, trace, profiler)

View File

@ -43,8 +43,8 @@ internal class EvaluationSessionImpl private constructor (
override fun controller() = controller
private fun launch(main: Constraint, profiler: Profiler?, storeView: StoreView?) : FeedbackStatus {
this.controller = ControllerImpl(supervisor, RuleIndex(program().handlers()), trace, profiler, storeView)
private fun launch(main: Constraint, profiler: Profiler?) : FeedbackStatus {
this.controller = ControllerImpl(supervisor, RuleIndex(program().handlers()), trace, profiler)
return controller.activate(main)
}
@ -54,15 +54,12 @@ internal class EvaluationSessionImpl private constructor (
var evaluationTrace: EvaluationTrace = EvaluationTrace.NULL
var storeView: StoreView? = null
override fun withTrace(computingTracer: EvaluationTrace): EvaluationSession.Config {
this.evaluationTrace = computingTracer
return this
}
override fun withStoreView(storeView: StoreView): EvaluationSession.Config {
this.storeView = storeView
return this
}
@ -85,7 +82,7 @@ internal class EvaluationSessionImpl private constructor (
var failure: Feedback? = null
try {
val main = parameters[ParameterKey.of("main", Constraint::class.java)] as Constraint
val status = session.launch(main, profiler, storeView)
val status = session.launch(main, profiler)
if (status is FAILED) {
failure = status.failure
}

View File

@ -49,12 +49,6 @@ internal class Frame: LogicalObserver, FrameObservable {
this.observers = prev.observers
}
constructor(stack: FrameStack, storeView: StoreView) {
this.stack = stack
this.store = Store(storeView) { stack.current }
this.observers = Maps.of()
}
override fun storeObserver() = store
override fun addObserver(logical: Logical<*>, obs: (FrameObservable) -> LogicalObserver) {
@ -94,14 +88,14 @@ internal class Frame: LogicalObserver, FrameObservable {
}
internal class FrameStack(storeView: StoreView?) : LogicalObserver {
internal class FrameStack : LogicalObserver {
var current: Frame
val observing = HashSet<Id<Logical<*>>>()
init {
this.current = if (storeView != null) Frame(this, storeView) else Frame(this)
this.current = Frame(this)
}
fun push(): Frame {

View File

@ -53,43 +53,7 @@ internal class Store : LogicalObserver {
this.symbol2occurrences = copyFrom.symbol2occurrences
this.logical2occurrences = copyFrom.logical2occurrences
}
/**
* Copy constructor that accepts a view on the store.
* This can be a view on the store from previous run of the program.
*/
constructor(copyFrom: StoreView, currentFrame: () -> FrameObservable) {
this.currentFrame = currentFrame
var log2occs = Maps.of<Id<Logical<*>>, IdHashSet<Occurrence>>()
val orig2occ = IdentityHashMap<ConstraintOccurrence, Occurrence>()
// process the original data and make copies as needed
for (orig in copyFrom.allOccurrences()) {
val occ = orig.constraint().occurrence(orig.arguments(), currentFrame)
orig2occ[orig] = occ
for (arg in orig.arguments()) {
when (arg) {
is Logical<*> -> {
val key = Id(arg.findRoot())
log2occs = log2occs.put(key,
log2occs[key]?.add(occ) ?: singletonIdSet(occ))
currentFrame().addObserver(arg) { frame -> frame.storeObserver() }
}
}
}
occ.stored = true
}
// set internal structures
this.logical2occurrences = log2occs
this.symbol2occurrences = copyFrom.constraintSymbols().fold(Maps.of()) { pmap, csym ->
val occs = IdHashSet(copyFrom.occurrences(csym).map { occ -> orig2occ.get(occ)!! })
pmap.put(csym, occs)
}
}
constructor(currentFrame: () -> FrameObservable) {
this.currentFrame = currentFrame
this.symbol2occurrences = Maps.of()

View File

@ -88,6 +88,10 @@ public abstract class EvaluationSession {
public abstract Config withTrace(EvaluationTrace computingTracer);
/**
* @deprecated passing store view is deprecated and doesn't have an effect
*/
@Deprecated
public abstract Config withStoreView(StoreView storeView);
public abstract EvaluationResult start(Supervisor supervisor);

View File

@ -1,4 +1,6 @@
import jetbrains.mps.logic.reactor.core.*
import jetbrains.mps.logic.reactor.evaluation.PredicateInvocation
import jetbrains.mps.logic.reactor.evaluation.StoreView
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.program.*
import program.MockConstraint
@ -112,7 +114,7 @@ fun equals(left: Any, right: Any): ConjBuilder.() -> Unit = {
}
fun occurrence(id: String, vararg args: Any): Occurrence =
MockConstraint(ConstraintSymbol.symbol(id, args.size)).occurrence(listOf(* args), { fooObservable })
MockConstraint(ConstraintSymbol.symbol(id, args.size)).occurrence(MockController(), listOf(* args))
fun sym0(id: String): ConstraintSymbol =
ConstraintSymbol(id, 0)
@ -123,15 +125,38 @@ fun sym1(id: String): ConstraintSymbol =
fun sym2(id: String): ConstraintSymbol =
ConstraintSymbol(id, 2)
object fooObservable : FrameObservable {
override fun storeObserver(): LogicalObserver {
class MockController : Controller {
override fun currentFrame(): FrameObservable = object : FrameObservable {
override fun storeObserver(): LogicalObserver {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun addObserver(logical: Logical<*>, obs: (FrameObservable) -> LogicalObserver) {
}
override fun removeObserver(logical: Logical<*>, obs: (FrameObservable) -> LogicalObserver) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
override fun ask(invocation: PredicateInvocation): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun addObserver(logical: Logical<*>, obs: (FrameObservable) -> LogicalObserver) {
override fun tell(invocation: PredicateInvocation) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun removeObserver(logical: Logical<*>, obs: (FrameObservable) -> LogicalObserver) {
override fun reactivate(occ: Occurrence) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun evaluate(occ: Occurrence): StoreView {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun storeView(): StoreView {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}

View File

@ -60,7 +60,7 @@ class TestController {
private fun Builder.controller(vararg occurrences: ConstraintOccurrence): Controller {
val program = MockProgram("test", rulesLists, registry = MockConstraintRegistry())
MockSession.init(program, MockSupervisor())
val controller = createController(MockSupervisor(), RuleIndex(program.rulesLists), storeView = MockStoreView(listOf(* occurrences)))
val controller = createController(MockSupervisor(), RuleIndex(program.rulesLists))
MockSession.ourBackend.session.controller = controller
return controller
}
@ -73,7 +73,7 @@ class TestController {
feedbackHandler(rule, feedback)
}
MockSession.init(program, supervisor)
val controller = createController(supervisor, RuleIndex(program.rulesLists), storeView = MockStoreView(listOf(* occurrences)))
val controller = createController(supervisor, RuleIndex(program.rulesLists))
MockSession.ourBackend.session.controller = controller
return controller
}

View File

@ -4,10 +4,7 @@ import jetbrains.mps.logic.reactor.evaluation.EvaluationSession
import jetbrains.mps.logic.reactor.evaluation.StoreView
import jetbrains.mps.logic.reactor.program.Constraint
import jetbrains.mps.logic.reactor.program.ConstraintSymbol
import org.junit.AfterClass
import org.junit.Assert
import org.junit.BeforeClass
import org.junit.Test
import org.junit.*
import program.MockConstraint
/*
@ -65,6 +62,7 @@ class TestIncrementalProgram {
}
@Test
@Ignore
fun replace() {
programWithRules(
rule("main",