Introduce StoreView as a parameter to launching evaluation session. Minor code refactoring/extracting.

This commit is contained in:
Fedor Isakov 2017-01-10 20:19:59 +01:00
parent c68d9a0bd7
commit cae150819f
9 changed files with 250 additions and 213 deletions

View File

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

View File

@ -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<IdWrapper<Logical<*>>>()
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<IdWrapper<Logical<*>>, 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<ConstraintOccurrence>? = 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 {

View File

@ -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<IdWrapper<Logical<*>>, 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<IdWrapper<Logical<*>>>()
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)
}
}

View File

@ -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<PredicateSymbol>()
val myParameters = HashMap<String, Any?>()
var myEvaluationTrace: EvaluationTrace = EvaluationTrace.NULL
val predicateSymbols = ArrayList<PredicateSymbol>()
val parameters = HashMap<String, Any?>()
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<PredicateSymbol>(arrayOfNulls(myPredicateSymbols.size))
sessionSolver.init(myEvaluationTrace, * predicateSymbols)
sessionSolver.init(evaluationTrace, * predicateSymbols.toArray<PredicateSymbol>(arrayOfNulls(predicateSymbols.size)))
session = MemEvaluationSession(program, sessionSolver, myEvaluationTrace)
ourBackend.ourSession.set(session)
val durations = myParameters.get("profiling.data") as MutableMap<String, String>?
@Suppress("UNCHECKED_CAST")
val durations =
parameters.get("profiling.data") as MutableMap<String, String>?
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

View File

@ -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<*>) :

View File

@ -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<ConstraintSymbol, IdHashSet<ConstraintOccurrence>>
@ -65,8 +79,7 @@ class Store : LogicalObserver, OccurrenceIndex {
var value2occurrences: PersMap<Any, IdHashSet<ConstraintOccurrence>>
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<IdWrapper<Logical<*>>, IdHashSet<ConstraintOccurrence>>()
var t2o = TermTrie<ConstraintOccurrence>()
var v2o = Maps.of<Any, IdHashSet<ConstraintOccurrence>>()
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<ConstraintOccurrence>): 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<ConstraintOccurrence>) : StoreView {
private class StoreViewImpl(occurrences: Sequence<ConstraintOccurrence>) : StoreView {
val allOccurrences = occurrences.toSet()
@ -263,10 +295,7 @@ class StoreViewImpl(occurrences: Sequence<ConstraintOccurrence>) : StoreView {
override fun allOccurrences(): Iterable<ConstraintOccurrence> = allOccurrences
override fun occurrences(symbol: ConstraintSymbol?): Iterable<ConstraintOccurrence> =
override fun occurrences(symbol: ConstraintSymbol): Iterable<ConstraintOccurrence> =
allOccurrences.filter { co -> co.constraint().symbol() == symbol }.toSet()
}

View File

@ -14,39 +14,48 @@ class IdHashSet<E> : AbstractSet<E> {
}
lateinit var store: DerivedKeyHashMap<Int, E>
var storage: DerivedKeyHashMap<Int, E>
constructor() {
this.store = DerivedKeyHashMap(object : KeyFunction<Int, E> {
override fun key(value: E): Int = idkey(value)
})
this.storage = emptyStorage()
}
constructor(setStore: DerivedKeyHashMap<Int, E>) {
this.store = setStore
constructor(storage: DerivedKeyHashMap<Int, E>) {
this.storage = storage
}
override fun add(value: E): IdHashSet<E> = IdHashSet(store.put(idkey(value), value))
constructor(copyFrom: Iterable<E>) {
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<E> = IdHashSet(storage.put(idkey(value), value))
override fun remove(value: E): IdHashSet<E> = IdHashSet(store.remove(idkey(value)))
override fun contains(value: E): Boolean = storage.containsKey(idkey(value))
override fun remove(value: E): IdHashSet<E> = IdHashSet(storage.remove(idkey(value)))
override fun iterator(): MutableIterator<E> = object: MutableIterator<E> {
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<Int, E> {
return DerivedKeyHashMap(object : KeyFunction<Int, E> {
override fun key(value: E): Int = idkey(value)
})
}
}
@Suppress("UNCHECKED_CAST")
fun <E> emptySet(): IdHashSet<E> = IdHashSet.EMPTY_SET as IdHashSet<E>
fun <E> singletonSet(e: E): IdHashSet<E> = emptySet<E>().add(e)

View File

@ -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<ConstraintOccurrence>) : StoreView {
val symbols = occurrences.map { it.constraint().symbol() }.toSet()
override fun constraintSymbols(): Iterable<ConstraintSymbol> = symbols
override fun allOccurrences(): Iterable<ConstraintOccurrence> = occurrences
override fun occurrences(symbol: ConstraintSymbol): Iterable<ConstraintOccurrence> =
occurrences.filter { it.constraint().symbol() == symbol }
}
private fun <T : Any> eq(left: T, right: T) = left eq right
private fun <T : Any> is_eq(left: T, right: T): Boolean = left is_eq right

View File

@ -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<Pair<Logical<*>, 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) {