diff --git a/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/EvaluationSession.java b/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/EvaluationSession.java index 0988fd22..3dab924c 100644 --- a/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/EvaluationSession.java +++ b/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/EvaluationSession.java @@ -6,9 +6,6 @@ import jetbrains.mps.logic.reactor.program.Program; import jetbrains.mps.logic.reactor.program.PredicateSymbol; import jetbrains.mps.logic.reactor.program.ConstraintSymbol; -/** - * AKA "Handler" - */ public abstract class EvaluationSession { public static EvaluationSession current() { @@ -31,6 +28,8 @@ public abstract class EvaluationSession { public abstract EvaluationSession.Config withTrace(EvaluationTrace computingTracer); + public abstract EvaluationSession.Config withStoreView(StoreView storeView); + public abstract EvaluationSession.Config withParam(String key, Object param); public abstract EvaluationSession start(SessionSolver sessionSolver); diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt index 4c33b61e..06348dbe 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt @@ -1,164 +1,33 @@ package jetbrains.mps.logic.reactor.core -import com.github.andrewoma.dexx.collection.ConsList -import com.github.andrewoma.dexx.collection.Maps -import com.github.andrewoma.dexx.collection.Map as PersMap import jetbrains.mps.logic.reactor.evaluation.* import jetbrains.mps.logic.reactor.logical.Logical import jetbrains.mps.logic.reactor.logical.LogicalContext -import jetbrains.mps.logic.reactor.logical.LogicalUnification import jetbrains.mps.logic.reactor.logical.MetaLogical -import jetbrains.mps.logic.reactor.program.* -import jetbrains.mps.logic.reactor.util.* -import java.util.* - -/** - * @author Fedor Isakov - */ +import jetbrains.mps.logic.reactor.program.Constraint +import jetbrains.mps.logic.reactor.program.Predicate +import jetbrains.mps.logic.reactor.program.Program +import jetbrains.mps.logic.reactor.program.Rule +import jetbrains.mps.logic.reactor.util.Profiler +import jetbrains.mps.logic.reactor.util.profile +import com.github.andrewoma.dexx.collection.Map as PersMap -class FrameStack : LogicalObserver { - - lateinit var current: Frame - - val observing = HashSet>>() - - constructor() { - this.current = Frame(this) - } - - fun push(): Frame { - val frame = Frame(this, current) - this.current = frame - return frame - } - - fun reset(frame: Frame): Unit { - this.current = frame - } - - fun addObserver(logical: Logical<*>) { - val token = IdWrapper(logical) - if (!observing.contains(token)) { - logical.addObserver(this) - observing.add(token) - } - } - - fun removeObserver(logical: Logical<*>) { - // NOP - // yes, keep listening, the updates are still filtered down the stream - } - - override fun valueUpdated(logical: Logical<*>) { - current.valueUpdated(logical) - } - - override fun parentUpdated(logical: Logical<*>) { - current.parentUpdated(logical) - } -} - -interface StoreHolder { - - fun store(): Store - - fun addObserver(logical: Logical<*>, obs: (StoreHolder) -> LogicalObserver) - - fun removeObserver(logical: Logical<*>, obs: (StoreHolder) -> LogicalObserver) -} - - -class Frame : LogicalObserver, StoreHolder +class Controller( + val program: Program, + val trace: EvaluationTrace = EvaluationTrace.NULL, + val profiler: Profiler? = null, + val storeView: StoreView? = null) { - val prev: Frame? - val store: Store + private val ruleIndex: RuleIndex = RuleIndex(program.handlers()) - private var stack: FrameStack - - private lateinit var observers: PersMap>, ConsList<(StoreHolder) -> LogicalObserver>> - - constructor(stack: FrameStack, prev: Frame? = null) { - this.stack = stack - this.prev = prev - this.observers = prev?.observers ?: Maps.of() - this.store = Store(prev?.store ?: Store { stack.current }, { stack.current }) - } - - override fun store() = store - - override fun addObserver(logical: Logical<*>, obs: (StoreHolder) -> LogicalObserver) { - val logicalId = IdWrapper(logical) - if (!observers.containsKey(logicalId)) { - stack.addObserver(logical) - } - this.observers = observers.put(logicalId, - observers[logicalId]?.prepend(obs) ?: cons(obs)) - } - - override fun removeObserver(logical: Logical<*>, obs: (StoreHolder) -> LogicalObserver) { - val logicalId = IdWrapper(logical) - observers[logicalId].remove(obs)?.let { newList -> - this.observers = observers.put(logicalId, newList) - if (newList.isEmpty) { - stack.removeObserver(logical) - } - } - } - - override fun valueUpdated(logical: Logical<*>) { - observers[IdWrapper(logical)]?.let { list -> - for (obs in list) { - obs(this).valueUpdated(logical) - } - } - } - - override fun parentUpdated(logical: Logical<*>) { - observers[IdWrapper(logical)]?.let { list -> - for (obs in list) { - obs(this).parentUpdated(logical) - } - } - } - -} - - -class Controller { - - private val frameStack = FrameStack() + private val frameStack = FrameStack(storeView) // persistent (functional) object. reassigned on update private var propHistory = PropagationHistory() - val program: Program - - val ruleIndex: RuleIndex - - private val trace: EvaluationTrace - - private val profiler: Profiler? - - constructor( - program: Program, - trace: EvaluationTrace = EvaluationTrace.NULL, - profiler: Profiler? = null, - // for testing purposes only - occurrences: Iterable? = null) - { - this.program = program - this.ruleIndex = RuleIndex(program.handlers()) - this.trace = trace - this.profiler = profiler - if (occurrences != null) { - frameStack.current.store.storeAll(occurrences) - } - } - - fun storeView(): StoreView = - frameStack.current.store.view() + fun storeView(): StoreView = frameStack.current.store.view() fun activate(constraint: Constraint) { try { diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Frame.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Frame.kt new file mode 100644 index 00000000..29f9000a --- /dev/null +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Frame.kt @@ -0,0 +1,119 @@ +package jetbrains.mps.logic.reactor.core + +import com.github.andrewoma.dexx.collection.ConsList +import com.github.andrewoma.dexx.collection.Map +import com.github.andrewoma.dexx.collection.Maps +import jetbrains.mps.logic.reactor.evaluation.StoreView +import jetbrains.mps.logic.reactor.logical.Logical +import jetbrains.mps.logic.reactor.util.IdWrapper +import jetbrains.mps.logic.reactor.util.cons +import jetbrains.mps.logic.reactor.util.remove +import java.util.* + +internal class Frame: LogicalObserver, StoreKeeper { + + val stack: FrameStack + + val store: Store + + private var observers: Map>, ConsList<(StoreKeeper) -> LogicalObserver>> + + constructor(stack: FrameStack) { + this.stack = stack + this.store = Store() { stack.current } + this.observers = Maps.of() + } + + constructor(stack: FrameStack, prev: Frame) { + this.stack = stack + this.store = Store(prev.store) { stack.current } + this.observers = prev.observers + } + + constructor(stack: FrameStack, storeView: StoreView) { + this.stack = stack + this.store = Store(storeView) { stack.current } + this.observers = Maps.of() + } + + override fun store() = store + + override fun addObserver(logical: Logical<*>, obs: (StoreKeeper) -> LogicalObserver) { + val logicalId = IdWrapper(logical) + if (!observers.containsKey(logicalId)) { + stack.addObserver(logical) + } + this.observers = observers.put(logicalId, + observers[logicalId]?.prepend(obs) ?: cons(obs)) + } + + override fun removeObserver(logical: Logical<*>, obs: (StoreKeeper) -> LogicalObserver) { + val logicalId = IdWrapper(logical) + observers[logicalId].remove(obs)?.let { newList -> + this.observers = observers.put(logicalId, newList) + if (newList.isEmpty) { + stack.removeObserver(logical) + } + } + } + + override fun valueUpdated(logical: Logical<*>) { + observers[IdWrapper(logical)]?.let { list -> + for (obs in list) { + obs(this).valueUpdated(logical) + } + } + } + + override fun parentUpdated(logical: Logical<*>) { + observers[IdWrapper(logical)]?.let { list -> + for (obs in list) { + obs(this).parentUpdated(logical) + } + } + } + +} + +internal class FrameStack(storeView: StoreView?) : LogicalObserver { + + var current: Frame + + val observing = HashSet>>() + + init { + this.current = if (storeView != null) Frame(this, storeView) else Frame(this) + } + + fun push(): Frame { + val frame = Frame(this, current) + this.current = frame + return frame + } + + fun reset(frame: Frame): Unit { + this.current = frame + } + + fun addObserver(logical: Logical<*>) { + val token = IdWrapper(logical) + if (!observing.contains(token)) { + logical.addObserver(this) + observing.add(token) + } + } + + fun removeObserver(logical: Logical<*>) { + // NOP + // yes, keep listening, the updates are still filtered down the stream + } + + override fun valueUpdated(logical: Logical<*>) { + current.valueUpdated(logical) + } + + override fun parentUpdated(logical: Logical<*>) { + current.parentUpdated(logical) + } + +} \ No newline at end of file diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/MemEvaluationSession.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/MemEvaluationSession.kt index 63f04d96..1ac128e7 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/MemEvaluationSession.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/MemEvaluationSession.kt @@ -21,26 +21,43 @@ interface SessionObjects { } -class MemEvaluationSession : EvaluationSession, SessionObjects { +class MemEvaluationSession private constructor ( + val program: Program, + val sessionSolver: SessionSolver, + val trace: EvaluationTrace) : EvaluationSession(), SessionObjects +{ + + lateinit var controller: Controller + + private fun launch(main: Constraint, profiler: Profiler?, storeView: StoreView?) { + this.controller = Controller(program, trace, profiler, storeView) + controller.activate(main) + } private class Config(val program: Program) : EvaluationSession.Config() { - val myPredicateSymbols = ArrayList() - val myParameters = HashMap() - var myEvaluationTrace: EvaluationTrace = EvaluationTrace.NULL + val predicateSymbols = ArrayList() + val parameters = HashMap() + var evaluationTrace: EvaluationTrace = EvaluationTrace.NULL + var storeView: StoreView? = null override fun withPredicates(vararg predicateSymbols: PredicateSymbol): EvaluationSession.Config { - myPredicateSymbols.addAll(Arrays.asList(* predicateSymbols)) + this.predicateSymbols.addAll(Arrays.asList(* predicateSymbols)) return this } override fun withTrace(computingTracer: EvaluationTrace): EvaluationSession.Config { - myEvaluationTrace = computingTracer + this.evaluationTrace = computingTracer + return this + } + + override fun withStoreView(storeView: StoreView): EvaluationSession.Config { + this.storeView = storeView return this } override fun withParam(key: String, param: Any): EvaluationSession.Config { - myParameters.put(key, param) + this.parameters.put(key, param) return this } @@ -48,16 +65,17 @@ class MemEvaluationSession : EvaluationSession, SessionObjects { var session = ourBackend.ourSession.get() if (session != null) throw IllegalStateException("session already active") - val predicateSymbols = myPredicateSymbols.toArray(arrayOfNulls(myPredicateSymbols.size)) - sessionSolver.init(myEvaluationTrace, * predicateSymbols) + sessionSolver.init(evaluationTrace, * predicateSymbols.toArray(arrayOfNulls(predicateSymbols.size))) - session = MemEvaluationSession(program, sessionSolver, myEvaluationTrace) - ourBackend.ourSession.set(session) - - val durations = myParameters.get("profiling.data") as MutableMap? + @Suppress("UNCHECKED_CAST") + val durations = + parameters.get("profiling.data") as MutableMap? val profiler = durations?.let { Profiler() } + + session = MemEvaluationSession(program, sessionSolver, evaluationTrace) + ourBackend.ourSession.set(session) try { - session.launch(myParameters["main"] as Constraint, profiler) + session.launch(parameters["main"] as Constraint, profiler, storeView) } finally { try { @@ -76,23 +94,6 @@ class MemEvaluationSession : EvaluationSession, SessionObjects { } } - val program: Program - val sessionSolver: SessionSolver - val trace: EvaluationTrace - - lateinit var controller: Controller - - private constructor(program: Program, sessionSolver: SessionSolver, trace: EvaluationTrace): super() { - this.program = program - this.sessionSolver = sessionSolver - this.trace = trace - } - - fun launch(main: Constraint, profiler: Profiler?) { - this.controller = Controller(program, trace, profiler) - controller.activate(main) - } - override fun handler() = controller override fun sessionSolver(): SessionSolver = sessionSolver diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt index 0e75cbf8..0a186807 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt @@ -6,14 +6,13 @@ import jetbrains.mps.logic.reactor.logical.Logical import jetbrains.mps.logic.reactor.logical.LogicalContext import jetbrains.mps.logic.reactor.program.Constraint import jetbrains.mps.logic.reactor.program.Program -import java.util.* /** * @author Fedor Isakov */ -fun Constraint.occurrence(currentFrame: () -> Frame, program: Program, context: LogicalContext): ConstraintOccurrence = +internal fun Constraint.occurrence(currentFrame: () -> Frame, program: Program, context: LogicalContext): ConstraintOccurrence = Occurrence(currentFrame, this, program.occurrenceArguments(this, context)) private data class Occurrence (val currentFrame: () -> Frame, val constraint: Constraint, val arguments: List<*>) : diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Store.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Store.kt index 6417af4b..f94bba85 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Store.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Store.kt @@ -31,9 +31,23 @@ fun ConstraintOccurrence.isAlive(): Boolean = (this as StoreItem).alive interface StoreItem { + var alive: Boolean + var stored: Boolean + fun terminate(): Unit + +} + +interface StoreKeeper { + + fun store(): Store + + fun addObserver(logical: Logical<*>, obs: (StoreKeeper) -> LogicalObserver) + + fun removeObserver(logical: Logical<*>, obs: (StoreKeeper) -> LogicalObserver) + } interface OccurrenceIndex { @@ -55,7 +69,7 @@ interface OccurrenceIndex { */ class Store : LogicalObserver, OccurrenceIndex { - val currentFrame: () -> StoreHolder + val currentFrame: () -> StoreKeeper var symbol2occurrences: PersMap> @@ -65,8 +79,7 @@ class Store : LogicalObserver, OccurrenceIndex { var value2occurrences: PersMap> - constructor(copyFrom: Store, currentFrame: () -> StoreHolder) - { + constructor(copyFrom: Store, currentFrame: () -> StoreKeeper) { this.currentFrame = currentFrame this.symbol2occurrences = copyFrom.symbol2occurrences this.logical2occurrences = copyFrom.logical2occurrences @@ -74,7 +87,32 @@ class Store : LogicalObserver, OccurrenceIndex { this.value2occurrences = copyFrom.value2occurrences } - constructor(currentFrame: () -> StoreHolder) { + constructor(copyFrom: StoreView, currentFrame: () -> StoreKeeper) { + this.currentFrame = currentFrame + this.symbol2occurrences = copyFrom.constraintSymbols() + .fold(Maps.of()) { map, sym -> map.put(sym, IdHashSet(copyFrom.occurrences(sym))) } + + var l2o = Maps.of>, IdHashSet>() + var t2o = TermTrie() + var v2o = Maps.of>() + + copyFrom.allOccurrences().forEach { occ -> + occ.arguments().forEach { a -> + when (a) { + is Logical<*> -> l2o = l2o.put(IdWrapper(a), l2o[IdWrapper(a)]?.add(occ) ?: singletonSet(occ)) + is Term -> t2o = t2o.put(a, occ) + is Any -> v2o = v2o.put(a, v2o[a]?.add(occ) ?: singletonSet(occ)) + } + } + + } + + this.logical2occurrences = l2o + this.term2occurrences = t2o + this.value2occurrences = v2o + } + + constructor(currentFrame: () -> StoreKeeper) { this.currentFrame = currentFrame this.symbol2occurrences = Maps.of() this.logical2occurrences = Maps.of() @@ -121,12 +159,6 @@ class Store : LogicalObserver, OccurrenceIndex { } } - fun storeAll(all: Iterable): Unit { - for(occ in all) { - store(occ) - } - } - fun store(occ: ConstraintOccurrence): Unit { val symbol = occ.constraint().symbol() @@ -253,7 +285,7 @@ class Store : LogicalObserver, OccurrenceIndex { } -class StoreViewImpl(occurrences: Sequence) : StoreView { +private class StoreViewImpl(occurrences: Sequence) : StoreView { val allOccurrences = occurrences.toSet() @@ -263,10 +295,7 @@ class StoreViewImpl(occurrences: Sequence) : StoreView { override fun allOccurrences(): Iterable = allOccurrences - override fun occurrences(symbol: ConstraintSymbol?): Iterable = + override fun occurrences(symbol: ConstraintSymbol): Iterable = allOccurrences.filter { co -> co.constraint().symbol() == symbol }.toSet() } - - - diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/util/IdHashSet.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/util/IdHashSet.kt index 5b81e5f1..3e49c407 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/util/IdHashSet.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/util/IdHashSet.kt @@ -14,39 +14,48 @@ class IdHashSet : AbstractSet { } - lateinit var store: DerivedKeyHashMap + var storage: DerivedKeyHashMap constructor() { - this.store = DerivedKeyHashMap(object : KeyFunction { - override fun key(value: E): Int = idkey(value) - }) + this.storage = emptyStorage() } - constructor(setStore: DerivedKeyHashMap) { - this.store = setStore + constructor(storage: DerivedKeyHashMap) { + this.storage = storage } - override fun add(value: E): IdHashSet = IdHashSet(store.put(idkey(value), value)) + constructor(copyFrom: Iterable) { + this.storage = copyFrom.fold(emptyStorage()) { s, e -> s.put(idkey(e), e) } + } - override fun contains(value: E): Boolean = store.containsKey(idkey(value)) + override fun add(value: E): IdHashSet = IdHashSet(storage.put(idkey(value), value)) - override fun remove(value: E): IdHashSet = IdHashSet(store.remove(idkey(value))) + override fun contains(value: E): Boolean = storage.containsKey(idkey(value)) + + override fun remove(value: E): IdHashSet = IdHashSet(storage.remove(idkey(value))) override fun iterator(): MutableIterator = object: MutableIterator { - val storeIt = store.iterator() + val storageIt = storage.iterator() - override fun hasNext(): Boolean = storeIt.hasNext() + override fun hasNext(): Boolean = storageIt.hasNext() - override fun next(): E = storeIt.next().component2() + override fun next(): E = storageIt.next().component2() override fun remove() { throw UnsupportedOperationException() } } + private fun emptyStorage(): DerivedKeyHashMap { + return DerivedKeyHashMap(object : KeyFunction { + override fun key(value: E): Int = idkey(value) + }) + } + } +@Suppress("UNCHECKED_CAST") fun emptySet(): IdHashSet = IdHashSet.EMPTY_SET as IdHashSet fun singletonSet(e: E): IdHashSet = emptySet().add(e) \ No newline at end of file diff --git a/reactor/Test/test/TestController.kt b/reactor/Test/test/TestController.kt index d21398a0..d04446f8 100644 --- a/reactor/Test/test/TestController.kt +++ b/reactor/Test/test/TestController.kt @@ -1,5 +1,6 @@ import jetbrains.mps.logic.reactor.core.Controller import jetbrains.mps.logic.reactor.core.SessionObjects +import jetbrains.mps.logic.reactor.core.Store import jetbrains.mps.logic.reactor.evaluation.* import jetbrains.mps.logic.reactor.logical.Logical import jetbrains.mps.logic.reactor.logical.LogicalContext @@ -60,11 +61,22 @@ class TestController { val solver = sessionSolver(env.expressionSolver, env.equalsSolver) MockSession.init(solver) val program = MockProgram("test", handlers, registry = MockConstraintRegistry(solver)) - val controller = Controller(program, occurrences = listOf(* occurrences)) + val controller = Controller(program, storeView = MockStoreView(listOf(* occurrences))) MockSession.ourBackend.session.controller = controller return controller } + private class MockStoreView(val occurrences: List) : StoreView { + val symbols = occurrences.map { it.constraint().symbol() }.toSet() + + override fun constraintSymbols(): Iterable = symbols + + override fun allOccurrences(): Iterable = occurrences + + override fun occurrences(symbol: ConstraintSymbol): Iterable = + occurrences.filter { it.constraint().symbol() == symbol } + } + private fun eq(left: T, right: T) = left eq right private fun is_eq(left: T, right: T): Boolean = left is_eq right diff --git a/reactor/Test/test/TestOccurrenceStore.kt b/reactor/Test/test/TestOccurrenceStore.kt index eed90357..2bc831a3 100644 --- a/reactor/Test/test/TestOccurrenceStore.kt +++ b/reactor/Test/test/TestOccurrenceStore.kt @@ -14,20 +14,20 @@ import org.junit.Before class TestOccurrenceStore { - class MockProxy(val _store: () -> Store) : LogicalObserver, StoreHolder { + internal class MockProxy(val _store: () -> Store) : LogicalObserver, StoreKeeper { private var observerList = emptyConsList, LogicalObserver>>() override fun store(): Store = _store() - override fun addObserver(logical: Logical<*>, obs: (StoreHolder) -> LogicalObserver) { + override fun addObserver(logical: Logical<*>, obs: (StoreKeeper) -> LogicalObserver) { if (!observerList.any { obs -> obs.first === logical }) { // referential equality! logical.addObserver(this) } this.observerList = observerList.prepend(logical.to(obs(this))) } - override fun removeObserver(logical: Logical<*>, obs: (StoreHolder) -> LogicalObserver) = TODO() + override fun removeObserver(logical: Logical<*>, obs: (StoreKeeper) -> LogicalObserver) = TODO() override fun valueUpdated(logical: Logical<*>) { for (obs in observerList) {