Coverage Summary for Class: StatePrimitiveExecution (it.unibo.tuprolog.argumentation.core.engine.fsm)

Class Class, % Method, % Branch, % Line, % Instruction, %
StatePrimitiveExecution 100% (1/1) 88.9% (8/9) 68.2% (15/22) 93.5% (29/31) 94.1% (272/289)


 // 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.fsm
 
 import it.unibo.tuprolog.argumentation.core.engine.PrologExecutionContext
 import it.unibo.tuprolog.core.Substitution
 import it.unibo.tuprolog.core.Term
 import it.unibo.tuprolog.utils.Cursor
 
 /**
  * "Primitive Execution": consumes exactly one response from the current primitive's response stream
  * ([PrologExecutionContext.primitives]). A successful response merges its substitution and returns to
  * `StateGoalSelection`; an empty stream moves to `StateBacktracking`; an exceptional response moves to
  * `StateException`.
  */
 data class StatePrimitiveExecution(
     override val context: PrologExecutionContext,
 ) : AbstractState(context) {
     private fun PrologExecutionContext.copyFromCurrentPrimitive(
         goals: Cursor<out Term>? = null,
         procedureFromAncestor: Int = 0,
         substitution: Substitution? = null,
     ): PrologExecutionContext {
         val ctx = primitives.current?.let { apply(it.sideEffects) } ?: this
         return ctx.copy(
             goals = goals ?: this.goals,
             procedure = pathToRoot.drop(procedureFromAncestor).map { it.procedure }.first(),
             primitives = Cursor.empty(),
             substitution = (substitution ?: this.substitution) as Substitution.Unifier,
             step = nextStep(),
         )
     }
 
     // arg2p: the answer of a primitive binds the variables of its goal, which had the substitution applied: it extends
     // the substitution (see extendedWith) unless it binds a variable the substitution does, merged then
     private fun merged(answer: Substitution): Substitution {
         val substitution = context.substitution
         val unifier = answer.takeIf { it.isSuccess }?.castToUnifier()
         return if (unifier != null && unifier.keys.none { it in substitution }) {
             substitution.extendedWith(unifier)
         } else {
             context.unificator.merge(substitution, answer, occurCheckEnabled = false)
         }
     }
 
     override fun computeNext(): State =
         try {
             context.primitives.current?.solution?.whenIs(
                 yes = {
                     StateGoalSelection(
                         context.copyFromCurrentPrimitive(
                             goals = context.goals.next,
                             procedureFromAncestor = 1,
                             substitution = merged(it.substitution),
                         ),
                     )
                 },
                 no = {
                     StateBacktracking(context.parent!!.copyFromCurrentPrimitive())
                 },
                 halt = {
                     StateException(
                         it.exception.updateLastContext(context.skipThrow()),
                         context.copyFromCurrentPrimitive(),
                     )
                 },
                 otherwise = { throw IllegalStateException("This should never happen") },
             ) ?: StateBacktracking(context.copyFromCurrentPrimitive())
         } catch (exception: Exception) {
             StateException(exception.asResolutionException(context).updateLastContext(context.skipThrow()), context.copy(step = nextStep()))
         }
 }