From 26f12f187d0a633f17712642ea3c71692ac253eb Mon Sep 17 00:00:00 2001 From: Fedor Isakov Date: Fri, 23 Dec 2016 16:50:13 +0100 Subject: [PATCH] Refactor the evaluation controller to have handlers. RuleIndex to select handlers with matching primary symbol first, then the "accept-all" handlers. Tests. --- .../mps/logic/reactor/core/Controller.kt | 9 +- .../reactor/core/MemEvaluationSession.kt | 2 +- .../mps/logic/reactor/core/RuleIndex.kt | 51 ++++-- reactor/Test/src/program/MockProgram.kt | 43 ++++- reactor/Test/test/RulesHelper.kt | 64 +++++--- reactor/Test/test/TestController.kt | 44 ++--- reactor/Test/test/TestMatcher.kt | 154 +++++++++++------- reactor/Test/test/TestProgram.kt | 14 +- reactor/Test/test/TestProgramBuilder.kt | 16 +- 9 files changed, 236 insertions(+), 161 deletions(-) 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 f007a1eb..8e501618 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt @@ -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, + handlers: Iterable, trace: EvaluationTrace = EvaluationTrace.NULL, profiler: Profiler? = null, // for testing purposes only occurrences: Iterable? = null) { - this.ruleIndex = RuleIndex(programRules) + this.ruleIndex = RuleIndex(handlers) this.trace = trace this.profiler = profiler if (occurrences != null) { 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 a081860c..4100b191 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/MemEvaluationSession.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/MemEvaluationSession.kt @@ -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) } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleIndex.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleIndex.kt index 8d909ee9..b3b9e0a8 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleIndex.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleIndex.kt @@ -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 { - private val symbol2valueIndex = HashMap() + private val allSymbol2valueIndex = HashMap() + + private val primarySymbol2valueIndex = HashMap() private val tag2rule = LinkedHashMap() - constructor(rules: Iterable) { - for (r in rules) { - tag2rule[r.tag()] = r + constructor(handlers: Iterable) { + 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 { - 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 = tag2rule.values.iterator() - private fun buildIndex(rules: Iterable) { - for (r in rules) { - for (c in r.headKept()) { - updateIndex(c, r) + private fun buildIndex(handlers: Iterable) { + 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) - } - } /** diff --git a/reactor/Test/src/program/MockProgram.kt b/reactor/Test/src/program/MockProgram.kt index 22a011e5..f0c4cd3d 100644 --- a/reactor/Test/src/program/MockProgram.kt +++ b/reactor/Test/src/program/MockProgram.kt @@ -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() + private val handlers = ArrayList() - 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() + + 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) : Handler() { + + override fun name(): String = name + + override fun primarySymbol(): ConstraintSymbol? = primary + + override fun rules(): Iterable = rules +} + class MockRule( val tag: String, val kept: Collection, @@ -72,7 +97,7 @@ class MockRule( else (kept + replaced).map { it as AndItem } + guard + body.flatten() } -class MockProgram(val name: String, val myRules : List, val registry: ConstraintRegistry) : Program() { +class MockProgram(val name: String, val handlers: List, val registry: ConstraintRegistry) : Program() { override fun name(): String = name @@ -85,9 +110,9 @@ class MockProgram(val name: String, val myRules : List, val registry: Cons override fun predicateSymbols(): Iterable = registry.predicateSymbols() - override fun rules(): Iterable = unmodifiableCollection(myRules) + override fun rules(): Iterable = handlers.flatMap { it.rules() } - override fun handlers(): MutableIterable = TODO() + override fun handlers(): Iterable = unmodifiableCollection(handlers) } diff --git a/reactor/Test/test/RulesHelper.kt b/reactor/Test/test/RulesHelper.kt index 15443358..a0cf1e37 100644 --- a/reactor/Test/test/RulesHelper.kt +++ b/reactor/Test/test/RulesHelper.kt @@ -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) { +class Builder(val env: Environment, val handlers: List) { + @Deprecated(message = "use handlers") + val rules: List + 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 Rule>): Builder { - val rules = ArrayList() +private fun programWithRules(env: Environment, ruleBuilders: Array 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 Handler>): Builder { + val handlers = ArrayList() 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, val env: Environment) { val constraints = ArrayList() - val type: Class - val env: Environment - - constructor(type: Class, env: Environment) { - this.type = type - this.env = env - } fun createConstraint(args: Array, 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 toArray(): Array = if (Constraint::class.java.isAssignableFrom(type)) Array(constraints.size) { @@ -128,7 +138,7 @@ private fun buildConjunction(type: Class, env: Environment, content: Array Unit>): ConjBuilder { - var conjBuilder = ConjBuilder(type, env) + val conjBuilder = ConjBuilder(type, env) for (c in content) { conjBuilder.c() } diff --git a/reactor/Test/test/TestController.kt b/reactor/Test/test/TestController.kt index aab9d7c6..8d0d5801 100644 --- a/reactor/Test/test/TestController.kt +++ b/reactor/Test/test/TestController.kt @@ -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("X") //"not initialized" - program( + programWithRules( rule("main", headKept( constraint("main") @@ -162,7 +162,7 @@ class TestController { var test : String? = "not initialized" val x = logical("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("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("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("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("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("X", "Y") - program( + programWithRules( rule("first", headKept( constraint("foo") ), body( constraint("bar", X), @@ -443,7 +443,7 @@ class TestController { fun correctRulesOrder() { val X= metaLogical("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("X1", "Y1", "Z1") val (X2,Y2,Z2) = metaLogical("X2", "Y2", "Z2") val (X3,Y3,Z3) = metaLogical("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("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("X2") val (X3,Y3) = metaLogical("X3", "Y3") val X4 = metaLogical("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("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("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("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("X", "Y", "Z") - program( + programWithRules( rule("main", headReplaced( constraint("main") ), body( statement({ x -> x.set(7) }, X), statement({ y -> y.set(13) }, Y), diff --git a/reactor/Test/test/TestMatcher.kt b/reactor/Test/test/TestMatcher.kt index 7d83d5b5..ecc977e7 100644 --- a/reactor/Test/test/TestMatcher.kt +++ b/reactor/Test/test/TestMatcher.kt @@ -15,43 +15,9 @@ import org.junit.Test class TestMatcher { - private fun Builder.matcher(vararg occurrence: ConstraintOccurrence): Pair { - - val stored = occurrence.toList() - - val aux = object : OccurrenceIndex { - override fun forSymbol(symbol: ConstraintSymbol): Iterable = - stored.filter { co -> co.constraint().symbol() == symbol } - - override fun forLogical(logical: Logical<*>): Iterable = - stored.filter { co -> - co.arguments().any { it is Logical<*> && it.isBound && it.findRoot() == logical.findRoot() } - } - - override fun forTerm(term: Term): Iterable = - stored.filter { co -> - co.arguments().any { it is Term && Unification.unify(it, term).isSuccessful } - } - - override fun forTermAndConstraint(term: Term, cst: Constraint): Iterable = - 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 = - 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("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("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("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("M", "N") val (O, P) = metaLogical("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("X") assertEquals( @@ -388,7 +382,7 @@ class TestMatcher { val (M, N) = metaLogical("M", "N") val (O, P) = metaLogical("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("b", "c") val (x, y) = metaLogical("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 { + + val stored = occurrence.toList() + + val aux = object : OccurrenceIndex { + override fun forSymbol(symbol: ConstraintSymbol): Iterable = + stored.filter { co -> co.constraint().symbol() == symbol } + + override fun forLogical(logical: Logical<*>): Iterable = + stored.filter { co -> + co.arguments().any { it is Logical<*> && it.isBound && it.findRoot() == logical.findRoot() } + } + + override fun forTerm(term: Term): Iterable = + stored.filter { co -> + co.arguments().any { it is Term && Unification.unify(it, term).isSuccessful } + } + + override fun forTermAndConstraint(term: Term, cst: Constraint): Iterable = + 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 = + 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 } + } diff --git a/reactor/Test/test/TestProgram.kt b/reactor/Test/test/TestProgram.kt index 34ca15e3..f6b720e5 100644 --- a/reactor/Test/test/TestProgram.kt +++ b/reactor/Test/test/TestProgram.kt @@ -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("X", "Y", "Z") - program( + programWithRules( rule("main", headReplaced( constraint("main") @@ -92,7 +94,7 @@ class TestProgram { fun simpleProgram() { val (X, Y) = metaLogical("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("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("M", "N") - program( + programWithRules( rule("main", headReplaced( constraint("main") ), body( statement({ n -> n.set(10) }, N), constraint("prime", N) ) diff --git a/reactor/Test/test/TestProgramBuilder.kt b/reactor/Test/test/TestProgramBuilder.kt index cd68e8f9..2f1eb257 100644 --- a/reactor/Test/test/TestProgramBuilder.kt +++ b/reactor/Test/test/TestProgramBuilder.kt @@ -31,19 +31,15 @@ class TestProgramBuilder { lateinit var programBuilder: ProgramBuilder - private fun ProgramBuilder.addRules(rules: List) { - 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)) } } }