This commit is contained in:
Tibo De Peuter 2025-05-02 23:50:29 +02:00
parent 80fb3d1e60
commit 5bfa1691dd
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
10 changed files with 276 additions and 17 deletions

View file

@ -1,5 +1,4 @@
import com.xenomachina.argparser.ArgParser
import com.xenomachina.argparser.mainBody
import interpreter.FileLoader
import io.GhentPrologArgParser
import io.Logger

View file

@ -92,6 +92,7 @@ open class Preprocessor {
term.functor == "between/3" && args.all { it is Expression } -> Between(args[0] as Expression, args[1] as Expression, args[2] as Expression)
// Database
term.functor == "retract/1" -> Retract(args[0])
term.functor == "assert/1" -> {
if (args[0] is Rule) {
Assert(args[0] as Rule)
@ -99,7 +100,20 @@ open class Preprocessor {
Assert(Fact(args[0] as Head))
}
}
term.functor == "asserta/1" -> AssertA(args[0] as Clause)
term.functor == "asserta/1" -> {
if (args[0] is Rule) {
AssertA(args[0] as Rule)
} else {
AssertA(Fact(args[0] as Head))
}
}
term.functor == "assertz/1" -> {
if (args[0] is Rule) {
AssertZ(args[0] as Rule)
} else {
AssertZ(Fact(args[0] as Head))
}
}
// Other
term.functor == "write/1" -> Write(args[0])

View file

@ -13,7 +13,7 @@ import prolog.ast.terms.Goal
*/
object Program : Resolvent {
val internalDb = Database("")
private val databases: MutableList<Database> = mutableListOf(internalDb)
val databases: MutableList<Database> = mutableListOf(internalDb)
var storeNewLine: Boolean = false
var variableRenamingStart: Int = 0

View file

@ -8,4 +8,4 @@ abstract class Substitution(val from: Term, val to: Term) {
}
typealias Substitutions = Map<Term, Term>
typealias Answer = Result<Substitutions>
typealias Answers = Sequence<Answer>
typealias Answers = Sequence<Answer>

View file

@ -8,6 +8,11 @@ import prolog.ast.terms.Structure
import prolog.ast.logic.Predicate
import prolog.Program
import prolog.ast.terms.Functor
import prolog.ast.terms.Term
import prolog.ast.logic.Fact
import prolog.ast.Database
import prolog.ast.terms.Operator
import prolog.logic.unifyLazy
class Assert(clause: Clause) : AssertZ(clause) {
override val functor: Functor = "assert/1"
@ -16,7 +21,7 @@ class Assert(clause: Clause) : AssertZ(clause) {
/**
* Assert a [Clause] as a first clause of the [Predicate] into the [Program].
*/
class AssertA(val clause: Clause) : Structure(Atom("asserta"), listOf(clause)) {
class AssertA(val clause: Clause) : Operator(Atom("asserta"), null, clause) {
override fun satisfy(subs: Substitutions): Answers {
// Add clause to the program
Program.load(listOf(clause), 0)
@ -28,7 +33,7 @@ class AssertA(val clause: Clause) : Structure(Atom("asserta"), listOf(clause)) {
/**
* Assert a [Clause] as a last clause of the [Predicate] into the [Program].
*/
open class AssertZ(val clause: Clause) : Structure(Atom("assertz"), listOf(clause)) {
open class AssertZ(val clause: Clause) : Operator(Atom("assertz"), null, clause) {
override fun satisfy(subs: Substitutions): Answers {
// Add clause to the program
Program.load(listOf(clause))
@ -36,3 +41,43 @@ open class AssertZ(val clause: Clause) : Structure(Atom("assertz"), listOf(claus
return sequenceOf(Result.success(emptyMap()))
}
}
/**
* When [Term] is an [Atom] or a term, it is unified with the first unifying [Clause] in the [Database].
* The [Fact] or Clause is removed from the Database. It respects the logical update view.
*
* @see [SWI-Prolog Predicate retract/1](https://www.swi-prolog.org/pldoc/doc_for?object=retract/1)
*/
class Retract(val term: Term) : Operator(Atom("retract"), null, term) {
override fun satisfy(subs: Substitutions): Answers = sequence {
// Check that term is a structure or atom
if (term !is Structure && term !is Atom) {
yield(Result.failure(Exception("Cannot retract a non-structure or non-atom")))
return@sequence
}
val functorName = term.functor
Program.databases
.filter { it.predicates.containsKey(functorName) }
.mapNotNull { it.predicates[functorName] }
.map { predicate ->
val clausesIterator = predicate.clauses.iterator()
while (clausesIterator.hasNext()) {
val clause = clausesIterator.next()
unifyLazy(term, clause.head, subs).forEach { unifyResult ->
unifyResult.fold(
onSuccess = { substitutions ->
// If unification is successful, remove the clause
yield(Result.success(substitutions))
clausesIterator.remove()
},
onFailure = {
// If unification fails, do nothing
}
)
}
}
}
}
}

View file

@ -42,10 +42,9 @@ class Repl {
val iterator = answers.iterator()
if (!iterator.hasNext()) {
io.say("false.")
io.say("false.\n")
} else {
var previous = iterator.next()
io.say(prettyPrint(previous))
io.say(prettyPrint(iterator.next()))
while (iterator.hasNext()) {
var command = io.prompt("")
@ -57,8 +56,7 @@ class Repl {
when (command) {
";" -> {
previous = iterator.next()
io.say(prettyPrint(previous))
io.say(prettyPrint(iterator.next()))
}
"a" -> return
"." -> return
@ -88,7 +86,7 @@ class Repl {
val subs = result.getOrNull()!!
if (subs.isEmpty()) {
io.checkNewLine()
return "true.\n"
return "true."
}
return subs.entries.joinToString(",\n") { "${it.key} = ${it.value}" }
},