mps-coderules/reactor/Test/test/RulesHelper.kt

172 lines
5.4 KiB
Kotlin

import jetbrains.mps.logic.reactor.core.StoreItem
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
import jetbrains.mps.logic.reactor.logical.LogicalContext
import jetbrains.mps.logic.reactor.program.*
import program.MockConstraint
import solver.EqualsSolver
import solver.TestEqPredicate
import java.util.*
/**
* @author Fedor Isakov
*/
class Builder(val env: Environment, val handlers: List<Handler>) {
val rules: List<Rule>
get() = handlers.flatMap { it.rules() }
}
class Environment(val programBuilder: ProgramBuilder? = null) {
val equalsSolver = EqualsSolver()
val expressionSolver = ExpressionSolver()
}
fun programWithRules(vararg ruleBuilders : Environment.() -> Rule): Builder {
return programWithRules(Environment(), ruleBuilders)
}
fun programWithRules(pb: ProgramBuilder, vararg ruleBuilders : Environment.() -> Rule): Builder {
return programWithRules(Environment(pb), ruleBuilders)
}
private fun programWithRules(env: Environment, ruleBuilders: Array<out Environment.() -> Rule>): Builder {
return builder(env, arrayOf(handler("test", emptyList(), * 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 (block in handlerBlocks) {
handlers.add(block())
}
}
return Builder(env, handlers)
}
fun handler(name: String, primary: Iterable<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 = {
val rb = RB(this, tag)
for (cmp in component) {
rb.cmp()
}
rb.toRule()
}
fun headKept(vararg content : ConjBuilder.() -> Unit): RB.() -> Unit = {
appendHeadKept( * buildConjunction(Constraint::class.java, env, content).toArray())
}
fun headReplaced(vararg content : ConjBuilder.() -> Unit): RB.() -> Unit = {
appendHeadReplaced( * buildConjunction(Constraint::class.java, env, content).toArray())
}
fun guard(vararg content : ConjBuilder.() -> Unit): RB.() -> Unit = {
appendGuard( * buildConjunction(Predicate::class.java, env, content).toArray())
}
fun body(vararg content : ConjBuilder.() -> Unit): RB.() -> Unit = {
appendBody(false, * buildConjunction(AndItem::class.java, env, content).toArray())
}
fun altBody(vararg content : ConjBuilder.() -> Unit): RB.() -> Unit = {
appendBody(true, * buildConjunction(AndItem::class.java, env, content).toArray())
}
fun constraint(id: String, vararg args: Any): ConjBuilder.() -> Unit = {
add(createConstraint(args, id))
}
fun equals(left: Any, right: Any): ConjBuilder.() -> Unit = {
add(TestEqPredicate(left, right))
}
fun occurrence(id: String, vararg args: Any) : ConstraintOccurrence = TestConstraintOccurrence(id, * args)
class RB(val env: Environment, tag: String) : RuleBuilder(tag) {
}
class ConjBuilder(val type: Class<out AndItem>, val env: Environment) {
val constraints = ArrayList<AndItem>()
fun createConstraint(args: Array<out Any>, id: String): Constraint {
return env.programBuilder ?.
constraint(ConstraintSymbol(id, args.size), * args) ?:
MockConstraint(ConstraintSymbol(id, args.size), * args)
}
fun add(item: AndItem): Unit {
if (!type.isAssignableFrom(item.javaClass))
throw IllegalArgumentException("unexpected constraint class '${item.javaClass}'")
constraints.add(item)
env.expressionSolver.addMaybeJavaPredicate(item)
}
@Suppress("UNCHECKED_CAST")
fun <T : AndItem> toArray(): Array<T> =
if (Constraint::class.java.isAssignableFrom(type))
Array<Constraint>(constraints.size) {
constraints.get(it) as Constraint
} as Array<T>
else if (Predicate::class.java.isAssignableFrom(type))
Array<Predicate>(constraints.size) {
constraints.get(it) as Predicate
} as Array<T>
else
Array<AndItem>(constraints.size) {
constraints.get(it)
} as Array<T>
}
private fun buildConjunction(type: Class<out AndItem>,
env: Environment,
content: Array<out ConjBuilder.() -> Unit>): ConjBuilder
{
val conjBuilder = ConjBuilder(type, env)
for (c in content) {
conjBuilder.c()
}
return conjBuilder
}
data class TestConstraintOccurrence(val constraint: Constraint, val arguments: List<Any>, val id: Int) :
ConstraintOccurrence,
StoreItem
{
override var alive: Boolean = true
override var stored: Boolean = false
companion object {
val random = Random()
}
constructor(id: String, vararg args: Any) :
this(MockConstraint(ConstraintSymbol.symbol(id, args.size)), listOf(* args), random.nextInt()) {}
override fun constraint(): Constraint = constraint
override fun arguments(): List<Any> = arguments
override fun logicalContext(): LogicalContext = TODO()
override fun terminate() {
this.alive = false
}
override fun toString(): String = "${constraint().symbol()}(${arguments().joinToString()})"
}