All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.it.unibo.tuprolog.solve.TestingClauseTheories.kt Maven / Gradle / Ivy

package it.unibo.tuprolog.solve

import it.unibo.tuprolog.core.Struct
import it.unibo.tuprolog.core.Term
import it.unibo.tuprolog.core.TermVisitor
import it.unibo.tuprolog.core.Var
import it.unibo.tuprolog.dsl.theory.prolog
import it.unibo.tuprolog.solve.PrologStandardExampleTheories.allPrologStandardTestingTheoryToRespectiveGoalsAndSolutions
import it.unibo.tuprolog.solve.exception.HaltException
import it.unibo.tuprolog.solve.exception.TimeOutException
import it.unibo.tuprolog.solve.exception.error.InstantiationError
import it.unibo.tuprolog.solve.exception.error.SystemError
import it.unibo.tuprolog.solve.exception.error.TypeError
import it.unibo.tuprolog.theory.Theory

/**
 * An object containing a collection of notable databases to be used when testing Solver functionality
 *
 * @author Enrico
 */
object TestingClauseTheories {

    internal val aContext = DummyInstances.executionContext

    internal val haltException = HaltException(context = aContext)

    internal fun instantiationError(functor: String, arity: Int, culprit: Var, index: Int? = null) =
        instantiationError(Signature(functor, arity), culprit, index)

    internal fun instantiationError(signature: Signature, culprit: Var, index: Int? = null) =
        if (index != null) {
            InstantiationError.forArgument(aContext, signature, culprit, index)
        } else {
            InstantiationError.forGoal(aContext, signature, culprit)
        }

    internal fun typeError(functor: String, arity: Int, actualValue: Term, index: Int? = null) =
        typeError(Signature(functor, arity), actualValue, index)

    internal fun typeError(signature: Signature, actualValue: Term, index: Int? = null) =
        if (index != null) {
            TypeError.forArgument(aContext, signature, TypeError.Expected.CALLABLE, actualValue, index)
        } else {
            TypeError.forGoal(aContext, signature, TypeError.Expected.CALLABLE, actualValue)
        }

    internal fun systemError(uncaught: Term) = SystemError.forUncaughtException(aContext, uncaught)

    internal val timeOutException = TimeOutException(context = aContext, exceededDuration = 1)

    /** Utility function to deeply replace all occurrences of one functor with another in a Struct */
    internal fun Struct.replaceAllFunctors(oldFunctor: String, withFunctor: String): Struct =
        prolog {
            val synonymReplacer = object : TermVisitor {
                override fun defaultValue(term: Term): Term = term
                override fun visit(term: Struct): Term = when (term.functor) {
                    oldFunctor -> withFunctor(term.args.single().accept(this))
                    else -> term.args.map { it.accept(this) }.let {
                        if (it.none()) term
                        else term.functor(it.first(), *it.drop(1).toTypedArray())
                    }
                }
            }

            [email protected](synonymReplacer) as Struct
        }

    /**
     * A database containing the following facts:
     * ```prolog
     * f(a).
     * g(a).
     * g(b).
     * h(a).
     * h(b).
     * h(c).
     * ```
     */
    val simpleFactTheory by lazy {
        prolog {
            theory(
                { "f"("a") },
                { "g"("a") },
                { "g"("b") },
                { "h"("a") },
                { "h"("b") },
                { "h"("c") }
            )
        }
    }

    /**
     * Notable [simpleFactTheory] request goals and respective expected [Solution]s
     * ```prolog
     * ?- f(A).
     * ?- g(A).
     * ?- h(A).
     * ```
     */
    val simpleFactTheoryNotableGoalToSolutions by lazy {
        prolog {
            ktListOf(
                "f"("A").hasSolutions(
                    { yes("A" to "a") }
                ),
                "g"("A").hasSolutions(
                    { yes("A" to "a") },
                    { yes("A" to "b") }
                ),
                "h"("A").hasSolutions(
                    { yes("A" to "a") },
                    { yes("A" to "b") },
                    { yes("A" to "c") }
                )
            )
        }
    }

