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:
parent
49a6ee0bd0
commit
26f12f187d
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) )
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue