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

commonMain.it.unibo.tuprolog.solve.function.ArithmeticEvaluator.kt Maven / Gradle / Ivy

package it.unibo.tuprolog.solve.function

import it.unibo.tuprolog.core.Atom
import it.unibo.tuprolog.core.Integer
import it.unibo.tuprolog.core.Numeric
import it.unibo.tuprolog.core.Struct
import it.unibo.tuprolog.core.Term
import it.unibo.tuprolog.core.Var
import it.unibo.tuprolog.solve.ExecutionContext
import it.unibo.tuprolog.solve.exception.error.InstantiationError
import it.unibo.tuprolog.solve.exception.error.TypeError
import it.unibo.tuprolog.solve.exception.error.TypeError.Expected.EVALUABLE
import it.unibo.tuprolog.solve.exception.error.TypeError.Expected.INTEGER
import it.unibo.tuprolog.solve.exception.error.TypeError.Expected.NUMBER
import it.unibo.tuprolog.solve.extractSignature
import it.unibo.tuprolog.solve.primitive.Solve
import it.unibo.tuprolog.solve.stdlib.CommonBuiltins
import it.unibo.tuprolog.solve.stdlib.function.BitwiseAnd
import it.unibo.tuprolog.solve.stdlib.function.BitwiseComplement
import it.unibo.tuprolog.solve.stdlib.function.BitwiseLeftShift
import it.unibo.tuprolog.solve.stdlib.function.BitwiseOr
import it.unibo.tuprolog.solve.stdlib.function.BitwiseRightShift

/**
 * Evaluates an expression as a [Numeric] term.
 * Throws a [TypeError] in case a non-evaluable sub-term is met.
 * Throws a [TypeError] in case the evaluation produces a non-numeric term.
 *
 * @param request the request of the primitive in which the evaluation should happen
 * @param index the index of the argument being evalued in the aforementioned primitive
 */
class ArithmeticEvaluator(request: Solve.Request, index: Int?) :
    AbstractEvaluator(request, index) {
    constructor(request: Solve.Request) : this(request, null)

    /** This method implements all the check required by the Prolog Standard for expressions to be considered valid (statically) */
    override fun Term.staticCheck() {
        when {
            this is Var ->
                throw InstantiationError.forArgument(request.context, request.signature, this, index)
            this is Atom ->
                throw TypeError.forArgument(request.context, request.signature, EVALUABLE, this, index)
            this is Struct && this.extractSignature() !in allowedArithmeticSignatures ->
                throw TypeError.forArgument(request.context, request.signature, EVALUABLE, this, index)
        }
    }

    /** This method implements all the check required by the Prolog Standard for expressions to be considered valid (dynamically) */
    override fun Term.dynamicCheck(enclosingTerm: Struct) {
        when {
            // the argument of an arithmetic functor is evaluated to a non-numeric value
            this !is Numeric ->
                throw TypeError.forArgument(request.context, request.signature, NUMBER, this)

            // the argument of a bitwise operator is evaluated to a non-integer value
            this !is Integer && enclosingTerm.extractSignature() in bitwiseStandardOperatorsSignatures ->
                throw TypeError.forArgument(request.context, request.signature, INTEGER, this)
        }
    }

    companion object {
        /** All allowed arithmetic signatures in Prolog expressions */
        val allowedArithmeticSignatures by lazy {
            CommonBuiltins.functions.map { (signature, _) -> signature }
        }

        /** Prolog standard bitwise operators */
        internal val bitwiseStandardOperatorsSignatures by lazy {
            listOf(
                BitwiseAnd,
                BitwiseComplement,
                BitwiseLeftShift,
                BitwiseOr,
                BitwiseRightShift,
            ).map { it.signature }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy