This commit is contained in:
Tibo De Peuter 2025-05-07 21:15:10 +02:00
parent 65c4d925d3
commit 752c278cb0
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
2 changed files with 218 additions and 41 deletions

View file

@ -3,56 +3,43 @@ package prolog.builtins
import prolog.Answers
import prolog.Substitutions
import prolog.ast.arithmetic.Integer
import prolog.ast.terms.AnonymousVariable
import prolog.ast.terms.Atom
import prolog.ast.terms.Head
import prolog.ast.terms.Structure
import prolog.ast.terms.Term
import prolog.ast.terms.Variable
import prolog.logic.applySubstitution
import prolog.logic.atomic
import prolog.logic.nonvariable
import prolog.logic.unifyLazy
import prolog.logic.variable
import prolog.ast.terms.*
import prolog.logic.*
import java.util.Locale
import java.util.Locale.getDefault
/**
* [True] when [Term] is a term with [FunctorInfo] Name/Arity.
*
* If Term is a [Variable] it is unified with a new term whose arguments are all different variables.
* If Term is [atomic], Arity will be unified with the integer 0, and Name will be unified with Term.
*/
class Functor(private val term: Term, private val functorName: Term, private val functorArity: Term) :
Structure(Atom("functor"), listOf(term, functorName, functorArity)) {
override fun satisfy(subs: Substitutions): Answers {
if (nonvariable(term, subs)) {
val t = applySubstitution(term, subs) as Head
return when {
nonvariable(term, subs) -> {
val t = applySubstitution(term, subs) as Head
return Conjunction(
Unify(t.functor.arity, functorArity),
Unify(t.functor.name, functorName)
).satisfy(subs)
}
if (variable(term, subs)) {
require(atomic(functorName, subs) && atomic(functorArity, subs)) {
"Arguments are not sufficiently instantiated"
Conjunction(
Unify(t.functor.arity, functorArity),
Unify(t.functor.name, functorName)
).satisfy(subs)
}
val name = applySubstitution(functorName, subs) as Atom
val arity = applySubstitution(functorArity, subs) as Integer
val result = if (arity.value == 0) {
functorName
} else {
val argList = mutableListOf<Term>()
for (i in 1..arity.value) {
argList.add(AnonymousVariable.create())
variable(term, subs) -> {
require(atomic(functorName, subs) && atomic(functorArity, subs)) {
"Arguments are not sufficiently instantiated"
}
Structure(name, argList)
val name = applySubstitution(functorName, subs) as Atom
val arity = applySubstitution(functorArity, subs) as Integer
val result = Structure(name, List(arity.value) { AnonymousVariable.create() })
sequenceOf(Result.success(mapOf(term to result)))
}
if (nonvariable(term, subs)) {
return unifyLazy(term, result, subs)
}
return sequenceOf(Result.success(mapOf(term to result)))
else -> throw IllegalStateException()
}
throw IllegalStateException()
}
override fun applySubstitution(subs: Substitutions): Functor = Functor(
@ -60,6 +47,51 @@ class Functor(private val term: Term, private val functorName: Term, private val
functorName.applySubstitution(subs),
functorArity.applySubstitution(subs)
)
override fun toString(): String = "functor($term, $functorName, $functorArity)"
}
class Arg(private val arg: Term, private val term: Term, private val value: Term) :
Structure(Atom("arg"), listOf(arg, term, value)) {
override fun satisfy(subs: Substitutions): Answers = sequence {
require(nonvariable(term, subs)) { "Arguments are not sufficiently instantiated" }
require(compound(term, subs)) {
val expected = CompoundTerm::class.simpleName?.lowercase()
val actual = term::class.simpleName?.lowercase()
"Type error: `$expected' expected, found `$term' ($actual)"
}
val t = applySubstitution(term, subs) as Structure
when {
variable(arg, subs) -> {
// Value will be unified with the successive arguments of term.
// On successful unification, arg is unified with the argument number.
// Backtracking yields alternative solutions.
var count = 0
for (argument in t.arguments) {
unifyLazy(value, argument, subs).forEach { result ->
result.map {
val sub = arg to Integer(count + 1)
yield(Result.success(it + sub))
}
}
count++
}
}
else -> {
val a = applySubstitution(arg, subs) as Integer
require(0 <= a.value) { "Domain error: not_less_than_zero" }
// Fail silently if the argument is out of bounds
if (0 == a.value || t.arguments.size < a.value) {
return@sequence
}
val argument = t.arguments[a.value - 1]
yieldAll(unifyLazy(argument, value, subs))
}
}
}
}