    /**
     * A database containing the following rules:
     * ```prolog
     * f(only) :- !.
     * f(a).
     *
     * g(a).
     * g(only) :- !.
     * g(b).
     *
     * h(A) :- e(A).
     * h(A) :- d(A).
     * e(a) :- !.
     * e(b).
     * d(c).
     * d(d).
     * ```
     */
    val simpleCutTheory = prolog {
        theory(
            { "f"("only") `if` "!" },
            { "f"("a") },

            { "g"("a") },
            { "g"("only") `if` "!" },
            { "g"("b") },

            { "h"("A") `if` "e"("A") },
            { "h"("A") `if` "d"("A") },
            { "e"("a") `if` "!" },
            { "e"("b") },
            { "d"("c") },
            { "d"("d") }
        )
    }

    /**
     * Notable [simpleCutTheory] request goals and respective expected [Solution]s
     * ```prolog
     * ?- f(A).
     * ?- g(A).
     * ?- h(A).
     * ```
     */
    val simpleCutTheoryNotableGoalToSolutions by lazy {
        prolog {
            ktListOf(
                "f"("A").hasSolutions(
                    { yes("A" to "only") }
                ),
                "g"("A").hasSolutions(
                    { yes("A" to "a") },
                    { yes("A" to "only") }
                ),
                "h"("A").hasSolutions(
                    { yes("A" to "a") },
                    { yes("A" to "c") },
                    { yes("A" to "d") }
                )
            )
        }
    }

    /**
     * A database containing the following rules:
     * ```prolog
     * f(X, Y) :- q(X), !, r(Y).
     * f(X, Y) :- r(X).
     * q(a).
     * q(b).
     * r(a1).
     * r(b1).
     * ```
     */
    val simpleCutAndConjunctionTheory = prolog {
        theory(
            { "f"("X", "Y") `if` ("q"("X") and "!" and "r"("Y")) },
            { "f"("X", "Y") `if` "r"("X") },
            { "q"("a") },
            { "q"("b") },
            { "r"("a1") },
            { "r"("b1") }
        )
    }

    /**
     * Notable [simpleCutAndConjunctionTheory] request goals and respective expected [Solution]s
     * ```prolog
     * ?- f(A, B).
     * ```
     */
    val simpleCutAndConjunctionTheoryNotableGoalToSolutions by lazy {
        prolog {
            ktListOf(
                "f"("A", "B").hasSolutions(
                    { yes("A" to "a", "B" to "a1") },
                    { yes("A" to "a", "B" to "b1") }
                )
            )
        }
    }

    /**
     * A database containing the following rules:
     * ```prolog
     * a(X) :- b(X).
     * a(6).
     * b(X) :- c(X), d(X).
     * b(4) :- !.
     * b(5).
     * c(1).
     * c(2) :- !.
     * c(3).
     * d(2).
     * d(3).
     * ```
     */
    val cutConjunctionAndBacktrackingTheory = prolog {
        theory(
            { "a"("X") `if` "b"("X") },
            { "a"(6) },
            { "b"("X") `if` ("c"("X") and "d"("X")) },
            { "b"(4) `if` "!" },
            { "b"(5) },
            { "c"(1) },
            { "c"(2) `if` "!" },
            { "c"(3) },
            { "d"(2) },
            { "d"(3) }
        )
    }

    /**
     * Notable [cutConjunctionAndBacktrackingTheory] request goals and respective expected [Solution]s
     * ```prolog
     * ?- a(X).
     * ```
     */
    val cutConjunctionAndBacktrackingTheoryNotableGoalToSolutions by lazy {
        prolog {
            ktListOf(
                "a"("X").hasSolutions(
                    { yes("X" to 2) },
                    { yes("X" to 4) },
                    { yes("X" to 6) }
                )
            )
        }
    }

    /**
     * A database containing the following rules:
     * ```prolog
     * a :- b.
     * b :- a.
     * ```
     */
    val infiniteComputationTheory = prolog {
        theory(
            { "a" `if` "b" },
            { "b" `if` "a" }
        )
    }

    /**
     * Notable [infiniteComputationTheory] request goals and respective expected [Solution]s
     * ```prolog
     * ?- a(X).
     * ```
     */
    val infiniteComputationTheoryNotableGoalToSolution by lazy {
        prolog {
            ktListOf(
                atomOf("a").hasSolutions({ halt(timeOutException) })
            )
        }
    }

    /**
     * A database that implements custom "reverse" over lists; it should test backtracking functionality
     *
     * ```prolog
     * my_reverse(L1, L2) :- my_rev(L1, L2, []).
     *
     * my_rev([], L2, L2) :- !.
     * my_rev([X | Xs], L2, Acc) :- my_rev(Xs, L2, [X | Acc]).
     * ```
     */
    val customReverseListTheory by lazy {
        prolog {
            theory(
                { "my_reverse"("L1", "L2") `if` "my_rev"("L1", "L2", emptyList) },
                { "my_rev"(emptyList, "L2", "L2") `if` "!" },
                {
                    "my_rev"(consOf("X", "Xs"), "L2", "Acc") `if`
                        "my_rev"("Xs", "L2", consOf("X", "Acc"))
                }
            )
        }
    }

    /**
     * Notable [customReverseListTheory] request goals and respective expected [Solution]s
     * ```prolog
     * ?- my_reverse([1, 2, 3, 4], L).
     * ```
     */
    val customReverseListTheoryNotableGoalToSolution by lazy {
        prolog {
            ktListOf(
                "my_reverse"(listOf(1, 2, 3, 4), "L").hasSolutions(
                    { yes("L" to listOf(4, 3, 2, 1)) }
                )
            )
        }
    }

    /**
     * A database that implements custom "range" to generate lists; it should test backtracking functionality along with arithmetic
     *
     * ```prolog
     * range(N, N, [N]) :- !.
     * range(M, N, [M | Ns]) :-
     *       M < N,
     *       M1 is M + 1,
     *       range(M1, N, Ns).
     * ```
     */
    val customRangeListGeneratorTheory by lazy {
        prolog {
            theory(
                { "range"("N", "N", listOf("N")) `if` "!" },
                {
                    "range"("M", "N", consOf("M", "Ns")) `if` (
                        "<"("M", "N") and
                            ("M1" `is` (varOf("M") + 1)) and
                            "range"("M1", "N", "Ns")
                        )
                }
            )
        }
    }

    /**
     * Notable [customRangeListGeneratorTheory] request goals and respective expected [Solution]s
     * ```prolog
     * ?- range(1, 4, [1, 2, 3, 4]).
     * ?- range(1, 4, [1, 2, 3, 4, 5]).
     * ?- range(1, 4, L).
     * ?- range(1, 1, L).
     * ?- range(2, 1, L).
     * ?- range(A, 4, [2, 3, 4]).
     * ?- range(2, A, [2, 3, 4]).
     * ```
     */
    val customRangeListGeneratorTheoryNotableGoalToSolution by lazy {
        val N = customRangeListGeneratorTheory.last().head!![1] as Var
        prolog {
            ktListOf(
                "range"(1, 4, listOf(1, 2, 3, 4)).hasSolutions({ yes() }),
                "range"(1, 4, listOf(1, 2, 3, 4, 5)).hasSolutions({ no() }),
                "range"(1, 4, "L").hasSolutions({ yes("L" to listOf(1, 2, 3, 4)) }),
                "range"(1, 1, "L").hasSolutions({ yes("L" to listOf(1)) }),
                "range"(2, 1, "L").hasSolutions({ no() }),
                "range"("A", 4, listOf(2, 3, 4)).hasSolutions({ yes("A" to 2) }),
                "range"(2, "A", listOf(2, 3, 4)).hasSolutions(
                    { halt(instantiationError("<", 2, N, 1)) }
                )
            )
        }
    }

