![JAR search and dependency download from the Maven repository](/logo.png)
commonMain.it.unibo.tuprolog.solve.primitive.PrimitiveWrapper.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of solve-jvm Show documentation
Show all versions of solve-jvm Show documentation
Resolution-agnostic API for logic solvers
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