Fix bits of logic responsible for launching 'at-start' rules through 'main'. Fix handling of discarded occs
This commit is contained in:
parent
767eb057f5
commit
5a9019e85c
|
|
@ -62,7 +62,9 @@ internal class ControllerImpl (
|
|||
// FIXME noLogicalContext
|
||||
val context = Context(NORMAL(), noLogicalContext)
|
||||
|
||||
activateConstraint(constraint, emptyJusts(), context)
|
||||
// fixme: is it valid to always provide current justifications?
|
||||
// while this method is used only in one place at program kick-off, yes, it's initial justs provided.
|
||||
activateConstraint(constraint, state.justs(), context)
|
||||
|
||||
return context.currentStatus()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ interface MatchJournal : MutableIterable<MatchJournal.Chunk> {
|
|||
}.map { it.wrapped }
|
||||
|
||||
abstract fun findOccurrence(ctr: Constraint): Occurrence?
|
||||
abstract fun principalConstraint(): Constraint?
|
||||
abstract fun principalOccurrence(): Occurrence?
|
||||
|
||||
override fun toString() = "(id=$id, $justifications, ${match.rule().uniqueTag()}, ${entriesLog()})"
|
||||
|
||||
|
|
@ -126,7 +126,8 @@ internal open class MatchJournalImpl(
|
|||
if (view == null) {
|
||||
hist = LinkedList()
|
||||
nextChunkId = 0
|
||||
val initChunk = ChunkImpl(ispec, InitRuleMatch, nextChunkId++, justsOf())
|
||||
val initChunk = ChunkImpl(ispec, InitRuleMatch, nextChunkId, justsOf(nextChunkId))
|
||||
nextChunkId++
|
||||
hist.add(initChunk)
|
||||
} else {
|
||||
// fixme: check somehow that initial chunk is present?
|
||||
|
|
@ -201,7 +202,15 @@ internal open class MatchJournalImpl(
|
|||
}
|
||||
|
||||
private fun replayOccurrences(controller: Controller, occSpecs: Iterable<MatchJournal.Chunk.Entry>) =
|
||||
occSpecs.forEach { if (it.isDiscarded) it.occ.terminate(controller) else it.occ.revive(controller) }
|
||||
occSpecs.forEach {
|
||||
if (it.isDiscarded) {
|
||||
it.occ.terminate(controller)
|
||||
it.occ.stored = false
|
||||
} else {
|
||||
it.occ.revive(controller)
|
||||
it.occ.stored = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun view() = MatchJournal.View(ArrayList(hist), nextChunkId)
|
||||
|
|
@ -236,17 +245,10 @@ internal open class MatchJournalImpl(
|
|||
override fun entriesLog(): List<MatchJournal.Chunk.Entry> = occurrences
|
||||
|
||||
override fun findOccurrence(ctr: Constraint): Occurrence? =
|
||||
occurrences.find { !it.isDiscarded && it.occ.constraint.symbol() == ctr.symbol() }?.occ
|
||||
activatedLog().find { it.constraint.symbol() == ctr.symbol() }
|
||||
|
||||
override fun principalConstraint(): Constraint? {
|
||||
try {
|
||||
val body = match.rule().bodyAlternation().first()
|
||||
val pProds = body.filter { it is Constraint && it.isPrincipal() }
|
||||
return pProds.first() as Constraint
|
||||
} catch (e: NoSuchElementException) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
override fun principalOccurrence(): Occurrence? =
|
||||
activatedLog().find { ispec.isPrincipal(it.constraint) }
|
||||
}
|
||||
|
||||
class IndexImpl(ispec: IncrementalProgramSpec, chunks: Iterable<MatchJournal.Chunk>): MatchJournal.Index
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
|
|||
: StoreAwareJournalImpl(journal)
|
||||
{
|
||||
private val ruleOrder: Map<Any, Int> = HashMap<Any, Int>().apply {
|
||||
put(MatchJournalImpl.InitRuleMatch.rule().uniqueTag(), -1)
|
||||
ruleIndex.forEachIndexed { index, rule -> put(rule.uniqueTag(), index) }
|
||||
}
|
||||
|
||||
|
|
@ -196,39 +197,35 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
|
|||
|
||||
private data class MatchCandidate(val rule: Rule, val occ: Occurrence, val occParentId: Int)
|
||||
|
||||
private fun canMatch(rule: Rule, occ: Occurrence): Boolean =
|
||||
(rule.headKept() + rule.headReplaced()).find { it.symbol() == occ.constraint.symbol() } != null
|
||||
|
||||
// todo: what if non-principal rules will be passed as input there?
|
||||
// only rules that can match on principal occurrences will pass through this method to the queue
|
||||
// so, no non-principal rule should pass it.
|
||||
fun addRuleMatches(rules: Iterable<Rule>) {
|
||||
|
||||
val activationCandidates = mutableListOf<MatchCandidate>()
|
||||
val (headedRules, headlessRules) = rules.partition { rule ->
|
||||
rule.headKept().count() != 0 || rule.headReplaced().count() != 0
|
||||
}
|
||||
|
||||
val it = this.iterator()
|
||||
var prevChunk = it.next() // skip initial chunk
|
||||
|
||||
while (it.hasNext()) { // note: if there's only an initial chunk, we have nothing to do
|
||||
val chunk = it.next()
|
||||
val pCtr = chunk.principalConstraint()
|
||||
var chunk = it.next() // initial chunk
|
||||
var prevChunk = chunk
|
||||
|
||||
while (true) {
|
||||
// Does this chunk have principal occurrence and can activate anything at all?
|
||||
if (pCtr != null) {
|
||||
for (rule in headedRules) {
|
||||
val pOcc = chunk.principalOccurrence()
|
||||
if (pOcc != null) {
|
||||
|
||||
for (rule in rules) {
|
||||
// Can this rule be matched by principal occurrence?
|
||||
// fixme: maybe use RuleIndex here?
|
||||
if (rule.headKept().contains(pCtr) || rule.headReplaced().contains(pCtr)) {
|
||||
|
||||
val princOcc = chunk.findOccurrence(pCtr)
|
||||
?: throw IllegalStateException("Chunk with principal occurrence must have it in activated occurrences!")
|
||||
if (canMatch(rule, pOcc)) {
|
||||
|
||||
// Then we will need to find the place among existing child chunks
|
||||
// (i.e. among some number of following ones)
|
||||
// to activate this occurrence, to (possibly) match this rule.
|
||||
// Also remember the parent justification of this rule candidate
|
||||
// to drop it from monitoring when child chunks end.
|
||||
activationCandidates.add(MatchCandidate(rule, princOcc, chunk.id))
|
||||
activationCandidates.add(MatchCandidate(rule, pOcc, chunk.id))
|
||||
// todo: also use the rule to help Dispatcher in future? i.e. try matching only on the candidate rule
|
||||
}
|
||||
}
|
||||
|
|
@ -256,19 +253,9 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
|
|||
aIt.remove()
|
||||
}
|
||||
|
||||
// Case for headless rules, which are activated at the 'top' level according to rule order in RuleIndex
|
||||
if (chunk.isTopLevel()) {
|
||||
for (candRule in headlessRules) {
|
||||
val placeToInsertFound = compareRuleOrders(chunk.match.rule(), candRule) > 0
|
||||
if (placeToInsertFound) {
|
||||
// TODO: how 'at start' rules get activated?
|
||||
// execQueue.offer(ExecPos(prevChunk, candOcc))
|
||||
// todo: remove added rule from headlessRules
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!it.hasNext()) break
|
||||
prevChunk = chunk
|
||||
chunk = it.next()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -292,12 +279,13 @@ internal class ProcessingStateImpl(private var dispatchingFront: Dispatcher.Disp
|
|||
|
||||
// Handles the case when several matches are added to the same position.
|
||||
// Then shouldn't replay, because currentPos is valid and more recent (!) than execPos.
|
||||
if (execPos != prevPos) {
|
||||
if (execPos != prevPos)
|
||||
replay(controller, execPos.pos)
|
||||
}
|
||||
prevPos = execPos
|
||||
|
||||
controller.reactivate(execPos.activeOcc)
|
||||
// If the occurrence is still in the store after replay (i.e. if it's valid to activate it)
|
||||
if (execPos.activeOcc.stored)
|
||||
controller.reactivate(execPos.activeOcc)
|
||||
} while (execQueue.isNotEmpty())
|
||||
}
|
||||
// Also replay to the end after queue is fully executed
|
||||
|
|
|
|||
|
|
@ -203,35 +203,41 @@ class TestIncrementalProgram {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Ignore("unresolved yet")
|
||||
fun addAtStartMatch() {
|
||||
val progSpec = MockIncrProgSpec(
|
||||
setOf("main", "empty-head.bar"),
|
||||
setOf(sym0("foo"))
|
||||
setOf("main.foo", "main.bar"),
|
||||
setOf(sym0("main"))
|
||||
)
|
||||
programWithRules(
|
||||
rule("main",
|
||||
headReplaced(
|
||||
constraint("main")
|
||||
// 'at-start' rules are launched with a 'main' occurrence, their heads aren't actually empty
|
||||
rule("main.foo",
|
||||
headKept(
|
||||
princConstraint("main")
|
||||
),
|
||||
body(
|
||||
princConstraint("foo")
|
||||
constraint("foo")
|
||||
))
|
||||
).launch("addRule", progSpec) { result ->
|
||||
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"))
|
||||
|
||||
println(result.token().journalView)
|
||||
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("main"))
|
||||
result.token().journalView.chunks.size shouldBe 2
|
||||
result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("foo"))
|
||||
|
||||
}.also { (builder, evalRes) ->
|
||||
builder.programWithRules(
|
||||
rule("empty-head.bar",
|
||||
headKept(),
|
||||
rule("main.bar",
|
||||
headKept(
|
||||
princConstraint("main")
|
||||
),
|
||||
body(
|
||||
constraint("bar")
|
||||
)
|
||||
)
|
||||
).relaunch("atStart", progSpec, evalRes.token()) { result ->
|
||||
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("bar"))
|
||||
|
||||
println(result.token().journalView)
|
||||
result.storeView().constraintSymbols() shouldBe setOf(sym0("foo"), sym0("main"), sym0("bar"))
|
||||
result.token().journalView.chunks.size shouldBe 3
|
||||
result.lastChunk().activatedLog().constraintSymbols() shouldBe listOf(sym0("bar"))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue