Refactor the evaluation controller to have handlers. RuleIndex to select handlers with matching primary symbol first, then the "accept-all" handlers. Tests.

This commit is contained in:
Fedor Isakov 2016-12-23 16:50:13 +01:00
parent 49a6ee0bd0
commit 26f12f187d
9 changed files with 236 additions and 161 deletions

View File

@ -8,10 +8,7 @@ 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.Constraint
import jetbrains.mps.logic.reactor.program.ConstraintSymbol
import jetbrains.mps.logic.reactor.program.Predicate
import jetbrains.mps.logic.reactor.program.Rule
import jetbrains.mps.logic.reactor.program.*
import jetbrains.mps.logic.reactor.util.*
import java.util.*
@ -143,13 +140,13 @@ class Controller {
private val profiler: Profiler?
constructor(
programRules: Iterable<Rule>,
handlers: Iterable<Handler>,
trace: EvaluationTrace = EvaluationTrace.NULL,
profiler: Profiler? = null,
// for testing purposes only
occurrences: Iterable<ConstraintOccurrence>? = null)
{
this.ruleIndex = RuleIndex(programRules)
this.ruleIndex = RuleIndex(handlers)
this.trace = trace
this.profiler = profiler
if (occurrences != null) {

View File

@ -89,7 +89,7 @@ class MemEvaluationSession : EvaluationSession, SessionObjects {
}
fun launch(main: Constraint, profiler: Profiler?) {
this.controller = Controller(program.rules(), trace, profiler)
this.controller = Controller(program.handlers(), trace, profiler)
controller.activate(main)
}

View File

@ -5,6 +5,7 @@ import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.logical.MetaLogical
import jetbrains.mps.logic.reactor.program.Constraint
import jetbrains.mps.logic.reactor.program.ConstraintSymbol
import jetbrains.mps.logic.reactor.program.Handler
import jetbrains.mps.logic.reactor.program.Rule
import jetbrains.mps.unification.Term
import java.util.*
@ -15,41 +16,57 @@ import java.util.*
class RuleIndex : Iterable<Rule> {
private val symbol2valueIndex = HashMap<ConstraintSymbol, ValueIndex>()
private val allSymbol2valueIndex = HashMap<ConstraintSymbol, ValueIndex>()
private val primarySymbol2valueIndex = HashMap<ConstraintSymbol, ValueIndex>()
private val tag2rule = LinkedHashMap<String, Rule>()
constructor(rules: Iterable<Rule>) {
for (r in rules) {
tag2rule[r.tag()] = r
constructor(handlers: Iterable<Handler>) {
for (h in handlers) {
for (r in h.rules()) {
tag2rule[r.tag()] = r
}
}
buildIndex(rules)
buildIndex(handlers)
}
fun byTag(tag: String): Rule? = tag2rule[tag]
fun forOccurrence(occ: ConstraintOccurrence): Iterable<Rule> {
return symbol2valueIndex.get(occ.constraint().symbol())?.select(occ) ?: emptyList()
val primary = primarySymbol2valueIndex.get(occ.constraint().symbol())?.select(occ) ?: emptyList()
val all = allSymbol2valueIndex.get(occ.constraint().symbol())?.select(occ) ?: emptyList()
return primary + all
}
override fun iterator(): Iterator<Rule> = tag2rule.values.iterator()
private fun buildIndex(rules: Iterable<Rule>) {
for (r in rules) {
for (c in r.headKept()) {
updateIndex(c, r)
private fun buildIndex(handlers: Iterable<Handler>) {
for (h in handlers) {
val primaryValueIdx = h.primarySymbol()?.let { symbol ->
allSymbol2valueIndex.getOrPut(symbol) { ValueIndex(symbol) }
}
for (c in r.headReplaced()) {
updateIndex(c, r)
for (r in h.rules()) {
for (c in r.headKept()) {
if (c.symbol() == h.primarySymbol()) {
primaryValueIdx?.update(r, c)
} else if (h.primarySymbol() == null) {
allSymbol2valueIndex.getOrPut(c.symbol()) { ValueIndex(c.symbol()) }.update(r, c)
}
}
for (c in r.headReplaced()) {
if (c.symbol() == h.primarySymbol()) {
primaryValueIdx?.update(r, c)
} else if (h.primarySymbol() == null) {
allSymbol2valueIndex.getOrPut(c.symbol()) { ValueIndex(c.symbol()) }.update(r, c)
}
}
}
}
}
private fun updateIndex(cst: Constraint, rule: Rule) {
val valueIdx = symbol2valueIndex.getOrPut(cst.symbol()) { ValueIndex(cst.symbol()) }
valueIdx.update(rule, cst)
}
}
/**

View File

@ -5,23 +5,36 @@
import jetbrains.mps.logic.reactor.evaluation.Queryable
import jetbrains.mps.logic.reactor.evaluation.SessionSolver
import jetbrains.mps.logic.reactor.program.*
import org.omg.CORBA.Environment
import program.MockConstraint
import java.util.*
import java.util.Collections.*
class ProgramBuilder(val registry: ConstraintRegistry) {
private val rules = ArrayList<Rule>()
private val handlers = ArrayList<Handler>()
fun program(name: String): Program = MockProgram(name, ArrayList(rules), registry)
fun addRule(rule: Rule) {
registry.update(rule)
rules.add(rule)
fun addHandler(handler: Handler) {
for (r in handler.rules()) {
registry.update(r)
}
handlers.add(handler)
}
fun constraint(symbol: ConstraintSymbol, vararg args: Any): Constraint = MockConstraint(symbol, listOf(* args))
fun program(name: String): Program = MockProgram(name, handlers, registry)
}
open class HandlerBuilder(val name: String, val primary: ConstraintSymbol?) {
val rules = ArrayList<Rule>()
fun appendRule(rule: Rule) {
rules.add(rule)
}
fun toHandler(): Handler = MockHandler(name, primary, rules)
}
open class RuleBuilder(val tag: String) {
@ -46,6 +59,18 @@ open class RuleBuilder(val tag: String) {
fun toRule(): Rule = MockRule(tag, kept, replaced, guard, body)
}
class MockHandler(
val name: String,
val primary: ConstraintSymbol?,
val rules: List<Rule>) : Handler() {
override fun name(): String = name
override fun primarySymbol(): ConstraintSymbol? = primary
override fun rules(): Iterable<Rule> = rules
}
class MockRule(
val tag: String,
val kept: Collection<Constraint>,
@ -72,7 +97,7 @@ class MockRule(
else (kept + replaced).map { it as AndItem } + guard + body.flatten()
}
class MockProgram(val name: String, val myRules : List<Rule>, val registry: ConstraintRegistry) : Program() {
class MockProgram(val name: String, val handlers: List<Handler>, val registry: ConstraintRegistry) : Program() {
override fun name(): String = name
@ -85,9 +110,9 @@ class MockProgram(val name: String, val myRules : List<Rule>, val registry: Cons
override fun predicateSymbols(): Iterable<PredicateSymbol> =
registry.predicateSymbols()
override fun rules(): Iterable<Rule> = unmodifiableCollection(myRules)
override fun rules(): Iterable<Rule> = handlers.flatMap { it.rules() }
override fun handlers(): MutableIterable<Handler> = TODO()
override fun handlers(): Iterable<Handler> = unmodifiableCollection(handlers)
}

View File

@ -4,6 +4,7 @@ import jetbrains.mps.logic.reactor.program.*
import program.MockConstraint
import TestConstraintOccurrence
import jetbrains.mps.logic.reactor.core.StoreItem
import org.jetbrains.kotlin.codegen.inline.getNewFieldsToGenerate
import solver.EqualsSolver
import solver.TestEqPredicate
import java.util.*
@ -12,7 +13,11 @@ import java.util.*
* @author Fedor Isakov
*/
class Builder(val env: Environment, val rules: List<Rule>) {
class Builder(val env: Environment, val handlers: List<Handler>) {
@Deprecated(message = "use handlers")
val rules: List<Rule>
get() = handlers.flatMap { it.rules() }
}
class Environment(val programBuilder: ProgramBuilder? = null) {
@ -20,34 +25,46 @@ class Environment(val programBuilder: ProgramBuilder? = null) {
val expressionSolver = ExpressionSolver()
}
fun program(vararg ruleBuilders : Environment.() -> Rule): Builder {
return builder(Environment(), ruleBuilders)
fun programWithRules(vararg ruleBuilders : Environment.() -> Rule): Builder {
return programWithRules(Environment(), ruleBuilders)
}
fun program(pb: ProgramBuilder, vararg ruleBuilders : Environment.() -> Rule): Builder {
return builder(Environment(pb), ruleBuilders)
fun programWithRules(pb: ProgramBuilder, vararg ruleBuilders : Environment.() -> Rule): Builder {
return programWithRules(Environment(pb), ruleBuilders)
}
private fun builder(env: Environment, ruleBuilders: Array<out Environment.() -> Rule>): Builder {
val rules = ArrayList<Rule>()
private fun programWithRules(env: Environment, ruleBuilders: Array<out Environment.() -> Rule>): Builder {
return builder(env, arrayOf(handler("test", null, * ruleBuilders)))
}
fun programWithHandlers(vararg handlerBuilders : Environment.() -> Handler): Builder {
return builder(Environment(), handlerBuilders)
}
private fun builder(env: Environment, handlerBlocks: Array<out Environment.() -> Handler>): Builder {
val handlers = ArrayList<Handler>()
with (env) {
for (rb in ruleBuilders) {
rules.add(rb())
for (block in handlerBlocks) {
handlers.add(block())
}
}
return Builder(env, rules)
return Builder(env, handlers)
}
fun handler(name: String, primary: ConstraintSymbol?, vararg ruleBlocks: Environment.() -> Rule): Environment.() -> Handler = {
val hb = HandlerBuilder(name, primary)
for (block in ruleBlocks) {
hb.appendRule(this.block())
}
hb.toHandler()
}
fun rule(tag: String, vararg component:RB.() -> Unit): Environment.() -> Rule = {
rule(tag, this, * component)
}
fun rule(tag: String, env: Environment, vararg component:RB.() -> Unit): Rule {
val rb = RB(tag, env)
val rb = RB(this, tag)
for (cmp in component) {
rb.cmp()
}
return rb.toRule()
rb.toRule()
}
fun headKept(vararg content : ConjBuilder.() -> Unit): RB.() -> Unit = {
@ -81,19 +98,12 @@ fun equals(left: Any, right: Any): ConjBuilder.() -> Unit = {
fun occurrence(id: String, vararg args: Any) : ConstraintOccurrence = TestConstraintOccurrence(id, * args)
class RB(tag: String, val env: Environment) : RuleBuilder(tag) {
class RB(val env: Environment, tag: String) : RuleBuilder(tag) {
}
class ConjBuilder {
class ConjBuilder(val type: Class<out AndItem>, val env: Environment) {
val constraints = ArrayList<AndItem>()
val type: Class<out AndItem>
val env: Environment
constructor(type: Class<out AndItem>, env: Environment) {
this.type = type
this.env = env
}
fun createConstraint(args: Array<out Any>, id: String): Constraint {
return env.programBuilder ?.
@ -101,7 +111,6 @@ class ConjBuilder {
MockConstraint(ConstraintSymbol(id, args.size), * args)
}
fun add(item: AndItem): Unit {
if (!type.isAssignableFrom(item.javaClass))
throw IllegalArgumentException("unexpected constraint class '${item.javaClass}'")
@ -109,6 +118,7 @@ class ConjBuilder {
env.expressionSolver.addMaybeJavaPredicate(item)
}
@Suppress("UNCHECKED_CAST")
fun <T : AndItem> toArray(): Array<T> =
if (Constraint::class.java.isAssignableFrom(type))
Array<Constraint>(constraints.size) {
@ -128,7 +138,7 @@ private fun buildConjunction(type: Class<out AndItem>,
env: Environment,
content: Array<out ConjBuilder.() -> Unit>): ConjBuilder
{
var conjBuilder = ConjBuilder(type, env)
val conjBuilder = ConjBuilder(type, env)
for (c in content) {
conjBuilder.c()
}

View File

@ -61,7 +61,7 @@ class TestController {
private fun Builder.handler(vararg occurrences: ConstraintOccurrence): Controller {
MockSession.init(sessionSolver(env.expressionSolver, env.equalsSolver))
val handler = Controller(rules, occurrences = listOf(* occurrences))
val handler = Controller(handlers, occurrences = listOf(* occurrences))
MockSession.ourBackend.session.controller = handler
return handler
}
@ -76,7 +76,7 @@ class TestController {
@Test
fun processSingle() {
program(
programWithRules(
rule("main1",
headKept(
constraint("main")
@ -95,7 +95,7 @@ class TestController {
@Test
fun processReplaced() {
program(
programWithRules(
rule("main1",
headKept(
constraint("main")
@ -125,7 +125,7 @@ class TestController {
@Test
fun basicExpression() {
var test : String = "not initialized"
program(
programWithRules(
rule("main",
headKept(
constraint("main")
@ -143,7 +143,7 @@ class TestController {
fun paramExpression() {
var test = logical<String>("X")
//"not initialized"
program(
programWithRules(
rule("main",
headKept(
constraint("main")
@ -162,7 +162,7 @@ class TestController {
var test : String? = "not initialized"
val x = logical<String>("x")
x.setValue("expected")
program(
programWithRules(
rule("main",
headKept(
constraint("main")
@ -181,7 +181,7 @@ class TestController {
var test : String? = "not initialized"
val (x,y) = logical<String>("x", "y")
x.setValue("expected")
program(
programWithRules(
rule("main",
headKept(
constraint("main")
@ -212,7 +212,7 @@ class TestController {
fun basicGuard() {
var test1 : String = "not initialized 1"
var test2 : String = "not initialized 2"
program(
programWithRules(
rule("main1",
headKept(
constraint("main")
@ -243,7 +243,7 @@ class TestController {
@Test
fun basicMetaLogical() {
val (X, Y) = metaLogical<String>("X", "Y")
program(
programWithRules(
rule("rule1",
headReplaced(
constraint("foo", X)
@ -269,7 +269,7 @@ class TestController {
@Test
fun occurrenceTerminated() {
program(
programWithRules(
rule("first",
headKept( constraint("foo") ), body( constraint("expected1") )
),
@ -294,7 +294,7 @@ class TestController {
@Test
fun occurrenceKeptActive() {
program(
programWithRules(
rule("first",
headKept( constraint("foo") ), body( constraint("bar") )
),
@ -320,7 +320,7 @@ class TestController {
@Test
fun occurrenceReactivated() {
val X = metaLogical<Int>("X")
program(
programWithRules(
rule("zeroth",
headKept( constraint("foo") ), body( statement({ x -> x.set(999) }, X),
constraint("bar", X))
@ -362,7 +362,7 @@ class TestController {
@Test
fun occurrenceReactivatedAfterUnion() {
val (X, Y) = metaLogical<Int>("X", "Y")
program(
programWithRules(
rule("first",
headKept( constraint("foo") ),
body( constraint("bar", X),
@ -401,7 +401,7 @@ class TestController {
@Test
fun occurrenceReactivatedAfterUnionUnbound() {
val (X, Y) = metaLogical<Int>("X", "Y")
program(
programWithRules(
rule("first",
headKept( constraint("foo") ),
body( constraint("bar", X),
@ -443,7 +443,7 @@ class TestController {
fun correctRulesOrder() {
val X= metaLogical<Int>("X")
program(
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x -> x.set(1) }, X),
constraint("bar"),
@ -476,7 +476,7 @@ class TestController {
val (X1,Y1,Z1) = metaLogical<Int>("X1", "Y1", "Z1")
val (X2,Y2,Z2) = metaLogical<Int>("X2", "Y2", "Z2")
val (X3,Y3,Z3) = metaLogical<Int>("X3", "Y3", "Z3")
program(
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ z -> z.set(0) }, Z1),
constraint("foo", X1, Z1),
@ -508,7 +508,7 @@ class TestController {
@Test
fun propagationHistory() {
val (X,Y,Z) = metaLogical<Int>("X", "Y", "Z")
program(
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x, y -> eq(x, y) }, X, Y), // rank(X) = 1
constraint("foo", Y),
@ -540,7 +540,7 @@ class TestController {
val X2 = metaLogical<Int>("X2")
val (X3,Y3) = metaLogical<Int>("X3", "Y3")
val X4 = metaLogical<Int>("X4")
program(
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x, y -> eq(x, y) }, X1, Y1), // rank(X) = 1
statement({ x -> x.set(42) }, X1),
@ -576,7 +576,7 @@ class TestController {
@Test
fun reactivateOnUnionKeepValue() {
val (X,Y,Z) = metaLogical<Int>("X", "Y", "Z")
program(
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x, y -> eq(x, y) }, X, Y), // rank(X) = 1
statement({ z -> z.set(42) }, Z),
@ -605,7 +605,7 @@ class TestController {
fun firstAlternative() {
val (X, Y) = metaLogical<Int>("X", "Y")
program(
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x -> x.set(7) }, X),
statement({ y -> y.set(7) }, Y),
@ -627,7 +627,7 @@ class TestController {
fun secondAlternative() {
val (X, Y) = metaLogical<Int>("X", "Y")
program(
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x -> x.set(7) }, X),
statement({ y -> y.set(13) }, Y),
@ -649,7 +649,7 @@ class TestController {
fun lastAlternativeFail() {
val (X, Y, Z) = metaLogical<Int>("X", "Y", "Z")
program(
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x -> x.set(7) }, X),
statement({ y -> y.set(13) }, Y),

View File

@ -15,43 +15,9 @@ import org.junit.Test
class TestMatcher {
private fun Builder.matcher(vararg occurrence: ConstraintOccurrence): Pair<RuleIndex, OccurrenceIndex> {
val stored = occurrence.toList()
val aux = object : OccurrenceIndex {
override fun forSymbol(symbol: ConstraintSymbol): Iterable<ConstraintOccurrence> =
stored.filter { co -> co.constraint().symbol() == symbol }
override fun forLogical(logical: Logical<*>): Iterable<ConstraintOccurrence> =
stored.filter { co ->
co.arguments().any { it is Logical<*> && it.isBound && it.findRoot() == logical.findRoot() }
}
override fun forTerm(term: Term): Iterable<ConstraintOccurrence> =
stored.filter { co ->
co.arguments().any { it is Term && Unification.unify(it, term).isSuccessful }
}
override fun forTermAndConstraint(term: Term, cst: Constraint): Iterable<ConstraintOccurrence> =
stored.filter { co ->
co.constraint().symbol() == cst.symbol() && co.arguments().any { it is Term && Unification.unify(it, term).isSuccessful }
}
override fun forValue(value: Any): Iterable<ConstraintOccurrence> =
stored.filter { co -> co.arguments().contains(value) }
}
return RuleIndex(rules).to(aux)
}
private fun Match.allOccurrences() = (keptOccurrences + discardedOccurrences)
private fun Matcher.matching() = this.filter { m -> m.successful }
@Test
fun matchSingle() {
program(
programWithRules(
rule("main",
headReplaced(
constraint("main")
@ -60,7 +26,7 @@ class TestMatcher {
constraint("foo")
))
).let { builder ->
builder.matcher().run {
builder.indices().run {
Matcher(first, occurrence("main"), second).matching().let { matches ->
val match = matches.single()
assertEquals(match.rule, builder.rules.first())
@ -74,7 +40,7 @@ class TestMatcher {
@Test
fun matchDiscardedKept() {
program(
programWithRules(
rule("main1",
headReplaced(
constraint("main")
@ -90,7 +56,7 @@ class TestMatcher {
constraint("bar")
))
).let { builder ->
builder.matcher().run {
builder.indices().run {
Matcher(first, occurrence("main"), second).matching().let { matches ->
assertTrue(matches.all {m -> m.successful})
assertEquals(builder.rules.toSet(), matches.map { m -> m.rule }.toSet())
@ -106,7 +72,7 @@ class TestMatcher {
@Test
fun matchComplementMissing() {
program(
programWithRules(
rule("main1",
headKept(
constraint("main")
@ -125,7 +91,7 @@ class TestMatcher {
constraint("bar")
))
).let { builder ->
builder.matcher().run {
builder.indices().run {
Matcher(first, occurrence("main"), second).matching().let { matches ->
assertTrue(matches.all {m -> m.successful})
assertEquals(builder.rules.drop(1).toSet(), matches.map { m -> m.rule }.toSet())
@ -136,7 +102,7 @@ class TestMatcher {
@Test
fun matchComplementPresent() {
program(
programWithRules(
rule("main1",
headKept(
constraint("main")
@ -155,7 +121,7 @@ class TestMatcher {
constraint("bar")
))
).let { builder ->
builder.matcher(occurrence("aux")).run {
builder.indices(occurrence("aux")).run {
Matcher(first, occurrence("main"), second).matching().let { matches ->
assertTrue(matches.all {m -> m.successful})
assertEquals(builder.rules.toSet(), matches.map { m -> m.rule }.toSet())
@ -166,7 +132,7 @@ class TestMatcher {
@Test
fun matchArgument() {
program(
programWithRules(
rule("main1",
headKept(
constraint("main", "foo")
@ -182,7 +148,7 @@ class TestMatcher {
constraint("bar")
))
).let { builder ->
builder.matcher().run{
builder.indices().run{
Matcher(first, occurrence("main", "bar"), second).matching().let { matches ->
assertTrue(matches.all {m -> m.successful})
assertEquals(builder.rules.drop(1), matches.map { m -> m.rule }.toList())
@ -193,7 +159,7 @@ class TestMatcher {
@Test
fun noMatchArgument() {
program(
programWithRules(
rule("main1",
headKept(
constraint("main", "foo")
@ -208,19 +174,47 @@ class TestMatcher {
body(
constraint("bar")
))
).matcher().run{
).indices().run{
Matcher(first, occurrence("main", "qux"), second).matching().let { matches ->
assertFalse(matches.any())
}
}
}
@Test
fun multipleHandlers() {
programWithHandlers(
handler("handler1", ConstraintSymbol("foo", 0),
rule("main1",
headKept(
constraint("foo")
))
),
handler("handler2", ConstraintSymbol("bar", 0),
rule("main2",
headKept(
constraint("bar")
))
)
).indices().run {
Matcher(first, occurrence("qux"), second).matching().let { matches ->
assertFalse(matches.any())
}
Matcher(first, occurrence("foo"), second).matching().let { matches ->
assertSame(1, matches.size)
}
Matcher(first, occurrence("bar"), second).matching().let { matches ->
assertSame(1, matches.size)
}
}
}
@Test
fun metaLogical() {
val (A, B, C) = metaLogical<String>("A", "B", "C")
val b = B.logical()
program(
programWithRules(
rule("main",
headKept(
constraint("foo", A, B)
@ -229,7 +223,7 @@ class TestMatcher {
constraint("bar", B)
)
)
).matcher().run {
).indices().run {
Matcher(first, occurrence("foo", "blah", b), second).first().run {
assert(successful)
assertEquals("blah", logicalContext.variable(A).findRoot().value())
@ -244,7 +238,7 @@ class TestMatcher {
fun matchMetaLogical() {
val (M, N) = metaLogical<Int>("M", "N")
program(
programWithRules(
rule("main1",
headKept(
constraint("foo", M)
@ -266,14 +260,14 @@ class TestMatcher {
constraint("bar")
))
).run {
matcher().run {
indices().run {
Matcher(first, occurrence("foo", 1), second).matching().let { matches ->
assertFalse(matches.any())
}
}
// same parameter -- 4 matches (all permutations)
matcher(occurrence("foo", 42)).run {
indices(occurrence("foo", 42)).run {
Matcher(first, occurrence("foo", 42), second).matching().let { matches ->
assertTrue(matches.all {m -> m.successful})
assertEquals(4, matches.count())
@ -281,7 +275,7 @@ class TestMatcher {
}
}
matcher(occurrence("foo", 42)).run {
indices(occurrence("foo", 42)).run {
Matcher(first, occurrence("foo", 16), second).matching().let { matches ->
assertTrue(matches.all {m -> m.successful})
assertEquals(2, matches.count())
@ -301,7 +295,7 @@ class TestMatcher {
x.set(123)
y.set(456)
matcher(occurrence("foo", x)).run {
indices(occurrence("foo", x)).run {
Matcher(first, occurrence("foo", y), second).matching().let { matches ->
assertTrue(matches.all {m -> m.successful})
assertEquals(2, matches.count())
@ -313,7 +307,7 @@ class TestMatcher {
val (v, w) = logical<Int>("v", "w")
w.findRoot().union(v)
matcher(occurrence("foo", v)).run {
indices(occurrence("foo", v)).run {
Matcher(first, occurrence("foo", w), second).matching().let { matches ->
assertTrue(matches.all {m -> m.successful})
assertEquals(2, matches.count())
@ -328,7 +322,7 @@ class TestMatcher {
val (M, N) = metaLogical<Int>("M", "N")
val (O, P) = metaLogical<Int>("O", "P")
program(
programWithRules(
rule("select_A_x",
headKept(
constraint("foo", "A", M)
@ -367,7 +361,7 @@ class TestMatcher {
body(
constraint("bar")
))
).matcher().first.run {
).indices().first.run {
val x = logical<Any>("X")
assertEquals(
@ -388,7 +382,7 @@ class TestMatcher {
val (M, N) = metaLogical<Int>("M", "N")
val (O, P) = metaLogical<Int>("O", "P")
program(
programWithRules(
rule("select_fg_x",
headKept(
constraint("foo", parse("f{g}"), M)
@ -417,7 +411,7 @@ class TestMatcher {
body(
constraint("bar")
))
).matcher().first.run {
).indices().first.run {
assertEquals(
listOf(byTag("select_fg_x")),
@ -437,7 +431,7 @@ class TestMatcher {
@Test
fun matchTermArguments() {
program(
programWithRules(
rule("select_abcde",
headKept(
constraint("foo", parse("a{b{h} c{d e}}"))
@ -459,7 +453,7 @@ class TestMatcher {
body(
constraint("bar")
))
).matcher().first.run {
).indices().first.run {
assertEquals(
listOf(byTag("select_abcde")),
@ -480,7 +474,7 @@ class TestMatcher {
val (b, c) = metaLogical<String>("b", "c")
val (x, y) = metaLogical<String>("x", "y")
val program = program(
val program = programWithRules(
rule("foo2",
headKept(
constraint("foo", b, parse("a{b}")),
@ -492,16 +486,50 @@ class TestMatcher {
)
)
program.matcher(occurrence("foo", x, parse("a{c}"))).run {
program.indices(occurrence("foo", x, parse("a{c}"))).run {
Matcher(first, occurrence("foo", y, parse("a{b}")), second).matching().let { matches ->
assertEquals("foo2", matches.single().rule.tag())
}
}
program.matcher(occurrence("foo", x, parse("a{b}"))).run {
program.indices(occurrence("foo", x, parse("a{b}"))).run {
Matcher(first, occurrence("foo", y, parse("a{c}")), second).matching().let { matches ->
assertEquals("foo2", matches.single().rule.tag())
}
}
}
private fun Builder.indices(vararg occurrence: ConstraintOccurrence): Pair<RuleIndex, OccurrenceIndex> {
val stored = occurrence.toList()
val aux = object : OccurrenceIndex {
override fun forSymbol(symbol: ConstraintSymbol): Iterable<ConstraintOccurrence> =
stored.filter { co -> co.constraint().symbol() == symbol }
override fun forLogical(logical: Logical<*>): Iterable<ConstraintOccurrence> =
stored.filter { co ->
co.arguments().any { it is Logical<*> && it.isBound && it.findRoot() == logical.findRoot() }
}
override fun forTerm(term: Term): Iterable<ConstraintOccurrence> =
stored.filter { co ->
co.arguments().any { it is Term && Unification.unify(it, term).isSuccessful }
}
override fun forTermAndConstraint(term: Term, cst: Constraint): Iterable<ConstraintOccurrence> =
stored.filter { co ->
co.constraint().symbol() == cst.symbol() && co.arguments().any { it is Term && Unification.unify(it, term).isSuccessful }
}
override fun forValue(value: Any): Iterable<ConstraintOccurrence> =
stored.filter { co -> co.arguments().contains(value) }
}
return RuleIndex(handlers).to(aux)
}
private fun Match.allOccurrences() = (keptOccurrences + discardedOccurrences)
private fun Matcher.matching() = this.filter { m -> m.successful }
}

View File

@ -29,7 +29,9 @@ class TestProgram {
private fun Builder.session(name: String): EvaluationSession {
val sessionSolver = MockSessionSolver(env.expressionSolver, env.equalsSolver)
val programBuilder = ProgramBuilder(ConstraintRegistry(sessionSolver))
rules.forEach { r -> programBuilder.addRule(r) }
for (h in handlers) {
programBuilder.addHandler(h)
}
return EvaluationSession.newSession(programBuilder.program(name)).
withPredicates(PredicateSymbol("equals", 2), JavaPredicateSymbol.EXPRESSION0, JavaPredicateSymbol.EXPRESSION1, JavaPredicateSymbol.EXPRESSION2, JavaPredicateSymbol.EXPRESSION3).
withParam("main", MockConstraint(ConstraintSymbol("main", 0))).start(sessionSolver)
@ -37,7 +39,7 @@ class TestProgram {
@Test
fun replace() {
program(
programWithRules(
rule("main",
headReplaced(
constraint("main")
@ -61,7 +63,7 @@ class TestProgram {
@Test
fun logicalValue() {
val (X, Y, Z) = metaLogical<Int>("X", "Y", "Z")
program(
programWithRules(
rule("main",
headReplaced(
constraint("main")
@ -92,7 +94,7 @@ class TestProgram {
fun simpleProgram() {
val (X, Y) = metaLogical<Int>("X", "Y")
program(
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x -> x.set(5) }, X),
constraint("val", X) )
@ -115,7 +117,7 @@ class TestProgram {
@Test
fun gcd() {
val (M, N, TMP) = metaLogical<Int>("M", "N", "TMP")
program(
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ m, n -> m.set(21); n.set(35) }, M, N),
constraint("gcd", M),
@ -144,7 +146,7 @@ class TestProgram {
@Test
fun primes() {
val (M, N) = metaLogical<Int>("M", "N")
program(
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ n -> n.set(10) }, N),
constraint("prime", N) )

View File

@ -31,19 +31,15 @@ class TestProgramBuilder {
lateinit var programBuilder: ProgramBuilder
private fun ProgramBuilder.addRules(rules: List<Rule>) {
rules.forEach { r -> addRule(r) }
}
@Test(expected = InvalidRuleException::class)
fun emptyBody() {
program(programBuilder,
programWithRules(programBuilder,
rule("foo",
headKept(
constraint("bar")
))).run {
programBuilder.addRules(rules)
programBuilder.addHandler(MockHandler("test", null, rules))
assertEquals(programBuilder.program("test").rules().count(), 1)
assertEquals(programBuilder.program("test").rules().count(), 1)
}
@ -51,7 +47,7 @@ class TestProgramBuilder {
@Test
fun replace() {
program(programBuilder,
programWithRules(programBuilder,
rule("foo",
headReplaced(
constraint("bar")
@ -70,14 +66,14 @@ class TestProgramBuilder {
constraint("blah")
))).run {
programBuilder.addRules(rules)
programBuilder.addHandler(MockHandler("test", null, rules))
assertEquals(programBuilder.program("test").rules().count(), 2)
}
}
@Test(expected = InvalidConstraintException::class)
fun fail() {
program(programBuilder,
programWithRules(programBuilder,
rule("foo",
headReplaced(
constraint("bar", 1)
@ -86,7 +82,7 @@ class TestProgramBuilder {
constraint("bar", "1")
))).run {
programBuilder.addRules(rules)
programBuilder.addHandler(MockHandler("test", null, rules))
}
}
}