Minor optimisations and code cleanup
This commit is contained in:
parent
bd876c3e25
commit
59a0d958d7
|
|
@ -38,7 +38,7 @@ class RuleIndex(handlers: Iterable<Handler>) : Iterable<Rule> {
|
|||
|
||||
val rules = ArrayList<Rule>()
|
||||
|
||||
private val slotIndices = ArrayList<SlotIndex>()
|
||||
private val slotIndices = ArrayList<SlotMask>()
|
||||
|
||||
init {
|
||||
buildIndex(handlers)
|
||||
|
|
@ -55,14 +55,16 @@ class RuleIndex(handlers: Iterable<Handler>) : Iterable<Rule> {
|
|||
* Returns a pair of rule and bit mask with 1's marking matching slots.
|
||||
*/
|
||||
fun forOccurrenceWithMask(occ: ConstraintOccurrence): Iterable<Pair<Rule, BitSet>> {
|
||||
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<Rule> = tag2rule.values.iterator()
|
||||
|
||||
private fun buildIndex(handlers: Iterable<Handler>) {
|
||||
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<Handler>) : Iterable<Rule> {
|
|||
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<Symbol, BitSet>()
|
||||
|
||||
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<Handler>) : Iterable<Rule> {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -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<FringeNode>,
|
||||
inner class MatchFringe(val nodes: List<FringeNode>,
|
||||
val seen: IdHashSet<ConstraintOccurrence>,
|
||||
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<FringeNode>()
|
||||
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<FringeNode>(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<ActiveFringeNode> {
|
||||
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<ActiveFringeNode> =
|
||||
unrelatedOrNull(occ)?.run {
|
||||
ArrayList<ActiveFringeNode>().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.
|
||||
|
|
|
|||
|
|
@ -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<Int> = object : Iterable<Int> {
|
||||
override fun iterator(): Iterator<Int> = object : Iterator<Int> {
|
||||
|
|
|
|||
|
|
@ -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<V> = TIntObjectHashMap<V>
|
||||
|
||||
class TermGraphUnifier {
|
||||
|
||||
companion object {
|
||||
|
|
@ -65,12 +67,12 @@ class TermGraphUnifier {
|
|||
|
||||
private val backref = IdentityHashMap<Any?, Int>()
|
||||
private val origin = ArrayList<Term>()
|
||||
private val innerClass = TIntArrayList()
|
||||
private val innerSchema = TIntArrayList()
|
||||
private val innerSize = TIntArrayList()
|
||||
private val innerVars = TIntObjectHashMap<TIntArrayList?>()
|
||||
private val innerAcyclic = TIntHashSet()
|
||||
private val innerVisited = TIntHashSet()
|
||||
private val innerClass = IntList()
|
||||
private val innerSchema = IntList()
|
||||
private val innerSize = IntList()
|
||||
private val innerVars = IntAnyHashMap<IntList?>()
|
||||
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue