feat: Cut

This commit is contained in:
Tibo De Peuter 2025-04-15 15:37:05 +02:00
parent 6469dd6ced
commit 229a8bbc3c
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
7 changed files with 294 additions and 81 deletions

View file

@ -7,6 +7,7 @@ import prolog.ast.terms.Atom
import prolog.ast.terms.Body
import prolog.ast.terms.Goal
import prolog.ast.logic.LogicOperator
import prolog.exceptions.AppliedCut
/**
* Always fail.
@ -29,21 +30,64 @@ object True : Atom("true"), Body {
// TODO Repeat/0
// TODO !/0 (Cut)
class Cut() : Atom("!") {
override fun satisfy(subs: Substitutions): Answers {
return sequenceOf(Result.failure(AppliedCut(emptyMap())))
}
}
/**
* Conjunction (and). True if both Goal1 and Goal2 are true.
*/
class Conjunction(private val left: LogicOperand, private val right: LogicOperand) : LogicOperator(Atom(","), left, right) {
class Conjunction(private val left: LogicOperand, private val right: LogicOperand) :
LogicOperator(Atom(","), left, right) {
override fun satisfy(subs: Substitutions): Answers = sequence {
left.satisfy(subs).forEach { leftResult ->
leftResult.mapCatching { left ->
right.satisfy(subs + left).forEach { rightResult ->
rightResult.map { right ->
yield(Result.success(left + right))
// Satisfy the left part first, which either succeeds or fails
left.satisfy(subs).forEach { left ->
left.fold(
// If it succeeds, satisfy the right part with the updated substitutions and return all results
onSuccess = { leftSubs ->
right.satisfy(subs + leftSubs).forEach {
it.fold(
// If the right part succeeds, yield the result with the left substitutions
onSuccess = { rightSubs ->
yield(Result.success(leftSubs + rightSubs))
},
// If the right part fails, check if it's a cut
onFailure = { exception ->
if (exception is AppliedCut) {
// If it's a cut, yield the result with the left substitutions
yield(Result.failure(AppliedCut(leftSubs + exception.subs)))
return@sequence
} else {
// If it's not a cut, yield the failure
yield(Result.failure(exception))
}
}
)
}
},
// If it fails, check these conditions:
onFailure = { exception ->
// 1. If the left part is a cut, satisfy the right part ONCE, and stop searching for more solutions
if (exception is AppliedCut) {
right.satisfy(subs + exception.subs).first().fold(
onSuccess = {
// If the right part succeeds, yield the result with the left substitutions
yield(Result.success(exception.subs + it))
return@sequence
},
onFailure = {
// If the right part fails, yield the failure
yield(Result.failure(it))
}
)
} else {
// 2. Any other failure should be returned as is
yield(Result.failure(exception))
}
}
}
)
}
}
}