Coverage Summary for Class: Throw (it.unibo.tuprolog.argumentation.core.engine.stdlib.primitive)

Class Class, % Method, % Branch, % Line, % Instruction, %
Throw 100% (1/1) 80% (4/5) 64.3% (9/14) 92.9% (13/14) 95.2% (100/105)


 // 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.primitive
 
 import it.unibo.tuprolog.core.Struct
 import it.unibo.tuprolog.core.Term
 import it.unibo.tuprolog.solve.ExecutionContext
 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.primitive.Solve
 import it.unibo.tuprolog.solve.primitive.UnaryPredicate
 
 /**
  * ISO `throw/1`: raises its argument as a [ResolutionException], to be looked for by an enclosing `catch/3` as
  * the state machine climbs the execution-context stack in `StateException`.
  *
  * If the argument is an `error(Type, Extra)` (or `error(Type)`) compound, it is wrapped into a [LogicError] of
  * the given `it.unibo.tuprolog.solve.exception.error` `Type`; any other term is wrapped into a generic
  * [MessageError] instead.
  *
  * @throws it.unibo.tuprolog.solve.exception.error.InstantiationError if the argument is an unbound variable.
  */
 object Throw : UnaryPredicate<ExecutionContext>("throw") {
     override val help: String =
         """
         `throw(+Ball)`
 
         Raises `Ball` as an exception: resolution unwinds to the innermost enclosing `catch/3` whose catcher unifies with `Ball`, or halts the query if none does. A ball shaped like `error(Type, Context)` (or `error(Type)`) is raised as the standard error of that `Type`, any other term as a user-defined exception carrying it. `Ball` must be instantiated, otherwise an instantiation error is raised instead.
 
         **Examples**
 
         ```prolog
         ?- catch(throw(my_error), E, true).
         E = my_error.
 
         ?- throw(error(type_error(integer, a), my_context)).
         throws error(type_error(integer, a), _).
 
         ?- throw(_).
         throws error(instantiation_error, _).
         ```
         """.trimIndent()
 
     private fun handleError(
         context: ExecutionContext,
         error: Term,
     ): ResolutionException =
         when {
             // arg2p: an error whose type is not a structure (error(1), error(_, C)) is raised as any other ball
             error is Struct && error.functor == "error" && error.arity in 1..2 && error[0] is Struct -> {
                 try {
                     LogicError.of(
                         context = context,
                         type = error[0] as Struct,
                         extraData = if (error.arity > 1) error[1] else null,
                     )
                 } catch (_: IllegalArgumentException) {
                     // arg2p: a type 2P-Kt does not know the arguments of (a domain_error of a domain outside ISO, as
                     // those of the languages of the profiles) is raised as any other ball, its message kept
                     MessageError.of(error, context)
                 }
             }
             else -> MessageError.of(error, context)
         }
 
     override fun Solve.Request<ExecutionContext>.computeAll(first: Term): Sequence<Solve.Response> =
         sequenceOf(
             ensuringAllArgumentsAreInstantiated()
                 .replyException(handleError(context, arguments[0])),
         )
 }