Logical value observers. Testing reactivation of constraint occurrences.
This commit is contained in:
parent
b90bdc84c4
commit
057c4d7c69
|
|
@ -3,6 +3,7 @@ package jetbrains.mps.logic.reactor.core
|
|||
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
|
||||
import jetbrains.mps.logic.reactor.evaluation.PredicateInvocation
|
||||
import jetbrains.mps.logic.reactor.evaluation.SessionSolver
|
||||
import jetbrains.mps.logic.reactor.logical.Logical
|
||||
import jetbrains.mps.logic.reactor.logical.LogicalContext
|
||||
import jetbrains.mps.logic.reactor.logical.LogicalPattern
|
||||
import jetbrains.mps.logic.reactor.program.*
|
||||
|
|
@ -14,10 +15,14 @@ import java.util.*
|
|||
|
||||
class Handler {
|
||||
|
||||
private val sessionSolver: SessionSolver
|
||||
val sessionSolver: SessionSolver
|
||||
|
||||
private val rules : MutableList<Rule> = ArrayList<Rule>()
|
||||
|
||||
private val stored : MutableList<ConstraintOccurrence> = ArrayList<ConstraintOccurrence>()
|
||||
|
||||
private val activeQueue : Queue<ConstraintOccurrence> = LinkedList<ConstraintOccurrence>()
|
||||
|
||||
constructor(
|
||||
sessionSolver: SessionSolver,
|
||||
programRules: Iterable<Rule>,
|
||||
|
|
@ -33,8 +38,17 @@ class Handler {
|
|||
|
||||
fun occurrences(): Set<ConstraintOccurrence> = stored.toSet()
|
||||
|
||||
fun process(active: ConstraintOccurrence) {
|
||||
store(active)
|
||||
fun queue(occurrence: ConstraintOccurrence) {
|
||||
activeQueue.add(occurrence)
|
||||
while (activeQueue.isNotEmpty()) {
|
||||
process(activeQueue.poll())
|
||||
}
|
||||
}
|
||||
|
||||
private fun process(active: ConstraintOccurrence) {
|
||||
if (!active.isStored()) {
|
||||
store(active)
|
||||
}
|
||||
|
||||
val matcher = object : Matcher(rules) {
|
||||
override fun findOccurrences(constraint: Constraint, acceptable: (ConstraintOccurrence) -> Boolean):
|
||||
|
|
@ -43,8 +57,8 @@ class Handler {
|
|||
}
|
||||
|
||||
for (match in matcher.lookupMatches(active).filter { pm -> pm.rule.checkGuard(pm.logicalContext()) }) {
|
||||
if (!active.isAlive()) return
|
||||
if (match.occurrences().any{ co -> !co.isAlive() }) continue
|
||||
if (!active.isStored()) return
|
||||
if (match.occurrences().any{ co -> !co.isStored() }) continue
|
||||
|
||||
for ((cst, occ) in match.discarded) {
|
||||
discard(occ)
|
||||
|
|
@ -62,11 +76,12 @@ class Handler {
|
|||
|
||||
private fun discard(occ: ConstraintOccurrence) {
|
||||
stored.remove(occ)
|
||||
occ.terminate()
|
||||
}
|
||||
|
||||
private fun activate(item: AndItem, logicalContext: LogicalContext) {
|
||||
when(item) {
|
||||
is Constraint -> process(item.occurrence(logicalContext))
|
||||
is Constraint -> process(item.occurrence(this@Handler, logicalContext))
|
||||
is Predicate -> tellPredicate(item.invocation(logicalContext))
|
||||
else -> throw IllegalArgumentException("unknown item ${item}")
|
||||
}
|
||||
|
|
@ -82,7 +97,7 @@ class Handler {
|
|||
sessionSolver.tell(invocation.predicate().symbol(), * invocation.arguments().toTypedArray())
|
||||
}
|
||||
|
||||
private fun ConstraintOccurrence.isAlive(): Boolean =
|
||||
private fun ConstraintOccurrence.isStored(): Boolean =
|
||||
stored.contains(this)
|
||||
|
||||
}
|
||||
|
|
@ -91,8 +106,8 @@ private fun AndItem.argumentValues(context: LogicalContext): List<Any> =
|
|||
arguments().map { arg -> if (arg is LogicalPattern<*>) context.valueFor(arg) else arg!! }.toList()
|
||||
|
||||
|
||||
private fun Constraint.occurrence(context: LogicalContext): ConstraintOccurrence =
|
||||
ReactorConstraintOccurrence(this, argumentValues(context))
|
||||
private fun Constraint.occurrence(handler: Handler, context: LogicalContext): ConstraintOccurrence =
|
||||
MemConstraintOccurrence(handler, this, argumentValues(context))
|
||||
|
||||
|
||||
private fun Predicate.invocation(logicalContext: LogicalContext): PredicateInvocation {
|
||||
|
|
@ -104,20 +119,52 @@ private fun Predicate.invocation(logicalContext: LogicalContext): PredicateInvoc
|
|||
}
|
||||
}
|
||||
|
||||
fun ConstraintOccurrence.terminate() {
|
||||
if (this is MemConstraintOccurrence) {
|
||||
_terminate()
|
||||
}
|
||||
}
|
||||
|
||||
private data class ReactorConstraintOccurrence(val constraint: Constraint, val arguments: List<Any>, val id: Int) : ConstraintOccurrence {
|
||||
private data class MemConstraintOccurrence(val handler: Handler, val constraint: Constraint, val arguments: List<Any>, val id: Int) :
|
||||
ConstraintOccurrence,
|
||||
LogicalValueObserver
|
||||
{
|
||||
|
||||
var alive = true
|
||||
|
||||
companion object {
|
||||
val random = Random()
|
||||
}
|
||||
|
||||
constructor(constraint: Constraint, arguments: List<Any>) :
|
||||
this(constraint, arguments, random.nextInt()) {}
|
||||
constructor(handler: Handler, constraint: Constraint, arguments: List<Any>) :
|
||||
this(handler, constraint, arguments, random.nextInt())
|
||||
{
|
||||
for (a in arguments) {
|
||||
if (a is Logical<*>) {
|
||||
a.addObserver(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun constraint(): Constraint = constraint
|
||||
|
||||
override fun arguments(): Collection<Any> = arguments
|
||||
|
||||
override fun valueUpdated(logical: Logical<*>) {
|
||||
handler.queue(this)
|
||||
}
|
||||
|
||||
|
||||
fun _terminate() {
|
||||
for (a in arguments) {
|
||||
if (a is Logical<*>) {
|
||||
a.removeObserver(this)
|
||||
}
|
||||
}
|
||||
alive = false
|
||||
}
|
||||
|
||||
|
||||
override fun toString(): String = "${constraint().symbol()}(${arguments().joinToString()})"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,26 @@ package jetbrains.mps.logic.reactor.core
|
|||
import jetbrains.mps.logic.reactor.logical.Logical
|
||||
import jetbrains.mps.logic.reactor.logical.LogicalPattern
|
||||
import jetbrains.mps.logic.reactor.logical.SolverLogical
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* @author Fedor Isakov
|
||||
*/
|
||||
|
||||
internal interface LogicalValueObserver {
|
||||
|
||||
fun valueUpdated(logical: Logical<*>)
|
||||
|
||||
}
|
||||
|
||||
internal fun Logical<*>.addObserver(observer: LogicalValueObserver) {
|
||||
(this as MemLogical<*>).observers.add(observer)
|
||||
}
|
||||
|
||||
internal fun Logical<*>.removeObserver(observer: LogicalValueObserver) {
|
||||
(this as MemLogical<*>).observers.remove(observer)
|
||||
}
|
||||
|
||||
fun <V> LogicalPattern<V>.logical(): Logical<V> = MemLogical<V>(name())
|
||||
|
||||
class MemLogical<T> : SolverLogical<T> {
|
||||
|
|
@ -27,6 +42,8 @@ class MemLogical<T> : SolverLogical<T> {
|
|||
|
||||
var rank = 0
|
||||
|
||||
internal val observers = ArrayList<LogicalValueObserver>()
|
||||
|
||||
constructor(value: T) {
|
||||
this.name = "$${anonIdx++}"
|
||||
this._value = value
|
||||
|
|
@ -55,15 +72,33 @@ class MemLogical<T> : SolverLogical<T> {
|
|||
|
||||
override fun setParent(parent: SolverLogical<T>) {
|
||||
this._parent = parent as MemLogical<T>
|
||||
if (find().isBound) {
|
||||
notifyObservers()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setValue(newValue: T) { this._value = newValue }
|
||||
override fun setValue(newValue: T) {
|
||||
this._value = newValue
|
||||
notifyObservers()
|
||||
}
|
||||
|
||||
override fun rank(): Int = rank
|
||||
|
||||
override fun incRank() { rank++ }
|
||||
|
||||
override fun mergeObservers(mergeFrom: SolverLogical<T>) = TODO()
|
||||
override fun mergeObservers(mergeFrom: SolverLogical<T>) {
|
||||
val other = mergeFrom as MemLogical<T>
|
||||
observers.addAll(other.observers)
|
||||
other.observers.clear()
|
||||
}
|
||||
|
||||
private fun notifyObservers() {
|
||||
val obs = ArrayList(observers)
|
||||
this.observers.clear()
|
||||
for (o in obs) {
|
||||
o.valueUpdated(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun find(): MemLogical<T> {
|
||||
val tmp = _parent
|
||||
|
|
@ -72,7 +72,7 @@ class MemEvaluationSession : EvaluationSession {
|
|||
|
||||
fun launch(main: ConstraintOccurrence) {
|
||||
this.handler = Handler(sessionSolver(), program.rules())
|
||||
handler.process(main)
|
||||
handler.queue(main)
|
||||
// FIXME: shutdown the session properly
|
||||
ourBackend.ourSession.set(null)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,22 +23,3 @@ data class MemConstraint(val symbol: ConstraintSymbol, val arguments: List<Any>)
|
|||
|
||||
}
|
||||
|
||||
data class MemConstraintOccurrence(val constraint: Constraint, val arguments: List<Any>, val id: Int) : ConstraintOccurrence {
|
||||
|
||||
companion object {
|
||||
val random = Random()
|
||||
}
|
||||
|
||||
constructor(constraint: Constraint, arguments: List<Any>) :
|
||||
this(constraint, arguments, random.nextInt()) {}
|
||||
|
||||
constructor(id: String, vararg args: Any) :
|
||||
this(MemConstraint(ConstraintSymbol.symbol(id, args.size)), listOf(* args), random.nextInt()) {}
|
||||
|
||||
override fun constraint(): Constraint = constraint
|
||||
|
||||
override fun arguments(): Collection<Any> = arguments
|
||||
|
||||
override fun toString(): String = "${constraint().symbol()}(${arguments().joinToString()})"
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
package solver
|
||||
|
||||
import jetbrains.mps.logic.reactor.evaluation.EvaluationSession
|
||||
import jetbrains.mps.logic.reactor.evaluation.Queryable
|
||||
import jetbrains.mps.logic.reactor.logical.Logical
|
||||
|
|
@ -74,6 +76,8 @@ class EqualsSolver : Queryable {
|
|||
}
|
||||
else {
|
||||
left.setParent(right)
|
||||
// the representative has all the observers
|
||||
right.mergeObservers(left);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +98,7 @@ class EqualsSolver : Queryable {
|
|||
right.findRoot().setValue(left)
|
||||
}
|
||||
}
|
||||
|
||||
fun tell_value_value(left: Any?, right: Any?) {
|
||||
check(left == right)
|
||||
}
|
||||
|
|
@ -108,3 +113,7 @@ class EqualsSolver : Queryable {
|
|||
infix fun <T : Any> Logical<T>.eq(value: T) {
|
||||
EvaluationSession.current().sessionSolver().tell(PredicateSymbol("equals", 2), this, value)
|
||||
}
|
||||
|
||||
infix fun <T : Any> Logical<T>.eq(other: Logical<T>) {
|
||||
EvaluationSession.current().sessionSolver().tell(PredicateSymbol("equals", 2), this, other)
|
||||
}
|
||||
|
|
@ -33,6 +33,8 @@ inline fun <reified T: Any> logicalPattern(name1: String, name2: String, name3:
|
|||
|
||||
fun <T: Any> Logical<T>.get(): T = findRoot().value()
|
||||
|
||||
fun <T: Any> Logical<T>.getNullable(): T? = findRoot().value()
|
||||
|
||||
fun <T: Any> Logical<T>.set(t: T) {
|
||||
if (this is SolverLogical<T>)
|
||||
findRoot().setValue(t)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ import jetbrains.mps.logic.reactor.logical.LogicalContext
|
|||
import jetbrains.mps.logic.reactor.logical.LogicalPattern
|
||||
import jetbrains.mps.logic.reactor.program.*
|
||||
import program.MemConstraint
|
||||
import program.MemConstraintOccurrence
|
||||
import TestConstraintOccurrence
|
||||
import solver.EqualsSolver
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
|
|
@ -73,7 +74,7 @@ fun equals(left: Any, right: Any): ConjBuilder.() -> Unit = {
|
|||
add(TestEqPredicate(left, right))
|
||||
}
|
||||
|
||||
fun occurrence(id: String, vararg args: Any) : ConstraintOccurrence = MemConstraintOccurrence(id, * args)
|
||||
fun occurrence(id: String, vararg args: Any) : ConstraintOccurrence = TestConstraintOccurrence(id, * args)
|
||||
|
||||
class RB(tag: String, val env: Environment) : RuleBuilder(tag) {
|
||||
|
||||
|
|
@ -128,3 +129,23 @@ private fun buildConjunction(type: Class<out AndItem>,
|
|||
}
|
||||
return conjBuilder
|
||||
}
|
||||
|
||||
data class TestConstraintOccurrence(val constraint: Constraint, val arguments: List<Any>, val id: Int) : ConstraintOccurrence {
|
||||
|
||||
companion object {
|
||||
val random = Random()
|
||||
}
|
||||
|
||||
constructor(constraint: Constraint, arguments: List<Any>) :
|
||||
this(constraint, arguments, random.nextInt()) {}
|
||||
|
||||
constructor(id: String, vararg args: Any) :
|
||||
this(MemConstraint(ConstraintSymbol.symbol(id, args.size)), listOf(* args), random.nextInt()) {}
|
||||
|
||||
override fun constraint(): Constraint = constraint
|
||||
|
||||
override fun arguments(): Collection<Any> = arguments
|
||||
|
||||
override fun toString(): String = "${constraint().symbol()}(${arguments().joinToString()})"
|
||||
|
||||
}
|
||||
|
|
@ -20,13 +20,17 @@ import org.junit.Test
|
|||
|
||||
class TestHandler {
|
||||
|
||||
fun sessionSolver(exprSolver: Queryable, equalsSolver: Queryable) : SessionSolver =
|
||||
private fun sessionSolver(exprSolver: Queryable, equalsSolver: Queryable) : SessionSolver =
|
||||
MemSessionSolver(exprSolver, equalsSolver).apply {
|
||||
init(PredicateSymbol("equals", 2), JavaPredicateSymbol.EXPRESSION0, JavaPredicateSymbol.EXPRESSION1, JavaPredicateSymbol.EXPRESSION2, JavaPredicateSymbol.EXPRESSION3) }
|
||||
|
||||
private fun Builder.handler(vararg occurrences: ConstraintOccurrence): Handler =
|
||||
Handler(sessionSolver(env.expressionSolver, env.equalsSolver), rules, listOf(* occurrences))
|
||||
|
||||
private fun <T : Any> Handler.eq(left: Logical<T>, right: Logical<T>) {
|
||||
sessionSolver.tell(PredicateSymbol("equals", 2), left, right)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@BeforeClass @JvmStatic fun setup() {
|
||||
|
||||
|
|
@ -48,7 +52,7 @@ class TestHandler {
|
|||
constraint("foo")
|
||||
))
|
||||
).run {
|
||||
handler().apply { process(occurrence("main")) }.let { rh ->
|
||||
handler().apply { queue(occurrence("main")) }.let { rh ->
|
||||
assertEquals(
|
||||
setOf(ConstraintSymbol("main", 0), ConstraintSymbol("foo", 0)),
|
||||
rh.occurrences().map { it.constraint().symbol() }.toSet())
|
||||
|
|
@ -77,7 +81,7 @@ class TestHandler {
|
|||
constraint("bar")
|
||||
))
|
||||
).run {
|
||||
handler().apply { process(occurrence("main")) }.let { rh ->
|
||||
handler().apply { queue(occurrence("main")) }.let { rh ->
|
||||
assertEquals(
|
||||
setOf(ConstraintSymbol("bar", 0), ConstraintSymbol("foo", 0)),
|
||||
rh.occurrences().map { it.constraint().symbol() }.toSet())
|
||||
|
|
@ -97,7 +101,7 @@ class TestHandler {
|
|||
statement { test = "value" }
|
||||
))
|
||||
).run {
|
||||
handler().process(occurrence("main"))
|
||||
handler().queue(occurrence("main"))
|
||||
assertEquals("value", test)
|
||||
}
|
||||
}
|
||||
|
|
@ -115,7 +119,7 @@ class TestHandler {
|
|||
statement ({ test.set("value") })
|
||||
))
|
||||
).run {
|
||||
handler().process(occurrence("main"))
|
||||
handler().queue(occurrence("main"))
|
||||
assertEquals("value", test.get())
|
||||
}
|
||||
}
|
||||
|
|
@ -134,7 +138,7 @@ class TestHandler {
|
|||
statement ({ test = x.get() } )
|
||||
))
|
||||
).run {
|
||||
handler().process(occurrence("main"))
|
||||
handler().queue(occurrence("main"))
|
||||
assertEquals("expected", test)
|
||||
}
|
||||
}
|
||||
|
|
@ -161,7 +165,7 @@ class TestHandler {
|
|||
statement ({ test = y.get() })
|
||||
))
|
||||
).run {
|
||||
handler().apply { process(occurrence("main")) }.let { rh ->
|
||||
handler().apply { queue(occurrence("main")) }.let { rh ->
|
||||
assertEquals(
|
||||
setOf(ConstraintSymbol("main", 0), ConstraintSymbol("next", 0)),
|
||||
rh.occurrences().map { it.constraint().symbol() }.toSet())
|
||||
|
|
@ -196,7 +200,7 @@ class TestHandler {
|
|||
statement { test2 = "expected" }
|
||||
))
|
||||
).run {
|
||||
handler().process(occurrence("main"))
|
||||
handler().queue(occurrence("main"))
|
||||
assertEquals("not initialized 1", test1)
|
||||
assertEquals("expected", test2)
|
||||
}
|
||||
|
|
@ -218,7 +222,7 @@ class TestHandler {
|
|||
).handler().run {
|
||||
val a = logical<String>("a")
|
||||
a.set("value")
|
||||
process(occurrence("foo", a))
|
||||
queue(occurrence("foo", a))
|
||||
assertEquals(1, occurrences().size)
|
||||
val co = occurrences().first()
|
||||
assertEquals(ConstraintSymbol("bar",1), co.constraint().symbol())
|
||||
|
|
@ -247,7 +251,7 @@ class TestHandler {
|
|||
body( constraint("expected2") )
|
||||
)
|
||||
).handler().run {
|
||||
process(occurrence("foo"))
|
||||
queue(occurrence("foo"))
|
||||
assertEquals(
|
||||
setOf(ConstraintSymbol("expected1", 0), ConstraintSymbol("expected2", 0)),
|
||||
occurrences().map { co -> co.constraint().symbol() }.toSet())
|
||||
|
|
@ -272,13 +276,139 @@ class TestHandler {
|
|||
body( constraint("expected2") )
|
||||
)
|
||||
).handler().run {
|
||||
process(occurrence("foo"))
|
||||
queue(occurrence("foo"))
|
||||
assertEquals(
|
||||
setOf(ConstraintSymbol("expected1", 0), ConstraintSymbol("expected2", 0)),
|
||||
occurrences().map { co -> co.constraint().symbol() }.toSet())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun occurrenceReactivated() {
|
||||
val X = logicalPattern<Int>("X")
|
||||
program(
|
||||
rule("zeroth",
|
||||
headKept( constraint("foo") ), body( statement({ x -> x.set(999) }, X),
|
||||
constraint("bar", X))
|
||||
),
|
||||
rule("first",
|
||||
headKept( constraint("foo") ),
|
||||
body( constraint("bar", X),
|
||||
constraint("qux", X))
|
||||
),
|
||||
rule("second",
|
||||
headReplaced( constraint("qux", X) ),
|
||||
body( constraint("expected1"),
|
||||
statement({ x -> x.set(123) }, X))
|
||||
),
|
||||
rule("third",
|
||||
headReplaced( constraint("foo") ),
|
||||
body( constraint("unexpected"))
|
||||
),
|
||||
rule("fourth",
|
||||
headReplaced( constraint("foo") ),
|
||||
headReplaced( constraint("bar", X) ), guard(expression({ x -> x.getNullable() == 123 }, X)),
|
||||
body( constraint("expected2") )
|
||||
),
|
||||
rule("fifth",
|
||||
headReplaced( constraint("bar", X) ), guard(expression({ x -> x.getNullable() == 999 }, X)),
|
||||
body( constraint("expected3", X))
|
||||
)
|
||||
).handler().run {
|
||||
queue(occurrence("foo"))
|
||||
assertEquals(3, occurrences().count())
|
||||
assertEquals(
|
||||
setOf(ConstraintSymbol("expected1", 0), ConstraintSymbol("expected2", 0), ConstraintSymbol("expected3", 1)),
|
||||
occurrences().map { co -> co.constraint().symbol() }.toSet())
|
||||
val ex3 = occurrences().filter { co -> co.constraint().symbol() == ConstraintSymbol("expected3", 1) }.first()
|
||||
assertEquals(999, (ex3.arguments().first() as Logical<Int>).value())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun occurrenceReactivatedAfterUnion() {
|
||||
val (X, Y) = logicalPattern<Int>("X", "Y")
|
||||
var handler : Handler? = null
|
||||
program(
|
||||
rule("first",
|
||||
headKept( constraint("foo") ),
|
||||
body( constraint("bar", X),
|
||||
constraint("qux", Y),
|
||||
statement({ x, y -> handler!!.eq(x, y) }, X, Y))
|
||||
),
|
||||
rule("second",
|
||||
headReplaced( constraint("qux", Y) ),
|
||||
body( constraint("expected1"),
|
||||
statement({ y -> y.set(123) }, Y))
|
||||
),
|
||||
rule("third",
|
||||
headReplaced( constraint("foo") ),
|
||||
body( constraint("unexpected"))
|
||||
),
|
||||
rule("fourth",
|
||||
headReplaced( constraint("foo") ),
|
||||
headReplaced( constraint("bar", X) ), guard(expression({ x -> x.getNullable() == 123 }, X)),
|
||||
body( constraint("expected2") )
|
||||
),
|
||||
rule("fifth",
|
||||
headKept( constraint("bar", X) ),
|
||||
body( constraint("expected3", X))
|
||||
)
|
||||
).handler().run {
|
||||
handler = this
|
||||
queue(occurrence("foo"))
|
||||
assertEquals(3, occurrences().count())
|
||||
assertEquals(
|
||||
setOf(ConstraintSymbol("expected1", 0), ConstraintSymbol("expected2", 0), ConstraintSymbol("expected3", 1)),
|
||||
occurrences().map { co -> co.constraint().symbol() }.toSet())
|
||||
val ex3 = occurrences().filter { co -> co.constraint().symbol() == ConstraintSymbol("expected3", 1) }.first()
|
||||
assertEquals(null, (ex3.arguments().first() as Logical<Int>).value())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun occurrenceReactivatedAfterUnionUnbound() {
|
||||
val (X, Y) = logicalPattern<Int>("X", "Y")
|
||||
var handler : Handler? = null
|
||||
program(
|
||||
rule("first",
|
||||
headKept( constraint("foo") ),
|
||||
body( constraint("bar", X),
|
||||
statement({ x, y -> handler!!.eq(x, y) }, X, Y),
|
||||
constraint("qux", Y))
|
||||
),
|
||||
rule("second",
|
||||
headReplaced( constraint("qux", Y) ),
|
||||
body( constraint("expected1"),
|
||||
statement({ y -> y.set(123) }, Y))
|
||||
),
|
||||
rule("third",
|
||||
headReplaced( constraint("foo") ),
|
||||
body( constraint("unexpected"))
|
||||
),
|
||||
rule("fourth",
|
||||
headReplaced( constraint("foo") ),
|
||||
headReplaced( constraint("bar", X) ), guard(expression({ x -> x.getNullable() == 123 }, X)),
|
||||
body( constraint("expected2") )
|
||||
),
|
||||
rule("fifth",
|
||||
headKept( constraint("bar", X) ),
|
||||
body( constraint("expected3", X))
|
||||
)
|
||||
).handler().run {
|
||||
handler = this
|
||||
queue(occurrence("foo"))
|
||||
assertEquals(3, occurrences().count())
|
||||
assertEquals(
|
||||
setOf(ConstraintSymbol("expected1", 0), ConstraintSymbol("expected2", 0), ConstraintSymbol("expected3", 1)),
|
||||
occurrences().map { co -> co.constraint().symbol() }.toSet())
|
||||
val ex3 = occurrences().filter { co -> co.constraint().symbol() == ConstraintSymbol("expected3", 1) }.first()
|
||||
assertEquals(null, (ex3.arguments().first() as Logical<Int>).value())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import jetbrains.mps.logic.reactor.program.JavaPredicateSymbol
|
|||
import jetbrains.mps.logic.reactor.program.PredicateSymbol
|
||||
import org.junit.*
|
||||
import org.junit.Assert.*
|
||||
import solver.eq
|
||||
|
||||
/**
|
||||
* @author Fedor Isakov
|
||||
|
|
|
|||
Loading…
Reference in New Issue