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:
parent
19a096b12f
commit
a9a7f66a3a
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,4 +23,7 @@ public interface InvocationContext {
|
|||
|
||||
void report(EvaluationFeedback feedback);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 ->
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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) {
|
|||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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())
|
||||
|
|
@ -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() }
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()})"
|
||||
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
Loading…
Reference in New Issue