Coverage Summary for Class: AbstractModuleGoal (it.unibo.tuprolog.argumentation.core.primitives.modules)

Class Method, % Branch, % Line, % Instruction, %
AbstractModuleGoal 100% (4/4) 50% (2/4) 88.9% (16/18) 83.9% (94/112)
AbstractModuleGoal$solve$1 100% (2/2) 50% (1/2) 100% (5/5) 90.7% (39/43)
Total 100% (6/6) 50% (3/6) 91.3% (21/23) 85.8% (133/155)


 package it.unibo.tuprolog.argumentation.core.primitives.modules
 
 import it.unibo.tuprolog.argumentation.core.engine.DelegatedCall
 import it.unibo.tuprolog.argumentation.core.engine.Delegation
 import it.unibo.tuprolog.argumentation.core.engine.Inlining
 import it.unibo.tuprolog.argumentation.core.primitives.PrimitiveWithSignature
 import it.unibo.tuprolog.argumentation.core.runtime.modules.DynamicLoader
 import it.unibo.tuprolog.argumentation.core.runtime.modules.Module
 import it.unibo.tuprolog.argumentation.core.runtime.modules.solveForCaller
 import it.unibo.tuprolog.core.Atom
 import it.unibo.tuprolog.core.Struct
 import it.unibo.tuprolog.solve.ExecutionContext
 import it.unibo.tuprolog.solve.Solution
 import it.unibo.tuprolog.solve.SolveOptions
 import it.unibo.tuprolog.solve.TimeDuration
 import it.unibo.tuprolog.solve.exception.error.TypeError
 import it.unibo.tuprolog.solve.primitive.Solve
 
 /**
  * A module call: its goal solved against the libraries of the module, loaded by [loader] at the first call.
  * The arg2p engine inlines it (DelegatedCall): the goal is solved in place, against the libraries of the module.
  * Other solvers solve it in a solver of the module, cached.
  */
 internal abstract class AbstractModuleGoal(
     private val loader: DynamicLoader,
 ) : PrimitiveWithSignature,
     DelegatedCall {
     /** What the call does before its goal is solved, in [module] */
     protected abstract fun enter(
         request: Solve.Request<ExecutionContext>,
         module: Module,
     )
 
     // The module and the goal of the call, checked
     private fun call(request: Solve.Request<ExecutionContext>): Pair<Atom, Struct> {
         val name = request.arguments[0]
         val goal = request.arguments[1]
         if (name !is Atom) {
             throw TypeError.forGoal(request.context, request.signature, TypeError.Expected.ATOM, name)
         }
         if (goal !is Struct) {
             throw TypeError.forGoal(request.context, request.signature, TypeError.Expected.CALLABLE, goal)
         }
         return name to goal
     }
 
     override fun delegate(request: Solve.Request<ExecutionContext>): Delegation {
         val (name, goal) = call(request)
         val module = loader.modules.of(request, name)
         enter(request, module)
         return Inlining(goal, module.libraries)
     }
 
     override fun solve(request: Solve.Request<ExecutionContext>): Sequence<Solve.Response> {
         val (name, goal) = call(request)
         val module = loader.modules.of(request, name)
         enter(request, module)
         val solver = loader.solverOf(request, module)
         return sequence {
             yieldAll(
                 solver.solveForCaller(goal, SolveOptions.allLazilyWithTimeout(TimeDuration.MAX_VALUE)).map {
                     when (it) {
                         // Halt carries a failed substitution, so replying with it would turn an
                         // error inside the module into an ordinary failure: indistinguishable
                         // from a goal that simply does not hold.
                         is Solution.Halt -> throw it.exception
                         else -> request.replyWith(it.substitution)
                     }
                 },
             )
         }
     }
 }