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. val (headEnd, headRenaming) = numbervars(head, Program.variableRenamingStart, subs) Program.variableRenamingStart = headEnd val renamedHead = applySubstitution(head, headRenaming) unifyLazy(goal, renamedHead, subs).forEach { headAnswer -> headAnswer.map { headSubs -> // If the body can be proven, yield the (combined) substitutions val preBody = applySubstitution(body, headRenaming + headSubs) as Body preBody.satisfy(subs).forEach { bodyAnswer -> bodyAnswer.fold( onSuccess = { bodySubs -> val newSubs = headSubs + bodySubs val result = newSubs .mapValues { applySubstitution(it.value, newSubs) } .filter { it.key !is AnonymousVariable && 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() } }