Remove unnecessary feature: StateFrameStack is no longer used.
StateFrameStack adds no functionality but requires too much processing. Rename StateFrameStack -> LogicalState. Rename ProcessingStateImpl -> ConstraintsProcessing. Simplify occurrence's logical observer. Introduce transient dependency on controller to LogicalState. Keep logical state as part of session token, avoid re-initializing all logical observers. Make replaying of match journal independent of controller.
This commit is contained in:
parent
fa7532f34f
commit
d208e802b9
|
|
@ -33,7 +33,9 @@ interface Controller {
|
|||
|
||||
fun reactivate(occ: Occurrence): FeedbackStatus
|
||||
|
||||
fun state(): ProcessingState
|
||||
fun logicalState(): LogicalStateObservable
|
||||
|
||||
fun clearState()
|
||||
|
||||
/** For tests only */
|
||||
fun evaluate(occ: Occurrence): StoreView
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
package jetbrains.mps.logic.reactor.core
|
||||
|
||||
import jetbrains.mps.logic.reactor.core.internal.LogicalImpl
|
||||
import jetbrains.mps.logic.reactor.core.internal.StateFrameStack
|
||||
import jetbrains.mps.logic.reactor.core.internal.LogicalState
|
||||
import jetbrains.mps.logic.reactor.logical.Logical
|
||||
|
||||
/**
|
||||
|
|
@ -35,8 +35,8 @@ interface LogicalObserver {
|
|||
|
||||
// Helps ensuring absence of memory leak of StateFrameStack through variable's internal arrays of observers
|
||||
internal fun checkForwardingObserverUniqueInstance(logical: Logical<*>): Boolean = with(logical as LogicalImpl<*>) {
|
||||
valueObservers.map { it.second }.filterIsInstance<StateFrameStack>().toHashSet().size <= 1 &&
|
||||
parentObservers.map { it.second }.filterIsInstance<StateFrameStack>().toHashSet().size <= 1
|
||||
valueObservers.map { it.second }.filterIsInstance<LogicalState>().toHashSet().size <= 1 &&
|
||||
parentObservers.map { it.second }.filterIsInstance<LogicalState>().toHashSet().size <= 1
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -22,10 +22,26 @@ import jetbrains.mps.logic.reactor.logical.Logical
|
|||
* @author Fedor Isakov
|
||||
*/
|
||||
|
||||
interface ProcessingState {
|
||||
interface LogicalStateObservable {
|
||||
|
||||
fun addForwardingObserver(logical: Logical<*>, observer: LogicalObserver)
|
||||
fun addForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver)
|
||||
|
||||
fun removeForwardingObserver(logical: Logical<*>, observer: LogicalObserver)
|
||||
|
||||
}
|
||||
fun removeForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver)
|
||||
|
||||
fun init(controller: Controller): InitToken
|
||||
|
||||
interface InitToken {
|
||||
|
||||
fun clear()
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface ForwardingLogicalObserver {
|
||||
|
||||
fun valueUpdated(logical: Logical<*>, controller: Controller)
|
||||
|
||||
fun parentUpdated(logical: Logical<*>, controller: Controller)
|
||||
|
||||
}
|
||||
|
|
@ -23,7 +23,6 @@ import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
|
|||
|
||||
import jetbrains.mps.logic.reactor.logical.Logical
|
||||
import jetbrains.mps.logic.reactor.logical.LogicalContext
|
||||
import jetbrains.mps.logic.reactor.logical.MetaLogical
|
||||
import jetbrains.mps.logic.reactor.program.Constraint
|
||||
|
||||
|
||||
|
|
@ -34,40 +33,18 @@ fun justsOf(vararg elements: Int) = TIntHashSet(elements)
|
|||
fun justsFromCollection(collection: Collection<Int>) = TIntHashSet(collection)
|
||||
fun justsCopy(other: Justs) = TIntHashSet(other)
|
||||
|
||||
|
||||
data class OccurrenceObserver(val occurrence: Occurrence, val controller: Controller) : LogicalObserver {
|
||||
|
||||
override fun valueUpdated(logical: Logical<*>) = doReactivate()
|
||||
|
||||
override fun parentUpdated(logical: Logical<*>) = doReactivate()
|
||||
|
||||
private fun doReactivate() {
|
||||
if (occurrence.alive) {
|
||||
val status = controller.reactivate(occurrence)
|
||||
// FIXME propagate the status further up the call stack
|
||||
handleFeedbackStatus(status)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleFeedbackStatus(status: FeedbackStatus) {
|
||||
if (status is FeedbackStatus.FAILED) {
|
||||
throw status.failure.failureCause()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing a single constraint occurrence.
|
||||
*
|
||||
* @author Fedor Isakov
|
||||
*/
|
||||
class Occurrence (controller: Controller,
|
||||
class Occurrence (observable: LogicalStateObservable,
|
||||
val constraint: Constraint,
|
||||
val logicalContext: LogicalContext,
|
||||
val arguments: List<*>,
|
||||
val justifications: Justs,
|
||||
val ruleUniqueTag: Any? = null):
|
||||
ConstraintOccurrence
|
||||
ConstraintOccurrence, ForwardingLogicalObserver
|
||||
{
|
||||
|
||||
var alive = true
|
||||
|
|
@ -77,7 +54,7 @@ class Occurrence (controller: Controller,
|
|||
val identity = System.identityHashCode(this)
|
||||
|
||||
init {
|
||||
revive(controller)
|
||||
revive(observable)
|
||||
}
|
||||
|
||||
override fun constraint(): Constraint = constraint
|
||||
|
|
@ -90,34 +67,49 @@ class Occurrence (controller: Controller,
|
|||
|
||||
override fun justifications(): Justs = justifications
|
||||
|
||||
fun terminate(controller: Controller) {
|
||||
val obs = OccurrenceObserver(this, controller)
|
||||
override fun valueUpdated(logical: Logical<*>, controller: Controller) = doReactivate(controller)
|
||||
|
||||
override fun parentUpdated(logical: Logical<*>, controller: Controller) = doReactivate(controller)
|
||||
|
||||
fun terminate(observable: LogicalStateObservable) {
|
||||
for (a in arguments) {
|
||||
if (a is Logical<*>) {
|
||||
controller.state().removeForwardingObserver(a, obs)
|
||||
observable.removeForwardingObserver(a, this)
|
||||
}
|
||||
}
|
||||
alive = false
|
||||
}
|
||||
|
||||
fun revive(controller: Controller) {
|
||||
val obs = OccurrenceObserver(this, controller)
|
||||
fun revive(observable: LogicalStateObservable) {
|
||||
for (a in arguments) {
|
||||
if (a is Logical<*>) {
|
||||
controller.state().addForwardingObserver(a, obs)
|
||||
observable.addForwardingObserver(a, this)
|
||||
}
|
||||
}
|
||||
alive = true
|
||||
}
|
||||
|
||||
override fun toString(): String = "${constraint().symbol()}(${arguments().joinToString()})"
|
||||
private fun doReactivate(controller: Controller) {
|
||||
if (alive) {
|
||||
val status = controller.reactivate(this)
|
||||
// FIXME propagate the status further up the call stack
|
||||
handleFeedbackStatus(status)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleFeedbackStatus(status: FeedbackStatus) {
|
||||
if (status is FeedbackStatus.FAILED) {
|
||||
throw status.failure.failureCause()
|
||||
}
|
||||
}
|
||||
override fun toString(): String = "${constraint().symbol()}(${arguments().joinToString()})"
|
||||
|
||||
}
|
||||
|
||||
fun Constraint.occurrence(controller: Controller,
|
||||
fun Constraint.occurrence(observable: LogicalStateObservable,
|
||||
arguments: List<*>,
|
||||
justifications: Justs,
|
||||
logicalContext: LogicalContext,
|
||||
ruleUniqueTag: Any? = null): Occurrence =
|
||||
Occurrence(controller, this, logicalContext, arguments, justifications, ruleUniqueTag)
|
||||
Occurrence(observable, this, logicalContext, arguments, justifications, ruleUniqueTag)
|
||||
|
||||
|
|
|
|||
|
|
@ -27,26 +27,24 @@ import jetbrains.mps.logic.reactor.util.profile
|
|||
|
||||
|
||||
/**
|
||||
* Processing of constraints comprises several major activities:
|
||||
* Represents the processing of constraints, which comprises several major activities:
|
||||
*
|
||||
* - finding match(-es) corresponding to an active occurrence
|
||||
* - checking the guard condition
|
||||
* - deactivating discarded occurrences from the match
|
||||
* - processing the body of a constraints rule.
|
||||
*
|
||||
* A ProcessingState maintains a stack of [StateFrame] instances through [StateFrameStack].
|
||||
* It also serves as a proxy for observers of [Logical] that are added during program evaluation.
|
||||
*
|
||||
* @author Fedor Isakov
|
||||
*/
|
||||
|
||||
internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.DispatchingFront,
|
||||
journal: MatchJournalImpl,
|
||||
private val ruleIndex: RuleIndex,
|
||||
private val ispec: IncrementalProgramSpec = IncrementalProgramSpec.DefaultSpec,
|
||||
val trace: EvaluationTrace = EvaluationTrace.NULL,
|
||||
val profiler: Profiler? = null)
|
||||
: StoreAwareJournalImpl(journal)
|
||||
internal class ConstraintsProcessing(private var dispatchingFront: Dispatcher.DispatchingFront,
|
||||
journal: MatchJournalImpl,
|
||||
private val ruleIndex: RuleIndex,
|
||||
val logicalState: LogicalState = LogicalState(),
|
||||
private val ispec: IncrementalProgramSpec = IncrementalProgramSpec.DefaultSpec,
|
||||
val trace: EvaluationTrace = EvaluationTrace.NULL,
|
||||
val profiler: Profiler? = null)
|
||||
: StoreAwareJournalImpl(journal, logicalState)
|
||||
{
|
||||
private val ruleOrdering: RuleOrdering = RuleOrdering(ruleIndex)
|
||||
|
||||
|
|
@ -202,7 +200,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
|
|||
val histView = view()
|
||||
resetStore() // clear observers
|
||||
val rules = ArrayList<Rule>().apply { ruleIndex.forEach { add(it) } }
|
||||
return SessionTokenImpl(histView, rules, dispatchingFront.state())
|
||||
return SessionTokenImpl(histView, rules, dispatchingFront.state(), logicalState)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -217,7 +215,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
|
|||
if (!active.stored) {
|
||||
active.stored = true
|
||||
logActivation(active)
|
||||
active.revive(controller)
|
||||
active.revive(logicalState)
|
||||
}
|
||||
|
||||
profiler.profile("dispatch_${active.constraint().symbol()}") {
|
||||
|
|
@ -308,7 +306,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
|
|||
|
||||
profiler.profile("terminateOccurrence") {
|
||||
|
||||
occ.terminate(controller)
|
||||
occ.terminate(logicalState)
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -30,23 +30,26 @@ import jetbrains.mps.logic.reactor.util.profile
|
|||
|
||||
internal class ControllerImpl (
|
||||
val supervisor: Supervisor,
|
||||
val state: ProcessingStateImpl,
|
||||
val processing: ConstraintsProcessing,
|
||||
val ispec: IncrementalProgramSpec = IncrementalProgramSpec.DefaultSpec,
|
||||
val trace: EvaluationTrace = EvaluationTrace.NULL,
|
||||
val profiler: Profiler? = null) : Controller
|
||||
{
|
||||
|
||||
private val initToken: LogicalStateObservable.InitToken = processing.init(this)
|
||||
|
||||
/** For tests only */
|
||||
override fun storeView(): StoreView = state.storeView()
|
||||
override fun storeView(): StoreView = processing.storeView()
|
||||
|
||||
/** For tests only */
|
||||
override fun evaluate(occ: Occurrence): StoreView {
|
||||
// create the internal occurrence
|
||||
val active = occ.constraint().occurrence(this, occ.arguments(), occ.justifications(), object: LogicalContext {
|
||||
val active = occ.constraint().occurrence(logicalState(), occ.arguments(), occ.justifications(), object: LogicalContext {
|
||||
override fun <V : Any> variable(metaLogical: MetaLogical<V>): Logical<V>? = null
|
||||
})
|
||||
|
||||
trace.activate(occ)
|
||||
val status = state.processActivated(this, active, NORMAL())
|
||||
val status = processing.processActivated(this, active, NORMAL())
|
||||
if (status is FAILED) {
|
||||
throw status.failure.failureCause()
|
||||
}
|
||||
|
|
@ -55,13 +58,13 @@ internal class ControllerImpl (
|
|||
|
||||
fun incrLaunch(constraint: Constraint, rulesDiff: RulesDiff): FeedbackStatus {
|
||||
profiler.profile("invalidation") {
|
||||
state.invalidateRuleMatches(rulesDiff.removed)
|
||||
processing.invalidateRuleMatches(rulesDiff.removed)
|
||||
}
|
||||
profiler.profile("adding_matches") {
|
||||
state.addRuleMatches(rulesDiff.added)
|
||||
processing.addRuleMatches(rulesDiff.added)
|
||||
}
|
||||
return profiler.profile<FeedbackStatus>("reexecution") {
|
||||
state.launchQueue(this)
|
||||
processing.launchQueue(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,13 +74,17 @@ internal class ControllerImpl (
|
|||
|
||||
// fixme: is it valid to always provide current justifications?
|
||||
// while this method is used only in one place at program kick-off, yes, it's initial justs provided.
|
||||
activateConstraint(constraint, state.justs(), context)
|
||||
activateConstraint(constraint, processing.justs(), context)
|
||||
|
||||
return context.currentStatus()
|
||||
}
|
||||
|
||||
override fun state(): ProcessingStateImpl =
|
||||
state
|
||||
override fun logicalState(): LogicalStateObservable =
|
||||
processing
|
||||
|
||||
override fun clearState() {
|
||||
initToken.clear()
|
||||
}
|
||||
|
||||
override fun ask(invocation: PredicateInvocation): Boolean {
|
||||
val solver = invocation.predicate().symbol().solver()
|
||||
|
|
@ -96,7 +103,7 @@ internal class ControllerImpl (
|
|||
profiler.profile<FeedbackStatus>("reactivate_${occ.constraint.symbol()}") {
|
||||
|
||||
trace.reactivate(occ)
|
||||
state.processActivated(this, occ, NORMAL())
|
||||
processing.processActivated(this, occ, NORMAL())
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -146,9 +153,9 @@ internal class ControllerImpl (
|
|||
}
|
||||
}
|
||||
|
||||
val savedPos = state.currentPos()
|
||||
val savedPos = processing.currentPos()
|
||||
|
||||
val currentJusts = state.justs()
|
||||
val currentJusts = processing.justs()
|
||||
|
||||
for (item in body) {
|
||||
val itemOk = when (item) {
|
||||
|
|
@ -199,7 +206,7 @@ internal class ControllerImpl (
|
|||
|
||||
if (!altOk) {
|
||||
// all constraints activated up to a failure are lost
|
||||
state.reset(savedPos)
|
||||
processing.reset(savedPos)
|
||||
|
||||
} else {
|
||||
// body finished normally
|
||||
|
|
@ -216,9 +223,9 @@ internal class ControllerImpl (
|
|||
|
||||
profiler.profile<FeedbackStatus>("activate_${constraint.symbol()}") {
|
||||
|
||||
constraint.occurrence(this, args, justsCopy(justs), context.logicalContext, context.ruleUniqueTag).let { occ ->
|
||||
constraint.occurrence(logicalState(), args, justsCopy(justs), context.logicalContext, context.ruleUniqueTag).let { occ ->
|
||||
trace.activate(occ)
|
||||
state.processActivated(this, occ, status)
|
||||
processing.processActivated(this, occ, status)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -333,10 +340,11 @@ fun createController(
|
|||
|
||||
ControllerImpl(
|
||||
supervisor,
|
||||
ProcessingStateImpl(
|
||||
ConstraintsProcessing(
|
||||
Dispatcher(ruleIndex).front(),
|
||||
MatchJournalImpl(),
|
||||
ruleIndex,
|
||||
LogicalState(),
|
||||
IncrementalProgramSpec.DefaultSpec,
|
||||
trace,
|
||||
profiler
|
||||
|
|
|
|||
|
|
@ -49,24 +49,25 @@ internal class EvaluationSessionImpl private constructor (
|
|||
val ruleIndex = RuleIndex(program.rulesLists())
|
||||
|
||||
if (ispec is IncrementalProgramSpec.NonIncrSpec || token == null) {
|
||||
val state = ProcessingStateImpl(
|
||||
val processing
|
||||
= ConstraintsProcessing(
|
||||
Dispatcher(ruleIndex).front(),
|
||||
MatchJournalImpl(ispec),
|
||||
ruleIndex, ispec, trace, profiler
|
||||
ruleIndex, LogicalState(), ispec, trace, profiler
|
||||
)
|
||||
|
||||
this.controller = ControllerImpl(supervisor, state, ispec, trace, profiler)
|
||||
this.controller = ControllerImpl(supervisor, processing, ispec, trace, profiler)
|
||||
return controller.activate(main)
|
||||
|
||||
} else {
|
||||
val tkn = token as SessionTokenImpl
|
||||
val state = ProcessingStateImpl(
|
||||
val processing = ConstraintsProcessing(
|
||||
Dispatcher(ruleIndex, tkn.getFrontState()).front(),
|
||||
MatchJournalImpl(ispec, tkn.journalView),
|
||||
ruleIndex, ispec, trace, profiler
|
||||
ruleIndex, tkn.logicalState, ispec, trace, profiler
|
||||
)
|
||||
|
||||
this.controller = ControllerImpl(supervisor, state, ispec, trace, profiler)
|
||||
this.controller = ControllerImpl(supervisor, processing, ispec, trace, profiler)
|
||||
return controller.incrLaunch(main, rulesDiff)
|
||||
}
|
||||
}
|
||||
|
|
@ -108,7 +109,7 @@ internal class EvaluationSessionImpl private constructor (
|
|||
override fun start(supervisor: Supervisor): EvaluationResult {
|
||||
var session = Backend.ourBackend.ourSession.get()
|
||||
if (session != null) throw IllegalStateException("session already active")
|
||||
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val durations = parameters[ParameterKey.of("profiling.data", MutableMap::class.java)]
|
||||
as MutableMap<String, String>?
|
||||
|
|
@ -125,6 +126,7 @@ internal class EvaluationSessionImpl private constructor (
|
|||
}
|
||||
}
|
||||
finally {
|
||||
session.controller.clearState()
|
||||
try {
|
||||
profiler?.run {
|
||||
formattedData().entries.forEach { e -> durations.put(e.key, e.value) }
|
||||
|
|
@ -138,7 +140,7 @@ internal class EvaluationSessionImpl private constructor (
|
|||
}
|
||||
|
||||
return object : EvaluationResult {
|
||||
private val token = session.controller.state.endSession()
|
||||
private val token = session.controller.processing.endSession()
|
||||
|
||||
override fun token(): SessionToken = token
|
||||
|
||||
|
|
|
|||
|
|
@ -43,10 +43,10 @@ internal class ExecutionQueue(
|
|||
|
||||
private val seen: MutableSet<ExecPos> = HashSet()
|
||||
|
||||
fun run(controller: Controller, state: ProcessingStateImpl): FeedbackStatus {
|
||||
fun run(controller: Controller, processing: ConstraintsProcessing): FeedbackStatus {
|
||||
var status: FeedbackStatus = FeedbackStatus.NORMAL()
|
||||
if (execQueue.isNotEmpty()) {
|
||||
state.resetStore()
|
||||
processing.resetStore()
|
||||
|
||||
var prevPos: MatchJournal.Pos? = null
|
||||
do {
|
||||
|
|
@ -55,21 +55,21 @@ internal class ExecutionQueue(
|
|||
// Handles the case when several matches are added to the same position.
|
||||
// Then shouldn't replay, because currentPos is valid and more recent (!) than execPos.
|
||||
if (execPos.pos != prevPos) {
|
||||
state.replay(controller, execPos.pos)
|
||||
processing.replay(processing.logicalState, execPos.pos)
|
||||
lastIncrementalRootPos = execPos.pos
|
||||
}
|
||||
prevPos = execPos.pos
|
||||
|
||||
// If the occurrence is still in the store after replay (i.e. if it's valid to activate it)
|
||||
if (execPos.activeOcc.stored) {
|
||||
status = state.reactivate(controller, execPos.activeOcc)
|
||||
// Leave journal state as it was at the point of failure
|
||||
status = processing.reactivate(controller, execPos.activeOcc)
|
||||
// Leave journal processing as it was at the point of failure
|
||||
if (!status.operational) return status
|
||||
}
|
||||
} while (execQueue.isNotEmpty())
|
||||
}
|
||||
// Also replay to the end after queue is fully executed
|
||||
state.replay(controller, state.last().toPos())
|
||||
processing.replay(processing.logicalState, processing.last().toPos())
|
||||
return status
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* Copyright 2014-2019 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package jetbrains.mps.logic.reactor.core.internal
|
||||
|
||||
import jetbrains.mps.logic.reactor.core.*
|
||||
import jetbrains.mps.logic.reactor.logical.Logical
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Captures the state of logical variables in respect to observers.
|
||||
* Serves as a middle layer between logical variables and observers, the latter being notified of changes in the
|
||||
* state in context of the currenly active controller.
|
||||
* Keeps a reference to [Controller] instance, which is used to notify the observers.
|
||||
*
|
||||
* NOTE: the following is no longer relevant, as the "stack frame" functionality has been removed.
|
||||
*
|
||||
* Internally [LogicalState] maintains a stack of [LogicalStateFrame]s capturing processing
|
||||
* state related to logical variable observers. The top [LogicalStateFrame] contains
|
||||
* the current relevant set of [LogicalObserver]s. Events corresponding to
|
||||
* adding and removing observers are forwarded to the top frame.
|
||||
*
|
||||
* Frames are added on events which could require reverting processing state to
|
||||
* the point before them, which can be achieved by simply dropping a frame and
|
||||
* thus dropping all added observers.
|
||||
*/
|
||||
|
||||
class LogicalState : LogicalStateObservable, LogicalObserver
|
||||
{
|
||||
// invariant: never empty
|
||||
private val stateFrames = LinkedList<LogicalStateFrame>()
|
||||
|
||||
private var controller: Controller? = null
|
||||
|
||||
init {
|
||||
stateFrames.push(LogicalStateFrame())
|
||||
}
|
||||
|
||||
override fun init(controller: Controller): LogicalStateObservable.InitToken {
|
||||
assert(this@LogicalState.controller === null)
|
||||
this.controller = controller
|
||||
|
||||
return object : LogicalStateObservable.InitToken {
|
||||
override fun clear() {
|
||||
assert(this@LogicalState.controller !== null)
|
||||
this@LogicalState.controller = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun currentFrame() : LogicalStateFrame =
|
||||
stateFrames.first
|
||||
|
||||
fun push() : LogicalStateFrame {
|
||||
return currentFrame()
|
||||
// val newFrame = StateFrame(stateFrames.first)
|
||||
// stateFrames.push(newFrame)
|
||||
// return newFrame
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
// Clear logicals from this forwarding observer on full reset
|
||||
currentFrame().observed().forEach {
|
||||
it.removeObserver(this)
|
||||
}
|
||||
reset(stateFrames.last)
|
||||
}
|
||||
|
||||
fun reset(frame: LogicalStateFrame) {
|
||||
val it = stateFrames.iterator()
|
||||
while (it.hasNext()) {
|
||||
val next = it.next()
|
||||
if (frame === next) { // referential equality
|
||||
return
|
||||
}
|
||||
it.remove()
|
||||
}
|
||||
throw IllegalStateException("invalid frame")
|
||||
}
|
||||
|
||||
override fun addForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver) {
|
||||
if (!currentFrame().isObserving(logical)) {
|
||||
logical.addObserver(this)
|
||||
}
|
||||
currentFrame().addForwardingObserver(logical, observer)
|
||||
// assert(checkForwardingObserverUniqueInstance(logical))
|
||||
}
|
||||
|
||||
override fun removeForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver) {
|
||||
currentFrame().removeForwardingObserver(logical, observer)
|
||||
if (!currentFrame().isObserving(logical)) {
|
||||
logical.removeObserver(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun valueUpdated(logical: Logical<*>) {
|
||||
// forward to the top frame
|
||||
currentFrame().valueUpdated(logical, controller!!)
|
||||
}
|
||||
|
||||
override fun parentUpdated(logical: Logical<*>) {
|
||||
// forward to the top frame
|
||||
currentFrame().parentUpdated(logical, controller!!)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: this class is deprecated and the "stack of frames" functionality is no longer functioning.
|
||||
* To be removed soon.
|
||||
*
|
||||
* A [LogicalStateFrame] captures a logical state corresponding to currently relevant
|
||||
* observers of changes of logical variables. It roughly corresponds to a bunch of event
|
||||
* that update a set of such observers. By dropping a frame the processing state
|
||||
* can be reverted.
|
||||
*
|
||||
* The main (and only) example of such event is an occurrence activation and the
|
||||
* corresponding observer reactivates occurrence on changes of logical variables
|
||||
* used as its arguments.
|
||||
*
|
||||
* @author Fedor Isakov
|
||||
*/
|
||||
|
||||
@Deprecated("to be removed")
|
||||
class LogicalStateFrame : ForwardingLogicalObserver
|
||||
{
|
||||
// FIXME use Vector instead of ConsList
|
||||
// private var observers: PersMap<Id<Logical<*>>, PersList<LogicalObserver>> = Maps.of()
|
||||
|
||||
private val observers = IdentityHashMap<Logical<*>, ArrayList<ForwardingLogicalObserver>>()
|
||||
|
||||
// constructor(prototype: StateFrame) : this() {
|
||||
// this.observers = prototype.observers
|
||||
// }
|
||||
|
||||
fun addForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver) {
|
||||
// val logicalId = Id(logical)
|
||||
// this.observers = observers.assoc(logicalId,
|
||||
// observers[logicalId]?.prepend(observer) ?: Lists.of(observer))
|
||||
observers.getOrPut(logical) { arrayListOf() }.add(observer)
|
||||
}
|
||||
|
||||
fun removeForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver) {
|
||||
// val logicalId = Id(logical)
|
||||
// observers[logicalId]?.let {
|
||||
// this.observers = observers.assoc(logicalId, it.remove(observer)!!)
|
||||
// }
|
||||
observers[logical]?.remove(observer)
|
||||
}
|
||||
|
||||
fun isObserving(logical: Logical<*>) = observers.containsKey(logical)
|
||||
// observers[Id(logical)] != null
|
||||
|
||||
fun observed(): Sequence<Logical<*>> = observers.keys.asSequence()
|
||||
// observers.keys().asSequence().map { it.wrapped }
|
||||
|
||||
override fun valueUpdated(logical: Logical<*>, controller: Controller) {
|
||||
observers[logical]?.let { list ->
|
||||
for (observer in ArrayList(list)) {
|
||||
observer.valueUpdated(logical, controller)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun parentUpdated(logical: Logical<*>, controller: Controller) {
|
||||
observers[logical]?.let { list ->
|
||||
for (observer in ArrayList(list)) {
|
||||
observer.parentUpdated(logical, controller)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -67,7 +67,7 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
|
|||
* Advances journal position to specified position.
|
||||
* @throws IllegalStateException when position is not from the future (relative to current pos).
|
||||
*/
|
||||
fun replay(controller: Controller, futurePos: Pos)
|
||||
fun replay(observable: LogicalStateObservable, futurePos: Pos)
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -16,10 +16,7 @@
|
|||
|
||||
package jetbrains.mps.logic.reactor.core.internal
|
||||
|
||||
import jetbrains.mps.logic.reactor.core.Controller
|
||||
import jetbrains.mps.logic.reactor.core.Justs
|
||||
import jetbrains.mps.logic.reactor.core.Occurrence
|
||||
import jetbrains.mps.logic.reactor.core.justsOf
|
||||
import jetbrains.mps.logic.reactor.core.*
|
||||
import jetbrains.mps.logic.reactor.core.internal.MatchJournal.*
|
||||
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
|
||||
import jetbrains.mps.logic.reactor.evaluation.MatchJournalView
|
||||
|
|
@ -110,25 +107,27 @@ internal open class MatchJournalImpl(
|
|||
if (currentPos() != pastPos) throw IllegalStateException()
|
||||
}
|
||||
|
||||
override fun replay(controller: Controller, futurePos: MatchJournal.Pos) {
|
||||
override fun replay(observable: LogicalStateObservable, futurePos: MatchJournal.Pos) {
|
||||
while (posPtr.hasNext()) {
|
||||
current = posPtr.next()
|
||||
if (futurePos.chunk === current) {
|
||||
replayOccurrences(controller, current.entries.take(futurePos.entriesCount))
|
||||
replayOccurrences(observable, current.entries.take(futurePos.entriesCount))
|
||||
return
|
||||
}
|
||||
replayOccurrences(controller, current.entries)
|
||||
replayOccurrences(observable, current.entries)
|
||||
}
|
||||
if (currentPos() != futurePos) throw IllegalStateException()
|
||||
}
|
||||
|
||||
private fun replayOccurrences(controller: Controller, occSpecs: Iterable<MatchJournal.Chunk.Entry>) =
|
||||
private fun replayOccurrences(observable: LogicalStateObservable, occSpecs: Iterable<MatchJournal.Chunk.Entry>) =
|
||||
occSpecs.forEach {
|
||||
if (it.discarded) {
|
||||
it.occ.terminate(controller)
|
||||
// it.occ.terminate(observable)
|
||||
it.occ.alive = false
|
||||
it.occ.stored = false
|
||||
} else {
|
||||
it.occ.revive(controller)
|
||||
// it.occ.revive(observable)
|
||||
it.occ.alive = true
|
||||
it.occ.stored = true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,8 +24,10 @@ import jetbrains.mps.logic.reactor.program.Rule
|
|||
data class SessionTokenImpl(
|
||||
private val journalView: MatchJournal.View,
|
||||
private val rules: Iterable<Rule>,
|
||||
private val frontState: DispatchingFrontState
|
||||
) : SessionToken {
|
||||
private val frontState: DispatchingFrontState,
|
||||
val logicalState: LogicalState
|
||||
) : SessionToken
|
||||
{
|
||||
override fun getJournalView(): MatchJournalView = journalView
|
||||
override fun getRules(): Iterable<Rule> = rules
|
||||
fun getFrontState(): DispatchingFrontState = frontState
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014-2019 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package jetbrains.mps.logic.reactor.core.internal
|
||||
|
||||
import jetbrains.mps.logic.reactor.core.LogicalObserver
|
||||
import jetbrains.mps.logic.reactor.logical.Logical
|
||||
import jetbrains.mps.logic.reactor.util.*
|
||||
|
||||
/**
|
||||
* A [StateFrame] captures a processing state corresponding to current relevant
|
||||
* observers of changes of logical variables. It roughly corresponds to an event
|
||||
* that updates a set of such observers. By dropping a frame the processing state
|
||||
* can be reverted.
|
||||
*
|
||||
* The main (and only) example of such event is an occurrence activation and the
|
||||
* corresponding observer reactivates occurrence on changes of logical variables
|
||||
* used as its arguments.
|
||||
*
|
||||
* @author Fedor Isakov
|
||||
*/
|
||||
|
||||
internal class StateFrame constructor() : LogicalObserver
|
||||
{
|
||||
// FIXME use Vector instead of ConsList
|
||||
private var observers: PersMap<Id<Logical<*>>, PersList<LogicalObserver>> = Maps.of()
|
||||
|
||||
constructor(prototype: StateFrame) : this() {
|
||||
this.observers = prototype.observers
|
||||
}
|
||||
|
||||
fun addForwardingObserver(logical: Logical<*>, observer: LogicalObserver) {
|
||||
val logicalId = Id(logical)
|
||||
this.observers = observers.assoc(logicalId,
|
||||
observers[logicalId]?.prepend(observer) ?: Lists.of(observer))
|
||||
}
|
||||
|
||||
fun removeForwardingObserver(logical: Logical<*>, observer: LogicalObserver) {
|
||||
val logicalId = Id(logical)
|
||||
observers[logicalId]?.let {
|
||||
this.observers = observers.assoc(logicalId, it.remove(observer)!!)
|
||||
}
|
||||
}
|
||||
|
||||
fun isObserving(logical: Logical<*>) =
|
||||
observers[Id(logical)] != null
|
||||
|
||||
fun observed(): Sequence<Logical<*>> =
|
||||
observers.keys().asSequence().map { it.wrapped }
|
||||
|
||||
override fun valueUpdated(logical: Logical<*>) {
|
||||
observers[Id(logical)]?.let { list ->
|
||||
for (observer in list) {
|
||||
observer.valueUpdated(logical)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun parentUpdated(logical: Logical<*>) {
|
||||
observers[Id(logical)]?.let { list ->
|
||||
for (observer in list) {
|
||||
observer.parentUpdated(logical)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,98 +0,0 @@
|
|||
/*
|
||||
* Copyright 2014-2019 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package jetbrains.mps.logic.reactor.core.internal
|
||||
|
||||
import jetbrains.mps.logic.reactor.core.*
|
||||
import jetbrains.mps.logic.reactor.logical.Logical
|
||||
import java.util.*
|
||||
|
||||
|
||||
/**
|
||||
* A [StateFrameStack] maintains a stack of [StateFrame]s capturing processing
|
||||
* state related to logical variable observers. The top [StateFrame] contains
|
||||
* the current relevant set of [LogicalObserver]s. Events corresponding to
|
||||
* adding and removing observers are forwarded to the top frame.
|
||||
*
|
||||
* Frames are added on events which could require reverting processing state to
|
||||
* the point before them, which can be achieved by simply dropping a frame and
|
||||
* thus dropping all added observers.
|
||||
*/
|
||||
|
||||
internal open class StateFrameStack() : ProcessingState, LogicalObserver
|
||||
{
|
||||
// invariant: never empty
|
||||
private val stateFrames = LinkedList<StateFrame>()
|
||||
|
||||
init {
|
||||
stateFrames.push(StateFrame())
|
||||
}
|
||||
|
||||
fun currentFrame() : StateFrame =
|
||||
stateFrames.first
|
||||
|
||||
fun push() : StateFrame {
|
||||
val newFrame = StateFrame(stateFrames.first)
|
||||
stateFrames.push(newFrame)
|
||||
return newFrame
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
// Clear logicals from this forwarding observer on full reset
|
||||
currentFrame().observed().forEach {
|
||||
it.removeObserver(this)
|
||||
}
|
||||
reset(stateFrames.last)
|
||||
}
|
||||
|
||||
fun reset(frame: StateFrame) {
|
||||
val it = stateFrames.iterator()
|
||||
while (it.hasNext()) {
|
||||
val next = it.next()
|
||||
if (frame === next) { // referential equality
|
||||
return
|
||||
}
|
||||
it.remove()
|
||||
}
|
||||
throw IllegalStateException("invalid frame")
|
||||
}
|
||||
|
||||
override fun addForwardingObserver(logical: Logical<*>, observer: LogicalObserver) {
|
||||
if (!currentFrame().isObserving(logical)) {
|
||||
logical.addObserver(this)
|
||||
}
|
||||
currentFrame().addForwardingObserver(logical, observer)
|
||||
// assert(checkForwardingObserverUniqueInstance(logical))
|
||||
}
|
||||
|
||||
override fun removeForwardingObserver(logical: Logical<*>, observer: LogicalObserver) {
|
||||
currentFrame().removeForwardingObserver(logical, observer)
|
||||
if (!currentFrame().isObserving(logical)) {
|
||||
logical.removeObserver(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun valueUpdated(logical: Logical<*>) {
|
||||
// forward to the top frame
|
||||
currentFrame().valueUpdated(logical)
|
||||
}
|
||||
|
||||
override fun parentUpdated(logical: Logical<*>) {
|
||||
// forward to the top frame
|
||||
currentFrame().parentUpdated(logical)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,15 +16,15 @@
|
|||
|
||||
package jetbrains.mps.logic.reactor.core.internal
|
||||
|
||||
import jetbrains.mps.logic.reactor.core.ProcessingState
|
||||
import jetbrains.mps.logic.reactor.core.LogicalStateObservable
|
||||
import jetbrains.mps.logic.reactor.program.IncrementalProgramSpec
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
|
||||
/**
|
||||
* [MatchJournal] which also maintains observers in [ProcessingState] in sync with its current position.
|
||||
* [MatchJournal] which also maintains observers in [LogicalStateObservable] in sync with its current position.
|
||||
*/
|
||||
interface StoreAwareJournal : MatchJournal, ProcessingState {
|
||||
interface StoreAwareJournal : MatchJournal, LogicalStateObservable {
|
||||
|
||||
// Only for testing push() in Impl
|
||||
fun testPush()
|
||||
|
|
@ -41,12 +41,13 @@ interface StoreAwareJournal : MatchJournal, ProcessingState {
|
|||
}
|
||||
|
||||
|
||||
internal open class StoreAwareJournalImpl(private val journal: MatchJournal, private val state: StateFrameStack = StateFrameStack())
|
||||
: MatchJournal by journal, StoreAwareJournal, ProcessingState by state
|
||||
internal open class StoreAwareJournalImpl(private val journal: MatchJournal,
|
||||
private val state: LogicalState = LogicalState())
|
||||
: MatchJournal by journal, StoreAwareJournal, LogicalStateObservable by state
|
||||
{
|
||||
|
||||
private class FramePos(
|
||||
val frame: StateFrame,
|
||||
val frame: LogicalStateFrame,
|
||||
chunk: MatchJournal.Chunk,
|
||||
entriesCount: Int = 0
|
||||
) : MatchJournal.Pos(chunk, entriesCount)
|
||||
|
|
|
|||
|
|
@ -174,15 +174,15 @@ fun equals(left: Any, right: Any): ConjBuilder.() -> Unit = {
|
|||
|
||||
fun occurrence(id: String, vararg args: Any): Occurrence =
|
||||
MockConstraint(ConstraintSymbol.symbol(id, args.size))
|
||||
.occurrence(MockController(), listOf(* args), justsOf(), noLogicalContext)
|
||||
.occurrence(MockController().logicalState(), listOf(* args), justsOf(), noLogicalContext)
|
||||
|
||||
fun taggedOccurrence(ruleUniqueTag: Any, id: String, vararg args: Any): Occurrence =
|
||||
MockConstraint(ConstraintSymbol.symbol(id, args.size))
|
||||
.occurrence(MockController(), listOf(* args), justsOf(), noLogicalContext, ruleUniqueTag)
|
||||
.occurrence(MockController().logicalState(), listOf(* args), justsOf(), noLogicalContext, ruleUniqueTag)
|
||||
|
||||
fun justifiedOccurrence(id: String, justs: Justs, vararg args: Any): Occurrence =
|
||||
MockConstraint(ConstraintSymbol.symbol(id, args.size), true)
|
||||
.occurrence(MockController(), listOf(* args), justs, noLogicalContext)
|
||||
.occurrence(MockController().logicalState(), listOf(* args), justs, noLogicalContext)
|
||||
|
||||
fun justifiedOccurrence(id: String, justs: Collection<Int>, vararg args: Any): Occurrence =
|
||||
justifiedOccurrence(id, justsFromCollection(justs), * args)
|
||||
|
|
@ -201,7 +201,11 @@ private val noLogicalContext = object : LogicalContext {
|
|||
}
|
||||
|
||||
class MockController : Controller {
|
||||
|
||||
|
||||
override fun clearState() {
|
||||
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.
|
||||
}
|
||||
|
|
@ -218,11 +222,15 @@ class MockController : Controller {
|
|||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
|
||||
override fun state(): ProcessingState = object : ProcessingState {
|
||||
override fun addForwardingObserver(logical: Logical<*>, observer: LogicalObserver) {
|
||||
override fun logicalState(): LogicalStateObservable = object : LogicalStateObservable {
|
||||
override fun addForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver) {
|
||||
}
|
||||
|
||||
override fun removeForwardingObserver(logical: Logical<*>, observer: LogicalObserver) {
|
||||
override fun removeForwardingObserver(logical: Logical<*>, observer: ForwardingLogicalObserver) {
|
||||
}
|
||||
|
||||
override fun init(controller: Controller): LogicalStateObservable.InitToken {
|
||||
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ class TestStoreAwareJournal {
|
|||
nchunks shouldBe 4 * 2 // 4 rules, each activates 1 principal occurrence
|
||||
|
||||
// try replay the last chunk
|
||||
replay(mockController, lastPos)
|
||||
replay(mockController.logicalState(), lastPos)
|
||||
|
||||
// should change nothing
|
||||
storeView().constraintSymbols() shouldBe setOf(sym0("qux"), sym0("lax"))
|
||||
|
|
@ -175,11 +175,11 @@ class TestStoreAwareJournal {
|
|||
storeView().allOccurrences().count() shouldBe 0
|
||||
view().chunks.size shouldBe nchunks
|
||||
|
||||
replay(mockController, initPos)
|
||||
replay(mockController.logicalState(), initPos)
|
||||
|
||||
storeView().constraintSymbols() shouldBe setOf<ConstraintSymbol>()
|
||||
|
||||
replay(mockController, fooPos)
|
||||
replay(mockController.logicalState(), fooPos)
|
||||
|
||||
storeView().constraintSymbols() shouldBe setOf(sym0("foo"))
|
||||
// although storeView() results change, journal remains the same
|
||||
|
|
@ -231,7 +231,7 @@ class TestStoreAwareJournal {
|
|||
|
||||
storeView().allOccurrences().count() shouldBe 0
|
||||
|
||||
replay(mockController, savedPos)
|
||||
replay(mockController.logicalState(), savedPos)
|
||||
|
||||
storeView().allOccurrences() shouldBe oldStore
|
||||
currentPos() shouldBe savedPos
|
||||
|
|
@ -473,7 +473,7 @@ class TestStoreAwareJournal {
|
|||
// store is not longer valid after removing chunks from history, so reset it
|
||||
resetStore()
|
||||
// move to the point where we want to insert new rule
|
||||
replay(mockController, continueFrom)
|
||||
replay(mockController.logicalState(), continueFrom)
|
||||
|
||||
// according to the history 'qux' wasn't activated at this point & 'bar1' wasn't discarded
|
||||
storeView().constraintSymbols() shouldBe setOf<ConstraintSymbol>()
|
||||
|
|
@ -498,7 +498,7 @@ class TestStoreAwareJournal {
|
|||
assertNotEquals(lastPos, currentPos())
|
||||
|
||||
// finally, purely go the the end, applying the rest of the history to the store
|
||||
replay(mockController, lastPos)
|
||||
replay(mockController.logicalState(), lastPos)
|
||||
|
||||
currentPos().chunk shouldBeSame lastPos.chunk // we inserted in the middle -- the last chunk should remain the same
|
||||
storeView().constraintSymbols() shouldBe setOf(sym0("bar1"), sym0("qux"), sym0("marker"))
|
||||
|
|
|
|||
Loading…
Reference in New Issue