Fix the Store implementation and the test harness to support incremental

program launch.
This commit is contained in:
Fedor Isakov 2019-05-03 21:54:13 +02:00
parent 13e8f4a205
commit 09ca1f1236
4 changed files with 95 additions and 40 deletions

View File

@ -55,29 +55,38 @@ internal class Store : LogicalObserver {
}
/**
* 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 l2o = Maps.of<IdWrapper<Logical<*>>, IdHashSet<Occurrence>>()
val storeItems = IdentityHashMap<ConstraintOccurrence, Occurrence>()
copyFrom.allOccurrences().forEach { occ ->
val item = occ.constraint().occurrence(occ.arguments(), currentFrame)
storeItems.put(occ, item)
occ.arguments().forEach { arg ->
var log2occs = Maps.of<IdWrapper<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<*> -> {
l2o = l2o.put(IdWrapper(arg.findRoot()),
l2o[IdWrapper(arg.findRoot())]?.add(item) ?: singletonIdSet(item))
val key = IdWrapper(arg.findRoot())
log2occs = log2occs.put(key,
log2occs[key]?.add(occ) ?: singletonIdSet(occ))
currentFrame().addObserver(arg) { frame -> frame.storeObserver() }
}
}
}
occ.stored = true
}
this.logical2occurrences = l2o
this.symbol2occurrences = copyFrom.constraintSymbols().fold(Maps.of()) { map, sym ->
val copyFrom1 = copyFrom.occurrences(sym).map { occ -> storeItems.get(occ)!! }
val idHashSet = IdHashSet( copyFrom1 )
map.put(sym, idHashSet)
// 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)
}
}
@ -112,6 +121,7 @@ internal class Store : LogicalObserver {
symbol2occurrences[symbol]?.add(occ) ?: singletonIdSet(occ))
for (arg in occ.arguments()) {
// FIXME extracting the value is unnecessary here
val value = if (arg is Logical<*> && arg.isBound) arg.findRoot().value() else arg
when (value) {
is Logical<*> -> {

View File

@ -8,6 +8,7 @@ import jetbrains.mps.logic.reactor.logical.MetaLogical
import jetbrains.mps.logic.reactor.program.*
import java.util.*
import java.util.Collections.*
import kotlin.collections.LinkedHashMap
class ProgramBuilder(val registry: MockConstraintRegistry) {
@ -22,17 +23,24 @@ class ProgramBuilder(val registry: MockConstraintRegistry) {
}
open class HandlerBuilder(val name: String) {
val rules = ArrayList<Rule>()
class HandlerBuilder(val name: String) {
fun appendRule(rule: Rule) {
rules.add(rule)
val rules = LinkedHashMap<String, Rule>()
constructor(name: String, handler: Handler) : this(name) {
for (r in handler.rules()) {
appendRule(r)
}
}
fun toHandler(): Handler = MockHandler(name, rules)
fun appendRule(rule: Rule) {
rules[rule.tag()] = rule
}
fun toHandler(): Handler = MockHandler(name, rules.values.toList())
}
open class RuleBuilder(val tag: String) {
class RuleBuilder(val tag: String) {
val kept = ArrayList<Constraint>()
val replaced = ArrayList<Constraint>()
val guard = ArrayList<Predicate>()

View File

@ -4,6 +4,7 @@ import jetbrains.mps.logic.reactor.program.*
import program.MockConstraint
import solver.TestEqPredicate
import java.util.*
import kotlin.collections.HashMap
/**
* @author Fedor Isakov
@ -32,11 +33,7 @@ class Builder(var handlers: List<Handler>) : RuleLookup {
}
fun programWithRules(vararg ruleBuilders: () -> Rule): Builder {
return builder(arrayOf(handler("test", * ruleBuilders)))
}
private fun builder(handlerBlocks: Array<out () -> Handler>): Builder {
private fun createBuilder(handlerBlocks: Array<out () -> Handler>): Builder {
val handlers = ArrayList<Handler>()
for (block in handlerBlocks) {
handlers.add(block())
@ -44,7 +41,16 @@ private fun builder(handlerBlocks: Array<out () -> Handler>): Builder {
return Builder(handlers)
}
fun handler(name: String, vararg ruleBlocks: () -> Rule): () -> Handler = {
private fun updateBuilder(builder:Builder, handlerBlocks: Array<out () -> Handler>): Builder {
val name2handler = HashMap(builder.handlers.map { it.name() to it }.toMap())
for (block in handlerBlocks) {
val h = block()
name2handler[h.name()] = h
}
return Builder(name2handler.values.toList())
}
private fun createHandler(name: String, vararg ruleBlocks: () -> Rule): () -> Handler = {
val hb = HandlerBuilder(name)
for (block in ruleBlocks) {
hb.appendRule(block())
@ -52,6 +58,22 @@ fun handler(name: String, vararg ruleBlocks: () -> Rule): () -> Handler = {
hb.toHandler()
}
private fun updateHandler(name: String, handler: Handler, vararg ruleBlocks: () -> Rule): () -> Handler = {
val hb = HandlerBuilder(name, handler)
for (block in ruleBlocks) {
hb.appendRule(block())
}
hb.toHandler()
}
// builder DSL for constructing program
fun programWithRules(vararg ruleBuilders: () -> Rule): Builder =
createBuilder(arrayOf(createHandler("test", * ruleBuilders)))
fun Builder.programWithRules(vararg ruleBuilders: () -> Rule): Builder =
updateBuilder(this, arrayOf(updateHandler("test", handlers.first(), * ruleBuilders)))
fun rule(tag: String, vararg component: RuleBuilder.() -> Unit): () -> Rule = {
val rb = RuleBuilder(tag)
for (cmp in component) {
@ -92,6 +114,15 @@ 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 })
fun sym0(id: String): ConstraintSymbol =
ConstraintSymbol(id, 0)
fun sym1(id: String): ConstraintSymbol =
ConstraintSymbol(id, 1)
fun sym2(id: String): ConstraintSymbol =
ConstraintSymbol(id, 2)
object fooObservable : FrameObservable {
override fun storeObserver(): LogicalObserver {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.

View File

@ -56,20 +56,14 @@ class TestIncrementalProgram {
}
private fun Builder.relaunch(name: String, storeView: StoreView, resultHandler: (EvaluationResult) -> Unit) {
val result = EvaluationSession.newSession(program(name))
.withParameter(EvaluationSession.ParameterKey.of("main", Constraint::class.java), MockConstraint(ConstraintSymbol("main", 0)))
.withStoreView(storeView)
.start()
result.failure()?.let { throw it.cause }
resultHandler(result)
}
// private fun StoreView.launch(name: String): StoreView {
// val programBuilder = ProgramBuilder(MockConstraintRegistry())
// for (h in handlers) {
// programBuilder.addHandler(h)
// }
// val result = EvaluationSession.newSession(programBuilder.program(name)).withParam("main", MockConstraint(ConstraintSymbol("main", 0))).start()
// return result.storeView()
//
// }
@Test
fun replace() {
programWithRules(
@ -89,10 +83,22 @@ class TestIncrementalProgram {
)
)
).launch("replace") { result ->
result.storeView().constraintSymbols() shouldBe setOf(ConstraintSymbol("foo", 0), ConstraintSymbol("bar", 0))
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("bar"))
}.also { (builder, storeView) ->
builder.programWithRules (
rule("main.foo",
headKept(
constraint("foo")
),
body(
constraint("baz")
)
)
).relaunch("test", storeView) { result ->
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("bar"), sym0("baz"))
}
}
}