diff --git a/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/EvaluationSession.java b/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/EvaluationSession.java
index e846cdc7..09fbb3b7 100644
--- a/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/EvaluationSession.java
+++ b/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/EvaluationSession.java
@@ -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 current(Class 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 {
- 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();
+
}
}
diff --git a/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/InvocationContext.java b/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/InvocationContext.java
index 18569c2f..9649c306 100644
--- a/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/InvocationContext.java
+++ b/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/InvocationContext.java
@@ -23,4 +23,7 @@ public interface InvocationContext {
void report(EvaluationFeedback feedback);
+
+
+
}
diff --git a/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/MatchRule.java b/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/MatchRule.java
index 0d94ac37..e60bdc65 100644
--- a/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/MatchRule.java
+++ b/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/MatchRule.java
@@ -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
*/
diff --git a/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/SessionSolver.java b/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/SessionSolver.java
index 78b3e9be..454c6a1d 100644
--- a/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/SessionSolver.java
+++ b/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/SessionSolver.java
@@ -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) {
- }
-
}
diff --git a/reactor/API/src/jetbrains/mps/logic/reactor/program/Handler.java b/reactor/API/src/jetbrains/mps/logic/reactor/program/Handler.java
index a7997b19..21073763 100644
--- a/reactor/API/src/jetbrains/mps/logic/reactor/program/Handler.java
+++ b/reactor/API/src/jetbrains/mps/logic/reactor/program/Handler.java
@@ -19,20 +19,12 @@ package jetbrains.mps.logic.reactor.program;
/**
* A handler is a container of rules.
*
- * 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 primarySymbols() {
- throw new UnsupportedOperationException();
- }
-
public abstract Iterable rules();
}
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt
index 33ef4df2..ff295eb7 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt
@@ -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("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("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("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 variable(metaLogical: MetaLogical): Logical = 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() }
}
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Dispatcher.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Dispatcher.kt
index 1f562389..9135cf2b 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Dispatcher.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Dispatcher.kt
@@ -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()
@@ -43,7 +43,7 @@ class Dispatcher (val ruleIndex: RuleIndex) {
private var rule2probe: PersMap
- private val allMatches = arrayListOf()
+ private val allMatches = arrayListOf()
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)
}
}
@@ -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 ->
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/EvaluationSessionEx.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/EvaluationSessionEx.kt
new file mode 100644
index 00000000..34f5d86b
--- /dev/null
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/EvaluationSessionEx.kt
@@ -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)
+ }
+
+}
\ No newline at end of file
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/FrameObservable.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/FrameObservable.kt
new file mode 100644
index 00000000..2da15e38
--- /dev/null
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/FrameObservable.kt
@@ -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)
+
+}
\ No newline at end of file
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Invocation.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Invocation.kt
index ebd35da4..ae469464 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Invocation.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Invocation.kt
@@ -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)
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/LogicalObserver.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/LogicalObserver.kt
new file mode 100644
index 00000000..2017a7a5
--- /dev/null
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/LogicalObserver.kt
@@ -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 }
+}
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/MatchingProbe.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/MatchingProbe.kt
index 67cb7783..c6de0d65 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/MatchingProbe.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/MatchingProbe.kt
@@ -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
+ fun rule(): Rule
+
+ fun matches(): Collection
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
}
\ No newline at end of file
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt
index b1796259..815d8857 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Occurrence.kt
@@ -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 variable(metaLogical: MetaLogical): Logical? = null
+}
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/OccurrenceMatcher.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/OccurrenceMatcher.kt
index f0413bb5..dad4089d 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/OccurrenceMatcher.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/OccurrenceMatcher.kt
@@ -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, Any>
-
-fun emptySubst() = HashMap, Any>(4)
-
-class OccurrenceMatcher(val contextSubst: Subst? = null) {
-
- companion object {
- val EMPTY_SUBST : Subst = Collections.emptyMap()
- }
-
- private var matchSubst : MutableMap, 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 zipWhileTrue(first: Iterable, second: Iterable, 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
- }
-
-}
\ No newline at end of file
diff --git a/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/AbstractSolver.java b/reactor/Core/src/jetbrains/mps/logic/reactor/core/ReactorLifecycle.kt
similarity index 56%
rename from reactor/API/src/jetbrains/mps/logic/reactor/evaluation/AbstractSolver.java
rename to reactor/Core/src/jetbrains/mps/logic/reactor/core/ReactorLifecycle.kt
index f46f6aaf..e2929399 100644
--- a/reactor/API/src/jetbrains/mps/logic/reactor/evaluation/AbstractSolver.java
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/ReactorLifecycle.kt
@@ -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()
+ }
+ }
+}
\ No newline at end of file
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleIndex.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleIndex.kt
index d7d6e8f5..e596213e 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleIndex.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleIndex.kt
@@ -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) : Iterable {
private val symbol2index = HashMap()
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleMatcher.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleMatcher.kt
index 3abeef91..e39a1bfc 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleMatcher.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleMatcher.kt
@@ -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
-
- val propagation = rule.headReplaced().count() == 0
-
- fun probe(): MatchingProbe = RuleMatchFringe(listOf(MatchNode(emptySubst())),
- Sets.of(),
- Sets.of(),
- 0)
-
- inner class RuleMatchFringe(val nodes: List,
- val seen: PersSet>,
- val consumed: PersSet?>>,
- val genId: Int) : MatchingProbe
- {
- override fun rule(): Rule = rule
-
- override fun matches(): Collection {
- 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(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?> =
- 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: ConstraintOccurrence, genId: Int, matchingVacant: BitSet): List =
- unrelatedOrNull(occ)?.let { n ->
- ArrayList().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 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 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?> =
- (parent.signature.clone() as ArrayList?>)
- .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 =
- 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
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt
new file mode 100644
index 00000000..d692d1a8
--- /dev/null
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ControllerImpl.kt
@@ -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("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("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("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 variable(metaLogical: MetaLogical): Logical? = 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)
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/EvaluationSessionImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/EvaluationSessionImpl.kt
similarity index 59%
rename from reactor/Core/src/jetbrains/mps/logic/reactor/core/EvaluationSessionImpl.kt
rename to reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/EvaluationSessionImpl.kt
index 99e32219..87109321 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/EvaluationSessionImpl.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/EvaluationSessionImpl.kt
@@ -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()
val parameters = HashMap()
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(arrayOfNulls(predicateSymbols.size)))
+ sessionSolver?.init(evaluationTrace)
@Suppress("UNCHECKED_CAST")
val durations =
parameters.get("profiling.data") as MutableMap?
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 {
val ourSession = ThreadLocal()
- 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)
- }
}
-}
\ No newline at end of file
+
+}
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Frame.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/Frame.kt
similarity index 86%
rename from reactor/Core/src/jetbrains/mps/logic/reactor/core/Frame.kt
rename to reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/Frame.kt
index 28d71442..8e9c2fee 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Frame.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/Frame.kt
@@ -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>, ConsList<(StoreKeeper) -> LogicalObserver>>
+ private var observers: Map>, 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)
}
-}
\ No newline at end of file
+}
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Logical.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/LogicalImpl.kt
similarity index 85%
rename from reactor/Core/src/jetbrains/mps/logic/reactor/core/Logical.kt
rename to reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/LogicalImpl.kt
index 3eceec55..5385e96b 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Logical.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/LogicalImpl.kt
@@ -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 MetaLogical.logical(): Logical = LogicalImpl(this)
-
-fun MetaLogical.logical(value: V): Logical = LogicalImpl(name(), value)
-
-class LogicalImpl : JoinableLogical {
+internal class LogicalImpl : JoinableLogical {
companion object {
var lastIdx = 0
@@ -225,4 +199,15 @@ class LogicalImpl : JoinableLogical {
}
-class DefaultMetaLogical (val name: String) : MetaLogical(name, Object::class.java as Class) {}
\ No newline at end of file
+class DefaultMetaLogical (val name: String) : MetaLogical(name, Object::class.java as Class) {}
+
+// Used from tests
+
+fun anonLogical(value: V): JoinableLogical = LogicalImpl(value)
+
+fun namedLogical(name: String): JoinableLogical = LogicalImpl(name)
+
+fun MetaLogical.logical(): JoinableLogical = LogicalImpl(this)
+
+fun MetaLogical.logical(value: V): JoinableLogical = LogicalImpl(name(), value)
+
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/MatchRuleImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchRuleImpl.kt
similarity index 73%
rename from reactor/Core/src/jetbrains/mps/logic/reactor/core/MatchRuleImpl.kt
rename to reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchRuleImpl.kt
index 6aeb02a4..9e1ef134 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/MatchRuleImpl.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/MatchRuleImpl.kt
@@ -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,
- val headReplaced: MutableIterable) : MatchRule {
+internal class MatchRuleImpl(val rule: Rule,
+ val subst: Subst,
+ val headKept: Iterable,
+ val headReplaced: Iterable) : MatchRule {
private val logicalContext = object : LogicalContext {
@@ -47,10 +48,10 @@ class MatchRuleImpl(val origin: Any,
override fun rule(): Rule = rule
- override fun matchHeadKept(): MutableIterable = headKept
-
- override fun matchHeadReplaced(): MutableIterable = headReplaced
+ override fun matchHeadKept(): Iterable = headKept
+ override fun matchHeadReplaced(): Iterable = headReplaced
+
override fun logicalContext(): LogicalContext = logicalContext
}
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/OccurrenceMatcherImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/OccurrenceMatcherImpl.kt
new file mode 100644
index 00000000..24c677fe
--- /dev/null
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/OccurrenceMatcherImpl.kt
@@ -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, 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 zipWhileTrue(first: Iterable, second: Iterable, 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, Any>(4)
+
+fun createOccurrenceMatcher(contextSubst: Subst? = null): OccurrenceMatcher =
+ OccurrenceMatcherImpl(contextSubst)
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/ProcessingState.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingState.kt
similarity index 97%
rename from reactor/Core/src/jetbrains/mps/logic/reactor/core/ProcessingState.kt
rename to reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingState.kt
index 3b678384..8ffd1f40 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/ProcessingState.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ProcessingState.kt
@@ -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
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/ReteNetwork.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ReteRuleMatcherImpl.kt
similarity index 84%
rename from reactor/Core/src/jetbrains/mps/logic/reactor/core/ReteNetwork.kt
rename to reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ReteRuleMatcherImpl.kt
index e4362341..ae8e7ed6 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/ReteNetwork.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/ReteRuleMatcherImpl.kt
@@ -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, allSubst: MutableMap, Any>) {}
+ open fun collectData(occArray: Array, allSubst: MutableMap, 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()
@@ -139,7 +144,7 @@ class ReteRuleMatcher(val rule: Rule) {
override fun combine(that: AlphaNode): ReteNode = BetaNode(this, that)
- override fun collectData(occArray: Array, allSubst: MutableMap, Any>) {
+ override fun collectData(occArray: Array, allSubst: MutableMap, 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, allSubst: MutableMap, Any>) {
+ override fun collectData(occArray: Array, allSubst: MutableMap, 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 = nodesList.subList(startIdx, nodesList.size)
fun allNodes() : Iterable = nodesList
@@ -234,7 +239,7 @@ class ReteRuleMatcher(val rule: Rule) {
init {
assert(layers.isNotEmpty())
}
-
+
fun introduce(occIdx: Int, alphaNodes: Collection): Generation {
// propagation history
val initLayer = layers.last()
@@ -280,27 +285,26 @@ class ReteRuleMatcher(val rule: Rule) {
}
- fun matches(): Collection {
+ fun matches(): Collection {
val topLayer = layers.first()
if (topLayer.final) {
- val matches = ArrayList()
+ val matches = ArrayList()
for (n in topLayer.nodes()) {
// any excluded occurrences?
if (n.containsOccurrence(skipOccIndices)) continue
val allSubst = HashMap, Any>()
- val occArray = arrayOfNulls(headSize)
+ val occArray = arrayOfNulls(headSize)
n.collectData(occArray, allSubst)
-
- val occList = occArray.toMutableList()
+
+ val occList = occArray.toList() as List
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()
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 = generations.last().matches()
+ override fun matches(): Collection = 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) {
}
-}
-
-
-
-
-
-
-
+}
\ No newline at end of file
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/RuleMatcherImpl.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/RuleMatcherImpl.kt
new file mode 100644
index 00000000..f4b7ddf0
--- /dev/null
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/RuleMatcherImpl.kt
@@ -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()
+
+
+ override fun probe(): MatchingProbe = RuleMatchFringe(listOf(MatchNode(emptySubst())),
+ Sets.of(),
+ Sets.of(),
+ 0)
+
+ inner class RuleMatchFringe(val nodes: List,
+ val seen: PersSet>,
+ val consumed: PersSet?>>,
+ val genId: Int) : MatchingProbe {
+ override fun rule(): Rule = rule
+
+ override fun matches(): Collection {
+ 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(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?> =
+ 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 =
+ unrelatedOrNull(occ)?.let { n ->
+ ArrayList().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 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 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?> =
+ 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 =
+ fold(arrayOfNulls(head.size)) { arr, rn ->
+ arr[rn.headIndex] = rn.occurrence; arr
+ } as Array
+
+ val mr = MatchRuleImpl(rule,
+ subst,
+ ArrayList(matched.take(rule.headKept().count())),
+ ArrayList(matched.takeLast(rule.headReplaced().count())))
+ origins.put(mr, this)
+ return mr
+ }
+ }
+
+
+}
diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Store.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/Store.kt
similarity index 71%
rename from reactor/Core/src/jetbrains/mps/logic/reactor/core/Store.kt
rename to reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/Store.kt
index 6fafb630..28165dd7 100644
--- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Store.kt
+++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/internal/Store.kt
@@ -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>
+ var symbol2occurrences: PersMap>
- var logical2occurrences: PersMap>, IdHashSet>
+ var logical2occurrences: PersMap>, IdHashSet>
- 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>, IdHashSet>()
- var v2o = Maps.of>()
-
+ var l2o = Maps.of>, IdHashSet>()
+ val storeItems = IdentityHashMap()
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 {
- return symbol2occurrences.values().flatten().filter { co -> co.isStored() }.asSequence()
+ return symbol2occurrences.values().flatten().filter { co -> co.stored }.asSequence()
}
fun view(): StoreView = StoreViewImpl(allOccurrences())
diff --git a/reactor/Test/src/program/MockProgram.kt b/reactor/Test/src/program/MockProgram.kt
index 3ea7fa1d..a5dfcbc4 100644
--- a/reactor/Test/src/program/MockProgram.kt
+++ b/reactor/Test/src/program/MockProgram.kt
@@ -116,7 +116,7 @@ class MockProgram(val name: String, val handlers: List, val registry: M
}
-class MockConstraintRegistry(val sessionSolver: SessionSolver) {
+class MockConstraintRegistry() {
private val myConstraintArgTypes = HashMap>>().withDefault { Collections.emptyList() }
diff --git a/reactor/Test/src/solver/EqualsSolver.kt b/reactor/Test/src/solver/EqualsSolver.kt
index aa804284..a46466bd 100644
--- a/reactor/Test/src/solver/EqualsSolver.kt
+++ b/reactor/Test/src/solver/EqualsSolver.kt
@@ -117,7 +117,7 @@ infix fun 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.eq(value: T) = EvaluationSession.current().let { session ->
@@ -128,7 +128,7 @@ infix fun 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 {
diff --git a/reactor/Test/src/solver/MockSessionSolver.kt b/reactor/Test/src/solver/MockSessionSolver.kt
deleted file mode 100644
index 66f1a919..00000000
--- a/reactor/Test/src/solver/MockSessionSolver.kt
+++ /dev/null
@@ -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() {
-
-
-}
diff --git a/reactor/Test/test/LogicalHelper.kt b/reactor/Test/test/LogicalHelper.kt
index ee6971c8..da2c3e04 100644
--- a/reactor/Test/test/LogicalHelper.kt
+++ b/reactor/Test/test/LogicalHelper.kt
@@ -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 anon(value: T) = LogicalImpl(value)
+fun anon(value: T) = anonLogical(value)
-fun logical(name: String) = LogicalImpl(name)
+fun logical(name: String) = namedLogical(name)
-fun logical(name1: String, name2: String) = Pair(LogicalImpl(name1), LogicalImpl(name2))
+fun logical(name1: String, name2: String) = Pair(namedLogical(name1), namedLogical(name2))
-fun logical(name1: String, name2: String, name3: String) =
- Triple(LogicalImpl(name1), LogicalImpl(name2), LogicalImpl(name3))
+fun logical(name1: String, name2: String, name3: String) =
+ Triple(namedLogical(name1), namedLogical(name2), namedLogical(name3))
-inline fun metaLogical(name: String) = MetaLogical(name, T::class.java)
+inline fun metaLogical(name: String) = MetaLogical(name, T::class.java)
-inline fun metaLogical(name1: String, name2: String) =
+inline fun metaLogical(name1: String, name2: String) =
Pair(MetaLogical(name1, T::class.java), MetaLogical(name2, T::class.java))
-inline fun metaLogical(name1: String, name2: String, name3: String) =
+inline fun metaLogical(name1: String, name2: String, name3: String) =
Triple(
MetaLogical(name1, T::class.java),
MetaLogical(name2, T::class.java),
MetaLogical(name3, T::class.java))
-fun Logical.get(): T = findRoot().value()
+fun Logical.get(): T = findRoot().value()
-fun Logical.getNullable(): T? = findRoot().value()
+fun Logical.getNullable(): T? = findRoot().value()
-fun Logical.set(t: T) {
+fun Logical.set(t: T) {
if (this is JoinableLogical)
findRoot().setValue(t)
else
@@ -52,9 +52,13 @@ class MockObserver : LogicalObserver {
val events = ArrayList()
- 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 {
val tmp = ArrayList(events)
diff --git a/reactor/Test/test/RulesHelper.kt b/reactor/Test/test/RulesHelper.kt
index 7f6b5ef2..b55b2f2f 100644
--- a/reactor/Test/test/RulesHelper.kt
+++ b/reactor/Test/test/RulesHelper.kt
@@ -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) {
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 Handler): Builder {
+fun programWithHandlers(vararg handlerBuilders: Environment.() -> Handler): Builder {
return builder(Environment(), handlerBuilders)
}
private fun builder(env: Environment, handlerBlocks: Array Handler>): Builder {
val handlers = ArrayList()
- with (env) {
+ with(env) {
for (block in handlerBlocks) {
handlers.add(block())
}
@@ -56,7 +58,7 @@ fun handler(name: String, primary: Iterable, 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, val env: Environment) {
val constraints = ArrayList()
fun createConstraint(args: Array, 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, val env: Environment) {
private fun buildConjunction(type: Class,
env: Environment,
- content: Array Unit>): ConjBuilder
-{
+ content: Array Unit>): ConjBuilder {
val conjBuilder = ConjBuilder(type, env)
for (c in content) {
conjBuilder.c()
}
return conjBuilder
}
-
-data class TestConstraintOccurrence(val constraint: Constraint, val arguments: List, 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 = arguments
-
- override fun logicalContext(): LogicalContext = TODO()
-
- override fun terminate() {
- this.alive = false
- }
-
- override fun toString(): String = "${constraint().symbol()}(${arguments().joinToString()})"
-
-}
\ No newline at end of file
diff --git a/reactor/Test/test/TestController.kt b/reactor/Test/test/TestController.kt
index e468c1cc..cb0e8a2f 100644
--- a/reactor/Test/test/TestController.kt
+++ b/reactor/Test/test/TestController.kt
@@ -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 {
+ 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("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("x", "y")
+ var test: String? = "not initialized"
+ val (x, y) = logical("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("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("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("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).value())
}
@@ -456,27 +456,27 @@ class TestController {
@Test
fun correctRulesOrder() {
- val X= metaLogical("X")
+ val X = metaLogical("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("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("X1", "Y1", "Z1")
- val (X2,Y2,Z2) = metaLogical("X2", "Y2", "Z2")
- val (X3,Y3,Z3) = metaLogical("X3", "Y3", "Z3")
+ val (X1, Y1, Z1) = metaLogical("X1", "Y1", "Z1")
+ val (X2, Y2, Z2) = metaLogical("X2", "Y2", "Z2")
+ val (X3, Y3, Z3) = metaLogical("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("X", "Y", "Z")
+ val (X, Y, Z) = metaLogical("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("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("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("X1", "Y1", "Z1")
+ val (X1, Y1, Z1) = metaLogical("X1", "Y1", "Z1")
val X2 = metaLogical("X2")
- val (X3,Y3) = metaLogical("X3", "Y3")
+ val (X3, Y3) = metaLogical("X3", "Y3")
val X4 = metaLogical("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