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

Class Class, % Method, % Branch, % Line, % Instruction, %
StateException 100% (1/1) 100% (10/10) 70% (14/20) 91.5% (43/47) 92.1% (268/291)


 // 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.argumentation.core.engine.stdlib.rule.Catch
 import it.unibo.tuprolog.core.Struct
 import it.unibo.tuprolog.core.Substitution
 import it.unibo.tuprolog.core.Term
 import it.unibo.tuprolog.solve.exception.LogicError
 import it.unibo.tuprolog.solve.exception.ResolutionException
 import it.unibo.tuprolog.solve.exception.error.MessageError
 import it.unibo.tuprolog.solve.exception.error.SystemError
 import it.unibo.tuprolog.unify.Unificator
 import it.unibo.tuprolog.utils.Cursor
 import it.unibo.tuprolog.utils.plus
 
 /**
  * "Exception": reached whenever a primitive's response, or an ISO error raised mid-resolution, carries an
  * exception rather than a substitution. Climbs the execution-context stack (`context.parent`, one frame at a
  * time, mirroring the paper's "search for a `catch/3` frame") looking for a currently-executing
  * `catch(Goal, Catcher, Recovery)` whose `Catcher` unifies with the exception's content. If found, the recovery
  * goal becomes the new goal stream and the machine resumes at `StateGoalSelection`; if the root context is
  * reached without a match, it moves to `StateHalt` with the exception attached to the emitted solution (an
  * internal [it.unibo.tuprolog.solve.exception.error.MessageError] is converted to a public
  * [it.unibo.tuprolog.solve.exception.error.SystemError] at that point).
  */
 data class StateException(
     override val exception: ResolutionException,
     override val context: PrologExecutionContext,
 ) : AbstractState(context),
     ExceptionalState {
     private fun Struct.isCatch(): Boolean = arity == 3 && functor == Catch.functor
 
     private fun LogicError.getExceptionContent(): Term =
         when (this) {
             is MessageError -> content
             else -> errorStruct
         }
 
     private fun ResolutionException.toPublicException(): ResolutionException =
         when (this) {
             is MessageError -> SystemError.forUncaughtError(this)
             else -> this
         }
 
     private val finalState: EndState
         get() =
             StateHalt(
                 exception.toPublicException(),
                 context.copy(step = nextStep()),
             )
 
     private val handleExceptionInParentContext: StateException
         get() =
             StateException(
                 exception,
                 context.parent!!.copy(step = nextStep()),
             )
 
     private fun handleStruct(
         unificator: Unificator,
         catchGoal: Struct,
         error: LogicError,
     ): State =
         when {
             catchGoal.isCatch() -> {
                 val catcher = unificator.mgu(catchGoal[1], error.getExceptionContent())
                 handleCatch(catchGoal, catcher)
             }
             context.isRoot -> finalState
             else -> handleExceptionInParentContext
         }
 
     private fun handleCatch(
         catchGoal: Struct,
         catcher: Substitution,
     ) = when {
         catcher.isSuccess -> {
             val newSubstitution =
                 (context.substitution + catcher).filter { (it, _) ->
                     context.isVariableInteresting(it)
                 }
             val subGoals = catchGoal[2][newSubstitution]
             val newGoals = subGoals.toGoals() + context.goals.next
 
             StateGoalSelection(
                 context.copy(
                     goals = newGoals,
                     rules = Cursor.empty(),
                     primitives = Cursor.empty(),
                     substitution = newSubstitution.castToUnifier(),
                     step = nextStep(),
                 ),
             )
         }
         context.isRoot -> finalState
         else -> handleExceptionInParentContext
     }
 
     override fun computeNext(): State =
         when (exception) {
             is LogicError -> {
                 val catchGoal = context.currentGoal!!
                 when {
                     catchGoal.isStruct -> handleStruct(context.unificator, catchGoal.castToStruct(), exception)
                     context.isRoot -> finalState
                     else -> handleExceptionInParentContext
                 }
             }
             else -> finalState
         }
 }