Coverage Summary for Class: NegationAsFailure (it.unibo.tuprolog.argumentation.core.engine.stdlib.rule)

Class Method, % Branch, % Line, % Instruction, %
NegationAsFailure 66.7% (2/3) 75% (3/4) 87.5% (14/16)
NegationAsFailure$Companion
NegationAsFailure$Fail 100% (2/2) 100% (6/6) 100% (60/60)
NegationAsFailure$Success 100% (2/2) 100% (2/2) 100% (7/7)
Total 85.7% (6/7) 91.7% (11/12) 97.6% (81/83)


 // Forked from 2p-kt's solve-prolog 3.0.2 (Apache License 2.0): see README.md in this package
 package it.unibo.tuprolog.argumentation.core.engine.stdlib.rule
 
 import it.unibo.tuprolog.core.Scope
 import it.unibo.tuprolog.core.Term
 import it.unibo.tuprolog.solve.ExecutionContext
 import it.unibo.tuprolog.solve.rule.RuleWrapper
 import it.unibo.tuprolog.solve.stdlib.magic.MagicCut
 import it.unibo.tuprolog.solve.stdlib.primitive.EnsureExecutable
 import kotlin.collections.List as KtList
 
 /**
  * ISO negation-as-failure `'\+'/1`, encoded as the two clauses of the standard Prolog idiom
  * `\+ X :- must_be_executable(X), call(X), !, fail. \+ X :- true.`: [Fail] tries `X`, and if it succeeds at all,
  * cuts (via [it.unibo.tuprolog.solve.stdlib.magic.MagicCut], since an ordinary `!` here would only be
  * transparent up to this clause, not back to the caller that invoked `\+`) and fails; [Success] is only ever
  * reached, via backtracking into the second clause, when [Fail]'s `X` had no solution at all. Both
  * implementations are registered together (see [it.unibo.tuprolog.argumentation.core.engine.stdlib.DefaultBuiltins]) so
  * that ordinary clause selection on `\+/1` finds exactly these two clauses, in order.
  */
 sealed class NegationAsFailure : RuleWrapper<ExecutionContext>(FUNCTOR, ARITY) {
     override val help: String =
         """
         `\+ +Goal`
 
         Negation as failure: succeeds if `Goal` has no solution, and fails as soon as `Goal` has one. It never binds the variables of `Goal`, and is equivalent to `not/1`. `Goal` must be callable, otherwise an instantiation or type error is raised.
 
         **Examples**
 
         ```prolog
         ?- \+ member(d, [a, b, c]).
         yes.
 
         ?- \+ X = 1.
         no.
 
         ?- \+ (X = 1, X = 2), var(X).
         yes.
 
         ?- \+ 3.
         throws error(type_error(callable, 3), _).
         ```
         """.trimIndent()
 
     override val Scope.head: KtList<Term>
         get() = listOf(varOf("X"))
 
     abstract override val Scope.body: Term
 
     /** The first clause: prove `X`, and on success cut back to the caller of `\+` and fail. */
     object Fail : NegationAsFailure() {
         override val Scope.body: Term
             get() =
                 tupleOf(
                     structOf(EnsureExecutable.functor, varOf("X")),
                     structOf(Call.functor, varOf("X")),
                     MagicCut,
                     truthOf(false),
                 )
     }
 
     /** The fallback clause: reached only if [Fail]'s `X` failed outright, so `\+ X` succeeds. */
     object Success : NegationAsFailure() {
         override val Scope.body: Term
             get() = truthOf(true)
     }
 
     companion object {
         const val FUNCTOR: String = "\\+"
 
         const val ARITY: Int = 1
     }
 }