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/ast/logic/Clause.kt
2025-05-04 21:50:58 +02:00

87 lines
3.5 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package prolog.ast.logic
import prolog.Answers
import prolog.ast.Database.Program
import prolog.Substitutions
import prolog.ast.terms.*
import prolog.builtins.True
import prolog.flags.AppliedCut
import prolog.logic.applySubstitution
import prolog.logic.numbervars
import prolog.logic.occurs
import prolog.logic.unifyLazy
/**
* Sentence of a Prolog program.
*
* A clause consists of a [Head] and body separated by the neck operator, or it is a [Fact].
*
* @see [Variable]
* @see [Predicate]
*/
abstract class Clause(var head: Head, var body: Body) : Term, Resolvent {
val functor: Functor = head.functor
override fun solve(goal: Goal, subs: Substitutions): Answers = sequence {
// If the clause is a rule, unify the goal with the head and then try to prove the body.
// Only if the body can be proven, the substitutions should be returned.
// Do this in a lazy way.
// Since we are only interested in substitutions in the goal (as opposed to the head of this clause),
// we can use variable renaming and filter out the substitutions that are not in the goal.
val (end, renamed: Substitutions) = numbervars(head, Program.variableRenamingStart, subs)
val reverse = renamed.entries.associate { (a, b) -> b to a }
Program.variableRenamingStart = end
var newSubs: Substitutions = subs + renamed
unifyLazy(applySubstitution(goal, subs), head, newSubs).forEach { headAnswer ->
headAnswer.map { headSubs ->
// If the body can be proven, yield the (combined) substitutions
newSubs = subs + renamed + headSubs
body.satisfy(newSubs).forEach { bodyAnswer ->
bodyAnswer.fold(
onSuccess = { bodySubs ->
var result = (headSubs + bodySubs)
.mapKeys { applySubstitution(it.key, reverse)}
.mapValues { applySubstitution(it.value, reverse) }
result = result.map { it.key to applySubstitution(it.value, result) }
.toMap()
.filterNot { it.key in renamed.keys && !occurs(it.key as Variable, goal, emptyMap())}
yield(Result.success(result))
},
onFailure = { error ->
if (error is AppliedCut) {
// Find single solution and return immediately
if (error.subs != null) {
yield(Result.failure(AppliedCut(headSubs + error.subs)))
} else {
yield(Result.failure(AppliedCut()))
}
return@sequence
} else {
yield(Result.failure(error))
}
}
)
}
}
}
}
override fun toString(): String = if (body is True) head.toString() else "$head :- $body"
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Clause) return false
if (head != other.head) return false
if (body != other.body) return false
return true
}
override fun hashCode(): Int {
return super.hashCode()
}
}