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 5ade1367..9461521c 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleIndex.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleIndex.kt @@ -38,7 +38,7 @@ class RuleIndex(handlers: Iterable) : Iterable { val rules = ArrayList() - private val slotIndices = ArrayList() + private val slotIndices = ArrayList() init { buildIndex(handlers) @@ -55,14 +55,16 @@ class RuleIndex(handlers: Iterable) : Iterable { * Returns a pair of rule and bit mask with 1's marking matching slots. */ fun forOccurrenceWithMask(occ: ConstraintOccurrence): Iterable> { - val ruleIndices = symbol2index[occ.constraint().symbol()]?.select(occ) ?: return emptyList() - return ruleIndices.allSetBits().mapNotNull { idx -> slotIndices[idx][occ]?.let { mask -> rules[idx] to mask }} + val ruleBits = symbol2index[occ.constraint().symbol()]?.select(occ) ?: return emptyList() + return ruleBits.allSetBits().mapNotNull { ruleBit -> + slotIndices[ruleBit][occ]?.let { mask -> rules[ruleBit] to mask } + } } override fun iterator(): Iterator = tag2rule.values.iterator() private fun buildIndex(handlers: Iterable) { - var pos = 0 + var ruleBit = 0 for (h in handlers) { for (rule in h.rules()) { if (tag2rule.containsKey(rule.tag())) throw IllegalStateException("duplicate rule tag ${rule.tag()}") @@ -71,30 +73,38 @@ class RuleIndex(handlers: Iterable) : Iterable { rules.add(rule) val head = rule.headKept() + rule.headReplaced() - val symbol2mask = SlotIndex() - for ((bit, cst) in head.withIndex()) { - symbol2index.getOrPut(cst.symbol()) { ArgumentRuleIndex(cst.symbol()) }.update(pos, cst) - symbol2mask.update(cst, bit) + val cst2mask = SlotMask() + for ((pos, cst) in head.withIndex()) { + symbol2index.getOrPut(cst.symbol()) { ArgumentRuleIndex(cst.symbol()) }.update(cst, ruleBit) + cst2mask.update(cst, pos) } - slotIndices.add(symbol2mask) + slotIndices.add(cst2mask) - pos += 1 + ruleBit += 1 } } } - class SlotIndex() { + + /** + * Represents a mask associated with a single rule. + * The mask tells whether or not a particular constraint occurrence can match + * any of the rule's constraints. + */ + class SlotMask { val symbol2mask = HashMap() - fun update(cst: Constraint, pos: Int) { - symbol2mask.getOrPut(cst.symbol()) { BitSet() }.set(pos) + fun update(cst: Constraint, posInHead: Int) { + symbol2mask.getOrPut(cst.symbol()) { BitSet() }.set(posInHead) } + /** + * When null is returned, there can be no match for this constraint + */ operator fun get(occ: ConstraintOccurrence): BitSet? = - symbol2mask[occ.constraint().symbol()] // guaranteed to != null - + symbol2mask[occ.constraint().symbol()] } /** @@ -118,17 +128,17 @@ class RuleIndex(handlers: Iterable) : Iterable { } } - fun update(bit: Int, cst: Constraint) { + fun update(cst: Constraint, ruleBit: Int) { for ((idx, arg) in cst.arguments().withIndex()) { val value2indices = anySelectors[idx] when (arg) { is MetaLogical<*> -> // all values should be accepted by a meta logical - wildcardSelectors[idx].set(bit) + wildcardSelectors[idx].set(ruleBit) is Term -> - termSelectors.set(idx, termSelectors[idx].put(arg, bit)) + termSelectors.set(idx, termSelectors[idx].put(arg, ruleBit)) is Any -> - value2indices.getOrPut(arg) { BitSet() }.also { it.set(bit) } + value2indices.getOrPut(arg) { BitSet() }.apply { set(ruleBit) } else -> throw NullPointerException() // never happens 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 6c2e7f0d..e3289e3a 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleMatcher.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/RuleMatcher.kt @@ -18,7 +18,6 @@ package jetbrains.mps.logic.reactor.core import com.github.andrewoma.dexx.collection.Map as PersMap import com.github.andrewoma.dexx.collection.List as PersList -import com.github.andrewoma.dexx.collection.ConsList import com.github.andrewoma.dexx.collection.Maps import com.github.andrewoma.dexx.collection.Vector as PersVector import jetbrains.mps.logic.reactor.evaluation.ConstraintOccurrence @@ -32,6 +31,7 @@ import jetbrains.mps.logic.reactor.program.Rule import jetbrains.mps.logic.reactor.util.* import jetbrains.mps.unification.Term import java.util.* +import kotlin.collections.ArrayList /** * @author Fedor Isakov @@ -48,9 +48,9 @@ class RuleMatcher(val rule: Rule) { val propagation = rule.headReplaced().count() == 0 - fun fringe() = MatchFringe(cons(FringeNode(emptySubst())), emptySet(), 0) + fun fringe() = MatchFringe(listOf(FringeNode(emptySubst())), emptySet(), 0) - inner class MatchFringe(val nodes: ConsList, + inner class MatchFringe(val nodes: List, val seen: IdHashSet, val genId: Int) { @@ -72,27 +72,33 @@ class RuleMatcher(val rule: Rule) { // select complete (leaf) nodes and make them appear as if newly expanded // *unless* propagation (implement propagation history feature) if (propagation) return MatchFringe(nodes, seen, genId + 1) - - val newNodes = nodes.asSequence().mapNotNull { fn -> - if (fn is ActiveFringeNode && fn.complete) + + val newNodes = ArrayList() + for (fn in nodes) { + if (fn is ActiveFringeNode && fn.complete) { fn.unrelatedOrCopy(occ, genId + 1) - else - fn - }.toConsList() + + } else { fn }?.let { newNodes.add(it) } + } + return MatchFringe(newNodes, seen, genId + 1) } else { - val newNodes = nodes.asSequence().filter { fn -> - mask == null || fn.matchesVacant(mask) - }.flatMap { fn -> - fn.expand(occ, genId + 1) } - return MatchFringe(newNodes.prependTo(nodes), seen.add(occ), genId + 1) + val newNodes = ArrayList(nodes) + for (fn in nodes) { + // TODO: mask can't be null in normal circumstances + if (mask == null || fn.matchesVacant(mask)) { + newNodes.addAll(fn.expand(occ, genId + 1)) + } + } + + return MatchFringe(newNodes, seen.add(occ), genId + 1) } } fun cleanup(occ: ConstraintOccurrence): MatchFringe { - val newNodes = nodes.asSequence().mapNotNull { it.unrelatedOrNull(occ) } - return MatchFringe(newNodes.toConsList(), seen.remove(occ), genId + 1) + val newNodes = nodes.mapNotNull { it.unrelatedOrNull(occ) } + return MatchFringe(newNodes, seen.remove(occ), genId + 1) } } @@ -104,13 +110,16 @@ class RuleMatcher(val rule: Rule) { * 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): Sequence { - val unrelated = unrelatedOrNull(occ) ?: return emptySequence() - return unrelated.vacant.allSetBits().map { idx -> - idx to match(head[idx] !!, occ, subst) }.asSequence().mapNotNull { (idx, newSubst) -> - newSubst?.let { ActiveFringeNode(this, occ, idx, genId, it) } - } - } + fun expand(occ: ConstraintOccurrence, genId: Int): List = + unrelatedOrNull(occ)?.run { + ArrayList().also { expanded -> + for (idx in vacant.allSetBits()) { + match(head[idx]!!, occ, subst)?.let { newSubst -> + expanded.add(ActiveFringeNode(this, occ, idx, genId, newSubst)) + } + } + } + } ?: emptyList() /** * Returns this node if it doesn't have the occurrence in its path, null otherwise. diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/util/BitSet.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/util/BitSet.kt index 4fe56d37..3e743b11 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/util/BitSet.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/util/BitSet.kt @@ -26,13 +26,13 @@ inline fun BitSet.copyApply (f: BitSet.() -> Unit): BitSet = (clone() as BitSet).apply(f) fun bitSetOfOnes(size: Int): BitSet = - BitSet(size).also { it.set(0, size) } + BitSet(size).apply { set(0, size) } fun BitSet.setBit(bit: Int): BitSet = - BitSet.valueOf(this.toLongArray()).also { it.set(bit) } + BitSet.valueOf(this.toLongArray()).apply { set(bit) } fun BitSet.clearBit(bit: Int): BitSet = - BitSet.valueOf(this.toLongArray()).also { it.clear(bit) } + BitSet.valueOf(this.toLongArray()).apply { clear(bit) } fun BitSet.allSetBits(): Iterable = object : Iterable { override fun iterator(): Iterator = object : Iterator { diff --git a/reactor/Core/src/jetbrains/mps/unification/TermGraphUnifier.kt b/reactor/Core/src/jetbrains/mps/unification/TermGraphUnifier.kt index dfc80812..28ba2516 100644 --- a/reactor/Core/src/jetbrains/mps/unification/TermGraphUnifier.kt +++ b/reactor/Core/src/jetbrains/mps/unification/TermGraphUnifier.kt @@ -16,7 +16,6 @@ package jetbrains.mps.unification -import gnu.trove.TIntHashSet import gnu.trove.list.array.TIntArrayList import gnu.trove.map.hash.TIntObjectHashMap import jetbrains.mps.unification.Substitution.FailureCause.CYCLE_DETECTED @@ -52,6 +51,9 @@ import java.util.* * @author Fedor Isakov */ +typealias IntList = TIntArrayList +typealias IntAnyHashMap = TIntObjectHashMap + class TermGraphUnifier { companion object { @@ -65,12 +67,12 @@ class TermGraphUnifier { private val backref = IdentityHashMap() private val origin = ArrayList() - private val innerClass = TIntArrayList() - private val innerSchema = TIntArrayList() - private val innerSize = TIntArrayList() - private val innerVars = TIntObjectHashMap() - private val innerAcyclic = TIntHashSet() - private val innerVisited = TIntHashSet() + private val innerClass = IntList() + private val innerSchema = IntList() + private val innerSize = IntList() + private val innerVars = IntAnyHashMap() + private val innerAcyclic = BitSet() + private val innerVisited = BitSet() constructor() { this.wrapper = TermWrapper.ID @@ -96,23 +98,23 @@ class TermGraphUnifier { private fun findSolution(s: Int, defSubs: Substitution) : Substitution { val z = innerSchema[find(s)] - if (innerAcyclic.contains(z)) { return defSubs } // not part of a cycle - if (innerVisited.contains(z)) { return failedSubstitution(CYCLE_DETECTED) } // there exists a cycle + if (innerAcyclic[z]) { return defSubs } // not part of a cycle + if (innerVisited[z]) { return failedSubstitution(CYCLE_DETECTED) } // there exists a cycle var subs = defSubs if (origin[z].`is`(FUN)) { - innerVisited.add(z) + innerVisited.set(z) for (c in origin[z].arguments()) { subs = findSolution(toInner(c), subs) if (!subs.isSuccessful) break } - innerVisited.add(z) + innerVisited.set(z) } if (subs.isSuccessful) { - innerAcyclic.add(z) + innerAcyclic.set(z) // avoid unnecessary instatiation val success = if (subs is SuccessfulSubstitution) subs else SuccessfulSubstitution(subs) @@ -240,7 +242,7 @@ class TermGraphUnifier { if (repr != innerClass[repr]) { // find representative and compress paths - val path = TIntArrayList() + val path = IntList() path.add(t) while (repr != innerClass[repr]) { path.add(repr) @@ -258,7 +260,7 @@ class TermGraphUnifier { private fun prependVars(t: Int, vars: TIntArrayList?) { if (vars?.isEmpty ?: true) { return } - val newVars = TIntArrayList(innerVars[t] ?: EMPTY) + val newVars = IntList(innerVars[t] ?: EMPTY) newVars.addAll(vars) innerVars.put (t, newVars) } @@ -281,7 +283,7 @@ class TermGraphUnifier { innerClass.add(next) innerSize.add(1) if (wrapped.`is`(VAR)) { - innerVars.put(next, TIntArrayList(intArrayOf(next))) + innerVars.put(next, IntList(intArrayOf(next))) } backref.put(key, next) next