    /**
     * Call primitive request goals and respective expected [Solution]s
     *
     * Contained requests:
     * ```prolog
     * ?- call(true).
     * ?- call(fail).
     * ?- call(halt).
     * ?- call((true, true)).
     * ?- call('!').
     * ?- call(X).
     * ?- call((true, 1)).
     * ```
     */
    fun callTestingGoalsToSolutions(errorSignature: Signature) =
        prolog {
            ktListOf(
                call(true).hasSolutions({ yes() }),
                call(false).hasSolutions({ no() }),
                call(halt).hasSolutions({ halt(haltException) }),
                call("true" and "true").hasSolutions({ yes() }),
                call(cut).hasSolutions({ yes() }),
                call("X").hasSolutions({ halt(instantiationError(errorSignature, varOf("X"))) }),
                call("true" and 1).hasSolutions({ halt(typeError(errorSignature, ("true" and 1))) })
            )
        }

    /**
     * Catch primitive request goals and respective expected [Solution]s
     *
     * Contained requests:
     * ```prolog
     * ?- catch(true, _, fail).
     * ?- catch(catch(throw(external(deepBall)), internal(I), fail), external(E), true).
     * ?- catch(throw(first), X, throw(second)).
     * ?- catch(throw(hello), X, true).
     * ?- catch((throw(hello), fail), X, true).
     * ```
     */
    val catchTestingGoalsToSolutions by lazy {
        prolog {
            ktListOf(
                catch(true, `_`, false).hasSolutions({ yes() }),
                catch(catch(`throw`("external"("deepBall")), "internal"("I"), false), "external"("E"), true)
                    .hasSolutions({ yes("E" to "deepBall") }),
                catch(`throw`("first"), "X", `throw`("second")).hasSolutions(
                    { halt(systemError(atomOf("second"))) }
                ),
                catch(`throw`("hello"), "X", true).hasSolutions({ yes("X" to "hello") }),
                catch(`throw`("hello") and false, "X", true).hasSolutions({ yes("X" to "hello") })
            )
        }
    }

    /**
     * Halt primitive request goals and respective expected [Solution]s
     *
     * Contained requests:
     * ```prolog
     * ?- halt.
     * ?- catch(halt, _, true).
     * ?- catch(catch(throw(something), _, halt), _, true).
     * ```
     */
    val haltTestingGoalsToSolutions by lazy {
        prolog {
            ktListOf(
                halt.hasSolutions({ halt(haltException) }),
                catch(halt, `_`, true).hasSolutions({ halt(haltException) }),
                catch(catch(`throw`("something"), `_`, halt), `_`, true).hasSolutions(
                    { halt(haltException) }
                )
            )
        }
    }

    /** Collection of all Prolog example (custom created and from Prolog Standard) databases and their respective callable goals with expected solutions */
    fun allPrologTestingTheoriesToRespectiveGoalsAndSolutions(
        callErrorSignature: Signature,
        nafErrorSignature: Signature,
        notErrorSignature: Signature
    ) = mapOf(
        simpleFactTheory to simpleFactTheoryNotableGoalToSolutions,
        simpleCutTheory to simpleCutTheoryNotableGoalToSolutions,
        simpleCutAndConjunctionTheory to simpleCutAndConjunctionTheoryNotableGoalToSolutions,
        cutConjunctionAndBacktrackingTheory to cutConjunctionAndBacktrackingTheoryNotableGoalToSolutions,
        customReverseListTheory to customReverseListTheoryNotableGoalToSolution,
        Theory.empty() to callTestingGoalsToSolutions(callErrorSignature),
        Theory.empty() to catchTestingGoalsToSolutions,
        Theory.empty() to haltTestingGoalsToSolutions
    ) + allPrologStandardTestingTheoryToRespectiveGoalsAndSolutions(
        callErrorSignature,
        nafErrorSignature,
        notErrorSignature
    )
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy