This repository has been archived on 2025-09-23. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
2025LogProg-project-GhentPr.../src/prolog/builtins/controlOperators.kt
2025-05-05 18:04:37 +02:00

148 lines
5.6 KiB
Kotlin

package prolog.builtins
import prolog.Answers
import prolog.Substitutions
import prolog.ast.logic.LogicOperand
import prolog.ast.logic.LogicOperator
import prolog.ast.terms.Atom
import prolog.ast.terms.Body
import prolog.ast.terms.Goal
import prolog.ast.terms.Structure
import prolog.flags.AppliedCut
import prolog.logic.applySubstitution
/**
* Always fail.
*/
object Fail : Atom("fail"), Body {
override fun satisfy(subs: Substitutions): Answers = emptySequence()
override fun applySubstitution(subs: Substitutions): Fail = Fail
}
/**
* Same as fail, but the name has a more declarative connotation.
*/
typealias False = Fail
/**
* Always succeed.
*/
object True : Atom("true"), Body {
override fun satisfy(subs: Substitutions): Answers = sequenceOf(Result.success(emptyMap()))
override fun applySubstitution(subs: Substitutions): True = True
}
// TODO Repeat/0
class Cut() : Atom("!") {
override fun satisfy(subs: Substitutions): Answers {
return sequenceOf(Result.failure(AppliedCut(emptyMap())))
}
override fun applySubstitution(subs: Substitutions): Cut = Cut()
}
/**
* Conjunction (and). True if both Goal1 and Goal2 are true.
*/
class Conjunction(val left: LogicOperand, private val right: LogicOperand) :
LogicOperator(Atom(","), left, right) {
override fun satisfy(subs: Substitutions): Answers = sequence {
// 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 (exception.subs != null) {
// If it's a cut, yield the result with the left substitutions
yield(Result.failure(AppliedCut(leftSubs + exception.subs)))
} else {
yield(Result.failure(AppliedCut()))
}
return@sequence
}
// 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!!)).firstOrNull()?.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))
}
) ?: yield(Result.failure(AppliedCut()))
} else {
// 2. Any other failure should be returned as is
yield(Result.failure(exception))
}
}
)
}
}
override fun applySubstitution(subs: Substitutions): Conjunction = Conjunction(
applySubstitution(left, subs) as LogicOperand,
applySubstitution(right, subs) as LogicOperand
)
}
/**
* Disjunction (or). True if either Goal1 or Goal2 succeeds.
*/
open class Disjunction(private val left: LogicOperand, private val right: LogicOperand) :
LogicOperator(Atom(";"), left, right) {
override fun satisfy(subs: Substitutions): Answers = sequence {
yieldAll(left.satisfy(subs))
yieldAll(right.satisfy(subs))
}
override fun applySubstitution(subs: Substitutions): Disjunction = Disjunction(
applySubstitution(left, subs) as LogicOperand,
applySubstitution(right, subs) as LogicOperand
)
}
@Deprecated("Use Disjunction instead")
class Bar(leftOperand: LogicOperand, rightOperand: LogicOperand) : Disjunction(leftOperand, rightOperand)
// TODO ->
// TODO *->
/**
* True if 'Goal' cannot be proven.
*/
class Not(private val goal: Goal) : LogicOperator(Atom("\\+"), rightOperand = goal) {
override fun satisfy(subs: Substitutions): Answers {
// If the goal can be proven, return an empty sequence
val goalResults = goal.satisfy(subs).iterator()
if (goalResults.hasNext()) {
return emptySequence()
}
// If the goal cannot be proven, return a sequence with an empty map
return sequenceOf(Result.success(emptyMap()))
}
override fun applySubstitution(subs: Substitutions): Not = Not(applySubstitution(goal, subs) as Goal)
}