refactor: Herstructurering

This commit is contained in:
Tibo De Peuter 2025-04-06 19:16:50 +02:00
parent 1acd1cfb67
commit e3c84e1761
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
33 changed files with 290 additions and 178 deletions

View file

@ -0,0 +1,44 @@
package prolog.ast.logic
import prolog.logic.Substituted
import prolog.ast.terms.Body
import prolog.ast.terms.Functor
import prolog.ast.terms.Goal
import prolog.ast.terms.Head
import prolog.builtins.True
import prolog.logic.unifyLazy
/**
* Sentence of a Prolog program.
*
* A clause consists of a [Head] and body separated by the neck operator, or it is a [Fact].
*
* @see [prolog.ast.terms.Variable]
* @see [Predicate]
*/
abstract class Clause(private val head: Head, private val body: Body) : Resolvent {
val functor: Functor = head.functor
override fun solve(goal: Goal, subs: Substituted): Sequence<Substituted> = sequence {
// If the clause is a rule, unify the goal with the head and then try to prove the body.
// Only if the body can be proven, the substitutions should be returned.
// Do this in a lazy way.
unifyLazy(goal, head, subs).forEach { newHeadSubs ->
// If the body can be proven, yield the (combined) substitutions
body.prove(subs + newHeadSubs).forEach { newBodySubs ->
yield(newHeadSubs + newBodySubs)
// Unbind the newly bound variables, so they can be reused.
newBodySubs.keys.forEach { it.unbind() }
}
// Unbind the newly bound variables, so they can be reused.
newHeadSubs.keys.forEach { it.unbind() }
}
}
override fun toString(): String {
return when {
body == True() -> head.toString()
else -> "$head :- $body"
}
}
}

View file

@ -0,0 +1,6 @@
package prolog.ast.logic
import prolog.ast.terms.Head
import prolog.builtins.True
class Fact(head: Head) : Clause(head, True())

View file

@ -0,0 +1,57 @@
package prolog.ast.logic
import prolog.logic.Substituted
import prolog.ast.terms.Functor
import prolog.ast.terms.Goal
/**
* Collection of [Clause]s with the same [Functor].
*
* If a goal is proved, the system looks for a predicate with the same functor, then uses indexing
* to select candidate clauses and then tries these clauses one-by-one.
*/
class Predicate : Resolvent {
val functor: Functor
val clauses: MutableList<Clause>
/**
* Creates a predicate with the given functor and an empty list of clauses.
*/
constructor(functor: Functor) {
this.functor = functor
this.clauses = mutableListOf()
}
/**
* Creates a predicate with the given clauses.
*/
constructor(clauses: List<Clause>) {
this.functor = clauses.first().functor
require(clauses.all { it.functor == functor }) { "All clauses must have the same functor" }
this.clauses = clauses.toMutableList()
}
/**
* Adds a clause to the predicate.
*/
fun add(clause: Clause) {
require(clause.functor == functor) { "Clause functor does not match predicate functor" }
clauses.add(clause)
}
/**
* Adds a list of clauses to the predicate.
*/
fun addAll(clauses: List<Clause>) {
require(clauses.all { it.functor == functor }) { "All clauses must have the same functor" }
this.clauses.addAll(clauses)
}
override fun solve(goal: Goal, subs: Substituted): Sequence<Substituted> = sequence {
require(goal.functor == functor) { "Goal functor does not match predicate functor" }
// Try to unify the goal with the clause
// If the unification is successful, yield the substitutions
clauses.forEach { yieldAll(it.solve(goal, subs)) }
}
}

View file

@ -0,0 +1,13 @@
package prolog.ast.logic
import prolog.logic.Substituted
interface Provable {
/**
* Proves the current [Provable] instance.
*
* @return a sequence of [Substituted] instances representing the results of the proof.
* If the proof fails, an empty sequence is returned.
*/
fun prove(subs: Substituted): Sequence<Substituted>
}

View file

@ -0,0 +1,17 @@
package prolog.ast.logic
import prolog.logic.Substituted
import prolog.ast.terms.Goal
/**
* Can be instructed to solve a goal.
*/
interface Resolvent {
/**
* Attempts to solve the given goal.
*
* @return A sequence of substitutions that can be applied to the goal to unify it with this resolvent.
* If the goal cannot be unified with this resolvent, an empty sequence is returned.
*/
fun solve(goal: Goal, subs: Substituted): Sequence<Substituted>
}

View file

@ -0,0 +1,6 @@
package prolog.ast.logic
import prolog.ast.terms.Body
import prolog.ast.terms.Head
class Rule(head: Head, body: Body) : Clause(head, body)