Refactorings and code cleanup in reactor lib. Documenting the code.

Breaking change: ReactorLifecycle to be used to initialize session
backend.

Remove deprecated stuff, deprecate some more.
Consolidate useless interfaces into bigger ones.
Extract interfaces for core classes, move implementation into "internal"
subpackage.
This commit is contained in:
Fedor Isakov 2019-04-24 20:23:57 +02:00
parent 19a096b12f
commit a9a7f66a3a
39 changed files with 1567 additions and 1373 deletions

View File

@ -22,7 +22,6 @@ import jetbrains.mps.logic.reactor.program.Constraint;
import jetbrains.mps.logic.reactor.program.Predicate;
import jetbrains.mps.logic.reactor.program.PredicateSymbol;
import jetbrains.mps.logic.reactor.program.Program;
import org.jetbrains.annotations.NotNull;
/**
* The starting point to evaluate a program.
@ -35,7 +34,12 @@ import org.jetbrains.annotations.NotNull;
*/
public abstract class EvaluationSession {
private static EvaluationSession.Backend ourBackend;
private static EvaluationSession.Backend<? extends EvaluationSession> ourBackend;
@SuppressWarnings("unchecked")
public static <S extends EvaluationSession> S current(Class<S> sessionClass) {
return (S) current();
}
public static EvaluationSession current() {
if (ourBackend == null) {
@ -51,47 +55,32 @@ public abstract class EvaluationSession {
return ourBackend.createConfig(program);
}
protected static void setBackend(EvaluationSession.Backend backend) {
protected static void setBackend(EvaluationSession.Backend<? extends EvaluationSession> backend) {
if (ourBackend != null) {
throw new IllegalStateException("backend already assigned");
}
ourBackend = backend;
}
protected static void clearBackend(EvaluationSession.Backend backend) {
protected static void clearBackend(EvaluationSession.Backend<? extends EvaluationSession> backend) {
if (ourBackend != backend) {
throw new IllegalStateException("illegal access");
}
ourBackend = null;
}
@Deprecated
public abstract SessionSolver sessionSolver();
public abstract StoreView storeView();
public abstract Program program();
@Deprecated
public PredicateInvocation invocation(Predicate predicate, LogicalContext logicalContext) {
// FIXME delete the method after all code has been migrated
// keep compatibility with existing code
throw new UnsupportedOperationException();
}
public abstract boolean ask(PredicateInvocation invocation);
@Deprecated
public ConstraintOccurrence occurrence(Constraint constraint, LogicalContext logicalContext) {
// FIXME delete the method after all code has been migrated
// keep compatibility with existing code
throw new UnsupportedOperationException();
}
public abstract void tell(PredicateInvocation invocation);
public Program program() {
// FIXME delete the implementation after all code has been migrated
// keep compatibility with existing code
throw new UnsupportedOperationException();
}
public interface Backend<S extends EvaluationSession> {
protected interface Backend {
EvaluationSession current();
S current();
EvaluationSession.Config createConfig(Program program);
@ -99,11 +88,6 @@ public abstract class EvaluationSession {
public static abstract class Config {
@Deprecated
public EvaluationSession.Config withPredicates(PredicateSymbol... predicateSymbols){
return this;
}
public EvaluationSession.Config withTrace(EvaluationTrace computingTracer) {
return this;
}
@ -112,10 +96,6 @@ public abstract class EvaluationSession {
return this;
}
public EvaluationSession.Config withFailureHandler(FailureHandler handler) {
return this;
}
public EvaluationSession.Config withFeedbackHandler(EvaluationFeedbackHandler handler) {
return this;
}
@ -124,8 +104,11 @@ public abstract class EvaluationSession {
return this;
}
@Deprecated
public abstract EvaluationResult start(SessionSolver sessionSolver);
public abstract EvaluationResult start();
}
}

View File

@ -23,4 +23,7 @@ public interface InvocationContext {
void report(EvaluationFeedback feedback);
}

View File

@ -21,7 +21,7 @@ import jetbrains.mps.logic.reactor.logical.LogicalContext;
import jetbrains.mps.logic.reactor.program.Rule;
/**
* A binding of a rule and the constraints that matched its head.
* A binding of a rule and the constraint occurrences that matched its head.
*
* @author Fedor Isakov
*/

View File

@ -31,7 +31,9 @@ import java.util.Map;
*
*
* @author Fedor Isakov
* @deprecated Use EvalutionSession
*/
@Deprecated
public class SessionSolver implements Solver {
private EvaluationTrace tracer = EvaluationTrace.NULL;
@ -40,17 +42,13 @@ public class SessionSolver implements Solver {
tracer = evaluationTrace;
}
@Deprecated
public void init(PredicateSymbol... predicateSymbols) {
}
@Deprecated
public void init(EvaluationTrace evaluationTrace, PredicateSymbol... predicateSymbols) {
tracer = evaluationTrace;
init(predicateSymbols);
}
/**
* @deprecated Use EvaluationSession
* @param invocation
* @return
*/
@Override
@Deprecated
public boolean ask(PredicateInvocation invocation) {
Solver solver = invocation.predicate().symbol().solver();
boolean result = solver.ask(invocation);
@ -58,20 +56,16 @@ public class SessionSolver implements Solver {
return result;
}
/**
* @deprecated Use EvaluationSession
* @param invocation
*/
@Override
@Deprecated
public void tell(PredicateInvocation invocation) {
Solver solver = invocation.predicate().symbol().solver();
tracer.tell(invocation);
solver.tell(invocation);
}
@Deprecated
protected void registerSymbol(PredicateSymbol predicateSymbol, EvaluationTrace computingTracer) {
throw new UnsupportedOperationException();
}
@Deprecated
protected void registerSolver(PredicateSymbol constraint, AbstractSolver solver) {
}
}

View File

@ -19,20 +19,12 @@ package jetbrains.mps.logic.reactor.program;
/**
* A handler is a container of rules.
* <p>
* If the {@code primarySymbols} is a non-empty collection, only constraints with these symbols are processed
* by this handler.
*
* @author Fedor Isakov
*/
public abstract class Handler {
public abstract String name();
@Deprecated
public Iterable<ConstraintSymbol> primarySymbols() {
throw new UnsupportedOperationException();
}
public abstract Iterable<Rule> rules();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 JetBrains s.r.o.
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,299 +16,23 @@
package jetbrains.mps.logic.reactor.core
import jetbrains.mps.logic.reactor.core.ProcessingState.*
import jetbrains.mps.logic.reactor.evaluation.*
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.logical.LogicalContext
import jetbrains.mps.logic.reactor.logical.MetaLogical
import jetbrains.mps.logic.reactor.program.Constraint
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
import jetbrains.mps.logic.reactor.evaluation.StoreView
import jetbrains.mps.logic.reactor.program.Predicate
import jetbrains.mps.logic.reactor.program.Program
import jetbrains.mps.logic.reactor.util.Profiler
import jetbrains.mps.logic.reactor.util.profile
import com.github.andrewoma.dexx.collection.Map as PersMap
class Controller(
val program: Program,
val trace: EvaluationTrace = EvaluationTrace.NULL,
val profiler: Profiler? = null,
val storeView: StoreView? = null,
val feedbackHandler: EvaluationFeedbackHandler? = null)
{
private inner class Context(inState: ProcessingState,
val logicalContext: LogicalContext) : InvocationContext
{
private var state = inState
/**
* Allows access to the internal mechanics of the reactor.
*
* @author Fedor Isakov
*/
interface Controller {
fun currentState(): ProcessingState = state
fun reactivate(occ: Occurrence)
override fun report(feedback: EvaluationFeedback) {
when (feedback) {
is EvaluationFailure -> this.state = state.fail(feedback)
is DetailedFeedback -> this.state = state.report(feedback)
}
}
/** For tests only */
fun evaluate(occ: Occurrence): StoreView
inline fun withState(block: (ProcessingState) -> Unit) {
block.invoke(state)
}
/** For tests only */
fun storeView(): StoreView
inline fun updateState(block: (ProcessingState) -> ProcessingState) : Boolean {
this.state = block.invoke(state)
return state.operational
}
inline fun evalSafe(block: (ProcessingState) -> ProcessingState) : Boolean {
if (state.operational) {
try {
this.state = block.invoke(state)
} catch (ex: EvaluationFailureException) {
this.state = state.fail(EvaluationFailure(ex))
}
}
return state.operational
}
inline fun runSafe(block: () -> Unit) : Boolean {
if (state.operational) {
try {
block()
} catch (ex: EvaluationFailureException) {
this.state = state.fail(EvaluationFailure(ex))
}
}
return state.operational
}
}
// FIXME move to parameter
private val session: EvaluationSession = EvaluationSession.current()
private val ruleIndex: RuleIndex = RuleIndex(program.handlers())
private var dispatchFringe = Dispatcher(ruleIndex).fringe()
// FIXME move to context
private val frameStack = FrameStack(storeView)
fun storeView(): StoreView = frameStack.current.store.view()
fun activate(constraint: Constraint) : ProcessingState {
// FIXME noLogicalContext
val context = Context(NORMAL(), noLogicalContext)
activateConstraint(constraint, context)
return context.currentState()
}
fun reactivate(occurrence: ConstraintOccurrence) {
// FIXME propagate the processing state further up the call stack
// TODO: introduce processing state to solver API?
val state = process(occurrence, NORMAL())
if (state is FAILED) {
throw state.failure.cause
}
}
/** For tests only */
fun evaluate(occurrence: ConstraintOccurrence): StoreView {
val state = process(occurrence, NORMAL())
if (state is FAILED) {
throw state.failure.cause
}
return storeView()
}
private fun process(active: ConstraintOccurrence, inState: ProcessingState) : ProcessingState {
assert(active.isAlive())
return profiler.profile<ProcessingState>("process_${active.constraint().symbol()}") {
if (!active.isStored()) {
frameStack.current.store.store(active)
trace.activate(active)
} else {
trace.reactivate(active)
}
val activatedFringe = dispatchFringe.expand(active)
this.dispatchFringe = activatedFringe
val outState = activatedFringe.matches().toList().fold(inState) { state, match ->
// TODO: paranoid check. should be isAlive() instead
// FIXME: move this check elsewhere
if (state.operational && active.isStored() && match.allStored())
processMatch(state, match)
else
state
}
// TODO: should be isAlive()
if (active.isStored()) {
trace.suspend(active)
}
outState
}
}
private fun processMatch(inState: ProcessingState, match: MatchRule) : ProcessingState {
val context = Context(inState, match.logicalContext())
// invoke matched pattern predicates
for (prd in match.patternPredicates()) {
if (!tellPredicate(prd, context)) break
}
trace.trying(match)
// check guard
for (gprd in match.rule().guard()) {
if (!askPredicate(gprd, context)) break
}
context.updateState { state ->
when (state) {
is ABORTED -> { // guard is not satisfied
trace.reject(match)
return state.recover()
}
is FAILED -> { // guard failed
trace.failure(state.failure)
return state.recover()
}
else -> state
}
}
this.dispatchFringe = dispatchFringe.consume(match)
trace.trigger(match)
for (occ in match.matchHeadReplaced()) {
this.dispatchFringe = dispatchFringe.contract(occ)
frameStack.current.store.discard(occ)
trace.discard(occ)
}
val altIt = match.rule().bodyAlternation().iterator()
while (altIt.hasNext()) {
val body = altIt.next()
context.updateState { state ->
if (state is FAILED) {
trace.retry(match)
state.recover()
} else {
state
}
}
val savedFrame = frameStack.current
frameStack.push()
for (item in body) {
val itemOk = when (item) {
is Constraint -> activateConstraint(item, context)
is Predicate -> tellPredicate(item, context)
else -> throw IllegalArgumentException("unknown item ${item}")
}
if (itemOk) {
context.withState { state ->
if (feedbackHandler != null && state.feedback?.alreadyHandled() == false) {
state.feedback.handle(match.rule(), feedbackHandler)
}
}
} else {
// state is not operational after constraint/predicate processing
break
}
}
val altOk = context.updateState { state ->
if (state is FAILED) {
trace.failure(state.failure)
if (altIt.hasNext()) {
// clear the failure handled status
state.failure.handle(match.rule()) { _, _ -> true }
state
} else if (feedbackHandler != null && state.feedback?.alreadyHandled() == false &&
state.failure.handle(match.rule(), feedbackHandler))
{
state.recover()
} else {
state
}
} else {
state
}
}
if (!altOk) {
// all constraints activated up to a failure are lost
frameStack.reset(savedFrame)
} else {
// body finished normally
break
}
}
trace.finish(match)
return context.currentState()
}
private fun activateConstraint(constraint: Constraint, context: Context) : Boolean {
val args = program.instantiateArguments(constraint.arguments(), context.logicalContext, context)
return context.updateState { state ->
process(constraint.occurrence(context.logicalContext, args, frameStack), state)
}
}
private fun askPredicate(predicate: Predicate, context: Context) : Boolean =
profiler.profile<Boolean>("ask_${predicate.symbol()}") {
context.evalSafe { state ->
val args = program.instantiateArguments(predicate.arguments(), context.logicalContext, context)
if (session.sessionSolver().ask(predicate.invocation(args, context.logicalContext, context)))
state
else
state.abort(DetailedFeedback("predicate not satisfied"))
}
}
private fun tellPredicate(predicate: Predicate, context: Context) : Boolean =
profiler.profile<Boolean>("tell_${predicate.symbol()}") {
context.runSafe {
val args = program.instantiateArguments(predicate.arguments(), context.logicalContext, context)
session.sessionSolver().tell(predicate.invocation(args, context.logicalContext, context))
}
}
private val noLogicalContext: LogicalContext = object: LogicalContext {
override fun <V : Any> variable(metaLogical: MetaLogical<V>): Logical<V> = TODO()
}
private fun MatchRule.patternPredicates() =
(rule().headKept() + rule().headReplaced()).zip(matchHeadKept() + matchHeadReplaced()).flatMap {
it.first.patternPredicates(it.second.arguments())
}.toList()
private fun MatchRule.allStored() = (matchHeadKept() + matchHeadReplaced()).all { co -> co.isStored() }
}

View File

@ -17,17 +17,17 @@
package jetbrains.mps.logic.reactor.core
import com.github.andrewoma.dexx.collection.Maps
import jetbrains.mps.logic.reactor.core.internal.MatchRuleImpl
import jetbrains.mps.logic.reactor.core.internal.RuleMatcherImpl
import com.github.andrewoma.dexx.collection.Map as PersMap
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
import jetbrains.mps.logic.reactor.evaluation.MatchRule
import jetbrains.mps.logic.reactor.program.Rule
/**
* A front-end interface to [RuleMatcher].
*
* @author Fedor Isakov
*/
typealias Matcher = RuleMatcher
class Dispatcher (val ruleIndex: RuleIndex) {
private val rule2matcher = HashMap<Rule, Matcher>()
@ -43,7 +43,7 @@ class Dispatcher (val ruleIndex: RuleIndex) {
private var rule2probe: PersMap<Rule, MatchingProbe>
private val allMatches = arrayListOf<MatchRule>()
private val allMatches = arrayListOf<MatchRuleImpl>()
constructor() {
this.rule2probe = Maps.of()
@ -56,7 +56,7 @@ class Dispatcher (val ruleIndex: RuleIndex) {
this.rule2probe = pred.rule2probe
matching.forEach { probe ->
this.rule2probe = rule2probe.put(probe.rule(), probe)
allMatches.addAll(probe.matches())
allMatches.addAll(probe.matches() as Collection<MatchRuleImpl>)
}
}
@ -71,12 +71,13 @@ class Dispatcher (val ruleIndex: RuleIndex) {
fun consume(matchRule: MatchRule) = DispatchFringe(this, matchRule)
fun expand(activated: ConstraintOccurrence) = DispatchFringe(this,
fun expand(activated: Occurrence) = DispatchFringe(this,
ruleIndex.forOccurrenceWithMask(activated).mapNotNull { (rule, mask) ->
rule2probe[rule]?.expand(activated, mask)
rule2probe[rule]?.expand(activated, mask)
})
fun contract(discarded: ConstraintOccurrence) = DispatchFringe(this,
fun contract(discarded: Occurrence) = DispatchFringe(this,
ruleIndex.forOccurrence(discarded).mapNotNull { rule ->
rule2probe[rule]
}.map { probe ->

View File

@ -0,0 +1,52 @@
/*
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.core
import jetbrains.mps.logic.reactor.evaluation.EvaluationSession
import jetbrains.mps.logic.reactor.evaluation.EvaluationTrace
import jetbrains.mps.logic.reactor.evaluation.PredicateInvocation
import jetbrains.mps.logic.reactor.evaluation.SessionSolver
import jetbrains.mps.logic.reactor.program.Program
/**
* An extension of [EvaluationSession] with ability to access [Controller].
* @author Fedor Isakov
*/
abstract class EvaluationSessionEx(val program: Program,
val trace: EvaluationTrace,
val sessionSolver: SessionSolver? = null) : EvaluationSession() {
abstract fun controller(): Controller
override fun program(): Program = program
override fun sessionSolver(): SessionSolver = sessionSolver ?: SessionSolver()
override fun ask(invocation: PredicateInvocation): Boolean {
val solver = invocation.predicate().symbol().solver()
val result = solver.ask(invocation)
trace.ask(result, invocation)
return result
}
override fun tell(invocation: PredicateInvocation) {
val solver = invocation.predicate().symbol().solver()
trace.tell(invocation)
solver.tell(invocation)
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.core
import jetbrains.mps.logic.reactor.logical.Logical
/**
* Serves to add/remove observers of [Logical] that track the frame stack.
*/
interface FrameObservable {
/** Returns the store associated with this frame */
fun storeObserver(): LogicalObserver
fun addObserver(logical: Logical<*>, obs: (FrameObservable) -> LogicalObserver)
fun removeObserver(logical: Logical<*>, obs: (FrameObservable) -> LogicalObserver)
}

View File

@ -22,19 +22,16 @@ import jetbrains.mps.logic.reactor.logical.LogicalContext
import jetbrains.mps.logic.reactor.program.Predicate
/**
* Data class representing a single invocation of a predicate.
*
* @author Fedor Isakov
*/
fun Predicate.invocation(arguments: List<*>,
logicalContext: LogicalContext,
invocationContext: InvocationContext): PredicateInvocation =
Invocation(this, arguments, logicalContext, invocationContext)
private data class Invocation(val predicate: Predicate,
data class Invocation(val predicate: Predicate,
val invocationArguments: List<*>,
val logicalContext: LogicalContext,
val invocationContext: InvocationContext) : PredicateInvocation
{
override fun predicate(): Predicate = predicate
override fun arguments(): List<*> = invocationArguments
@ -44,3 +41,8 @@ private data class Invocation(val predicate: Predicate,
override fun invocationContext(): InvocationContext = invocationContext
}
fun Predicate.invocation(arguments: List<*>,
logicalContext: LogicalContext,
invocationContext: InvocationContext): PredicateInvocation =
Invocation(this, arguments, logicalContext, invocationContext)

View File

@ -0,0 +1,45 @@
/*
* Copyright 2014-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.core
import jetbrains.mps.logic.reactor.core.internal.LogicalImpl
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.logical.MetaLogical
import jetbrains.mps.logic.reactor.logical.JoinableLogical
/**
* An observer interface of a [Logical] instance.
*
* @author Fedor Isakov
*/
interface LogicalObserver {
fun valueUpdated(logical: Logical<*>)
fun parentUpdated(logical: Logical<*>)
}
fun Logical<*>.addObserver(observer: LogicalObserver) {
(this as LogicalImpl<*>).valueObservers.add(this.to(observer))
(this as LogicalImpl<*>).parentObservers.add(this.to(observer))
}
fun Logical<*>.removeObserver(observer: LogicalObserver) {
(this as LogicalImpl<*>).valueObservers.removeAll { p -> p.second == observer }
(this as LogicalImpl<*>).parentObservers.removeAll { p -> p.second == observer }
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 JetBrains s.r.o.
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -19,24 +19,26 @@ package jetbrains.mps.logic.reactor.core
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
import jetbrains.mps.logic.reactor.evaluation.MatchRule
import jetbrains.mps.logic.reactor.program.Rule
import java.util.*
import java.util.BitSet
/**
* Provides functionality relevant to finding match(-es) for a rule.
*
* @author Fedor Isakov
*/
interface MatchingProbe {
fun rule() : Rule
fun matches() : Collection<MatchRule>
fun rule(): Rule
fun matches(): Collection<MatchRule>
fun consumed(matchRule: MatchRule): MatchingProbe
fun expand(occ: ConstraintOccurrence) : MatchingProbe
fun expand(occ: Occurrence): MatchingProbe
fun expand(occ: ConstraintOccurrence, mask: BitSet) : MatchingProbe
fun expand(occ: Occurrence, mask: BitSet): MatchingProbe
fun contract(occ: ConstraintOccurrence): MatchingProbe
fun contract(occ: Occurrence): MatchingProbe
}

View File

@ -17,33 +17,35 @@
package jetbrains.mps.logic.reactor.core
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
import jetbrains.mps.logic.reactor.evaluation.EvaluationSession
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.logical.LogicalContext
import jetbrains.mps.logic.reactor.logical.MetaLogical
import jetbrains.mps.logic.reactor.program.Constraint
/**
* Data class representing a single constraint occurrence.
*
* @author Fedor Isakov
*/
internal fun Constraint.occurrence(logicalContext: LogicalContext, arguments: List<*>, frameStack: FrameStack): ConstraintOccurrence =
Occurrence(this, logicalContext, arguments, frameStack)
private data class Occurrence (val constraint: Constraint,
data class Occurrence (val constraint: Constraint,
val logicalContext: LogicalContext,
val arguments: List<*>,
val frameStack: FrameStack) :
val currentFrame: () -> FrameObservable) :
ConstraintOccurrence,
LogicalObserver,
StoreItem
LogicalObserver
{
override var alive = true
override var stored = false
var alive = true
var stored = false
init {
for (a in arguments) {
if (a is Logical<*>) {
frameStack.current.addObserver(a) { this }
currentFrame().addObserver(a) { this }
}
}
}
@ -56,20 +58,20 @@ private data class Occurrence (val constraint: Constraint,
override fun valueUpdated(logical: Logical<*>) {
if (alive) {
(EvaluationSession.current() as SessionObjects).controller().reactivate(this)
EvaluationSession.current(EvaluationSessionEx::class.java).controller().reactivate(this)
}
}
override fun parentUpdated(logical: Logical<*>) {
if (alive) {
(EvaluationSession.current() as SessionObjects).controller().reactivate(this)
EvaluationSession.current(EvaluationSessionEx::class.java).controller().reactivate(this)
}
}
override fun terminate() {
fun terminate() {
for (a in arguments) {
if (a is Logical<*>) {
frameStack.current.removeObserver(a) { this }
currentFrame().removeObserver(a) { this }
}
}
alive = false
@ -79,3 +81,15 @@ private data class Occurrence (val constraint: Constraint,
}
fun Constraint.occurrence(arguments: List<*>,
currentFrame: () -> FrameObservable,
logicalContext: LogicalContext): Occurrence =
Occurrence(this, logicalContext, arguments, currentFrame)
fun Constraint.occurrence(arguments: List<*>,
currentFrame: () -> FrameObservable): Occurrence =
Occurrence(this, noLogicalContext, arguments, currentFrame)
private val noLogicalContext: LogicalContext = object: LogicalContext {
override fun <V : Any> variable(metaLogical: MetaLogical<V>): Logical<V>? = null
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 JetBrains s.r.o.
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,162 +17,23 @@
package jetbrains.mps.logic.reactor.core
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.logical.LogicalOwner
import jetbrains.mps.logic.reactor.logical.MetaLogical
import jetbrains.mps.logic.reactor.program.Constraint
import jetbrains.mps.unification.Term
import java.util.*
/**
* Abstracts an algorithm for recursive matching of a [Constraint] and a [ConstraintOccurrence]
* or a pattern [Term] against a [Term] in a constraint occurrence's arguments.
*
* @author Fedor Isakov
*/
interface OccurrenceMatcher {
fun substitution(): Subst
fun matches(cst: Constraint, occ: ConstraintOccurrence): Boolean
fun match(left: Any?, right: Any?): Boolean
}
typealias Subst = Map<MetaLogical<*>, Any>
fun emptySubst() = HashMap<MetaLogical<*>, Any>(4)
class OccurrenceMatcher(val contextSubst: Subst? = null) {
companion object {
val EMPTY_SUBST : Subst = Collections.emptyMap()
}
private var matchSubst : MutableMap<MetaLogical<*>, Any>? = null
fun substitution(): Subst = matchSubst ?: (contextSubst ?: EMPTY_SUBST)
/**
* Matches constraint and occurrence.
* Recursively processes all arguments, including terms.
* Returns substitution of MetaLogical instances on success, null otherwise.
*/
fun matches(cst: Constraint, occ: ConstraintOccurrence): Boolean
{
if (cst.symbol() != occ.constraint().symbol()) return false
return zipWhileTrue(cst.arguments(), occ.arguments()) { cstarg, occarg ->
ptnMatchAny(cstarg, occarg)
}
}
/**
* Returns true for matching left and right parameters, false otherwise.
*/
fun match(left: Any?, right: Any?): Boolean {
return matchAny(left, right)
}
/**
* Matches target against pattern.
* Recursively iterates terms.
* Respects substitutions for MetaLogical instances.
* Returns either new substitution on successful match, or null.
*/
private fun ptnMatchAny(ptn: Any?, trg: Any?): Boolean =
when (ptn) {
is MetaLogical<*> -> {
// recursion with existing substitution or new substitution
if (matchSubst == null) {
this.matchSubst = if (contextSubst != null) HashMap(contextSubst) else emptySubst()
}
if (matchSubst!!.containsKey(ptn))
matchSubst!![ptn].let { matchAny(it, trg) }
else
matchSubst!!.put(ptn, trg!!).run { true }
}
is Term ->
when {
ptn.`is`(Term.Kind.REF) -> ptnMatchAny(resolve(ptn), trg)
else -> ptnMatchTerm(ptn, trg) // recursion into the term
}
else ->
when {
trg is Logical<*> -> ptnMatchAny(ptn, resolve(trg))
else ->
// compare two arbitrary values
(ptn == trg)
}
}
private fun matchAny(left: Any?, right: Any?): Boolean =
when (left) {
is Logical<*> ->
// match logical or its value
matchLogical(left.findRoot(), right)
is Term ->
when {
left.`is`(Term.Kind.REF) -> matchAny(resolve(left), right)
else -> matchTerm(left, right) // recursion into the term
}
else ->
when {
right is Logical<*> -> matchAny(left, resolve(right))
else -> // compare two arbitrary values
(left == right)
}
}
private fun ptnMatchTerm(ptn: Term, trg: Any?): Boolean {
if (trg == null) return false
val trgval = resolve(trg)
if (!(trgval is Term)) return false
if (ptn.`is`(Term.Kind.VAR)) return ptnMatchAny(ptn.get().symbol(), trgval)
if (!ptnMatchAny(ptn.get().symbol(), trgval.symbol())) return false
// FIXME: reversing the order of arguments leads to infinite cycle
// Example: two terms of the form f(... V_1 ...) and f(... V_2 ...) where
// V_1 is bound to g(... W_1 ...), V_2 -> g(... W_2 ...), W_1 -> f(... V_1 ...), and W_2 -> f(... V_2 ...)
return zipWhileTrue(ptn.get().arguments(), trgval.arguments()) { ptnarg, trgarg ->
ptnMatchAny (ptnarg, trgarg)
}
}
private fun matchTerm(left: Term, right: Any?): Boolean {
if (right == null) return false
val rval = resolve(right)
if (!(rval is Term)) return false
if (!matchAny(left.get().symbol(), rval.symbol())) return false
// FIXME: reversing the order of arguments leads to infinite cycle
// Example: two terms of the form f(... V_1 ...) and f(... V_2 ...) where
// V_1 is bound to g(... W_1 ...), V_2 -> g(... W_2 ...), W_1 -> f(... V_1 ...), and W_2 -> f(... V_2 ...)
return zipWhileTrue(left.get().arguments(), rval.arguments()) { larg, rarg ->
matchAny (larg, rarg)
}
}
private fun matchLogical(left: Logical<*>, right: Any?): Boolean =
when {
right is Logical<*> ->
when {
left.isBound -> matchAny(left.findRoot().value(),
right.findRoot().value())
left.findRoot() === right.findRoot() -> true // reference equality
else -> false
}
left.isBound -> matchAny(left.findRoot().value(), right)
else -> false
}
private fun resolve(obj: Any?): Any? =
when (obj) {
is LogicalOwner -> if (obj.logical().isBound) resolve(obj.logical()) else obj
is Logical<*> -> resolve(obj.findRoot().value())
is Term -> if (obj.`is`(Term.Kind.REF)) resolve(obj.get()) else obj
else -> obj
}
private inline fun<S,T> zipWhileTrue(first: Iterable<S>, second: Iterable<T>, action: (S, T) -> Boolean): Boolean {
val firstIt = first.iterator()
val secondIt = second.iterator()
while(firstIt.hasNext() && secondIt.hasNext()) {
if (!action(firstIt.next(), secondIt.next())) return false
}
return true
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 JetBrains s.r.o.
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,17 +14,24 @@
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.evaluation;
package jetbrains.mps.logic.reactor.core
import jetbrains.mps.logic.reactor.core.internal.EvaluationSessionImpl
/**
* Not very useful now.
* Serves as an abstract superclass for implementations, as a mixin of Instructible and Queryable.
* Must be invoked by the code responsible for application's initialization and teardown.
*
* @deprecated use Solver instead.
* @author Fedor Isakov
*/
@Deprecated
public abstract class AbstractSolver implements Solver {
class ReactorLifecycle {
companion object {
fun init() {
EvaluationSessionImpl.Backend.init()
}
}
fun deinit() {
EvaluationSessionImpl.Backend.deinit()
}
}
}

View File

@ -29,9 +29,10 @@ import kotlin.collections.ArrayList
import kotlin.collections.HashMap
/**
* A container for [Rule] instances with the ability to look up by [ConstraintOccurrence].
*
* @author Fedor Isakov
*/
class RuleIndex(handlers: Iterable<Handler>) : Iterable<Rule> {
private val symbol2index = HashMap<ConstraintSymbol, ArgumentRuleIndex>()

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 JetBrains s.r.o.
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,171 +16,20 @@
package jetbrains.mps.logic.reactor.core
import com.github.andrewoma.dexx.collection.Sets
import com.github.andrewoma.dexx.collection.Map as PersMap
import com.github.andrewoma.dexx.collection.Set as PersSet
import com.github.andrewoma.dexx.collection.List as PersList
import com.github.andrewoma.dexx.collection.Vector as PersVector
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
import jetbrains.mps.logic.reactor.evaluation.MatchRule
import jetbrains.mps.logic.reactor.program.Constraint
import jetbrains.mps.logic.reactor.core.internal.RuleMatcherImpl
import jetbrains.mps.logic.reactor.program.Rule
import jetbrains.mps.logic.reactor.util.*
import java.util.*
import kotlin.collections.ArrayList
/**
* Abstracts a rule matching algorithm.
*
* @author Fedor Isakov
*/
interface RuleMatcher {
class RuleMatcher(val rule: Rule) {
val head = rule.headKept().toCollection(ArrayList(4)).apply {
addAll(rule.headReplaced()) } as List<Constraint>
val propagation = rule.headReplaced().count() == 0
fun probe(): MatchingProbe = RuleMatchFringe(listOf(MatchNode(emptySubst())),
Sets.of(),
Sets.of(),
0)
inner class RuleMatchFringe(val nodes: List<MatchNode>,
val seen: PersSet<IdWrapper<ConstraintOccurrence>>,
val consumed: PersSet<ArrayList<IdWrapper<ConstraintOccurrence>?>>,
val genId: Int) : MatchingProbe
{
override fun rule(): Rule = rule
override fun matches(): Collection<MatchRule> {
return nodes.filter { it is ActiveMatchNode && it.complete && it.genId == genId }
.map { (it as ActiveMatchNode).toMatchRule() }
}
override fun consumed(matchRule: MatchRule): MatchingProbe =
RuleMatchFringe(nodes,
seen,
consumed.add(((matchRule as MatchRuleImpl).origin as ActiveMatchNode).signature),
genId)
override fun expand(occ: ConstraintOccurrence): MatchingProbe =
expand(occ, bitSetOfOnes(head.size))
/**
* Expands the fringe by creating new leaf nodes that match the occurrence.
* Mask specifies possible slots for the occurrence.
*/
override fun expand(occ: ConstraintOccurrence, mask: BitSet): RuleMatchFringe {
val reactivated = seen.contains(IdWrapper(occ))
val newSeen = if (reactivated) seen else seen.add(IdWrapper(occ))
val newNodes = ArrayList<MatchNode>(nodes)
val allSignatures = newNodes.map { it.signature }.toHashSet()
for (n in nodes) {
n.expand(occ, genId + 1, n.matchingVacant(mask))
.filter { allSignatures.add(it.signature) || reactivated } // ensure reactivated have effect
.filter { !(propagation && reactivated && consumed.contains(it.signature)) } // ...unless propagation
.forEach { newNodes.add(it) }
}
return RuleMatchFringe(newNodes, newSeen, consumed, genId + 1)
}
override fun contract(occ: ConstraintOccurrence): RuleMatchFringe {
val newNodes = nodes.mapNotNull { it.unrelatedOrNull(occ) }
return RuleMatchFringe(newNodes, seen, consumed,genId + 1)
}
}
open inner class MatchNode(val subst: Subst, val vacant: BitSet = bitSetOfOnes(head.size))
{
open val signature: ArrayList<IdWrapper<ConstraintOccurrence>?> =
arrayListOf(* arrayOfNulls<IdWrapper<ConstraintOccurrence>?>(head.size))
/**
* Returns the additional nodes built from this node on adding the occurrence.
* If the occurrence is already in the path, return empty sequence.
*/
fun expand(occ: ConstraintOccurrence, genId: Int, matchingVacant: BitSet): List<ActiveMatchNode> =
unrelatedOrNull(occ)?.let { n ->
ArrayList<ActiveMatchNode>().also { expanded ->
for (headIdx in matchingVacant.allSetBits()) {
OccurrenceMatcher(subst).run {
if (matches(head[headIdx], occ)) {
expanded.add(ActiveMatchNode(substitution(), n, occ, headIdx, genId))
}
}
}
}
} ?: emptyList()
/**
* Returns this node if it doesn't have the occurrence in its path, null otherwise.
*/
open fun unrelatedOrNull(occ: ConstraintOccurrence): MatchNode? = this
fun matchingVacant(mask: BitSet) = mask.copyApply { and(vacant) }
/**
* Folds the path to the root.
*/
inline protected fun <T> fold(init: T, action: (T, ActiveMatchNode) -> T): T {
var rn = this
var curr = init
while (rn is ActiveMatchNode) {
curr = action(curr, rn)
rn = rn.parent
}
return curr
}
/**
* Folds the path to the root. If an iteration yields null, fold is stopped and null is returned.
*/
inline protected fun <T> foldUntilNull(init: T, action: (T, ActiveMatchNode) -> T?): T? {
var rn = this
var curr: T? = init
while (rn is ActiveMatchNode) {
curr = action(curr !!, rn)
if (curr == null) return null
rn = rn.parent
}
return curr
}
}
inner class ActiveMatchNode(subst: Subst,
val parent: MatchNode,
val occurrence: ConstraintOccurrence,
val headIndex: Int,
val genId: Int) :
MatchNode(subst, parent.vacant.clearBit(headIndex))
{
val complete = vacant.cardinality() == 0
override val signature: ArrayList<IdWrapper<ConstraintOccurrence>?> =
(parent.signature.clone() as ArrayList<IdWrapper<ConstraintOccurrence>?>)
.also { it[headIndex] = IdWrapper(occurrence) }
fun constraint(): Constraint = head[headIndex]
override fun unrelatedOrNull(occ: ConstraintOccurrence): ActiveMatchNode? =
foldUntilNull(this) { acc, rn -> if (rn.occurrence === occ) null else acc }
fun toMatchRule(): MatchRule {
val matched: Array<ConstraintOccurrence?> =
fold(arrayOfNulls(head.size)) { arr, rn -> arr[rn.headIndex] = rn.occurrence; arr }
return MatchRuleImpl(this,
rule,
subst,
ArrayList(matched.take(rule.headKept().count())),
ArrayList(matched.takeLast(rule.headReplaced().count())))
}
}
fun probe(): MatchingProbe
}
fun createRuleMatcher(rule: Rule): RuleMatcher = Matcher(rule)
internal typealias Matcher = RuleMatcherImpl

View File

@ -0,0 +1,332 @@
/*
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.core.internal
import jetbrains.mps.logic.reactor.core.*
import jetbrains.mps.logic.reactor.core.internal.ProcessingState.*
import jetbrains.mps.logic.reactor.evaluation.*
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.logical.LogicalContext
import jetbrains.mps.logic.reactor.logical.MetaLogical
import jetbrains.mps.logic.reactor.program.Constraint
import jetbrains.mps.logic.reactor.program.Predicate
import jetbrains.mps.logic.reactor.program.Program
import jetbrains.mps.logic.reactor.util.Profiler
import jetbrains.mps.logic.reactor.util.profile
import com.github.andrewoma.dexx.collection.Map as PersMap
internal class ControllerImpl (
val program: Program,
val trace: EvaluationTrace = EvaluationTrace.NULL,
val profiler: Profiler? = null,
val storeView: StoreView? = null,
val feedbackHandler: EvaluationFeedbackHandler? = null) : Controller
{
// FIXME move to parameter
private val session: EvaluationSession = EvaluationSession.current()
private val ruleIndex: RuleIndex = RuleIndex(program.handlers())
private var dispatchFringe = Dispatcher(ruleIndex).fringe()
// FIXME move to context
private val frameStack = FrameStack(storeView)
/** For tests only */
override fun storeView(): StoreView = frameStack.current.store.view()
/** For tests only */
override fun evaluate(occ: Occurrence): StoreView {
// create the internal occurrence
val active = occ.constraint().occurrence(occ.arguments(), { frameStack.current })
val state = process(active, NORMAL())
if (state is FAILED) {
throw state.failure.cause
}
return storeView()
}
fun activate(constraint: Constraint) : ProcessingState {
// FIXME noLogicalContext
val context = Context(NORMAL(), noLogicalContext)
activateConstraint(constraint, context)
return context.currentState()
}
override fun reactivate(occ: Occurrence) {
// FIXME propagate the processing state further up the call stack
// TODO: introduce processing state to solver API?
val state = process(occ, NORMAL())
if (state is FAILED) {
throw state.failure.cause
}
}
private fun process(active: Occurrence, inState: ProcessingState) : ProcessingState {
assert(active.alive)
return profiler.profile<ProcessingState>("process_${active.constraint().symbol()}") {
if (!active.stored) {
frameStack.current.store.store(active)
trace.activate(active)
} else {
trace.reactivate(active)
}
val activatedFringe = dispatchFringe.expand(active)
this.dispatchFringe = activatedFringe
val outState = activatedFringe.matches().toList().fold(inState) { state, match ->
// TODO: paranoid check. should be isAlive() instead
// FIXME: move this check elsewhere
if (state.operational && active.stored && match.allStored())
processMatch(state, match as MatchRuleImpl)
else
state
}
// TODO: should be isAlive()
if (active.stored) {
trace.suspend(active)
}
outState
}
}
private fun processMatch(inState: ProcessingState, match: MatchRuleImpl) : ProcessingState {
val context = Context(inState, match.logicalContext())
// invoke matched pattern predicates
for (prd in match.patternPredicates()) {
if (!tellPredicate(prd, context)) break
}
trace.trying(match)
// check guard
for (gprd in match.rule().guard()) {
if (!askPredicate(gprd, context)) break
}
context.updateState { state ->
when (state) {
is ABORTED -> { // guard is not satisfied
trace.reject(match)
return state.recover()
}
is FAILED -> { // guard failed
trace.failure(state.failure)
return state.recover()
}
else -> state
}
}
this.dispatchFringe = dispatchFringe.consume(match)
trace.trigger(match)
for (occ in match.headReplaced) {
this.dispatchFringe = dispatchFringe.contract(occ)
frameStack.current.store.discard(occ)
trace.discard(occ)
}
val altIt = match.rule().bodyAlternation().iterator()
while (altIt.hasNext()) {
val body = altIt.next()
context.updateState { state ->
if (state is FAILED) {
trace.retry(match)
state.recover()
} else {
state
}
}
val savedFrame = frameStack.current
frameStack.push()
for (item in body) {
val itemOk = when (item) {
is Constraint -> activateConstraint(item, context)
is Predicate -> tellPredicate(item, context)
else -> throw IllegalArgumentException("unknown item ${item}")
}
if (itemOk) {
context.withState { state ->
if (feedbackHandler != null && state.feedback?.alreadyHandled() == false) {
state.feedback.handle(match.rule(), feedbackHandler)
}
}
} else {
// state is not operational after constraint/predicate processing
break
}
}
val altOk = context.updateState { state ->
if (state is FAILED) {
trace.failure(state.failure)
if (altIt.hasNext()) {
// clear the failure handled status
state.failure.handle(match.rule()) { _, _ -> true }
state
} else if (feedbackHandler != null && state.feedback?.alreadyHandled() == false &&
state.failure.handle(match.rule(), feedbackHandler))
{
state.recover()
} else {
state
}
} else {
state
}
}
if (!altOk) {
// all constraints activated up to a failure are lost
frameStack.reset(savedFrame)
} else {
// body finished normally
break
}
}
trace.finish(match)
return context.currentState()
}
private fun activateConstraint(constraint: Constraint, context: Context) : Boolean {
val args = program.instantiateArguments(constraint.arguments(), context.logicalContext, context)
return context.updateState { state ->
val active = constraint.occurrence(args, { frameStack.current }, context.logicalContext)
process(active, state)
}
}
private fun askPredicate(predicate: Predicate, context: Context) : Boolean =
profiler.profile<Boolean>("ask_${predicate.symbol()}") {
context.evalSafe { state ->
val args = program.instantiateArguments(predicate.arguments(), context.logicalContext, context)
if (session.ask(predicate.invocation(args, context.logicalContext, context)))
state
else
state.abort(DetailedFeedback("predicate not satisfied"))
}
}
private fun tellPredicate(predicate: Predicate, context: Context) : Boolean =
profiler.profile<Boolean>("tell_${predicate.symbol()}") {
context.runSafe {
val args = program.instantiateArguments(predicate.arguments(), context.logicalContext, context)
session.tell(predicate.invocation(args, context.logicalContext, context))
}
}
private val noLogicalContext: LogicalContext = object: LogicalContext {
override fun <V : Any> variable(metaLogical: MetaLogical<V>): Logical<V>? = null
}
private fun MatchRule.patternPredicates() =
(rule().headKept() + rule().headReplaced()).zip(matchHeadKept() + matchHeadReplaced()).flatMap {
it.first.patternPredicates(it.second.arguments())
}.toList()
private fun MatchRule.allStored() = (matchHeadKept() + matchHeadReplaced()).all { co -> (co as Occurrence).stored }
}
private class Context(inState: ProcessingState,
val logicalContext: LogicalContext) : InvocationContext
{
private var state = inState
fun currentState(): ProcessingState = state
override fun report(feedback: EvaluationFeedback) {
when (feedback) {
is EvaluationFailure -> this.state = state.fail(feedback)
is DetailedFeedback -> this.state = state.report(feedback)
}
}
inline fun withState(block: (ProcessingState) -> Unit) {
block.invoke(state)
}
inline fun updateState(block: (ProcessingState) -> ProcessingState) : Boolean {
this.state = block.invoke(state)
return state.operational
}
inline fun evalSafe(block: (ProcessingState) -> ProcessingState) : Boolean {
if (state.operational) {
try {
this.state = block.invoke(state)
} catch (ex: EvaluationFailureException) {
this.state = state.fail(EvaluationFailure(ex))
}
}
return state.operational
}
inline fun runSafe(block: () -> Unit) : Boolean {
if (state.operational) {
try {
block()
} catch (ex: EvaluationFailureException) {
this.state = state.fail(EvaluationFailure(ex))
}
}
return state.operational
}
}
/** Used to create controller from tests */
fun createController(
program: Program,
trace: EvaluationTrace = EvaluationTrace.NULL,
profiler: Profiler? = null,
storeView: StoreView? = null,
feedbackHandler: EvaluationFeedbackHandler? = null) : Controller =
ControllerImpl(program, trace, profiler, storeView, feedbackHandler)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 JetBrains s.r.o.
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,16 +14,15 @@
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.core
package jetbrains.mps.logic.reactor.core.internal
import jetbrains.mps.logic.reactor.core.ProcessingState.FAILED
import jetbrains.mps.logic.reactor.core.internal.ProcessingState.FAILED
import jetbrains.mps.logic.reactor.core.EvaluationSessionEx
import jetbrains.mps.logic.reactor.evaluation.*
import jetbrains.mps.logic.reactor.program.Constraint
import jetbrains.mps.logic.reactor.program.PredicateSymbol
import jetbrains.mps.logic.reactor.program.Program
import jetbrains.mps.logic.reactor.util.Profiler
import java.util.*
import kotlin.collections.ArrayList
import com.github.andrewoma.dexx.collection.LinkedList as PLinkedList
import com.github.andrewoma.dexx.collection.List as PList
@ -31,38 +30,27 @@ import com.github.andrewoma.dexx.collection.List as PList
* @author Fedor Isakov
*/
interface SessionObjects {
fun controller(): Controller
}
class EvaluationSessionImpl private constructor (
val program: Program,
val sessionSolver: SessionSolver,
val trace: EvaluationTrace,
val failureHandler: EvaluationFeedbackHandler?) : EvaluationSession(), SessionObjects
internal class EvaluationSessionImpl private constructor (
program: Program,
trace: EvaluationTrace,
sessionSolver: SessionSolver? = null) : EvaluationSessionEx(program, trace, sessionSolver)
{
lateinit var controller: Controller
lateinit var controller: ControllerImpl
private fun launch(main: Constraint, profiler: Profiler?, storeView: StoreView?) : ProcessingState {
this.controller = Controller(program, trace, profiler, storeView, failureHandler)
override fun controller() = controller
private fun launch(main: Constraint, profiler: Profiler?, storeView: StoreView?, feedbackHandler: EvaluationFeedbackHandler?) : ProcessingState {
this.controller = ControllerImpl(program, trace, profiler, storeView, feedbackHandler)
return controller.activate(main)
}
private class Config(val program: Program) : EvaluationSession.Config() {
val predicateSymbols = ArrayList<PredicateSymbol>()
val parameters = HashMap<String, Any?>()
var evaluationTrace: EvaluationTrace = EvaluationTrace.NULL
var storeView: StoreView? = null
var feedbackHandler: EvaluationFeedbackHandler? = null
override fun withPredicates(vararg predicateSymbols: PredicateSymbol): EvaluationSession.Config {
this.predicateSymbols.addAll(Arrays.asList(* predicateSymbols))
return this
}
var feedbackHandler: EvaluationFeedbackHandler? = null
override fun withTrace(computingTracer: EvaluationTrace): EvaluationSession.Config {
this.evaluationTrace = computingTracer
@ -74,11 +62,6 @@ class EvaluationSessionImpl private constructor (
return this
}
override fun withFailureHandler(handler: FailureHandler): EvaluationSession.Config {
this.feedbackHandler = handler
return this
}
override fun withFeedbackHandler(handler: EvaluationFeedbackHandler?): EvaluationSession.Config {
this.feedbackHandler = handler
return this
@ -89,22 +72,24 @@ class EvaluationSessionImpl private constructor (
return this
}
override fun start(sessionSolver: SessionSolver): EvaluationResult {
var session = ourBackend.ourSession.get()
override fun start(): EvaluationResult = start(SessionSolver())
override fun start(sessionSolver: SessionSolver?): EvaluationResult {
var session = Backend.ourBackend.ourSession.get()
if (session != null) throw IllegalStateException("session already active")
sessionSolver.init(evaluationTrace, * predicateSymbols.toArray<PredicateSymbol>(arrayOfNulls(predicateSymbols.size)))
sessionSolver?.init(evaluationTrace)
@Suppress("UNCHECKED_CAST")
val durations =
parameters.get("profiling.data") as MutableMap<String, String>?
val profiler = durations?.let { Profiler() }
session = EvaluationSessionImpl(program, sessionSolver, evaluationTrace, feedbackHandler)
ourBackend.ourSession.set(session)
session = EvaluationSessionImpl(program, evaluationTrace, sessionSolver)
Backend.ourBackend.ourSession.set(session)
var failure: EvaluationFailure? = null
try {
val state = session.launch(parameters["main"] as Constraint, profiler, storeView)
val state = session.launch(parameters["main"] as Constraint, profiler, storeView, feedbackHandler)
if (state is FAILED) {
failure = state.failure
}
@ -119,44 +104,38 @@ class EvaluationSessionImpl private constructor (
catch (t: Throwable) {
// avoid nested failure
}
ourBackend.ourSession.set(null)
Backend.ourBackend.ourSession.set(null)
}
return object : EvaluationResult {
override fun storeView(): StoreView? = session.storeView()
override fun storeView(): StoreView? = session.controller.storeView()
override fun failure(): EvaluationFailure? = failure
}
}
}
override fun controller() = controller
override fun sessionSolver(): SessionSolver = sessionSolver
override fun program(): Program = program
override fun storeView(): StoreView =
controller.storeView()
private class Backend : EvaluationSession.Backend {
internal class Backend : EvaluationSession.Backend<EvaluationSessionImpl> {
val ourSession = ThreadLocal<EvaluationSessionImpl>()
override fun current(): EvaluationSession = ourSession.get() ?: throw IllegalStateException("no session")
override fun current(): EvaluationSessionImpl = ourSession.get() ?: throw IllegalStateException("no session")
override fun createConfig(program: Program): EvaluationSession.Config = Config(program)
}
companion object {
private val ourBackend = Backend()
companion object {
val ourBackend = Backend()
fun init() {
setBackend(ourBackend)
fun init() {
EvaluationSession.setBackend(ourBackend)
}
fun deinit() {
EvaluationSession.clearBackend(ourBackend)
}
}
fun deinit() {
clearBackend(ourBackend)
}
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 JetBrains s.r.o.
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,11 +14,14 @@
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.core
package jetbrains.mps.logic.reactor.core.internal
import com.github.andrewoma.dexx.collection.ConsList
import com.github.andrewoma.dexx.collection.Map
import com.github.andrewoma.dexx.collection.Maps
import jetbrains.mps.logic.reactor.core.FrameObservable
import jetbrains.mps.logic.reactor.core.LogicalObserver
import jetbrains.mps.logic.reactor.core.addObserver
import jetbrains.mps.logic.reactor.evaluation.StoreView
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.util.IdWrapper
@ -26,13 +29,13 @@ import jetbrains.mps.logic.reactor.util.cons
import jetbrains.mps.logic.reactor.util.remove
import java.util.*
internal class Frame: LogicalObserver, StoreKeeper {
internal class Frame: LogicalObserver, FrameObservable {
val stack: FrameStack
val store: Store
private var observers: Map<IdWrapper<Logical<*>>, ConsList<(StoreKeeper) -> LogicalObserver>>
private var observers: Map<IdWrapper<Logical<*>>, ConsList<(FrameObservable) -> LogicalObserver>>
constructor(stack: FrameStack) {
this.stack = stack
@ -52,9 +55,9 @@ internal class Frame: LogicalObserver, StoreKeeper {
this.observers = Maps.of()
}
override fun store() = store
override fun storeObserver() = store
override fun addObserver(logical: Logical<*>, obs: (StoreKeeper) -> LogicalObserver) {
override fun addObserver(logical: Logical<*>, obs: (FrameObservable) -> LogicalObserver) {
val logicalId = IdWrapper(logical)
if (!observers.containsKey(logicalId)) {
stack.addObserver(logical)
@ -63,7 +66,7 @@ internal class Frame: LogicalObserver, StoreKeeper {
observers[logicalId]?.prepend(obs) ?: cons(obs))
}
override fun removeObserver(logical: Logical<*>, obs: (StoreKeeper) -> LogicalObserver) {
override fun removeObserver(logical: Logical<*>, obs: (FrameObservable) -> LogicalObserver) {
val logicalId = IdWrapper(logical)
observers[logicalId].remove(obs)?.let { newList ->
this.observers = observers.put(logicalId, newList)
@ -132,4 +135,4 @@ internal class FrameStack(storeView: StoreView?) : LogicalObserver {
current.parentUpdated(logical)
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 JetBrains s.r.o.
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,40 +14,14 @@
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.core
package jetbrains.mps.logic.reactor.core.internal
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.logical.MetaLogical
import jetbrains.mps.logic.reactor.core.LogicalObserver
import jetbrains.mps.logic.reactor.logical.JoinableLogical
import java.util.*
import jetbrains.mps.logic.reactor.logical.MetaLogical
import java.util.ArrayList
/**
* @author Fedor Isakov
*/
interface LogicalObserver {
fun valueUpdated(logical: Logical<*>)
fun parentUpdated(logical: Logical<*>)
}
fun Logical<*>.addObserver(observer: LogicalObserver) {
(this as LogicalImpl<*>).valueObservers.add(this.to(observer))
(this as LogicalImpl<*>).parentObservers.add(this.to(observer))
}
fun Logical<*>.removeObserver(observer: LogicalObserver) {
(this as LogicalImpl<*>).valueObservers.removeAll { p -> p.second == observer }
(this as LogicalImpl<*>).parentObservers.removeAll { p -> p.second == observer }
}
fun <V> MetaLogical<V>.logical(): Logical<V> = LogicalImpl<V>(this)
fun <V> MetaLogical<V>.logical(value: V): Logical<V> = LogicalImpl<V>(name(), value)
class LogicalImpl<T> : JoinableLogical<T> {
internal class LogicalImpl<T> : JoinableLogical<T> {
companion object {
var lastIdx = 0
@ -225,4 +199,15 @@ class LogicalImpl<T> : JoinableLogical<T> {
}
class DefaultMetaLogical<V> (val name: String) : MetaLogical<V>(name, Object::class.java as Class<V>) {}
class DefaultMetaLogical<V> (val name: String) : MetaLogical<V>(name, Object::class.java as Class<V>) {}
// Used from tests
fun <V> anonLogical(value: V): JoinableLogical<V> = LogicalImpl<V>(value)
fun <V> namedLogical(name: String): JoinableLogical<V> = LogicalImpl<V>(name)
fun <V> MetaLogical<V>.logical(): JoinableLogical<V> = LogicalImpl<V>(this)
fun <V> MetaLogical<V>.logical(value: V): JoinableLogical<V> = LogicalImpl<V>(name(), value)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 JetBrains s.r.o.
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,8 +14,10 @@
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.core
package jetbrains.mps.logic.reactor.core.internal
import jetbrains.mps.logic.reactor.core.Occurrence
import jetbrains.mps.logic.reactor.core.Subst
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
import jetbrains.mps.logic.reactor.evaluation.MatchRule
import jetbrains.mps.logic.reactor.logical.Logical
@ -24,11 +26,10 @@ import jetbrains.mps.logic.reactor.logical.LogicalOwner
import jetbrains.mps.logic.reactor.logical.MetaLogical
import jetbrains.mps.logic.reactor.program.Rule
class MatchRuleImpl(val origin: Any,
val rule: Rule,
val subst: Subst,
val headKept: MutableIterable<ConstraintOccurrence?>,
val headReplaced: MutableIterable<ConstraintOccurrence?>) : MatchRule {
internal class MatchRuleImpl(val rule: Rule,
val subst: Subst,
val headKept: Iterable<Occurrence>,
val headReplaced: Iterable<Occurrence>) : MatchRule {
private val logicalContext = object : LogicalContext {
@ -47,10 +48,10 @@ class MatchRuleImpl(val origin: Any,
override fun rule(): Rule = rule
override fun matchHeadKept(): MutableIterable<ConstraintOccurrence?> = headKept
override fun matchHeadReplaced(): MutableIterable<ConstraintOccurrence?> = headReplaced
override fun matchHeadKept(): Iterable<ConstraintOccurrence?> = headKept
override fun matchHeadReplaced(): Iterable<ConstraintOccurrence?> = headReplaced
override fun logicalContext(): LogicalContext = logicalContext
}

View File

@ -0,0 +1,181 @@
/*
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.core.internal
import jetbrains.mps.logic.reactor.core.OccurrenceMatcher
import jetbrains.mps.logic.reactor.core.Subst
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.logical.LogicalOwner
import jetbrains.mps.logic.reactor.logical.MetaLogical
import jetbrains.mps.logic.reactor.program.Constraint
import jetbrains.mps.unification.Term
import java.util.*
internal class OccurrenceMatcherImpl(val contextSubst: Subst? = null) : OccurrenceMatcher {
companion object {
val EMPTY_SUBST : Subst = Collections.emptyMap()
}
private var matchSubst : MutableMap<MetaLogical<*>, Any>? = null
override fun substitution(): Subst = matchSubst ?: (contextSubst ?: EMPTY_SUBST)
/**
* Matches constraint and occurrence.
* Recursively processes all arguments, including terms.
* Returns substitution of MetaLogical instances on success, null otherwise.
*/
override fun matches(cst: Constraint, occ: ConstraintOccurrence): Boolean
{
if (cst.symbol() != occ.constraint().symbol()) return false
return zipWhileTrue(cst.arguments(), occ.arguments()) { cstarg, occarg ->
ptnMatchAny(cstarg, occarg)
}
}
/**
* Returns true for matching left and right parameters, false otherwise.
*/
override fun match(left: Any?, right: Any?): Boolean {
return matchAny(left, right)
}
/**
* Matches target against pattern.
* Recursively iterates terms.
* Respects substitutions for MetaLogical instances.
* Returns either new substitution on successful match, or null.
*/
private fun ptnMatchAny(ptn: Any?, trg: Any?): Boolean =
when (ptn) {
is MetaLogical<*> -> {
// recursion with existing substitution or new substitution
if (matchSubst == null) {
this.matchSubst = if (contextSubst != null) HashMap(contextSubst) else emptySubst()
}
if (matchSubst!!.containsKey(ptn))
matchSubst!![ptn].let { matchAny(it, trg) }
else
matchSubst!!.put(ptn, trg!!).run { true }
}
is Term ->
when {
ptn.`is`(Term.Kind.REF) -> ptnMatchAny(resolve(ptn), trg)
else -> ptnMatchTerm(ptn, trg) // recursion into the term
}
else ->
when {
trg is Logical<*> -> ptnMatchAny(ptn, resolve(trg))
else ->
// compare two arbitrary values
(ptn == trg)
}
}
private fun matchAny(left: Any?, right: Any?): Boolean =
when (left) {
is Logical<*> ->
// match logical or its value
matchLogical(left.findRoot(), right)
is Term ->
when {
left.`is`(Term.Kind.REF) -> matchAny(resolve(left), right)
else -> matchTerm(left, right) // recursion into the term
}
else ->
when {
right is Logical<*> -> matchAny(left, resolve(right))
else -> // compare two arbitrary values
(left == right)
}
}
private fun ptnMatchTerm(ptn: Term, trg: Any?): Boolean {
if (trg == null) return false
val trgval = resolve(trg)
if (!(trgval is Term)) return false
if (ptn.`is`(Term.Kind.VAR)) return ptnMatchAny(ptn.get().symbol(), trgval)
if (!ptnMatchAny(ptn.get().symbol(), trgval.symbol())) return false
// FIXME: reversing the order of arguments leads to infinite cycle
// Example: two terms of the form f(... V_1 ...) and f(... V_2 ...) where
// V_1 is bound to g(... W_1 ...), V_2 -> g(... W_2 ...), W_1 -> f(... V_1 ...), and W_2 -> f(... V_2 ...)
return zipWhileTrue(ptn.get().arguments(), trgval.arguments()) { ptnarg, trgarg ->
ptnMatchAny (ptnarg, trgarg)
}
}
private fun matchTerm(left: Term, right: Any?): Boolean {
if (right == null) return false
val rval = resolve(right)
if (!(rval is Term)) return false
if (!matchAny(left.get().symbol(), rval.symbol())) return false
// FIXME: reversing the order of arguments leads to infinite cycle
// Example: two terms of the form f(... V_1 ...) and f(... V_2 ...) where
// V_1 is bound to g(... W_1 ...), V_2 -> g(... W_2 ...), W_1 -> f(... V_1 ...), and W_2 -> f(... V_2 ...)
return zipWhileTrue(left.get().arguments(), rval.arguments()) { larg, rarg ->
matchAny (larg, rarg)
}
}
private fun matchLogical(left: Logical<*>, right: Any?): Boolean =
when {
right is Logical<*> ->
when {
left.isBound -> matchAny(left.findRoot().value(),
right.findRoot().value())
left.findRoot() === right.findRoot() -> true // reference equality
else -> false
}
left.isBound -> matchAny(left.findRoot().value(), right)
else -> false
}
private fun resolve(obj: Any?): Any? =
when (obj) {
is LogicalOwner -> if (obj.logical().isBound) resolve(obj.logical()) else obj
is Logical<*> -> resolve(obj.findRoot().value())
is Term -> if (obj.`is`(Term.Kind.REF)) resolve(obj.get()) else obj
else -> obj
}
private inline fun<S,T> zipWhileTrue(first: Iterable<S>, second: Iterable<T>, action: (S, T) -> Boolean): Boolean {
val firstIt = first.iterator()
val secondIt = second.iterator()
while(firstIt.hasNext() && secondIt.hasNext()) {
if (!action(firstIt.next(), secondIt.next())) return false
}
return true
}
}
fun emptySubst() = HashMap<MetaLogical<*>, Any>(4)
fun createOccurrenceMatcher(contextSubst: Subst? = null): OccurrenceMatcher =
OccurrenceMatcherImpl(contextSubst)

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 JetBrains s.r.o.
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,7 +14,7 @@
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.core
package jetbrains.mps.logic.reactor.core.internal
import jetbrains.mps.logic.reactor.evaluation.CompositeFeedback
import jetbrains.mps.logic.reactor.evaluation.DetailedFeedback

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2018 JetBrains s.r.o.
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,29 +14,34 @@
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.core
package jetbrains.mps.logic.reactor.core.internal
import jetbrains.mps.logic.reactor.core.*
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
import jetbrains.mps.logic.reactor.evaluation.MatchRule
import jetbrains.mps.logic.reactor.logical.MetaLogical
import jetbrains.mps.logic.reactor.program.Rule
import jetbrains.mps.logic.reactor.util.*
import jetbrains.mps.logic.reactor.util.allSetBits
import jetbrains.mps.logic.reactor.util.bitSet
import jetbrains.mps.logic.reactor.util.bitSetOfOnes
import jetbrains.mps.logic.reactor.util.copyApply
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
/**
* An alternative implementation of RuleMatcherImpl. Has similar asymptotic characteristics as the default implementation,
* but in practice is a bit slower.
*
* Loosely based on "Rete network" algorithm.
*
* @author Fedor Isakov
*/
class ReteRuleMatcher(val rule: Rule) {
internal class ReteRuleMatcherImpl(val rule: Rule) : RuleMatcher {
val head = (rule.headKept() + rule.headReplaced()).toList()
val propagation = rule.headReplaced().count() == 0
fun probe(): ReteNetwork = ReteNetwork(head.size)
override fun probe(): ReteNetwork = ReteNetwork(head.size)
inner class ReteNetwork(val headSize: Int) : MatchingProbe {
@ -69,7 +74,7 @@ class ReteRuleMatcher(val rule: Rule) {
if (thisMetaIndices.cardinality() != 0 && thatMetaIndices.cardinality() != 0) {
for (shared in thisMetaIndices.copyApply { and(thatMetaIndices) }.allSetBits()) {
if (!OccurrenceMatcher().match(this.getSubst(shared), that.getSubst(shared))) return false
if (!createOccurrenceMatcher().match(this.getSubst(shared), that.getSubst(shared))) return false
}
}
@ -86,7 +91,7 @@ class ReteRuleMatcher(val rule: Rule) {
abstract fun combine(that: AlphaNode): ReteNode
open fun collectData(occArray: Array<ConstraintOccurrence?>, allSubst: MutableMap<MetaLogical<*>, Any>) {}
open fun collectData(occArray: Array<Occurrence?>, allSubst: MutableMap<MetaLogical<*>, Any>) {}
}
@ -107,13 +112,13 @@ class ReteRuleMatcher(val rule: Rule) {
/**
* A network node corresponding to a single occurrence matched against a constraint.
*/
inner class AlphaNode(val occurrence: ConstraintOccurrence,
inner class AlphaNode(val occurrence: Occurrence,
val posInHead: Int,
val subst: Subst) : ReteNode()
{
val metaIndices: BitSet? =
if (subst.isNotEmpty()) bitSet(subst.keys.map { metaLogical -> indexOf(metaLogical) }) else null
val occIdx = indexOf(occurrence)
private val idx2subst = HashMap<Int, Any?>()
@ -139,7 +144,7 @@ class ReteRuleMatcher(val rule: Rule) {
override fun combine(that: AlphaNode): ReteNode = BetaNode(this, that)
override fun collectData(occArray: Array<ConstraintOccurrence?>, allSubst: MutableMap<MetaLogical<*>, Any>) {
override fun collectData(occArray: Array<Occurrence?>, allSubst: MutableMap<MetaLogical<*>, Any>) {
occArray[posInHead] = occurrence
for ((k, v) in subst) {
allSubst.put(k, v)
@ -152,7 +157,7 @@ class ReteRuleMatcher(val rule: Rule) {
* A "deep" network node. Always has two parents, one of which is always an AlphaNode.
*/
inner class BetaNode : ReteNode {
val left: ReteNode
val right: AlphaNode
@ -200,7 +205,7 @@ class ReteRuleMatcher(val rule: Rule) {
override fun combine(that: AlphaNode): ReteNode = BetaNode(this, that)
override fun collectData(occArray: Array<ConstraintOccurrence?>, allSubst: MutableMap<MetaLogical<*>, Any>) {
override fun collectData(occArray: Array<Occurrence?>, allSubst: MutableMap<MetaLogical<*>, Any>) {
right.collectData(occArray, allSubst)
left.collectData(occArray, allSubst)
}
@ -222,7 +227,7 @@ class ReteRuleMatcher(val rule: Rule) {
fun addNode(n: ReteNode) {
nodesList.add(n)
}
fun nodes() : Iterable<ReteNode> = nodesList.subList(startIdx, nodesList.size)
fun allNodes() : Iterable<ReteNode> = nodesList
@ -234,7 +239,7 @@ class ReteRuleMatcher(val rule: Rule) {
init {
assert(layers.isNotEmpty())
}
fun introduce(occIdx: Int, alphaNodes: Collection<AlphaNode>): Generation {
// propagation history
val initLayer = layers.last()
@ -280,27 +285,26 @@ class ReteRuleMatcher(val rule: Rule) {
}
fun matches(): Collection<MatchRule> {
fun matches(): Collection<MatchRuleImpl> {
val topLayer = layers.first()
if (topLayer.final) {
val matches = ArrayList<MatchRule>()
val matches = ArrayList<MatchRuleImpl>()
for (n in topLayer.nodes()) {
// any excluded occurrences?
if (n.containsOccurrence(skipOccIndices)) continue
val allSubst = HashMap<MetaLogical<*>, Any>()
val occArray = arrayOfNulls<ConstraintOccurrence>(headSize)
val occArray = arrayOfNulls<Occurrence>(headSize)
n.collectData(occArray, allSubst)
val occList = occArray.toMutableList()
val occList = occArray.toList() as List<Occurrence>
val keptCount = rule.headKept().count()
matches.add(MatchRuleImpl(n,
rule,
allSubst,
occList.subList(0, keptCount),
occList.subList(keptCount, occList.size)))
matches.add(MatchRuleImpl(rule,
allSubst,
occList.subList(0, keptCount),
occList.subList(keptCount, occList.size)))
}
return matches
@ -315,10 +319,10 @@ class ReteRuleMatcher(val rule: Rule) {
override fun rule(): Rule = rule
// for tests only
override fun expand(occ: ConstraintOccurrence): ReteNetwork =
override fun expand(occ: Occurrence): ReteNetwork =
expand(occ, bitSetOfOnes(headSize))
override fun expand(occ: ConstraintOccurrence, mask: BitSet): ReteNetwork {
override fun expand(occ: Occurrence, mask: BitSet): ReteNetwork {
// raising from the dead, huh?
val occIdx = indexOf(occ)
skipOccIndices.clear(occIdx)
@ -326,7 +330,7 @@ class ReteRuleMatcher(val rule: Rule) {
val alphaNodes = arrayListOf<AlphaNode>()
for (posInHead in mask.allSetBits()) {
val matcher = OccurrenceMatcher(emptySubst())
val matcher = createOccurrenceMatcher(emptySubst())
if (matcher.matches(head[posInHead], occ)) {
alphaNodes.add(AlphaNode(occ, posInHead, matcher.substitution()))
}
@ -338,12 +342,12 @@ class ReteRuleMatcher(val rule: Rule) {
return this
}
override fun contract(occ: ConstraintOccurrence): ReteNetwork {
override fun contract(occ: Occurrence): ReteNetwork {
skipOccIndices.set(indexOf(occ))
return this
}
override fun matches(): Collection<MatchRule> = generations.last().matches()
override fun matches(): Collection<MatchRuleImpl> = generations.last().matches()
override fun consumed(matchRule: MatchRule): MatchingProbe {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
@ -358,11 +362,4 @@ class ReteRuleMatcher(val rule: Rule) {
}
}
}

View File

@ -0,0 +1,187 @@
/*
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.core.internal
import com.github.andrewoma.dexx.collection.Sets
import jetbrains.mps.logic.reactor.core.*
import jetbrains.mps.logic.reactor.evaluation.MatchRule
import jetbrains.mps.logic.reactor.program.Constraint
import jetbrains.mps.logic.reactor.program.Rule
import jetbrains.mps.logic.reactor.util.*
import java.util.*
import kotlin.collections.ArrayList
import com.github.andrewoma.dexx.collection.List as PersList
import com.github.andrewoma.dexx.collection.Map as PersMap
import com.github.andrewoma.dexx.collection.Set as PersSet
import com.github.andrewoma.dexx.collection.Vector as PersVector
/**
* @author Fedor Isakov
*/
internal class RuleMatcherImpl(val rule: Rule) : RuleMatcher {
val head = rule.headKept().toCollection(ArrayList(4)).apply { addAll(rule.headReplaced()) }
val propagation = rule.headReplaced().count() == 0
val origins = IdentityHashMap<MatchRule, ActiveMatchNode>()
override fun probe(): MatchingProbe = RuleMatchFringe(listOf(MatchNode(emptySubst())),
Sets.of(),
Sets.of(),
0)
inner class RuleMatchFringe(val nodes: List<MatchNode>,
val seen: PersSet<IdWrapper<Occurrence>>,
val consumed: PersSet<ArrayList<IdWrapper<Occurrence>?>>,
val genId: Int) : MatchingProbe {
override fun rule(): Rule = rule
override fun matches(): Collection<MatchRuleImpl> {
return nodes.filter { it is ActiveMatchNode && it.complete && it.genId == genId }
.map { (it as ActiveMatchNode).toMatchRule() }
}
override fun consumed(matchRule: MatchRule): MatchingProbe =
RuleMatchFringe(nodes,
seen,
consumed.add(origins.get(matchRule)?.signature!!),
// ((matchRule as MatchRuleImpl).origin as ActiveMatchNode).signature),
genId)
override fun expand(occ: Occurrence): MatchingProbe =
expand(occ, bitSetOfOnes(head.size))
/**
* Expands the fringe by creating new leaf nodes that match the occurrence.
* Mask specifies possible slots for the occurrence.
*/
override fun expand(occ: Occurrence, mask: BitSet): RuleMatchFringe {
val reactivated = seen.contains(IdWrapper(occ))
val newSeen = if (reactivated) seen else seen.add(IdWrapper(occ))
val newNodes = ArrayList<MatchNode>(nodes)
val allSignatures = newNodes.map { it.signature }.toHashSet()
for (n in nodes) {
n.expand(occ, genId + 1, n.matchingVacant(mask))
.filter { allSignatures.add(it.signature) || reactivated } // ensure reactivated have effect
.filter { !(propagation && reactivated && consumed.contains(it.signature)) } // ...unless propagation
.forEach { newNodes.add(it) }
}
return RuleMatchFringe(newNodes, newSeen, consumed, genId + 1)
}
override fun contract(occ: Occurrence): RuleMatchFringe {
val newNodes = nodes.mapNotNull { it.unrelatedOrNull(occ) }
return RuleMatchFringe(newNodes, seen, consumed, genId + 1)
}
}
open inner class MatchNode(val subst: Subst, val vacant: BitSet = bitSetOfOnes(head.size)) {
open val signature: ArrayList<IdWrapper<Occurrence>?> =
arrayListOf(* arrayOfNulls(head.size))
/**
* Returns the additional nodes built from this node on adding the occurrence.
* If the occurrence is already in the path, return empty sequence.
*/
fun expand(occ: Occurrence, genId: Int, matchingVacant: BitSet): List<ActiveMatchNode> =
unrelatedOrNull(occ)?.let { n ->
ArrayList<ActiveMatchNode>().also { expanded ->
for (headIdx in matchingVacant.allSetBits()) {
createOccurrenceMatcher(subst).run {
if (matches(head[headIdx], occ)) {
expanded.add(ActiveMatchNode(substitution(), n, occ, headIdx, genId))
}
}
}
}
} ?: emptyList()
/**
* Returns this node if it doesn't have the occurrence in its path, null otherwise.
*/
open fun unrelatedOrNull(occ: Occurrence): MatchNode? = this
fun matchingVacant(mask: BitSet) = mask.copyApply { and(vacant) }
/**
* Folds the path to the root.
*/
inline protected fun <T> fold(init: T, action: (T, ActiveMatchNode) -> T): T {
var rn = this
var curr = init
while (rn is ActiveMatchNode) {
curr = action(curr, rn)
rn = rn.parent
}
return curr
}
/**
* Folds the path to the root. If an iteration yields null, fold is stopped and null is returned.
*/
inline protected fun <T> foldUntilNull(init: T, action: (T, ActiveMatchNode) -> T?): T? {
var rn = this
var curr: T? = init
while (rn is ActiveMatchNode) {
curr = action(curr!!, rn)
if (curr == null) return null
rn = rn.parent
}
return curr
}
}
inner class ActiveMatchNode(subst: Subst,
val parent: MatchNode,
val occurrence: Occurrence,
val headIndex: Int,
val genId: Int) :
MatchNode(subst, parent.vacant.clearBit(headIndex)) {
val complete = vacant.cardinality() == 0
override val signature: ArrayList<IdWrapper<Occurrence>?> =
ArrayList(parent.signature).also { it[headIndex] = IdWrapper(occurrence) }
fun constraint(): Constraint = head[headIndex]
override fun unrelatedOrNull(occ: Occurrence): ActiveMatchNode? =
foldUntilNull(this) { acc, rn -> if (rn.occurrence === occ) null else acc }
fun toMatchRule(): MatchRuleImpl {
val matched: Array<Occurrence> =
fold(arrayOfNulls<Occurrence>(head.size)) { arr, rn ->
arr[rn.headIndex] = rn.occurrence; arr
} as Array<Occurrence>
val mr = MatchRuleImpl(rule,
subst,
ArrayList(matched.take(rule.headKept().count())),
ArrayList(matched.takeLast(rule.headReplaced().count())))
origins.put(mr, this)
return mr
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2014-2017 JetBrains s.r.o.
* Copyright 2014-2019 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,16 +14,19 @@
* limitations under the License.
*/
package jetbrains.mps.logic.reactor.core
package jetbrains.mps.logic.reactor.core.internal
import com.github.andrewoma.dexx.collection.Maps
import jetbrains.mps.logic.reactor.core.FrameObservable
import jetbrains.mps.logic.reactor.core.LogicalObserver
import jetbrains.mps.logic.reactor.core.Occurrence
import jetbrains.mps.logic.reactor.core.occurrence
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
import jetbrains.mps.logic.reactor.evaluation.StoreView
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.program.Constraint
import jetbrains.mps.logic.reactor.program.ConstraintSymbol
import jetbrains.mps.unification.Term
import jetbrains.mps.logic.reactor.util.*
import java.util.*
import com.github.andrewoma.dexx.collection.Map as PersMap
import com.github.andrewoma.dexx.collection.Set as PersSet
import com.github.andrewoma.dexx.collection.Vector as PersVector
@ -32,73 +35,53 @@ import com.github.andrewoma.dexx.collection.Vector as PersVector
* @author Fedor Isakov
*/
fun ConstraintOccurrence.isStored(): Boolean =
// TODO: superfluous cast
(this as StoreItem).stored
fun ConstraintOccurrence.isAlive(): Boolean =
// TODO: superfluous cast
(this as StoreItem).alive
interface StoreItem {
var alive: Boolean
var stored: Boolean
fun terminate()
}
interface StoreKeeper {
fun store(): Store
fun addObserver(logical: Logical<*>, obs: (StoreKeeper) -> LogicalObserver)
fun removeObserver(logical: Logical<*>, obs: (StoreKeeper) -> LogicalObserver)
}
/**
* Constrants storeObserver.
*
* TODO: make this class persistent.
*/
class Store : LogicalObserver {
internal class Store : LogicalObserver {
val currentFrame: () -> StoreKeeper
val currentFrame: () -> FrameObservable
var symbol2occurrences: PersMap<ConstraintSymbol, IdHashSet<ConstraintOccurrence>>
var symbol2occurrences: PersMap<ConstraintSymbol, IdHashSet<Occurrence>>
var logical2occurrences: PersMap<IdWrapper<Logical<*>>, IdHashSet<ConstraintOccurrence>>
var logical2occurrences: PersMap<IdWrapper<Logical<*>>, IdHashSet<Occurrence>>
constructor(copyFrom: Store, currentFrame: () -> StoreKeeper) {
constructor(copyFrom: Store, currentFrame: () -> FrameObservable) {
this.currentFrame = currentFrame
this.symbol2occurrences = copyFrom.symbol2occurrences
this.logical2occurrences = copyFrom.logical2occurrences
}
constructor(copyFrom: StoreView, currentFrame: () -> StoreKeeper) {
constructor(copyFrom: StoreView, currentFrame: () -> FrameObservable) {
this.currentFrame = currentFrame
this.symbol2occurrences = copyFrom.constraintSymbols()
.fold(Maps.of()) { map, sym -> map.put(sym, IdHashSet(copyFrom.occurrences(sym))) }
var l2o = Maps.of<IdWrapper<Logical<*>>, IdHashSet<ConstraintOccurrence>>()
var v2o = Maps.of<Any, IdHashSet<ConstraintOccurrence>>()
var l2o = Maps.of<IdWrapper<Logical<*>>, IdHashSet<Occurrence>>()
val storeItems = IdentityHashMap<ConstraintOccurrence, Occurrence>()
copyFrom.allOccurrences().forEach { occ ->
val item = occ.constraint().occurrence(occ.arguments(), currentFrame)
storeItems.put(occ, item)
occ.arguments().forEach { arg ->
when (arg) {
is Logical<*> -> l2o = l2o.put(IdWrapper(arg.findRoot()), l2o[IdWrapper(arg.findRoot())]?.add(occ) ?: singletonIdSet(occ))
is Any -> v2o = v2o.put(arg, v2o[arg]?.add(occ) ?: singletonIdSet(occ))
is Logical<*> -> {
l2o = l2o.put(IdWrapper(arg.findRoot()),
l2o[IdWrapper(arg.findRoot())]?.add(item) ?: singletonIdSet(item))
}
}
}
}
this.logical2occurrences = l2o
this.symbol2occurrences = copyFrom.constraintSymbols().fold(Maps.of()) { map, sym ->
val copyFrom1 = copyFrom.occurrences(sym).map { occ -> storeItems.get(occ)!! }
val idHashSet = IdHashSet( copyFrom1 )
map.put(sym, idHashSet)
}
}
constructor(currentFrame: () -> StoreKeeper) {
constructor(currentFrame: () -> FrameObservable) {
this.currentFrame = currentFrame
this.symbol2occurrences = Maps.of()
this.logical2occurrences = Maps.of()
@ -122,7 +105,7 @@ class Store : LogicalObserver {
}
}
fun store(occ: ConstraintOccurrence) {
fun store(occ: Occurrence) {
val symbol = occ.constraint().symbol()
this.symbol2occurrences = symbol2occurrences.put(symbol,
@ -136,16 +119,15 @@ class Store : LogicalObserver {
val argId = IdWrapper(value.findRoot())
this.logical2occurrences = logical2occurrences.put(argId,
logical2occurrences[argId]?.add(occ) ?: singletonIdSet(occ))
currentFrame().addObserver(value) { frame -> frame.store() }
currentFrame().addObserver(value) { frame -> frame.storeObserver() }
}
}
}
// TODO: superfluous cast
(occ as StoreItem).stored = true
occ.stored = true
}
fun discard(occ: ConstraintOccurrence, profiler: Profiler? = null, tag: String? = null): Unit {
fun discard(occ: Occurrence, profiler: Profiler? = null, tag: String? = null): Unit {
val symbol = occ.constraint().symbol()
symbol2occurrences[symbol]?.remove(occ)?.let { newList ->
@ -168,13 +150,12 @@ class Store : LogicalObserver {
}
}
// TODO: superfluous cast
(occ as StoreItem).stored = false
occ.stored = false
occ.terminate()
}
fun allOccurrences(): Sequence<ConstraintOccurrence> {
return symbol2occurrences.values().flatten().filter { co -> co.isStored() }.asSequence()
return symbol2occurrences.values().flatten().filter { co -> co.stored }.asSequence()
}
fun view(): StoreView = StoreViewImpl(allOccurrences())

View File

@ -116,7 +116,7 @@ class MockProgram(val name: String, val handlers: List<Handler>, val registry: M
}
class MockConstraintRegistry(val sessionSolver: SessionSolver) {
class MockConstraintRegistry() {
private val myConstraintArgTypes = HashMap<ConstraintSymbol, List<Class<*>>>().withDefault { Collections.emptyList() }

View File

@ -117,7 +117,7 @@ infix fun <T : Any> T.is_eq(value: T): Boolean = EvaluationSession.current().let
}
val args = session.program().instantiateArguments(predicate.arguments(), logicalContext, invocationContext)
val inv = predicate.invocation(args, logicalContext, invocationContext)
session.sessionSolver().ask(inv)
session.ask(inv)
}
infix fun <T : Any> T.eq(value: T) = EvaluationSession.current().let { session ->
@ -128,7 +128,7 @@ infix fun <T : Any> T.eq(value: T) = EvaluationSession.current().let { session -
}
val args = session.program().instantiateArguments(predicate.arguments(), logicalContext, invocationContext)
val inv = predicate.invocation(args, logicalContext, invocationContext)
session.sessionSolver().tell(inv)
session.tell(inv)
}
private fun mockLogicalContext(): LogicalContext {

View File

@ -1,15 +0,0 @@
package solver
import jetbrains.mps.logic.reactor.evaluation.AbstractSolver
import jetbrains.mps.logic.reactor.evaluation.EvaluationTrace
import jetbrains.mps.logic.reactor.evaluation.SessionSolver
import jetbrains.mps.logic.reactor.program.PredicateSymbol
/**
* @author Fedor Isakov
*/
open class MockSessionSolver() : SessionSolver() {
}

View File

@ -1,9 +1,9 @@
import jetbrains.mps.logic.reactor.core.LogicalObserver
import jetbrains.mps.logic.reactor.core.LogicalImpl
import jetbrains.mps.logic.reactor.evaluation.PredicateInvocation
import jetbrains.mps.logic.reactor.logical.*
import jetbrains.mps.logic.reactor.program.Predicate
import jetbrains.mps.logic.reactor.program.PredicateSymbol
import jetbrains.mps.logic.reactor.core.internal.anonLogical
import jetbrains.mps.logic.reactor.core.internal.namedLogical
import jetbrains.mps.logic.reactor.logical.JoinableLogical
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.logical.MetaLogical
import java.util.*
/**
@ -11,31 +11,31 @@ import java.util.*
*/
fun <T: Any> anon(value: T) = LogicalImpl(value)
fun <T : Any> anon(value: T) = anonLogical(value)
fun <T: Any> logical(name: String) = LogicalImpl<T>(name)
fun <T : Any> logical(name: String) = namedLogical<T>(name)
fun <T: Any> logical(name1: String, name2: String) = Pair(LogicalImpl<T>(name1), LogicalImpl<T>(name2))
fun <T : Any> logical(name1: String, name2: String) = Pair(namedLogical<T>(name1), namedLogical<T>(name2))
fun <T: Any> logical(name1: String, name2: String, name3: String) =
Triple(LogicalImpl<T>(name1), LogicalImpl<T>(name2), LogicalImpl<T>(name3))
fun <T : Any> logical(name1: String, name2: String, name3: String) =
Triple(namedLogical<T>(name1), namedLogical<T>(name2), namedLogical<T>(name3))
inline fun <reified T: Any> metaLogical(name: String) = MetaLogical<T>(name, T::class.java)
inline fun <reified T : Any> metaLogical(name: String) = MetaLogical<T>(name, T::class.java)
inline fun <reified T: Any> metaLogical(name1: String, name2: String) =
inline fun <reified T : Any> metaLogical(name1: String, name2: String) =
Pair(MetaLogical<T>(name1, T::class.java), MetaLogical<T>(name2, T::class.java))
inline fun <reified T: Any> metaLogical(name1: String, name2: String, name3: String) =
inline fun <reified T : Any> metaLogical(name1: String, name2: String, name3: String) =
Triple(
MetaLogical<T>(name1, T::class.java),
MetaLogical<T>(name2, T::class.java),
MetaLogical<T>(name3, T::class.java))
fun <T: Any> Logical<T>.get(): T = findRoot().value()
fun <T : Any> Logical<T>.get(): T = findRoot().value()
fun <T: Any> Logical<T>.getNullable(): T? = findRoot().value()
fun <T : Any> Logical<T>.getNullable(): T? = findRoot().value()
fun <T: Any> Logical<T>.set(t: T) {
fun <T : Any> Logical<T>.set(t: T) {
if (this is JoinableLogical<T>)
findRoot().setValue(t)
else
@ -52,9 +52,13 @@ class MockObserver : LogicalObserver {
val events = ArrayList<MockObserverEvent>()
override fun valueUpdated(logical: Logical<*>) { events.add(value(logical))}
override fun valueUpdated(logical: Logical<*>) {
events.add(value(logical))
}
override fun parentUpdated(logical: Logical<*>) { events.add(parent(logical))}
override fun parentUpdated(logical: Logical<*>) {
events.add(parent(logical))
}
fun getAndClearEvents(): Set<MockObserverEvent> {
val tmp = ArrayList(events)

View File

@ -1,10 +1,12 @@
import jetbrains.mps.logic.reactor.core.StoreItem
import jetbrains.mps.logic.reactor.core.FrameObservable
import jetbrains.mps.logic.reactor.core.LogicalObserver
import jetbrains.mps.logic.reactor.core.Occurrence
import jetbrains.mps.logic.reactor.core.occurrence
import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence
import jetbrains.mps.logic.reactor.evaluation.EvaluationFeedbackHandler
import jetbrains.mps.logic.reactor.logical.Logical
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.*
@ -22,11 +24,11 @@ class Builder(val env: Environment, val handlers: List<Handler>) {
class Environment(val programBuilder: ProgramBuilder? = null) {
}
fun programWithRules(vararg ruleBuilders : Environment.() -> Rule): Builder {
fun programWithRules(vararg ruleBuilders: Environment.() -> Rule): Builder {
return programWithRules(Environment(), ruleBuilders)
}
fun programWithRules(pb: ProgramBuilder, vararg ruleBuilders : Environment.() -> Rule): Builder {
fun programWithRules(pb: ProgramBuilder, vararg ruleBuilders: Environment.() -> Rule): Builder {
return programWithRules(Environment(pb), ruleBuilders)
}
@ -34,13 +36,13 @@ private fun programWithRules(env: Environment, ruleBuilders: Array<out Environme
return builder(env, arrayOf(handler("test", emptyList(), * ruleBuilders)))
}
fun programWithHandlers(vararg handlerBuilders : Environment.() -> Handler): Builder {
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) {
with(env) {
for (block in handlerBlocks) {
handlers.add(block())
}
@ -56,7 +58,7 @@ fun handler(name: String, primary: Iterable<ConstraintSymbol>, vararg ruleBlocks
hb.toHandler()
}
fun rule(tag: String, vararg component:RB.() -> Unit): Environment.() -> Rule = {
fun rule(tag: String, vararg component: RB.() -> Unit): Environment.() -> Rule = {
val rb = RB(this, tag)
for (cmp in component) {
rb.cmp()
@ -64,23 +66,23 @@ fun rule(tag: String, vararg component:RB.() -> Unit): Environment.() -> Rule =
rb.toRule()
}
fun headKept(vararg content : ConjBuilder.() -> Unit): RB.() -> Unit = {
appendHeadKept( * buildConjunction(Constraint::class.java, env, content).toArray())
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 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 guard(vararg content: ConjBuilder.() -> Unit): RB.() -> Unit = {
appendGuard(* buildConjunction(Predicate::class.java, env, content).toArray())
}
fun body(vararg content : ConjBuilder.() -> Unit): RB.() -> Unit = {
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 = {
fun altBody(vararg content: ConjBuilder.() -> Unit): RB.() -> Unit = {
appendBody(true, * buildConjunction(AndItem::class.java, env, content).toArray())
}
@ -93,7 +95,21 @@ fun equals(left: Any, right: Any): ConjBuilder.() -> Unit = {
add(TestEqPredicate(left, right))
}
fun occurrence(id: String, vararg args: Any) : ConstraintOccurrence = TestConstraintOccurrence(id, * args)
fun occurrence(id: String, vararg args: Any): Occurrence =
MockConstraint(ConstraintSymbol.symbol(id, args.size)).occurrence(listOf(* args), { fooObservable })
object fooObservable : FrameObservable {
override fun storeObserver(): LogicalObserver {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun addObserver(logical: Logical<*>, obs: (FrameObservable) -> LogicalObserver) {
}
override fun removeObserver(logical: Logical<*>, obs: (FrameObservable) -> LogicalObserver) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
class RB(val env: Environment, tag: String) : RuleBuilder(tag) {
@ -103,9 +119,8 @@ 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)
return env.programBuilder?.constraint(ConstraintSymbol(id, args.size), * args)
?: MockConstraint(ConstraintSymbol(id, args.size), * args)
}
fun add(item: AndItem): Unit {
@ -133,40 +148,10 @@ class ConjBuilder(val type: Class<out AndItem>, val env: Environment) {
private fun buildConjunction(type: Class<out AndItem>,
env: Environment,
content: Array<out ConjBuilder.() -> Unit>): ConjBuilder
{
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()})"
}

View File

@ -1,15 +1,18 @@
import jetbrains.mps.logic.reactor.core.*
import jetbrains.mps.logic.reactor.core.Controller
import jetbrains.mps.logic.reactor.core.EvaluationSessionEx
import jetbrains.mps.logic.reactor.core.internal.createController
import jetbrains.mps.logic.reactor.core.internal.logical
import jetbrains.mps.logic.reactor.evaluation.*
import jetbrains.mps.logic.reactor.logical.Logical
import jetbrains.mps.logic.reactor.program.*
import jetbrains.mps.logic.reactor.program.ConstraintSymbol
import jetbrains.mps.logic.reactor.program.Program
import jetbrains.mps.logic.reactor.program.Rule
import jetbrains.mps.unification.Term
import jetbrains.mps.unification.test.MockTerm.*
import org.junit.After
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import solver.EqualsSolver
import solver.MockSessionSolver
import solver.eq
import solver.is_eq
@ -21,30 +24,31 @@ import solver.is_eq
class TestController {
@Before fun beforeTest() {
@Before
fun beforeTest() {
}
@After fun afterTest() {
@After
fun afterTest() {
MockSession.deinit()
}
private class MockSession(val program: Program, val solver: SessionSolver) : EvaluationSession(), SessionObjects {
private class MockSession(program: Program) :
EvaluationSessionEx(program, EvaluationTrace.NULL) {
lateinit var controller: Controller
override fun controller(): Controller = controller
override fun sessionSolver(): SessionSolver = solver
override fun program(): Program = program
override fun storeView(): StoreView = TODO()
class MockBackend(val session: MockSession) : Backend {
override fun current(): EvaluationSession = session
override fun controller(): Controller = controller
class MockBackend(val session: MockSession) : Backend<MockSession> {
override fun current(): MockSession = session
override fun createConfig(program: Program): Config = TODO()
}
companion object {
lateinit var ourBackend : MockBackend
lateinit var ourBackend: MockBackend
fun init(program: Program, solver: SessionSolver) {
ourBackend = MockBackend(MockSession(program, solver))
fun init(program: Program) {
ourBackend = MockBackend(MockSession(program))
setBackend(ourBackend)
}
@ -54,24 +58,19 @@ class TestController {
}
}
private fun sessionSolver(): SessionSolver = MockSessionSolver()
private fun Builder.controller(vararg occurrences: ConstraintOccurrence): Controller {
val solver = sessionSolver()
val program = MockProgram("test", handlers, registry = MockConstraintRegistry(solver))
MockSession.init(program, solver)
val controller = Controller(program, storeView = MockStoreView(listOf(* occurrences)))
val program = MockProgram("test", handlers, registry = MockConstraintRegistry())
MockSession.init(program)
val controller = createController(program, storeView = MockStoreView(listOf(* occurrences)))
MockSession.ourBackend.session.controller = controller
return controller
}
private fun Builder.controllerWithFeedback(feedbackHandler: EvaluationFeedbackHandler,
vararg occurrences: ConstraintOccurrence): Controller
{
val solver = sessionSolver()
val program = MockProgram("test", handlers, registry = MockConstraintRegistry(solver))
MockSession.init(program, solver)
val controller = Controller(program, storeView = MockStoreView(listOf(* occurrences)), feedbackHandler = feedbackHandler)
vararg occurrences: ConstraintOccurrence): Controller {
val program = MockProgram("test", handlers, registry = MockConstraintRegistry())
MockSession.init(program)
val controller = createController(program, storeView = MockStoreView(listOf(* occurrences)), feedbackHandler = feedbackHandler)
MockSession.ourBackend.session.controller = controller
return controller
}
@ -146,7 +145,7 @@ class TestController {
@Test
fun basicExpression() {
var test : String = "not initialized"
var test: String = "not initialized"
programWithRules(
rule("main",
headKept(
@ -171,7 +170,7 @@ class TestController {
constraint("main")
),
body(
statement ({ test.set("value") })
statement({ test.set("value") })
))
).run {
controller().evaluate(occurrence("main"))
@ -181,7 +180,7 @@ class TestController {
@Test
fun basicLogical() {
var test : String? = "not initialized"
var test: String? = "not initialized"
val x = logical<String>("x")
x.setValue("expected")
programWithRules(
@ -190,7 +189,7 @@ class TestController {
constraint("main")
),
body(
statement ({ test = x.get() } )
statement({ test = x.get() })
))
).run {
controller().evaluate(occurrence("main"))
@ -200,8 +199,8 @@ class TestController {
@Test
fun logicalCopy() {
var test : String? = "not initialized"
val (x,y) = logical<String>("x", "y")
var test: String? = "not initialized"
val (x, y) = logical<String>("x", "y")
x.setValue("expected")
programWithRules(
rule("main",
@ -217,7 +216,7 @@ class TestController {
constraint("next")
),
body(
statement ({ test = y.get() })
statement({ test = y.get() })
))
).run {
controller().evaluate(occurrence("main")).run {
@ -231,8 +230,8 @@ class TestController {
@Test
fun basicGuard() {
var test1 : String = "not initialized 1"
var test2 : String = "not initialized 2"
var test1: String = "not initialized 1"
var test2: String = "not initialized 2"
programWithRules(
rule("main1",
headKept(
@ -242,14 +241,14 @@ class TestController {
expression { false }
),
body(
statement { test1 = "not expected" }
statement { test1 = "not expected" }
)),
rule("main2",
headKept(
constraint("main")
),
guard(
expression { true }
expression { true }
),
body(
statement { test2 = "expected" }
@ -276,10 +275,11 @@ class TestController {
)
)
).controller().apply {
a.set("value") }.evaluate(occurrence("foo", a)).run {
a.set("value")
}.evaluate(occurrence("foo", a)).run {
assertSame(1, allOccurrences().toList().size)
val co = allOccurrences().first()
assertEquals(ConstraintSymbol("bar",1), co.constraint().symbol())
assertEquals(ConstraintSymbol("bar", 1), co.constraint().symbol())
assertEquals(1, co.arguments().size)
val arg = co.arguments().first()
assertNotEquals(a, arg)
@ -291,18 +291,18 @@ class TestController {
fun occurrenceTerminated() {
programWithRules(
rule("first",
headKept( constraint("foo") ), body( constraint("expected1") )
headKept(constraint("foo")), body(constraint("expected1"))
),
rule("second",
headKept( constraint("foo") ), body( constraint("bar") )
headKept(constraint("foo")), body(constraint("bar"))
),
rule("third",
headKept( constraint("foo") ), body( constraint("unexpected") )
headKept(constraint("foo")), body(constraint("unexpected"))
),
rule("fourth",
headReplaced( constraint("bar"),
constraint("foo") ),
body( constraint("expected2") )
headReplaced(constraint("bar"),
constraint("foo")),
body(constraint("expected2"))
)
).controller().evaluate(occurrence("foo")).run {
assertEquals(
@ -315,18 +315,18 @@ class TestController {
fun occurrenceKeptActive() {
programWithRules(
rule("first",
headKept( constraint("foo") ), body( constraint("bar") )
headKept(constraint("foo")), body(constraint("bar"))
),
rule("second",
headReplaced( constraint("foo") ), body( constraint("expected1") )
headReplaced(constraint("foo")), body(constraint("expected1"))
),
rule("third",
headReplaced( constraint("foo") ), body( constraint("unexpected") )
headReplaced(constraint("foo")), body(constraint("unexpected"))
),
rule("fourth",
headKept( constraint("foo") ),
headReplaced( constraint("bar") ),
body( constraint("expected2") )
headKept(constraint("foo")),
headReplaced(constraint("bar")),
body(constraint("expected2"))
)
).controller().evaluate(occurrence("foo")).run {
assertEquals(
@ -340,31 +340,31 @@ class TestController {
val X = metaLogical<Int>("X")
programWithRules(
rule("zeroth",
headKept( constraint("foo") ), body( statement({ x -> x.set(999) }, X),
constraint("bar", X))
headKept(constraint("foo")), body(statement({ x -> x.set(999) }, X),
constraint("bar", X))
),
rule("first",
headKept( constraint("foo") ),
body( constraint("bar", X),
constraint("qux", X))
headKept(constraint("foo")),
body(constraint("bar", X),
constraint("qux", X))
),
rule("second",
headReplaced( constraint("qux", X) ),
body( constraint("expected1"),
statement({ x -> x.set(123) }, X))
headReplaced(constraint("qux", X)),
body(constraint("expected1"),
statement({ x -> x.set(123) }, X))
),
rule("third",
headReplaced( constraint("foo") ),
body( constraint("unexpected"))
headReplaced(constraint("foo")),
body(constraint("unexpected"))
),
rule("fourth",
headReplaced( constraint("foo") ),
headReplaced( constraint("bar", X) ), guard(expression({ x -> x.getNullable() == 123 }, X)),
body( constraint("expected2") )
headReplaced(constraint("foo")),
headReplaced(constraint("bar", X)), guard(expression({ x -> x.getNullable() == 123 }, X)),
body(constraint("expected2"))
),
rule("fifth",
headReplaced( constraint("bar", X) ), guard(expression({ x -> x.getNullable() == 999 }, X)),
body( constraint("expected3", X))
headReplaced(constraint("bar", X)), guard(expression({ x -> x.getNullable() == 999 }, X)),
body(constraint("expected3", X))
)
).controller().evaluate(occurrence("foo")).run {
assertEquals(3, allOccurrences().count())
@ -381,28 +381,28 @@ class TestController {
val (X, Y) = metaLogical<Int>("X", "Y")
programWithRules(
rule("first",
headKept( constraint("foo") ),
body( constraint("bar", X),
constraint("qux", Y),
statement({ x, y -> eq(x, y) }, X, Y))
headKept(constraint("foo")),
body(constraint("bar", X),
constraint("qux", Y),
statement({ x, y -> eq(x, y) }, X, Y))
),
rule("second",
headReplaced( constraint("qux", Y) ),
body( constraint("expected1"),
statement({ y -> y.set(123) }, Y))
headReplaced(constraint("qux", Y)),
body(constraint("expected1"),
statement({ y -> y.set(123) }, Y))
),
rule("third",
headReplaced( constraint("foo") ),
body( constraint("unexpected"))
headReplaced(constraint("foo")),
body(constraint("unexpected"))
),
rule("fourth",
headReplaced( constraint("foo") ),
headReplaced( constraint("bar", X) ), guard(expression({ x -> x.getNullable() == 123 }, X)),
body( constraint("expected2") )
headReplaced(constraint("foo")),
headReplaced(constraint("bar", X)), guard(expression({ x -> x.getNullable() == 123 }, X)),
body(constraint("expected2"))
),
rule("fifth",
headKept( constraint("bar", X) ),
body( constraint("expected3", X))
headKept(constraint("bar", X)),
body(constraint("expected3", X))
)
).controller().evaluate(occurrence("foo")).run {
assertEquals(3, allOccurrences().count())
@ -419,35 +419,35 @@ class TestController {
val (X, Y) = metaLogical<Int>("X", "Y")
programWithRules(
rule("first",
headKept( constraint("foo") ),
body( constraint("bar", X),
statement({ x, y -> eq(x, y) }, X, Y),
constraint("qux", Y))
headKept(constraint("foo")),
body(constraint("bar", X),
statement({ x, y -> eq(x, y) }, X, Y),
constraint("qux", Y))
),
rule("second",
headReplaced( constraint("qux", Y) ),
body( constraint("expected1"),
statement({ y -> y.set(123) }, Y))
headReplaced(constraint("qux", Y)),
body(constraint("expected1"),
statement({ y -> y.set(123) }, Y))
),
rule("third",
headReplaced( constraint("foo") ),
body( constraint("unexpected"))
headReplaced(constraint("foo")),
body(constraint("unexpected"))
),
rule("fourth",
headReplaced( constraint("foo") ),
headReplaced( constraint("bar", X) ), guard(expression({ x -> x.getNullable() == 123 }, X)),
body( constraint("expected2") )
headReplaced(constraint("foo")),
headReplaced(constraint("bar", X)), guard(expression({ x -> x.getNullable() == 123 }, X)),
body(constraint("expected2"))
),
rule("fifth",
headKept( constraint("bar", X) ),
body( constraint("expected3", X))
headKept(constraint("bar", X)),
body(constraint("expected3", X))
)
).controller().evaluate(occurrence("foo")).run {
assertEquals(3, allOccurrences().count())
assertEquals(setOf( ConstraintSymbol("expected1", 0),
ConstraintSymbol("expected2", 0),
ConstraintSymbol("expected3", 1)),
allOccurrences().map { co -> co.constraint().symbol() }.toSet())
assertEquals(setOf(ConstraintSymbol("expected1", 0),
ConstraintSymbol("expected2", 0),
ConstraintSymbol("expected3", 1)),
allOccurrences().map { co -> co.constraint().symbol() }.toSet())
val ex3 = allOccurrences().filter { co -> co.constraint().symbol() == ConstraintSymbol("expected3", 1) }.first()
assertEquals(123, (ex3.arguments().first() as Logical<Int>).value())
}
@ -456,27 +456,27 @@ class TestController {
@Test
fun correctRulesOrder() {
val X= metaLogical<Int>("X")
val X = metaLogical<Int>("X")
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x -> x.set(1) }, X),
constraint("bar"),
constraint("foo", X) )
headReplaced(constraint("main")), body(statement({ x -> x.set(1) }, X),
constraint("bar"),
constraint("foo", X))
),
rule("foo_if_zero",
headReplaced( constraint("foo", X) ), guard( expression({ x -> x.get() == 0 }, X) ),
body( constraint("foo_zero") )
headReplaced(constraint("foo", X)), guard(expression({ x -> x.get() == 0 }, X)),
body(constraint("foo_zero"))
),
rule("foo_and_bar",
headReplaced( constraint("foo", X) ),
headKept( constraint("bar") ),
body( constraint("foo_and_bar") )
headReplaced(constraint("foo", X)),
headKept(constraint("bar")),
body(constraint("foo_and_bar"))
),
rule("foo_if_non_zero",
headReplaced( constraint("foo", X) ),
guard( expression({ x -> x.get() != 0 }, X) ),
body( constraint("foo_non_zero") )
headReplaced(constraint("foo", X)),
guard(expression({ x -> x.get() != 0 }, X)),
body(constraint("foo_non_zero"))
)
).controller().evaluate(occurrence("main")).run {
assertEquals(setOf(ConstraintSymbol("bar", 0), ConstraintSymbol("foo_and_bar", 0)), constraintSymbols())
@ -491,19 +491,19 @@ class TestController {
val W = metaLogical<Int>("W")
programWithRules(
rule("main",
headReplaced(constraint("main")), body( statement({ w -> w.set(42) }, W),
constraint("foo", W, "a{c}"),
constraint("foo", W, "a{b}"),
constraint("foo", W, "a{d}"))
),
rule("expected",
headReplaced(
constraint("foo", X, "a{d}"),
constraint("foo", Y, "a{b}"),
constraint("foo", Z, "a{c}")), body(constraint("done")))
).controller().evaluate(occurrence("main")).run {
rule("main",
headReplaced(constraint("main")), body(statement({ w -> w.set(42) }, W),
constraint("foo", W, "a{c}"),
constraint("foo", W, "a{b}"),
constraint("foo", W, "a{d}"))
),
rule("expected",
headReplaced(
constraint("foo", X, "a{d}"),
constraint("foo", Y, "a{b}"),
constraint("foo", Z, "a{c}")), body(constraint("done")))
).controller().evaluate(occurrence("main")).run {
assertEquals(setOf(ConstraintSymbol("done", 0)), constraintSymbols())
assertEquals(1, occurrences(ConstraintSymbol.symbol("done", 0)).count())
}
@ -511,38 +511,38 @@ class TestController {
@Test
fun reactivateOnUnion() {
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")
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")
var count = 0
programWithRules(
rule("main",
headReplaced( constraint("main") ),
body(
statement({ z -> z.set(0) }, Z1),
constraint("foo", X1, Z1),
constraint("foo", Y1, Z1),
statement({ x, y -> eq(x, y) }, X1, Y1) )
headReplaced(constraint("main")),
body(
statement({ z -> z.set(0) }, Z1),
constraint("foo", X1, Z1),
constraint("foo", Y1, Z1),
statement({ x, y -> eq(x, y) }, X1, Y1))
),
rule("capture_foo",
headKept( constraint("foo", X2, Y2) ),
body(
statement({ z -> z.set(count++) }, Z2),
constraint("capture", Z2) )
headKept(constraint("foo", X2, Y2)),
body(
statement({ z -> z.set(count++) }, Z2),
constraint("capture", Z2))
),
rule("capture_foo_foo",
headKept( constraint("foo", X3, Z3) ),
headReplaced( constraint("foo", Y3, Z3) ),
guard(
expression({ x, y -> is_eq(x, y) }, X3, Y3)),
body(
constraint("replaced") )
headKept(constraint("foo", X3, Z3)),
headReplaced(constraint("foo", Y3, Z3)),
guard(
expression({ x, y -> is_eq(x, y) }, X3, Y3)),
body(
constraint("replaced"))
)
).controller().evaluate(occurrence("main")).run {
assertEquals(setOf( ConstraintSymbol("foo", 2),
ConstraintSymbol("capture", 1),
ConstraintSymbol("replaced", 0)),
constraintSymbols())
assertEquals(setOf(ConstraintSymbol("foo", 2),
ConstraintSymbol("capture", 1),
ConstraintSymbol("replaced", 0)),
constraintSymbols())
assertEquals(1, occurrences(ConstraintSymbol("foo", 2)).count())
assertEquals(2, occurrences(ConstraintSymbol("capture", 1)).count())
assertEquals(1, occurrences(ConstraintSymbol("replaced", 0)).count())
@ -551,25 +551,25 @@ class TestController {
@Test
fun propagationHistory() {
val (X,Y,Z) = metaLogical<Int>("X", "Y", "Z")
val (X, Y, Z) = metaLogical<Int>("X", "Y", "Z")
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x, y -> eq(x, y) }, X, Y), // rank(X) = 1
constraint("foo", Y),
constraint("bar", Z),
// update Z's parent
statement({ x, z -> eq(x, z) }, X, Z) )
headReplaced(constraint("main")), body(statement({ x, y -> eq(x, y) }, X, Y), // rank(X) = 1
constraint("foo", Y),
constraint("bar", Z),
// update Z's parent
statement({ x, z -> eq(x, z) }, X, Z))
),
rule("foobar",
headKept( constraint("foo", X) ),
headKept( constraint("bar", Y) ),
body( constraint("foobar") )
headKept(constraint("foo", X)),
headKept(constraint("bar", Y)),
body(constraint("foobar"))
)
).controller().evaluate(occurrence("main")).run {
assertEquals(setOf( ConstraintSymbol("foo", 1),
ConstraintSymbol("bar", 1),
ConstraintSymbol("foobar", 0)),
constraintSymbols())
assertEquals(setOf(ConstraintSymbol("foo", 1),
ConstraintSymbol("bar", 1),
ConstraintSymbol("foobar", 0)),
constraintSymbols())
assertEquals(1, occurrences(ConstraintSymbol("foo", 1)).count())
assertEquals(1, occurrences(ConstraintSymbol("bar", 1)).count())
assertEquals(1, occurrences(ConstraintSymbol("foobar", 0)).count())
@ -581,19 +581,19 @@ class TestController {
val X = metaLogical<Int>("X")
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( constraint("foo", X),
statement({ x -> eq(x, "doh") }, X)
)
headReplaced(constraint("main")), body(constraint("foo", X),
statement({ x -> eq(x, "doh") }, X)
)
),
rule("foobar",
headKept( constraint("foo", X) ),
guard( expression ({ x -> x.isBound }, X) ),
body( constraint("foobar") )
headKept(constraint("foo", X)),
guard(expression({ x -> x.isBound }, X)),
body(constraint("foobar"))
)
).controller().evaluate(occurrence("main")).run {
assertEquals(setOf( ConstraintSymbol("foo", 1),
ConstraintSymbol("foobar", 0)),
constraintSymbols())
assertEquals(setOf(ConstraintSymbol("foo", 1),
ConstraintSymbol("foobar", 0)),
constraintSymbols())
assertEquals(1, occurrences(ConstraintSymbol("foo", 1)).count())
assertEquals(1, occurrences(ConstraintSymbol("foobar", 0)).count())
}
@ -604,23 +604,23 @@ class TestController {
val (X, Y) = metaLogical<Int>("X", "Y")
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( constraint("foo", X),
constraint("bar", Y),
statement({ x -> eq(x, "doh") }, X)
)
headReplaced(constraint("main")), body(constraint("foo", X),
constraint("bar", Y),
statement({ x -> eq(x, "doh") }, X)
)
),
rule("foobar",
headKept( constraint("foo", X),
constraint("bar", Y)
headKept(constraint("foo", X),
constraint("bar", Y)
),
guard( expression ({ x -> x.isBound }, X) ),
body( constraint("foobar") )
guard(expression({ x -> x.isBound }, X)),
body(constraint("foobar"))
)
).controller().evaluate(occurrence("main")).run {
assertEquals(setOf( ConstraintSymbol("foo", 1),
ConstraintSymbol("bar", 1),
ConstraintSymbol("foobar", 0)),
constraintSymbols())
assertEquals(setOf(ConstraintSymbol("foo", 1),
ConstraintSymbol("bar", 1),
ConstraintSymbol("foobar", 0)),
constraintSymbols())
assertEquals(1, occurrences(ConstraintSymbol("foo", 1)).count())
assertEquals(1, occurrences(ConstraintSymbol("bar", 1)).count())
assertEquals(1, occurrences(ConstraintSymbol("foobar", 0)).count())
@ -629,37 +629,37 @@ class TestController {
@Test
fun removeObserver() {
val (X1,Y1,Z1) = metaLogical<Int>("X1", "Y1", "Z1")
val (X1, Y1, Z1) = metaLogical<Int>("X1", "Y1", "Z1")
val X2 = metaLogical<Int>("X2")
val (X3,Y3) = metaLogical<Int>("X3", "Y3")
val (X3, Y3) = metaLogical<Int>("X3", "Y3")
val X4 = metaLogical<Int>("X4")
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x, y -> eq(x, y) }, X1, Y1), // rank(X) = 1
statement({ x -> x.set(42) }, X1),
constraint("match", Z1, X1),
constraint("trigger", Z1) )
headReplaced(constraint("main")), body(statement({ x, y -> eq(x, y) }, X1, Y1), // rank(X) = 1
statement({ x -> x.set(42) }, X1),
constraint("match", Z1, X1),
constraint("trigger", Z1))
),
rule("trigger",
headReplaced( constraint("trigger", X2) ),
body( constraint("foobar", X2) )
headReplaced(constraint("trigger", X2)),
body(constraint("foobar", X2))
),
rule("nofoobar",
headReplaced( constraint("foobar", X3),
constraint("match", X3, Y3) ),
body( constraint("expected"),
constraint("blah"),
statement({ x, z -> eq(x, z) }, X3, Y3) )
headReplaced(constraint("foobar", X3),
constraint("match", X3, Y3)),
body(constraint("expected"),
constraint("blah"),
statement({ x, z -> eq(x, z) }, X3, Y3))
),
rule("blah",
headReplaced( constraint("blah") ),
headReplaced( constraint("foobar", X4) ),
body( constraint("unexpected") )
headReplaced(constraint("blah")),
headReplaced(constraint("foobar", X4)),
body(constraint("unexpected"))
)
).controller().evaluate(occurrence("main")).run {
assertEquals(setOf( ConstraintSymbol("blah", 0),
ConstraintSymbol("expected", 0)),
constraintSymbols())
assertEquals(setOf(ConstraintSymbol("blah", 0),
ConstraintSymbol("expected", 0)),
constraintSymbols())
assertEquals(1, occurrences(ConstraintSymbol("blah", 0)).count())
assertEquals(1, occurrences(ConstraintSymbol("expected", 0)).count())
}
@ -667,26 +667,26 @@ class TestController {
@Test
fun reactivateOnUnionKeepValue() {
val (X,Y,Z) = metaLogical<Int>("X", "Y", "Z")
val (X, Y, Z) = metaLogical<Int>("X", "Y", "Z")
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x, y -> eq(x, y) }, X, Y), // rank(X) = 1
statement({ z -> z.set(42) }, Z),
constraint("foo", Z),
statement({ x, z -> eq(x, z) }, X, Z) )
headReplaced(constraint("main")), body(statement({ x, y -> eq(x, y) }, X, Y), // rank(X) = 1
statement({ z -> z.set(42) }, Z),
constraint("foo", Z),
statement({ x, z -> eq(x, z) }, X, Z))
),
rule("capture_foo_free",
headKept( constraint("foo", X) ), guard( expression({ x -> x.getNullable() == null }, X) ),
body( constraint("free") )
headKept(constraint("foo", X)), guard(expression({ x -> x.getNullable() == null }, X)),
body(constraint("free"))
),
rule("capture_foo_assigned",
headKept( constraint("foo", X) ), guard( expression({ x -> x.getNullable() != null }, X) ),
body( constraint("assigned") )
headKept(constraint("foo", X)), guard(expression({ x -> x.getNullable() != null }, X)),
body(constraint("assigned"))
)
).controller().evaluate(occurrence("main")).run {
assertEquals(setOf( ConstraintSymbol("foo", 1),
ConstraintSymbol("assigned", 0)),
constraintSymbols())
assertEquals(setOf(ConstraintSymbol("foo", 1),
ConstraintSymbol("assigned", 0)),
constraintSymbols())
assertEquals(1, occurrences(ConstraintSymbol("foo", 1)).count())
assertEquals(1, occurrences(ConstraintSymbol("assigned", 0)).count())
}
@ -698,14 +698,14 @@ class TestController {
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x -> x.set(7) }, X),
statement({ y -> y.set(7) }, Y),
constraint("aux", X, Y) )
headReplaced(constraint("main")), body(statement({ x -> x.set(7) }, X),
statement({ y -> y.set(7) }, Y),
constraint("aux", X, Y))
),
rule("aux",
headReplaced( constraint("aux", X, Y) ), body( equals(X, Y),
constraint("expected") ),
altBody( constraint("unexpected") )
headReplaced(constraint("aux", X, Y)), body(equals(X, Y),
constraint("expected")),
altBody(constraint("unexpected"))
)
).controller().evaluate(occurrence("main")).run {
assertEquals(setOf(ConstraintSymbol("expected", 0)), constraintSymbols())
@ -719,14 +719,14 @@ class TestController {
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x -> x.set(7) }, X),
statement({ y -> y.set(13) }, Y),
constraint("aux", X, Y) )
headReplaced(constraint("main")), body(statement({ x -> x.set(7) }, X),
statement({ y -> y.set(13) }, Y),
constraint("aux", X, Y))
),
rule("aux",
headReplaced( constraint("aux", X, Y) ), body( equals(X, Y),
constraint("unexpected") ),
altBody( constraint("expected") )
headReplaced(constraint("aux", X, Y)), body(equals(X, Y),
constraint("unexpected")),
altBody(constraint("expected"))
)
).controller().evaluate(occurrence("main")).run {
assertEquals(setOf(ConstraintSymbol("expected", 0)), constraintSymbols())
@ -740,22 +740,21 @@ class TestController {
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x -> x.set(7) }, X),
statement({ y -> y.set(13) }, Y),
statement({ z -> z.set(17) }, Z),
constraint("aux", X, Y, Z) )
headReplaced(constraint("main")), body(statement({ x -> x.set(7) }, X),
statement({ y -> y.set(13) }, Y),
statement({ z -> z.set(17) }, Z),
constraint("aux", X, Y, Z))
),
rule("aux",
headReplaced( constraint("aux", X, Y, Z) ), body( equals(X, Y),
constraint("unexpected1") ),
altBody( equals(X, Z),
constraint("unexpected2") )
headReplaced(constraint("aux", X, Y, Z)), body(equals(X, Y),
constraint("unexpected1")),
altBody(equals(X, Z),
constraint("unexpected2"))
)
).controller().run {
try {
evaluate(occurrence("main"))
}
finally {
} finally {
assertEquals(emptySet<ConstraintSymbol>(), storeView().constraintSymbols())
}
}
@ -783,7 +782,7 @@ class TestController {
constraint("foo", term("bar", metaVar(X)))
),
guard(
expression ({ x, y -> x.findRoot() == y.findRoot() }, X, Y)
expression({ x, y -> x.findRoot() == y.findRoot() }, X, Y)
),
body(
constraint("triggered1", X, Y)
@ -795,15 +794,14 @@ class TestController {
constraint("foo", X)
),
guard(
expression ({ x, y -> !y.isBound && x.findRoot() == y.findRoot() }, X, Y)
expression({ x, y -> !y.isBound && x.findRoot() == y.findRoot() }, X, Y)
),
body(
constraint("triggered2", X, Y)
)
)
).controller().run {
evaluate(occurrence("main"))
storeView().run {
evaluate(occurrence("main")).run {
val foo = ConstraintSymbol("foo", 1)
val t1 = ConstraintSymbol("triggered1", 2)
constraintSymbols() shouldBe setOf(t1, foo)
@ -817,18 +815,20 @@ class TestController {
@Test(expected = EvaluationFailureException::class)
fun failureHandler() {
val failureHandler = object : FailureHandler {
val failureHandler = object : EvaluationFeedbackHandler {
val failures = ArrayList<Pair<EvaluationFailure, String>>()
override fun handleFailure(failure: EvaluationFailure, rule: Rule): EvaluationFailure? {
failures.add(failure to rule.tag())
return failure
override fun handleFeedback(rule: Rule, feedback: EvaluationFeedback): Boolean {
if (feedback is EvaluationFailure) {
failures.add(feedback to rule.tag())
}
return false
}
}
programWithRules(
rule("main",
headReplaced(constraint("main")),
body (
body(
constraint("foo")
)
),
@ -859,19 +859,21 @@ class TestController {
@Test
fun failureHandlerRecover() {
val failureHandler = object : FailureHandler {
val failureHandler = object : EvaluationFeedbackHandler {
val failures = ArrayList<Pair<EvaluationFailure, String>>()
override fun handleFailure(failure: EvaluationFailure, rule: Rule): EvaluationFailure? {
failures.add(failure to rule.tag())
if (rule.tag()?.startsWith("recoverable") == true) return null
return failure
override fun handleFeedback(rule: Rule, feedback: EvaluationFeedback): Boolean {
if (feedback is EvaluationFailure) {
failures.add(feedback to rule.tag())
return (rule.tag()?.startsWith("recoverable") == true)
}
return false
}
}
programWithRules(
rule("main",
headReplaced(constraint("main")),
body (
body(
constraint("foo")
)
),
@ -889,7 +891,7 @@ class TestController {
constraint("recovered", 2)
)
),
rule ("recoverable",
rule("recoverable",
headReplaced(constraint("bar")),
body(
constraint("bazz")
@ -902,14 +904,13 @@ class TestController {
)
)
).controllerWithFeedback(failureHandler).run {
evaluate(occurrence("main"))
storeView().run {
evaluate(occurrence("main")).run {
val recovered = ConstraintSymbol("recovered", 1)
constraintSymbols() shouldBe setOf(recovered)
occurrences(recovered).map { it.arguments()[0] }.toSet() shouldBe setOf(1, 2)
}
}
failureHandler.failures.map { (f, t) -> "${f.cause.message}@$t"}.toList() shouldBe
failureHandler.failures.map { (f, t) -> "${f.cause.message}@$t" }.toList() shouldBe
listOf("handled@rule3", "handled@recoverable")
}
@ -926,7 +927,7 @@ class TestController {
programWithRules(
rule("main",
headReplaced(constraint("main")),
body (
body(
constraint("foo")
)
),
@ -949,8 +950,8 @@ class TestController {
}
feedbackHandler.feedbacks.map { (f, t) -> "${f.message}@$t"}.toList() shouldBe
feedbackHandler.feedbacks.map { (f, t) -> "${f.message}@$t" }.toList() shouldBe
listOf("catchme@rule1", "propagateme@rule2", "propagateme@rule1", "propagateme@main")
}
}
}

View File

@ -1,10 +1,7 @@
import jetbrains.mps.logic.reactor.core.LogicalImpl
import jetbrains.mps.logic.reactor.core.LogicalObserver
import jetbrains.mps.logic.reactor.core.addObserver
import jetbrains.mps.logic.reactor.logical.JoinableLogical
import jetbrains.mps.logic.reactor.logical.Logical
import org.junit.Test
import kotlin.math.log
/*
* Copyright 2014-2019 JetBrains s.r.o.
@ -28,9 +25,7 @@ import kotlin.math.log
class TestLogical {
inline fun <reified T> logical(name: String): JoinableLogical<T> = LogicalImpl<T>(name)
class Observer(logical: Logical<*>): LogicalObserver {
class Observer(logical: Logical<*>) : LogicalObserver {
var parentUpdated: Pair<Logical<*>, Logical<*>>? = null
var valueUpdated: Pair<Logical<*>, Any>? = null
@ -52,7 +47,7 @@ class TestLogical {
}
@Test
fun union_notifies_observers () {
fun union_notifies_observers() {
val xLogical = logical<String>("X")
val yLogical = logical<String>("Y")
val zLogical = logical<String>("Z")
@ -85,7 +80,7 @@ class TestLogical {
xLogical.union(yLogical)
observerY.parentUpdated shouldBe (yLogical to xLogical)
observerX.parentUpdated shouldBe (null)
yLogical.findRoot() shouldBeSame xLogical
yLogical.findRoot() shouldBeSame xLogical
yLogical.findRoot().setValue("foobar")
observerX.valueUpdated shouldBe (xLogical to "foobar")

View File

@ -1,10 +1,7 @@
import jetbrains.mps.logic.reactor.core.LogicalObserver
import jetbrains.mps.logic.reactor.core.LogicalImpl
import jetbrains.mps.logic.reactor.core.addObserver
import jetbrains.mps.logic.reactor.logical.Logical
import org.junit.Assert.assertEquals
import org.junit.Assert.assertSame
import org.junit.Test
import org.junit.Assert.*
import java.util.*
/**
* @author Fedor Isakov

View File

@ -1,15 +1,14 @@
import jetbrains.mps.logic.reactor.core.EvaluationSessionImpl
import jetbrains.mps.logic.reactor.evaluation.AbstractSolver
import jetbrains.mps.logic.reactor.core.ReactorLifecycle
import jetbrains.mps.logic.reactor.evaluation.EvaluationSession
import jetbrains.mps.logic.reactor.evaluation.SessionSolver
import jetbrains.mps.logic.reactor.evaluation.StoreView
import jetbrains.mps.logic.reactor.logical.Logical
import solver.MockSessionSolver
import jetbrains.mps.logic.reactor.program.ConstraintSymbol
import jetbrains.mps.logic.reactor.program.PredicateSymbol
import org.junit.*
import org.junit.Assert.*
import org.junit.AfterClass
import org.junit.Assert.assertEquals
import org.junit.BeforeClass
import org.junit.Test
import program.MockConstraint
import solver.EqualsSolver
import solver.eq
/**
@ -19,22 +18,25 @@ import solver.eq
class TestProgram {
companion object {
@BeforeClass @JvmStatic fun setup() {
EvaluationSessionImpl.init();
@BeforeClass
@JvmStatic
fun setup() {
ReactorLifecycle.init();
}
@AfterClass @JvmStatic fun teardown() {
EvaluationSessionImpl.deinit();
@AfterClass
@JvmStatic
fun teardown() {
ReactorLifecycle.deinit();
}
}
private fun Builder.session(name: String): StoreView {
val sessionSolver = MockSessionSolver()
val programBuilder = ProgramBuilder(MockConstraintRegistry(sessionSolver))
val programBuilder = ProgramBuilder(MockConstraintRegistry())
for (h in handlers) {
programBuilder.addHandler(h)
}
val session = EvaluationSession.newSession(programBuilder.program(name)).
withParam("main", MockConstraint(ConstraintSymbol("main", 0))).start(sessionSolver)
val session = EvaluationSession.newSession(programBuilder.program(name)).withParam("main", MockConstraint(ConstraintSymbol("main", 0))).start()
return session.storeView()
}
@ -79,7 +81,7 @@ class TestProgram {
constraint("foo", X)
),
body(
statement({x, y -> y eq (x.get() * 2) }, X, Y),
statement({ x, y -> y eq (x.get() * 2) }, X, Y),
constraint("bar", Y)
)
)
@ -97,14 +99,14 @@ class TestProgram {
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ x -> x.set(5) }, X),
constraint("val", X) )
headReplaced(constraint("main")), body(statement({ x -> x.set(5) }, X),
constraint("val", X))
),
rule("dec",
headReplaced( constraint("val", X) ), guard( expression({ x -> x.get() > 0 }, X) ),
body( constraint("trail", X),
statement({ x, y -> y.set(x.get() - 1)}, X, Y),
constraint("val", Y) )
headReplaced(constraint("val", X)), guard(expression({ x -> x.get() > 0 }, X)),
body(constraint("trail", X),
statement({ x, y -> y.set(x.get() - 1) }, X, Y),
constraint("val", Y))
)
).session("dec").run {
assertEquals(setOf(ConstraintSymbol("val", 1), ConstraintSymbol("trail", 1)), constraintSymbols())
@ -120,20 +122,20 @@ class TestProgram {
val (M, N, TMP) = metaLogical<Int>("M", "N", "TMP")
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ m, n -> m.set(21); n.set(35) }, M, N),
constraint("gcd", M),
constraint("gcd", N) )
headReplaced(constraint("main")), body(statement({ m, n -> m.set(21); n.set(35) }, M, N),
constraint("gcd", M),
constraint("gcd", N))
),
rule("trivial",
headReplaced( constraint("gcd", M) ), guard( expression({ x -> x.get() == 0 }, M) ),
body( statement { } /*nothing*/ )
headReplaced(constraint("gcd", M)), guard(expression({ x -> x.get() == 0 }, M)),
body(statement { } /*nothing*/)
),
rule("step",
headKept( constraint("gcd", N) ),
headReplaced( constraint("gcd", M) ),
guard( expression({ m, n -> m.get() >= n.get()}, M, N) ),
body( statement({ m, n, tmp -> tmp.set(m.get() - n.get())}, M, N, TMP),
constraint("gcd", TMP)
headKept(constraint("gcd", N)),
headReplaced(constraint("gcd", M)),
guard(expression({ m, n -> m.get() >= n.get() }, M, N)),
body(statement({ m, n, tmp -> tmp.set(m.get() - n.get()) }, M, N, TMP),
constraint("gcd", TMP)
)
)
).session("gcd").run {
@ -149,26 +151,23 @@ class TestProgram {
val (M, N) = metaLogical<Int>("M", "N")
programWithRules(
rule("main",
headReplaced( constraint("main") ), body( statement({ n -> n.set(10) }, N),
constraint("prime", N) )
headReplaced(constraint("main")), body(statement({ n -> n.set(10) }, N),
constraint("prime", N))
),
rule("gen",
headKept( constraint("prime", N)), guard( expression({ n -> n.get() > 2 }, N)),
body( statement({ m, n -> m.set(n.get() - 1) }, M, N),
constraint("prime", M) )
headKept(constraint("prime", N)), guard(expression({ n -> n.get() > 2 }, N)),
body(statement({ m, n -> m.set(n.get() - 1) }, M, N),
constraint("prime", M))
),
rule("sift",
headKept( constraint("prime", M)),
headReplaced( constraint("prime", N)),
guard( expression({ m, n -> (n.get() % m.get()) == 0 }, M, N)),
body( statement { } /* nothing */ )
headKept(constraint("prime", M)),
headReplaced(constraint("prime", N)),
guard(expression({ m, n -> (n.get() % m.get()) == 0 }, M, N)),
body(statement { } /* nothing */)
)
).session("primes").run {
assertEquals(4, allOccurrences().count())
assertEquals(setOf(2,3,5,7), occurrences(ConstraintSymbol.symbol("prime", 1)).
flatMap { co -> co.arguments() }.
map { a -> (a as Logical<Int>).findRoot().value() }.
toSet())
assertEquals(setOf(2, 3, 5, 7), occurrences(ConstraintSymbol.symbol("prime", 1)).flatMap { co -> co.arguments() }.map { a -> (a as Logical<Int>).findRoot().value() }.toSet())
}
}

View File

@ -1,11 +1,8 @@
import jetbrains.mps.logic.reactor.evaluation.AbstractSolver
import jetbrains.mps.logic.reactor.evaluation.PredicateInvocation
import jetbrains.mps.logic.reactor.program.InvalidConstraintException
import jetbrains.mps.logic.reactor.program.InvalidRuleException
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import solver.MockSessionSolver
/**
* @author Fedor Isakov
@ -13,10 +10,8 @@ import solver.MockSessionSolver
class TestProgramBuilder {
val sessionSolver = MockSessionSolver()
@Before fun beforeTest() {
programBuilder = ProgramBuilder(MockConstraintRegistry(sessionSolver))
programBuilder = ProgramBuilder(MockConstraintRegistry())
}
lateinit var programBuilder: ProgramBuilder

View File

@ -1,10 +1,11 @@
import jetbrains.mps.logic.reactor.core.*
import jetbrains.mps.logic.reactor.core.internal.createOccurrenceMatcher
import jetbrains.mps.logic.reactor.core.internal.logical
import jetbrains.mps.logic.reactor.program.ConstraintSymbol
import jetbrains.mps.logic.reactor.program.ConstraintSymbol.symbol
import jetbrains.mps.unification.Term
import jetbrains.mps.unification.test.MockTerm.*
import jetbrains.mps.unification.test.MockTermsParser.*
import org.junit.Assert.assertEquals
import org.junit.Ignore
import org.junit.Test
import program.MockConstraint
@ -42,25 +43,25 @@ class TestRuleMatcher {
val foox = MockConstraint(ConstraintSymbol("foo", 1), X)
// foo("bar") = foo("bar")
OccurrenceMatcher().
createOccurrenceMatcher().
matches(foobar, occurrence("foo", "bar")) shouldBe true
// foo("bar") != foo(X)
OccurrenceMatcher().
createOccurrenceMatcher().
matches(foobar, occurrence("foo", xLogical)) shouldBe false
// foo("bar") = foo(X = "bar")
xLogical.set("bar")
OccurrenceMatcher().
createOccurrenceMatcher().
matches(foobar, occurrence("foo", xLogical)) shouldBe true
// foo(X) = foo(Y)
OccurrenceMatcher().
createOccurrenceMatcher().
matches(foox, occurrence("foo", yLogical)) shouldBe true
// foo(X) = foo("bar")
OccurrenceMatcher().
createOccurrenceMatcher().
matches(foox, occurrence("foo", "bar")) shouldBe true
// foo(X) = foo(Y = "bar")
yLogical.set("bar")
OccurrenceMatcher().
createOccurrenceMatcher().
matches(foox, occurrence("foo", yLogical)) shouldBe true
}
@ -74,25 +75,25 @@ class TestRuleMatcher {
val foofx = MockConstraint(ConstraintSymbol("foo", 1), term("f", metaVar(X)))
// foo(f { h }) = foo(f { h })
OccurrenceMatcher().
createOccurrenceMatcher().
matches(foofh, occurrence("foo", parseTerm("f { h }"))) shouldBe true
// foo(f { h }) != foo(f { X })
OccurrenceMatcher().
createOccurrenceMatcher().
matches(foofh, occurrence("foo", term("f", logicalVar(xLogical)))) shouldBe false
// foo(f { h }) != foo(f { X = h })
xLogical.set(term("h"))
OccurrenceMatcher().
createOccurrenceMatcher().
matches(foofh, occurrence("foo", term("f", logicalVar(xLogical)))) shouldBe true
// foo(f { X }) = foo(f { h })
OccurrenceMatcher().
createOccurrenceMatcher().
matches(foofx, occurrence("foo", parseTerm("f { h }"))) shouldBe true
// foo(f { X }) = foo(f { Y })
OccurrenceMatcher().
createOccurrenceMatcher().
matches(foofx, occurrence("foo", term("f", logicalVar(yLogical)))) shouldBe true
// foo(f { X }) = foo(f { Y = h })
xLogical.set(term("h"))
OccurrenceMatcher().
createOccurrenceMatcher().
matches(foofx, occurrence("foo", term("f", logicalVar(yLogical)))) shouldBe true
}
@ -106,28 +107,28 @@ class TestRuleMatcher {
val foofx = MockConstraint(ConstraintSymbol("foo", 1), term("f", metaVar(X)))
// [X -> free] |- foo(f { X }) = foo(f { X })
OccurrenceMatcher(arrayOf(X to logicalVar(xLogical)).toMap()).
createOccurrenceMatcher(arrayOf(X to logicalVar(xLogical)).toMap()).
matches(foofx, occurrence("foo", term("f", logicalVar(xLogical)))) shouldBe true
// [X -> free] |- foo(f { X }) != foo(f { Y })
OccurrenceMatcher(arrayOf(X to logicalVar(xLogical)).toMap()).
createOccurrenceMatcher(arrayOf(X to logicalVar(xLogical)).toMap()).
matches(foofx, occurrence("foo", term("f", logicalVar(yLogical)))) shouldBe false
// [X -> free] |- foo(f { X }) != foo(f { h })
OccurrenceMatcher(arrayOf(X to logicalVar(xLogical)).toMap()).
createOccurrenceMatcher(arrayOf(X to logicalVar(xLogical)).toMap()).
matches(foofx, occurrence("foo", parseTerm("f { h }"))) shouldBe false
// [X -> free] |- foo(f { X }) != foo(f { Y = h })
yLogical.set(term("h"))
OccurrenceMatcher(arrayOf(X to logicalVar(xLogical)).toMap()).
createOccurrenceMatcher(arrayOf(X to logicalVar(xLogical)).toMap()).
matches(foofx, occurrence("foo", term("f", logicalVar(yLogical)))) shouldBe false
// [X -> h] |- foo(f { X }) = foo(f { h })
OccurrenceMatcher(arrayOf(X to term("h")).toMap()).
createOccurrenceMatcher(arrayOf(X to term("h")).toMap()).
matches(foofx, occurrence("foo", parseTerm("f { h }"))) shouldBe true
// [X -> h] |- foo(f { X }) != foo(f { Z })
OccurrenceMatcher(arrayOf(X to term("h")).toMap()).
createOccurrenceMatcher(arrayOf(X to term("h")).toMap()).
matches(foofx, occurrence("foo", term("f", logicalVar(zLogical)))) shouldBe false
// [X -> h] |- foo(f { X }) = foo(f { Z = h })
zLogical.set(term("h"))
OccurrenceMatcher(arrayOf(X to term("h")).toMap()).
createOccurrenceMatcher(arrayOf(X to term("h")).toMap()).
matches(foofx, occurrence("foo", term("f", logicalVar(zLogical)))) shouldBe true
}
@ -784,5 +785,5 @@ class TestRuleMatcher {
}
}
private fun Builder.ruleMatcher() = Matcher(rules.first())
private fun Builder.ruleMatcher() = createRuleMatcher(rules.first())
}

View File

@ -16,7 +16,9 @@
package jetbrains.mps.unification.test;
import jetbrains.mps.logic.reactor.core.LogicalImpl;
import jetbrains.mps.logic.reactor.core.LogicalObserverKt;
import jetbrains.mps.logic.reactor.core.internal.LogicalImplKt;
import jetbrains.mps.logic.reactor.logical.JoinableLogical;
import jetbrains.mps.logic.reactor.logical.MetaLogical;
import jetbrains.mps.unification.Substitution;
import jetbrains.mps.unification.Term;
@ -27,10 +29,11 @@ import org.junit.Test;
import java.util.Collection;
import java.util.Collections;
import static jetbrains.mps.unification.Substitution.FailureCause.*;
import static jetbrains.mps.unification.Substitution.FailureCause.CYCLE_DETECTED;
import static jetbrains.mps.unification.Substitution.FailureCause.SYMBOL_CLASH;
import static jetbrains.mps.unification.test.AssertUnification.*;
import static jetbrains.mps.unification.test.MockTerm.*;
import static jetbrains.mps.unification.test.MockTermsParser.*;
import static jetbrains.mps.unification.test.MockTermsParser.parseTerm;
/**
* Created by fyodor on 09.06.2014.
@ -408,12 +411,32 @@ public class SolverTests {
class Wrapper implements Term {
Term wrapped;
Wrapper(Term term) { this.wrapped = term; }
@Override public Object symbol() { return wrapped; }
@Override public Collection<? extends Term> arguments() { return Collections.emptyList(); }
@Override public Term get() { return this; }
@Override public boolean is(Kind kind) { return Kind.FUN == kind; }
@Override public int compareTo(@NotNull Term other) {
Wrapper(Term term) {
this.wrapped = term;
}
@Override
public Object symbol() {
return wrapped;
}
@Override
public Collection<? extends Term> arguments() {
return Collections.emptyList();
}
@Override
public Term get() {
return this;
}
@Override
public boolean is(Kind kind) {
return Kind.FUN == kind;
}
@Override
public int compareTo(@NotNull Term other) {
return String.valueOf(symbol()).compareTo(String.valueOf(other.symbol()));
}
@ -427,7 +450,7 @@ public class SolverTests {
@Override
public Term unwrap(Term maybeWrapper) {
return maybeWrapper instanceof Wrapper ? ((Wrapper)maybeWrapper).wrapped : maybeWrapper;
return maybeWrapper instanceof Wrapper ? ((Wrapper) maybeWrapper).wrapped : maybeWrapper;
}
};
@ -551,13 +574,13 @@ public class SolverTests {
MetaLogical<Term> X = new MetaLogical<>("X", Term.class);
MetaLogical<Term> Y = new MetaLogical<>("Y", Term.class);
MetaLogical<Term> Z = new MetaLogical<>("Z", Term.class);
LogicalImpl<Term> xLogical = new LogicalImpl<>(X);
LogicalImpl<Term> yLogical = new LogicalImpl<>(Y);
LogicalImpl<Term> zLogical = new LogicalImpl<>(Z);
JoinableLogical<Term> xLogical = LogicalImplKt.logical(X);
JoinableLogical<Term> yLogical = LogicalImplKt.logical(Y);
JoinableLogical<Term> zLogical = LogicalImplKt.logical(Z);
Term left = term("foo", term("bar", logicalVar(yLogical)), logicalVar(zLogical));
Term right = term("foo", term("bar", logicalVar(xLogical)), logicalVar(zLogical));
assertUnifiesWithBindings(left, right,
new Substitution.Binding(logicalVar(xLogical), logicalVar(yLogical)));
@ -571,9 +594,9 @@ public class SolverTests {
MetaLogical<Term> X = new MetaLogical<>("X", Term.class);
MetaLogical<Term> Y = new MetaLogical<>("Y", Term.class);
MetaLogical<Term> Z = new MetaLogical<>("Z", Term.class);
LogicalImpl<Term> xLogical = new LogicalImpl<>(X);
LogicalImpl<Term> yLogical = new LogicalImpl<>(Y);
LogicalImpl<Term> zLogical = new LogicalImpl<>(Z);
JoinableLogical<Term> xLogical = LogicalImplKt.logical(X);
JoinableLogical<Term> yLogical = LogicalImplKt.logical(Y);
JoinableLogical<Term> zLogical = LogicalImplKt.logical(Z);
Term left = logicalVar(yLogical);
Term right = term("foo", term("bar", logicalVar(xLogical)), logicalVar(zLogical));