commonMain.it.unibo.tuprolog.solve.primitive.PrimitiveWrapper.kt Maven / Gradle / Ivy
package it.unibo.tuprolog.solve.primitive
import it.unibo.tuprolog.core.Clause
import it.unibo.tuprolog.core.Integer
import it.unibo.tuprolog.core.Numeric
import it.unibo.tuprolog.core.Struct
import it.unibo.tuprolog.core.Substitution
import it.unibo.tuprolog.core.Term
import it.unibo.tuprolog.core.TermVisitor
import it.unibo.tuprolog.core.operators.Specifier
import it.unibo.tuprolog.solve.AbstractWrapper
import it.unibo.tuprolog.solve.ExecutionContext
import it.unibo.tuprolog.solve.Signature
import it.unibo.tuprolog.solve.exception.error.DomainError
import it.unibo.tuprolog.solve.exception.error.DomainError.Expected.NOT_LESS_THAN_ZERO
import it.unibo.tuprolog.solve.exception.error.DomainError.Expected.OPERATOR_SPECIFIER
import it.unibo.tuprolog.solve.exception.error.DomainError.Expected.WELL_FORMED_LIST
import it.unibo.tuprolog.solve.exception.error.InstantiationError
import it.unibo.tuprolog.solve.exception.error.PermissionError
import it.unibo.tuprolog.solve.exception.error.PermissionError.Permission.PRIVATE_PROCEDURE
import it.unibo.tuprolog.solve.exception.error.PermissionError.Permission.STATIC_PROCEDURE
import it.unibo.tuprolog.solve.exception.error.RepresentationError
import it.unibo.tuprolog.solve.exception.error.RepresentationError.Limit.MAX_ARITY
import it.unibo.tuprolog.solve.exception.error.SystemError
import it.unibo.tuprolog.solve.exception.error.TypeError
import it.unibo.tuprolog.solve.exception.error.TypeError.Expected.ATOM
import it.unibo.tuprolog.solve.exception.error.TypeError.Expected.ATOMIC
import it.unibo.tuprolog.solve.exception.error.TypeError.Expected.CHARACTER
import it.unibo.tuprolog.solve.exception.error.TypeError.Expected.INTEGER
import it.unibo.tuprolog.solve.exception.error.TypeError.Expected.LIST
import it.unibo.tuprolog.solve.exception.error.TypeError.Expected.PREDICATE_INDICATOR
import it.unibo.tuprolog.solve.extractSignature
import it.unibo.tuprolog.solve.flags.MaxArity
import org.gciatto.kt.math.BigInteger
import kotlin.jvm.JvmStatic
/**
* Wrapper class for [Primitive] implementation
*
* @author Enrico
* @author Giovanni
*/
abstract class PrimitiveWrapper : AbstractWrapper {
constructor(signature: Signature) : super(signature)
constructor(name: String, arity: Int, vararg: Boolean = false) : super(name, arity, vararg)
/** The function expressing the implementation of the primitive, without any check for application to correct signature */
protected abstract fun uncheckedImplementation(request: Solve.Request): Sequence
/** Checked primitive implementation */
final override val implementation: Primitive = Primitive.enforcingSignature(signature, ::uncheckedImplementation)
companion object {
@JvmStatic
fun Solve.Request.mgu(
term1: Term,
term2: Term,
): Substitution = context.unificator.mgu(term1, term2)
@JvmStatic
fun Solve.Request.match(
term1: Term,
term2: Term,
): Boolean = context.unificator.match(term1, term2)
@JvmStatic
fun Solve.Request.unify(
term1: Term,
term2: Term,
): Term? = context.unificator.unify(term1, term2)
/**
* Utility factory to build a [PrimitiveWrapper] out of a [Signature] and a [Primitive] function
*/
@JvmStatic
fun wrap(
signature: Signature,
primitive: Primitive,
): PrimitiveWrapper = FromFunction(signature, primitive)
/**
* Utility factory to build a [PrimitiveWrapper] out of a [Primitive] function
*/
@JvmStatic
fun wrap(
name: String,
arity: Int,
vararg: Boolean,
primitive: Primitive,
): PrimitiveWrapper = FromFunction(name, arity, vararg, primitive)
/**
* Utility factory to build a [PrimitiveWrapper] out of a [Primitive] function
*/
@JvmStatic
fun wrap(
name: String,
arity: Int,
primitive: Primitive,
): PrimitiveWrapper = wrap(name, arity, false, primitive)
/** Private class to support the wrap methods, without using the object literal notation */
private class FromFunction(
signature: Signature,
private val uncheckedPrimitive: Primitive,
) : PrimitiveWrapper(signature) {
constructor(name: String, arity: Int, vararg: Boolean = false, uncheckedPrimitive: Primitive) :
this(Signature(name, arity, vararg), uncheckedPrimitive)
override fun uncheckedImplementation(request: Solve.Request): Sequence =
uncheckedPrimitive.solve(
request,
)
}
private fun ensurerVisitor(
context: ExecutionContext,
procedure: Signature,
): TermVisitor =
object : TermVisitor {
override fun defaultValue(term: Term): Nothing? = null
override fun visitStruct(term: Struct) =
when {
Clause.notableFunctors.contains(term.functor) && term.arity == 2 -> {
term.argsSequence.map { it.accept(this) }.filterNotNull().firstOrNull()
}
else -> defaultValue(term)
}
override fun visitNumeric(term: Numeric): TypeError =
TypeError.forGoal(context, procedure, TypeError.Expected.CALLABLE, term)
}
@JvmStatic
fun Solve.Request.checkTermIsRecursivelyCallable(term: Term): TypeError? =
term.accept(ensurerVisitor(context, signature))
/** Utility function to ensure that all arguments of Solve.Request are instantiated and *not* (still) Variables */
@JvmStatic
fun Solve.Request.ensuringAllArgumentsAreInstantiated(): Solve.Request =
arguments.withIndex().firstOrNull { it.value.isVar }?.let {
ensureIsInstantiated(it.value, it.index)
} ?: this
private fun Solve.Request.ensureIsInstantiated(
term: Term?,
index: Int,
): Solve.Request =
term?.asVar()?.let {
throw InstantiationError.forArgument(
context,
signature,
it,
index,
)
} ?: this
@JvmStatic
fun Solve.Request.ensuringProcedureHasPermission(
signature: Signature?,
operation: PermissionError.Operation,
): Solve.Request {
if (signature != null) {
if (context.libraries.hasProtected(signature)) {
throw PermissionError.of(
context,
this.signature,
operation,
PRIVATE_PROCEDURE,
signature.toIndicator(),
)
}
if (context.staticKb.contains(signature.toIndicator())) {
throw PermissionError.of(
context,
this.signature,
operation,
STATIC_PROCEDURE,
signature.toIndicator(),
)
}
}
return this
}
@JvmStatic
fun Solve.Request.ensuringClauseProcedureHasPermission(
clause: Clause,
operation: PermissionError.Operation,
): Solve.Request {
val headSignature: Signature? = clause.head?.extractSignature()
return ensuringProcedureHasPermission(headSignature, operation)
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsWellFormedIndicator(
index: Int,
): Solve.Request {
ensuringArgumentIsInstantiated(index)
val candidate = arguments[index]
when {
candidate.isIndicator -> {
val (name, arity) = candidate.castToIndicator()
when {
name.isVar -> {
throw InstantiationError.forArgument(context, signature, name.castToVar(), index)
}
arity.isVar -> {
throw InstantiationError.forArgument(context, signature, arity.castToVar(), index)
}
!name.isAtom -> throw TypeError.forArgument(context, signature, ATOM, name, index)
!arity.isInteger -> throw TypeError.forArgument(context, signature, INTEGER, arity, index)
arity.castToInteger().value < BigInteger.ZERO ->
throw DomainError.forArgument(context, signature, NOT_LESS_THAN_ZERO, arity, index)
context.flags[MaxArity]?.castToInteger()?.value?.let {
arity.castToInteger().value > it
} ?: false -> throw RepresentationError.of(context, signature, MAX_ARITY)
else -> return this
}
}
else -> throw TypeError.forArgument(context, signature, PREDICATE_INDICATOR, candidate, index)
}
}
@JvmStatic
fun Solve.Request.notImplemented(
message: String = "Primitive for ${signature.name}/${signature.arity} is not implemented, yet",
): Solve.Response = throw SystemError.forUncaughtException(context, NotImplementedError(message))
@JvmStatic
fun Solve.Request.notSupported(
message: String = "Operation ${signature.name}/${signature.arity} is not supported",
): Solve.Response = throw SystemError.forUncaughtException(context, IllegalStateException(message))
@JvmStatic
fun Solve.Request.ensuringArgumentIsWellFormedClause(index: Int): Solve.Request {
ensuringArgumentIsInstantiated(index)
ensuringArgumentIsStruct(index)
val candidate = arguments[index]
return when {
candidate.isClause -> {
if (!candidate.castToClause().isWellFormed) {
throw DomainError.forArgument(context, signature, DomainError.Expected.CLAUSE, candidate, index)
}
this
}
candidate.asStruct()?.let { it.functor == Clause.FUNCTOR && it.arity == 2 } ?: false ->
throw DomainError.forArgument(context, signature, DomainError.Expected.CLAUSE, candidate, index)
candidate.isStruct -> this
else -> throw TypeError.forArgument(context, signature, TypeError.Expected.CALLABLE, candidate, index)
}
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsInstantiated(index: Int): Solve.Request =
ensureIsInstantiated(arguments[index], index)
@JvmStatic
fun Solve.Request.ensuringArgumentIsNumeric(index: Int): Solve.Request {
val arg = arguments[index]
return when {
!arg.isNumber -> throw TypeError.forArgument(context, signature, TypeError.Expected.NUMBER, arg, index)
else -> this
}
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsStruct(index: Int): Solve.Request {
val arg = arguments[index]
return when {
!arg.isStruct -> throw TypeError.forArgument(
context,
signature,
TypeError.Expected.CALLABLE,
arg,
index,
)
else -> this
}
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsCallable(index: Int): Solve.Request =
ensuringArgumentIsStruct(index)
@JvmStatic
fun Solve.Request.ensuringArgumentIsVariable(index: Int): Solve.Request {
val arg = arguments[index]
return when {
!arg.isVar -> throw TypeError.forArgument(
context,
signature,
TypeError.Expected.VARIABLE,
arg,
index,
)
else -> this
}
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsCompound(index: Int): Solve.Request {
val arg = arguments[index]
return when {
!arg.isStruct || arg.isAtom -> throw TypeError.forArgument(
context,
signature,
TypeError.Expected.COMPOUND,
arg,
index,
)
else -> this
}
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsAtom(index: Int): Solve.Request {
val arg = arguments[index]
return when {
!arg.isAtom -> throw TypeError.forArgument(context, signature, ATOM, arg, index)
else -> this
}
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsConstant(index: Int): Solve.Request {
val arg = arguments[index]
return when {
!arg.isConstant -> throw TypeError.forArgument(context, signature, ATOMIC, arg, index)
else -> this
}
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsGround(index: Int): Solve.Request =
arguments[index].let {
when {
!it.isGround -> {
throw InstantiationError.forArgument(context, signature, it.variables.first(), index)
}
else -> this
}
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsChar(index: Int): Solve.Request {
val arg = arguments[index]
return when {
arg.isAtom -> {
val string = arg.castToAtom().value
when (string.length) {
1 -> this
else -> throw TypeError.forArgument(context, signature, CHARACTER, arg, index)
}
}
else -> {
throw TypeError.forArgument(context, signature, CHARACTER, arg, index)
}
}
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsSpecifier(index: Int): Solve.Request =
arguments[index].let { arg ->
when {
!arg.isAtom -> throw DomainError.forArgument(context, signature, OPERATOR_SPECIFIER, arg, index)
else -> {
try {
Specifier.fromTerm(arg)
this
} catch (e: IllegalArgumentException) {
throw DomainError.forArgument(context, signature, OPERATOR_SPECIFIER, arg, index)
}
}
}
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsInteger(index: Int): Solve.Request {
val arg = arguments[index]
when {
!arg.isInteger -> throw TypeError.forArgument(context, signature, INTEGER, arg, index)
else -> return this
}
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsList(index: Int): Solve.Request {
val arg = arguments[index]
return when {
!arg.isList -> throw TypeError.forArgument(context, signature, LIST, arg, index)
else -> this
}
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsArity(index: Int): Solve.Request =
ensuringArgumentIsNonNegativeInteger(index).run {
val arity = arguments[index].castToInteger()
context.flags[MaxArity]?.castToInteger()?.value?.let {
if (arity.intValue > it) {
throw RepresentationError.of(context, signature, MAX_ARITY)
}
}
return this
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsNonNegativeInteger(index: Int): Solve.Request =
ensuringArgumentIsInteger(index).arguments[index].let { arg ->
when {
!arg.isInteger || arg.castToInteger().intValue < BigInteger.ZERO -> throw DomainError.forArgument(
context,
signature,
NOT_LESS_THAN_ZERO,
arg,
index,
)
else -> this
}
}
private val MIN_CHAR = BigInteger.of(Char.MIN_VALUE.code)
private val MAX_CHAR = BigInteger.of(Char.MAX_VALUE.code)
@JvmStatic
fun Integer.isCharacterCode(): Boolean = intValue !in MIN_CHAR..MAX_CHAR
@JvmStatic
fun Solve.Request.ensuringTermIsCharCode(term: Term): Solve.Request =
when {
!term.isInteger || term.castToInteger().isCharacterCode() ->
throw RepresentationError.of(context, signature, RepresentationError.Limit.CHARACTER_CODE)
else -> this
}
@JvmStatic
fun Solve.Request.ensuringTermIsWellFormedList(term: Term): Solve.Request =
when {
!term.isList || !term.castToList().isWellFormed ->
throw DomainError.forTerm(context, WELL_FORMED_LIST, term)
else -> this
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsWellFormedList(index: Int): Solve.Request {
val term = arguments[index]
return when {
term.isList ->
when {
term.castToList().isWellFormed -> this
else -> throw DomainError.forArgument(context, signature, WELL_FORMED_LIST, term, index)
}
else -> throw TypeError.forArgument(context, signature, LIST, term, index)
}
}
@JvmStatic
fun Solve.Request.ensuringArgumentIsCharCode(index: Int): Solve.Request =
ensuringArgumentIsInteger(index).ensuringTermIsCharCode(arguments[index])
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy