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 aa30744a..5c2b7a99 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Controller.kt @@ -41,6 +41,8 @@ class Controller( private val frameStack = FrameStack(storeView) + private val matcher = Matcher(ruleIndex, profiler) + // persistent (functional) object. reassigned on update private var propHistory = PropagationHistory() @@ -84,7 +86,7 @@ class Controller( trace.reactivate(active) } - for (match in Matcher(ruleIndex, active, frameStack.current.store, profiler)) { + for (match in matcher.matches(active, frameStack.current.store)) { // TODO: paranoid check. should be isAlive() instead if (!active.isStored()) break if (!match.successful) continue diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Matcher.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Matcher.kt index 35bbb1a1..57c7f345 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Matcher.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Matcher.kt @@ -30,7 +30,6 @@ import jetbrains.mps.logic.reactor.util.Profiler import jetbrains.mps.unification.Substitution import jetbrains.mps.unification.Term import jetbrains.mps.unification.TermWrapper -import java.util.* import com.github.andrewoma.dexx.collection.List as PersList /** @@ -38,21 +37,33 @@ import com.github.andrewoma.dexx.collection.List as PersList */ class Matcher(val ruleIndex: RuleIndex, - val activeOcc: ConstraintOccurrence, - val aux: OccurrenceIndex, - val profiler: Profiler? = null) : Iterable + val profiler: Profiler? = null) { - private val matchTries: LazyIterable by lazy(LazyThreadSafetyMode.NONE) { - LazyIterable (ruleIndex.forOccurrence(activeOcc).toList()) { rule -> - PartialMatchTrie(rule, activeOcc, aux, profiler) - } - } + private val rule2matchTrieSet = HashMap() - override fun iterator() = matchTries.asSequence().flatMap { matchTrie -> - matchTrie.matches().asSequence() }.map { partialMatch -> - partialMatch.complete(profiler) }.filter { match -> - match.successful }.iterator() + fun matches(activeOcc: ConstraintOccurrence, aux: OccurrenceIndex) : Iterable = object : Iterable { + + override fun iterator(): Iterator { + + val relevantRules = ruleIndex.forOccurrence(activeOcc).toList() + + val tries = LazyIterable(relevantRules) { rule -> + rule2matchTrieSet[rule] ?: MatchTrieSet(rule, profiler).apply { + rule2matchTrieSet[rule] = this + } + }.asSequence() + + return tries.flatMap { matchTrieSet -> + matchTrieSet.matches(activeOcc, aux) + }.map { partialMatch -> + partialMatch.complete(profiler) + }.filter { match -> + match.successful + }.iterator() + } + + } } diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/PartialMatch.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/PartialMatch.kt index e420b8da..851fe0b7 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/PartialMatch.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/PartialMatch.kt @@ -74,4 +74,5 @@ internal class PartialMatch(val rule: Rule) { return match } + override fun toString(): String = "${keptOccurrences.toList()} \\ ${discardedOccurrences.toList()}" } \ No newline at end of file diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/PartialMatchTrie.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/PartialMatchTrie.kt index 3651a427..28ca7d2e 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/PartialMatchTrie.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/PartialMatchTrie.kt @@ -32,344 +32,422 @@ import com.github.andrewoma.dexx.collection.Map as PersMap * @author Fedor Isakov */ -internal class PartialMatchTrie(val rule: Rule, - val activeOcc: ConstraintOccurrence, - val occIndex: OccurrenceIndex, - val profiler: Profiler?) -{ - constructor(prototype: PartialMatchTrie, - activeOcc: ConstraintOccurrence, - occurrenceIndex: OccurrenceIndex, - profiler: Profiler?) : - this(prototype.rule, activeOcc, occurrenceIndex, profiler) - { - this._prototype = prototype - } - - private var _prototype: PartialMatchTrie? = null - - private val prototype: PartialMatchTrie? - get() = _prototype +internal class MatchTrieSet(val rule: Rule, val profiler: Profiler?) { private val keptConstraints = rule.headKept().toCollection(ArrayList(4)) private val discardedConstraints = rule.headReplaced().toCollection(ArrayList(4)) - private val root: RootMatchTrieNode by lazy(LazyThreadSafetyMode.NONE) { - prototype?.root ?: RootMatchTrieNode() - } - // for each slot keep a set of "related" slots, that is the slots with matching (meta-)logicals // this map is built only once per rule - private val relatedSlots: List by lazy(LazyThreadSafetyMode.NONE) { - prototype?.relatedSlots ?: HashMap, BitSet>().let { meta2slots -> + private val relatedSlots: List = + HashMap, BitSet>().let { meta2slots -> + (keptConstraints + discardedConstraints).forEachIndexed { slot, cst -> + cst.arguments().forEach { arg -> + if (arg is MetaLogical<*>) { + val bs = meta2slots[arg] ?: BitSet().apply { meta2slots[arg] = this } + bs.set(slot) + } + } + } + + ArrayList().apply { (keptConstraints + discardedConstraints).forEachIndexed { slot, cst -> + val bs = BitSet() cst.arguments().forEach { arg -> if (arg is MetaLogical<*>) { - val bs = meta2slots[arg] ?: BitSet().apply { meta2slots[arg] = this } - bs.set(slot) + bs.or(meta2slots[arg]) } } - } - - ArrayList().apply { - (keptConstraints + discardedConstraints).forEachIndexed { slot, cst -> - val bs = BitSet() - cst.arguments().forEach { arg -> - if (arg is MetaLogical<*>) { - bs.or(meta2slots[arg]) - } - } - bs.clear(slot) - add(bs) - } + bs.clear(slot) + add(bs) } } - } - - fun matches(): Iterable = object : Iterable { - - override fun iterator() = object : Iterator { - - val firstRoot: MatchTrieNode? = root.firstChild - var next: PartialMatch? = null - - override fun hasNext(): Boolean { - if (next == null) { - this.next = calcNext() - } - return (next != null) - } - - override fun next(): PartialMatch { - if (next == null) { - this.next = calcNext() - } - if (next != null) { - val tmp: PartialMatch = next!! - this.next = calcNext() - return tmp - } - throw NoSuchElementException() - } - - private fun calcNext(): PartialMatch? { - // the search always starts at the first root - if (firstRoot == null) { - return null - } - var mtn: MatchTrieNode? = firstRoot - - while (mtn != null) { - if (mtn.isSeen) { - if (mtn.nextSibling == null) { - mtn.parent?.setSeen() - } - mtn = mtn.nextSibling ?: mtn.parent - } - else if (mtn.isLeaf) { - mtn.setSeen() - return mtn.match - } - else { - // calculate the children - if (mtn.firstChild == null) { - mtn.setSeen() - } - mtn = mtn.firstChild ?: mtn - } - } - - return null - } } - } + val tries = ArrayList() - private abstract inner class BaseMatchTrieNode() { + fun matches(activeOcc: ConstraintOccurrence, occIndex: OccurrenceIndex): Sequence { + val constraints = keptConstraints + discardedConstraints - var _firstChild: MatchTrieNode? = null - - var _nextSibling: MatchTrieNode? = null - - abstract fun reset() - - } - - private inner class RootMatchTrieNode() : BaseMatchTrieNode() { - - val firstChild: MatchTrieNode? - get() { - if (_firstChild == null) { - var first: MatchTrieNode? = null - var prev: MatchTrieNode? = null - for ((idx, cst) in keptConstraints.withIndex()) { - if (cst.symbol() == activeOcc.constraint().symbol()) { - val next = MatchTrieNode(null, true, cst, activeOcc, idx) - if (first == null) { - first = next - } - prev?._nextSibling = next - prev = next - } + val relevantTries = ArrayList() + val newTries = ArrayList() + for (t in tries) { + if (!t.vacant.isEmpty) { + t.vacant.stream() + newTries.add(t) + for (idx in t.vacant.stream()) { + if (activeOcc.constraint().symbol() == constraints[idx].symbol()) { + relevantTries.add(PartialMatchTrie(t, activeOcc, occIndex)) + newTries.removeAt(newTries.size - 1) + break } - for ((idx, cst) in discardedConstraints.withIndex()) { - if (cst.symbol() == activeOcc.constraint().symbol()) { - val next = MatchTrieNode(null, false, cst, activeOcc, idx) - if (first == null) { - first = next - } - prev?._nextSibling = next - prev = next - } - } - - this._firstChild = first } - return _firstChild } - - override fun reset() { - while (!(_firstChild?.occurrence?.isAlive() ?: true)) { - this._firstChild = _firstChild?._nextSibling - } - _firstChild?.reset() + } + if (relevantTries.isEmpty()) { + relevantTries.add(PartialMatchTrie(activeOcc, occIndex)) } + this.tries.clear() + this.tries.addAll(newTries) + this.tries.addAll(relevantTries) + + return relevantTries.asSequence().flatMap { t -> t.matches() } } - private inner class MatchTrieNode(val parent: MatchTrieNode?, - val keepConstraint: Boolean, - val constraint: Constraint, - val occurrence: ConstraintOccurrence, - val index: Int) : BaseMatchTrieNode() + inner class PartialMatchTrie(val activeOcc: ConstraintOccurrence, + val occIndex: OccurrenceIndex) { - private var _isSeen: Boolean = false + constructor(prototype: PartialMatchTrie, + activeOcc: ConstraintOccurrence, + occurrenceIndex: OccurrenceIndex) : + this(activeOcc, occurrenceIndex) + { + this._prototype = prototype + } - val isSeen: Boolean - get() = _isSeen + private var _prototype: PartialMatchTrie? = null - val vacantSlots: BitSet by lazy(LazyThreadSafetyMode.NONE) { - val result = BitSet() - result.set(0, keptConstraints.size + discardedConstraints.size) - foldPath(result) { bits, mtn -> - val slot = if (mtn.keepConstraint) mtn.index else mtn.index + keptConstraints.size - bits.clear(slot) - bits + private val prototype: PartialMatchTrie? + get() = _prototype + + private val root: RootMatchTrieNode by lazy(LazyThreadSafetyMode.NONE) { + prototype?.root?.let { root -> RootMatchTrieNode(root) } ?: RootMatchTrieNode() + } + + val vacant = BitSet() + + fun matches(): Sequence = object : Sequence { + + override fun iterator() = object : Iterator { + + val firstRoot: MatchTrieNode? = root.firstChild + + var next: PartialMatch? = null + + override fun hasNext(): Boolean { + if (next == null) { + this.next = calcNext() + } + return (next != null) + } + + override fun next(): PartialMatch { + if (next == null) { + this.next = calcNext() + } + if (next != null) { + val tmp: PartialMatch = next!! + this.next = calcNext() + return tmp + } + throw NoSuchElementException() + } + + private fun calcNext(): PartialMatch? { + // the search always starts at the first root + if (firstRoot == null) { + return null + } + var mtn: MatchTrieNode? = firstRoot + + while (mtn != null) { + if (mtn.isSeen) { + if (mtn.nextSibling == null) { + mtn.parent?.setSeen() + } + mtn = mtn.nextSibling ?: mtn.parent + } + else if (mtn.isLeaf) { + mtn.setSeen() + vacant.clear() + return mtn.match + } + else if (mtn.firstChild == null) { + mtn.setSeen() + vacant.or(mtn.vacantSlots) + vacant.andNot(mtn.relatedFirstVacantSlots) + } + else { + mtn = mtn.firstChild + } + } + + return null + } } - result + } - val relatedFirstVacantSlots: BitSet by lazy(LazyThreadSafetyMode.NONE) { - val result = vacantSlots.clone() as BitSet - result.and(relatedSlots[if (keepConstraint) index else index + keptConstraints.size]) - result + private abstract inner class BaseMatchTrieNode() { + + var _firstChild: MatchTrieNode? = null + } - val isLeaf: Boolean - get() = vacantSlots.isEmpty + private inner class RootMatchTrieNode(copyFrom: RootMatchTrieNode? = null) : BaseMatchTrieNode() { - val match: PartialMatch by lazy(LazyThreadSafetyMode.NONE) { - val parentMatch = parent?.match ?: PartialMatch(rule, keptConstraints.size, discardedConstraints.size) - if (keepConstraint) parentMatch.keep(occurrence, index) - else parentMatch.discard(occurrence, index) - } + init { + copyFrom?.firstChild?.firstAlive()?.let { mtn -> - fun setSeen() { - this._isSeen = true - } + var first = MatchTrieNode(null, mtn) + var prev = first + var next = mtn.nextSibling?.firstAlive() - val firstChild: MatchTrieNode? - get() { - if (_firstChild == null) { - var first: MatchTrieNode? = null - var prev: MatchTrieNode? = null - for (next in calcChildren()) { + while (next != null) { + val n = MatchTrieNode(null, next) + prev._nextSibling = n + prev = n + next = n.nextSibling?.firstAlive() + } + +// prev._nextSibling = buildRoots() + + this._firstChild = first + + } + } + + val firstChild: MatchTrieNode? + get() { + if (_firstChild == null) { + this._firstChild = buildRoots() + } + return _firstChild + } + + private fun buildRoots(): MatchTrieNode? { + var first: MatchTrieNode? = null + var prev: MatchTrieNode? = null + for ((idx, cst) in keptConstraints.withIndex()) { + if (cst.symbol() == activeOcc.constraint().symbol()) { + val next = MatchTrieNode(null, true, cst, activeOcc, idx) if (first == null) { first = next } prev?._nextSibling = next prev = next } + } + for ((idx, cst) in discardedConstraints.withIndex()) { + if (cst.symbol() == activeOcc.constraint().symbol()) { + val next = MatchTrieNode(null, false, cst, activeOcc, idx) + if (first == null) { + first = next + } + prev?._nextSibling = next + prev = next + } + } + return first + } + + } + + private inner class MatchTrieNode(val parent: MatchTrieNode?, + val keepConstraint: Boolean, + val constraint: Constraint, + val occurrence: ConstraintOccurrence, + val index: Int) : BaseMatchTrieNode() + { + + constructor(parent: MatchTrieNode?, copyFrom: MatchTrieNode) : + this(parent, copyFrom.keepConstraint, copyFrom.constraint, copyFrom.occurrence, copyFrom.index) + { + copyChildren(copyFrom) + } + + private var _isSeen: Boolean = false + + val isSeen: Boolean + get() = _isSeen + + val vacantSlots: BitSet by lazy(LazyThreadSafetyMode.NONE) { + val result = BitSet() + result.set(0, keptConstraints.size + discardedConstraints.size) + foldPath(result) { bits, mtn -> + val slot = if (mtn.keepConstraint) mtn.index else mtn.index + keptConstraints.size + bits.clear(slot) + bits + } + result + } + + val relatedFirstVacantSlots: BitSet by lazy(LazyThreadSafetyMode.NONE) { + val result = vacantSlots.clone() as BitSet + result.and(relatedSlots[if (keepConstraint) index else index + keptConstraints.size]) + result + } + + val isLeaf: Boolean + get() = vacantSlots.isEmpty + + val match: PartialMatch by lazy(LazyThreadSafetyMode.NONE) { + val parentMatch = parent?.match ?: PartialMatch(rule, keptConstraints.size, discardedConstraints.size) + if (keepConstraint) parentMatch.keep(occurrence, index) + else parentMatch.discard(occurrence, index) + } + + fun setSeen() { + this._isSeen = true + } + + val firstChild: MatchTrieNode? + get() { + if (_firstChild == null) { + var first: MatchTrieNode? = null + var prev: MatchTrieNode? = null + for (next in calcChildren()) { + if (first == null) { + first = next + } + prev?._nextSibling = next + prev = next + } + + this._firstChild = first + } + return _firstChild + } + + var _nextSibling: MatchTrieNode? = null + + val nextSibling: MatchTrieNode? + get() = _nextSibling + + private fun calcChildren(): Iterable { + val nextSlots = if (!relatedFirstVacantSlots.isEmpty) relatedFirstVacantSlots else vacantSlots + val nextSlot = nextSlots.nextSetBit(0) + if (nextSlot < 0) { + return emptyList() + } + + val keep = nextSlot < keptConstraints.size + val nextIdx = if (keep) nextSlot else nextSlot - keptConstraints.size + val nextCst = if (keep) keptConstraints[nextSlot] else discardedConstraints[nextIdx] + + + return if (nextCst.symbol() == activeOcc.constraint().symbol() && + !anyInPath { it -> it.constraint.symbol() == activeOcc.constraint().symbol() }) + { + arrayListOf(MatchTrieNode(this, keep, nextCst, activeOcc, nextIdx)) + } + else { + val (result, auxOccurrences) = lookupAuxOccurrences(nextCst) + when (result) { + Result.DEFINITIVE -> parent + Result.NONE -> this + Result.INCONCLUSIVE -> null + }?.forEachInPath { mtn -> mtn._isSeen = true } + + auxOccurrences.map { auxOcc -> MatchTrieNode(this, keep, nextCst, auxOcc, nextIdx) } + } + } + + // the first component indicates the quality of the result: + // DEFINITIVE indicates the only possible matches were returned + // INCONCLUSIVE may yield some (ir-)relevant results + // NONE means no results could have been found at all, so it's useless to attempt again with the same input + private fun lookupAuxOccurrences(cst: Constraint): Pair> { + return profiler.profile>> ("lookupAux_${cst.symbol()}") { + + val fromArgs = ArrayList(4) + for (arg in metaInstances(cst.arguments()) + cst.arguments().filter { arg -> !(arg is MetaLogical<*>) }) { + when (arg) { + is Logical<*> -> fromArgs.addAll(occIndex.forLogical(arg)) + + is Term -> fromArgs.addAll(occIndex.forTermAndConstraint(arg, cst)) + + is Any -> fromArgs.addAll(occIndex.forValue(arg)) + } + } + if (fromArgs.isNotEmpty()) { + Result.DEFINITIVE.to(fromArgs.filter { occ -> occ.constraint().symbol() == cst.symbol() && !hasOccurrence(occ) }) + + } else { + return profiler.profile>> ("lookupAux_default_${cst.symbol()}") { + + val hasRelated = relatedFirstVacantSlots == vacantSlots + val allArgsMetaLogicals = cst.arguments().all { a -> a is MetaLogical<*> } + val noArgs = cst.arguments().isEmpty() + + if (noArgs || (allArgsMetaLogicals && !hasRelated)) { + Result.INCONCLUSIVE.to(occIndex.forSymbol(cst.symbol()).filter { occ -> !hasOccurrence(occ) }) + + } + else { + // all candidate occurrences should have been found by this time; if not, then there's none + Result.NONE.to(emptyList()) + } + + } + } + } + } + + private fun copyChildren(copyFrom: MatchTrieNode) { + copyFrom.firstChild?.firstAlive()?.let { mtn -> + + val first = MatchTrieNode(this, mtn) + var prev = first + var next = mtn.nextSibling?.firstAlive() + + while (next != null) { + val n = MatchTrieNode(this, next) + prev._nextSibling = n + prev = n + next = n.nextSibling?.firstAlive() + } this._firstChild = first } - return _firstChild } - val nextSibling: MatchTrieNode? - get() = _nextSibling - private fun calcChildren(): Iterable { - val nextSlots = if (!relatedFirstVacantSlots.isEmpty) relatedFirstVacantSlots else vacantSlots - val nextSlot = nextSlots.nextSetBit(0) - if (nextSlot < 0) { - return emptyList() + fun firstAlive(): MatchTrieNode? { + var fca: MatchTrieNode? = this + while (!(fca?.occurrence?.isAlive() ?: true)) { + fca = fca?._nextSibling + } + return fca } - val keep = nextSlot < keptConstraints.size - val nextIdx = if (keep) nextSlot else nextSlot - keptConstraints.size - val nextCst = if (keep) keptConstraints[nextSlot] else discardedConstraints[nextIdx] - - val (result, auxOccurrences) = lookupAuxOccurrences(nextCst) - when (result) { - Result.DEFINITIVE -> parent - Result.NONE -> this - Result.INCONCLUSIVE -> null - }?.forEachInPath { mtn -> mtn._isSeen = true } - - return auxOccurrences.map { auxOcc -> MatchTrieNode(this, keep, nextCst, auxOcc, nextIdx) } - } - - // the first component indicates the quality of the result: - // DEFINITIVE indicates the only possible matches were returned - // INCONCLUSIVE may yield some (ir-)relevant results - // NONE means no results could have been found at all, so it's useless to attempt again with the same input - private fun lookupAuxOccurrences(cst: Constraint): Pair> { - return profiler.profile>> ("lookupAux_${cst.symbol()}") { - - val fromArgs = ArrayList(4) - for (arg in metaInstances(cst.arguments()) + cst.arguments().filter { arg -> !(arg is MetaLogical<*>) }) { - when (arg) { - is Logical<*> -> fromArgs.addAll(occIndex.forLogical(arg)) - - is Term -> fromArgs.addAll(occIndex.forTermAndConstraint(arg, cst)) - - is Any -> fromArgs.addAll(occIndex.forValue(arg)) + private fun collectMetaInstances(meta: Collection<*>, list: ArrayList): ArrayList { + constraint.arguments().zip(occurrence.arguments()).forEach { p -> + if (p.first is MetaLogical<*> && meta.contains(p.first) && !list.contains(p.second)) { + list.add(p.second!!) } } - if (fromArgs.isNotEmpty()) { - Result.DEFINITIVE.to(fromArgs.filter { occ -> occ.constraint().symbol() == cst.symbol() && !hasOccurrence(occ) }) + return list + } - } else { - return profiler.profile>> ("lookupAux_default_${cst.symbol()}") { + private fun metaInstances(meta: Collection<*>): List = + foldPath(ArrayList(4)) { list, mtn -> mtn.collectMetaInstances(meta, list) } - val hasRelated = relatedFirstVacantSlots == vacantSlots - val allArgsMetaLogicals = cst.arguments().all { a -> a is MetaLogical<*> } - val noArgs = cst.arguments().isEmpty() + private fun hasOccurrence(occ: ConstraintOccurrence): Boolean = + anyInPath { mtn -> mtn.occurrence == occ } - if (noArgs || (allArgsMetaLogicals && !hasRelated)) { - Result.INCONCLUSIVE.to(occIndex.forSymbol(cst.symbol()).filter { occ -> !hasOccurrence(occ) }) + private inline fun anyInPath(predicate: (MatchTrieNode) -> Boolean): Boolean = + foldPath(false) { b, mtn -> b || predicate(mtn) } - } - else { - // all candidate occurrences should have been found by this time; if not, then there's none - Result.NONE.to(emptyList()) - } + private inline fun forEachInPath(proc: (MatchTrieNode) -> Unit) = + foldPath(Unit) { b, mtn -> proc(mtn) } - } + private inline fun foldPath(initVal: T, step: (T, MatchTrieNode) -> T): T { + var mtn: MatchTrieNode? = this + var currVal = initVal + while(mtn != null) { + currVal = step(currVal, mtn) + mtn = mtn.parent } + return currVal } - } - // prepare the node for re-use - override fun reset() { - this._isSeen = false - - while (!(_firstChild?.occurrence?.isAlive() ?: true)) { - this._firstChild = _firstChild?._nextSibling - } - _firstChild?.reset() - - while(!(_nextSibling?.occurrence?.isAlive() ?: true)) { - this._nextSibling = _nextSibling?._nextSibling - } - _nextSibling?.reset() - } - - private fun collectMetaInstances(meta: Collection<*>, list: ArrayList): ArrayList { - constraint.arguments().zip(occurrence.arguments()).forEach { p -> - if (p.first is MetaLogical<*> && meta.contains(p.first) && !list.contains(p.second)) { - list.add(p.second!!) - } - } - return list - } - - private fun metaInstances(meta: Collection<*>): List = - foldPath(ArrayList(4)) { list, mtn -> mtn.collectMetaInstances(meta, list) } - - private fun hasOccurrence(occ: ConstraintOccurrence): Boolean = - anyInPath { mtn -> mtn.occurrence == occ } - - private inline fun anyInPath(predicate: (MatchTrieNode) -> Boolean): Boolean = - foldPath(false) { b, mtn -> b || predicate(mtn) } - - private inline fun forEachInPath(proc: (MatchTrieNode) -> Unit) = - foldPath(Unit) { b, mtn -> proc(mtn) } - - private inline fun foldPath(initVal: T, step: (T, MatchTrieNode) -> T): T { - var mtn: MatchTrieNode? = this - var currVal = initVal - while(mtn != null) { - currVal = step(currVal, mtn) - mtn = mtn.parent - } - return currVal } } @@ -382,3 +460,8 @@ internal class PartialMatchTrie(val rule: Rule, } + + + + + diff --git a/reactor/Test/test/TestMatcher.kt b/reactor/Test/test/TestMatcher.kt index baac1496..52e9c42f 100644 --- a/reactor/Test/test/TestMatcher.kt +++ b/reactor/Test/test/TestMatcher.kt @@ -27,7 +27,7 @@ class TestMatcher { )) ).let { builder -> builder.indices().run { - Matcher(first, occurrence("main"), second).matching().let { matches -> + Matcher(first).matching(occurrence("main"), second).let { matches -> val match = matches.single() assertEquals(match.rule, builder.rules.first()) assertTrue(match.keptOccurrences.isEmpty()) @@ -57,7 +57,7 @@ class TestMatcher { )) ).let { builder -> builder.indices().run { - Matcher(first, occurrence("main"), second).matching().let { matches -> + Matcher(first).matching(occurrence("main"), second).let { matches -> assertTrue(matches.all {m -> m.successful}) assertEquals(builder.rules.toSet(), matches.map { m -> m.rule }.toSet()) matches.forEach { m -> assertTrue(m.keptOccurrences.size + m.discardedOccurrences.size == 1) } @@ -92,7 +92,7 @@ class TestMatcher { )) ).let { builder -> builder.indices().run { - Matcher(first, occurrence("main"), second).matching().let { matches -> + Matcher(first).matching(occurrence("main"), second).let { matches -> assertTrue(matches.all {m -> m.successful}) assertEquals(builder.rules.drop(1).toSet(), matches.map { m -> m.rule }.toSet()) } @@ -122,7 +122,7 @@ class TestMatcher { )) ).let { builder -> builder.indices(occurrence("aux")).run { - Matcher(first, occurrence("main"), second).matching().let { matches -> + Matcher(first).matching(occurrence("main"), second).let { matches -> assertTrue(matches.all {m -> m.successful}) assertEquals(builder.rules.toSet(), matches.map { m -> m.rule }.toSet()) } @@ -149,7 +149,7 @@ class TestMatcher { )) ).let { builder -> builder.indices().run{ - Matcher(first, occurrence("main", "bar"), second).matching().let { matches -> + Matcher(first).matching(occurrence("main", "bar"), second).let { matches -> assertTrue(matches.all {m -> m.successful}) assertEquals(builder.rules.drop(1), matches.map { m -> m.rule }.toList()) } @@ -175,7 +175,7 @@ class TestMatcher { constraint("bar") )) ).indices().run{ - Matcher(first, occurrence("main", "qux"), second).matching().let { matches -> + Matcher(first).matching(occurrence("main", "qux"), second).let { matches -> assertFalse(matches.any()) } } @@ -197,13 +197,13 @@ class TestMatcher { )) ) ).indices().run { - Matcher(first, occurrence("qux"), second).matching().let { matches -> + Matcher(first).matching(occurrence("qux"), second).let { matches -> assertFalse(matches.any()) } - Matcher(first, occurrence("foo"), second).matching().let { matches -> + Matcher(first).matching(occurrence("foo"), second).let { matches -> assertSame(1, matches.size) } - Matcher(first, occurrence("bar"), second).matching().let { matches -> + Matcher(first).matching(occurrence("bar"), second).let { matches -> assertSame(1, matches.size) } } @@ -224,7 +224,7 @@ class TestMatcher { ) ) ).indices().run { - Matcher(first, occurrence("foo", "blah", b), second).first().run { + Matcher(first).matches(occurrence("foo", "blah", b), second).first().run { assert(successful) assertEquals("blah", logicalContext.variable(A).findRoot().value()) assertSame(b, logicalContext.variable(B)) @@ -261,14 +261,14 @@ class TestMatcher { )) ).run { indices().run { - Matcher(first, occurrence("foo", 1), second).matching().let { matches -> + Matcher(first).matching(occurrence("foo", 1), second).let { matches -> assertFalse(matches.any()) } } // same parameter -- 4 matches (all permutations) indices(occurrence("foo", 42)).run { - Matcher(first, occurrence("foo", 42), second).matching().let { matches -> + Matcher(first).matching(occurrence("foo", 42), second).let { matches -> assertTrue(matches.all {m -> m.successful}) assertEquals(4, matches.count()) assertTrue(matches.all { m -> m.allOccurrences().toSet().size == 2 }) @@ -276,7 +276,7 @@ class TestMatcher { } indices(occurrence("foo", 42)).run { - Matcher(first, occurrence("foo", 16), second).matching().let { matches -> + Matcher(first).matching(occurrence("foo", 16), second).let { matches -> assertTrue(matches.all {m -> m.successful}) assertEquals(2, matches.count()) assertEquals(listOf("main1", "main1"), matches.map { m -> m.rule.tag() }.toList()) @@ -296,7 +296,7 @@ class TestMatcher { y.set(456) indices(occurrence("foo", x)).run { - Matcher(first, occurrence("foo", y), second).matching().let { matches -> + Matcher(first).matching(occurrence("foo", y), second).let { matches -> assertTrue(matches.all {m -> m.successful}) assertEquals(2, matches.count()) assertEquals(listOf("main1", "main1"), matches.map { m -> m.rule.tag() }.toList()) @@ -308,7 +308,7 @@ class TestMatcher { w.findRoot().union(v) indices(occurrence("foo", v)).run { - Matcher(first, occurrence("foo", w), second).matching().let { matches -> + Matcher(first).matching(occurrence("foo", w), second).let { matches -> assertTrue(matches.all {m -> m.successful}) assertEquals(2, matches.count()) assertTrue(matches.all{ m -> m.allOccurrences().toSet().size == 2 }) @@ -487,12 +487,12 @@ class TestMatcher { ) program.indices(occurrence("foo", x, parse("a{c}"))).run { - Matcher(first, occurrence("foo", y, parse("a{b}")), second).matching().let { matches -> + Matcher(first).matching(occurrence("foo", y, parse("a{b}")), second).let { matches -> assertEquals("foo2", matches.single().rule.tag()) } } program.indices(occurrence("foo", x, parse("a{b}"))).run { - Matcher(first, occurrence("foo", y, parse("a{c}")), second).matching().let { matches -> + Matcher(first).matching(occurrence("foo", y, parse("a{c}")), second).let { matches -> assertEquals("foo2", matches.single().rule.tag()) } } @@ -530,6 +530,6 @@ class TestMatcher { private fun Match.allOccurrences() = (keptOccurrences + discardedOccurrences) - private fun Matcher.matching() = this.filter { m -> m.successful } + private fun Matcher.matching(activeOcc: ConstraintOccurrence, aux: OccurrenceIndex) = this.matches(activeOcc, aux).filter { m -> m.successful } }