RetractAll
This commit is contained in:
parent
4d334c1600
commit
1179e6a29b
4 changed files with 94 additions and 16 deletions
|
@ -94,6 +94,7 @@ open class Preprocessor {
|
||||||
// Database
|
// Database
|
||||||
term.functor == "dynamic/1" -> Dynamic((args[0] as Atom).name)
|
term.functor == "dynamic/1" -> Dynamic((args[0] as Atom).name)
|
||||||
term.functor == "retract/1" -> Retract(args[0])
|
term.functor == "retract/1" -> Retract(args[0])
|
||||||
|
term.functor == "retractall/1" -> RetractAll(args[0])
|
||||||
term.functor == "assert/1" -> {
|
term.functor == "assert/1" -> {
|
||||||
if (args[0] is Rule) {
|
if (args[0] is Rule) {
|
||||||
Assert(args[0] as Rule)
|
Assert(args[0] as Rule)
|
||||||
|
@ -138,4 +139,4 @@ open class Preprocessor {
|
||||||
|
|
||||||
return prepped
|
return prepped
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package prolog.builtins
|
package prolog.builtins
|
||||||
|
|
||||||
|
import io.Logger
|
||||||
import prolog.Answers
|
import prolog.Answers
|
||||||
import prolog.Substitutions
|
import prolog.Substitutions
|
||||||
import prolog.ast.logic.Clause
|
import prolog.ast.logic.Clause
|
||||||
|
@ -86,7 +87,7 @@ open class AssertZ(val clause: Clause) : Operator(Atom("assertz"), null, clause)
|
||||||
*
|
*
|
||||||
* @see [SWI-Prolog Predicate retract/1](https://www.swi-prolog.org/pldoc/doc_for?object=retract/1)
|
* @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) {
|
open class Retract(val term: Term) : Operator(Atom("retract"), null, term) {
|
||||||
override fun satisfy(subs: Substitutions): Answers = sequence {
|
override fun satisfy(subs: Substitutions): Answers = sequence {
|
||||||
// Check that term is a structure or atom
|
// Check that term is a structure or atom
|
||||||
if (term !is Structure && term !is Atom) {
|
if (term !is Structure && term !is Atom) {
|
||||||
|
@ -101,6 +102,12 @@ class Retract(val term: Term) : Operator(Atom("retract"), null, term) {
|
||||||
return@sequence
|
return@sequence
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if the predicate is dynamic
|
||||||
|
if (!predicate.dynamic) {
|
||||||
|
yield(Result.failure(Exception("No permission to modify static procedure '$functorName'")))
|
||||||
|
return@sequence
|
||||||
|
}
|
||||||
|
|
||||||
predicate.clauses.toList().forEach { clause ->
|
predicate.clauses.toList().forEach { clause ->
|
||||||
unifyLazy(term, clause.head, subs).forEach { unifyResult ->
|
unifyLazy(term, clause.head, subs).forEach { unifyResult ->
|
||||||
unifyResult.fold(
|
unifyResult.fold(
|
||||||
|
@ -119,3 +126,29 @@ class Retract(val term: Term) : Operator(Atom("retract"), null, term) {
|
||||||
|
|
||||||
override fun applySubstitution(subs: Substitutions): Retract = Retract(applySubstitution(term, subs))
|
override fun applySubstitution(subs: Substitutions): Retract = Retract(applySubstitution(term, subs))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RetractAll(term: Term) : Retract(term) {
|
||||||
|
override fun satisfy(subs: Substitutions): Answers {
|
||||||
|
// Check that term is a structure or atom
|
||||||
|
if (term !is Structure && term !is Atom) {
|
||||||
|
return sequenceOf(Result.failure(Exception("Cannot retract a non-structure or non-atom")))
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the predicate does not exist, implicitly create it
|
||||||
|
val functor = term.functor
|
||||||
|
val predicate = Program.db.predicates[functor]
|
||||||
|
if (predicate == null) {
|
||||||
|
Logger.debug("Predicate $functor not found, creating it")
|
||||||
|
Program.db.predicates += functor to Predicate(functor, dynamic = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Propagate errors from the super class
|
||||||
|
super.satisfy(subs).forEach {
|
||||||
|
if (it.isFailure) {
|
||||||
|
return sequenceOf(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sequenceOf(Result.success(emptyMap()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -95,9 +95,7 @@ class Repl {
|
||||||
return subs.entries.joinToString(",\n") { "${it.key} = ${it.value}" }
|
return subs.entries.joinToString(",\n") { "${it.key} = ${it.value}" }
|
||||||
},
|
},
|
||||||
onFailure = {
|
onFailure = {
|
||||||
val text = "Failure: ${it.message}"
|
return "ERROR: ${it.message}"
|
||||||
Logger.warn(text)
|
|
||||||
return text
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import prolog.ast.logic.Fact
|
||||||
import prolog.ast.logic.Predicate
|
import prolog.ast.logic.Predicate
|
||||||
import prolog.ast.logic.Rule
|
import prolog.ast.logic.Rule
|
||||||
import prolog.ast.terms.Atom
|
import prolog.ast.terms.Atom
|
||||||
|
import prolog.ast.terms.Functor
|
||||||
import prolog.ast.terms.Structure
|
import prolog.ast.terms.Structure
|
||||||
import prolog.ast.terms.Variable
|
import prolog.ast.terms.Variable
|
||||||
|
|
||||||
|
@ -131,7 +132,7 @@ class DatabaseOperatorsTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `simple retract`() {
|
fun `simple retract`() {
|
||||||
val predicate = Predicate(listOf(Fact(Atom("a"))))
|
val predicate = Predicate(listOf(Fact(Atom("a"))), dynamic = true)
|
||||||
Program.db.load(predicate)
|
Program.db.load(predicate)
|
||||||
|
|
||||||
assertEquals(1, Program.query(Atom("a")).count())
|
assertEquals(1, Program.query(Atom("a")).count())
|
||||||
|
@ -146,11 +147,13 @@ class DatabaseOperatorsTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `retract atom`() {
|
fun `retract atom`() {
|
||||||
val predicate = Predicate(listOf(
|
val predicate = Predicate(
|
||||||
Fact(Atom("a")),
|
listOf(
|
||||||
Fact(Atom("a")),
|
Fact(Atom("a")),
|
||||||
Fact(Atom("a"))
|
Fact(Atom("a")),
|
||||||
))
|
Fact(Atom("a"))
|
||||||
|
), dynamic = true
|
||||||
|
)
|
||||||
Program.db.load(predicate)
|
Program.db.load(predicate)
|
||||||
|
|
||||||
val control = Program.query(Atom("a")).toList()
|
val control = Program.query(Atom("a")).toList()
|
||||||
|
@ -181,11 +184,13 @@ class DatabaseOperatorsTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `retract compound with variable`() {
|
fun `retract compound with variable`() {
|
||||||
val predicate = Predicate(listOf(
|
val predicate = Predicate(
|
||||||
Fact(Structure(Atom("a"), listOf(Atom("b")))),
|
listOf(
|
||||||
Fact(Structure(Atom("a"), listOf(Atom("c")))),
|
Fact(Structure(Atom("a"), listOf(Atom("b")))),
|
||||||
Fact(Structure(Atom("a"), listOf(Atom("d"))))
|
Fact(Structure(Atom("a"), listOf(Atom("c")))),
|
||||||
))
|
Fact(Structure(Atom("a"), listOf(Atom("d"))))
|
||||||
|
), dynamic = true
|
||||||
|
)
|
||||||
Program.db.load(predicate)
|
Program.db.load(predicate)
|
||||||
|
|
||||||
val control = Program.query(Structure(Atom("a"), listOf(Variable("X")))).toList()
|
val control = Program.query(Structure(Atom("a"), listOf(Variable("X")))).toList()
|
||||||
|
@ -289,4 +294,45 @@ class DatabaseOperatorsTests {
|
||||||
assertEquals(Atom("bob"), result2[Variable("X")], "Expected bob")
|
assertEquals(Atom("bob"), result2[Variable("X")], "Expected bob")
|
||||||
assertEquals(Atom("sushi"), result2[Variable("Y")], "Expected sushi")
|
assertEquals(Atom("sushi"), result2[Variable("Y")], "Expected sushi")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `retract all`() {
|
||||||
|
val predicate = Predicate(
|
||||||
|
listOf(
|
||||||
|
Fact(Structure(Atom("a"), listOf(Atom("b")))),
|
||||||
|
Fact(Structure(Atom("a"), listOf(Atom("c")))),
|
||||||
|
Fact(Structure(Atom("a"), listOf(Atom("d"))))
|
||||||
|
), dynamic = true
|
||||||
|
)
|
||||||
|
|
||||||
|
Program.db.load(predicate)
|
||||||
|
|
||||||
|
val control = Program.query(Structure(Atom("a"), listOf(Variable("X")))).toList()
|
||||||
|
assertEquals(3, control.size, "Expected 3 results")
|
||||||
|
assertEquals(3, Program.db.predicates["a/1"]!!.clauses.size, "Expected 3 clauses")
|
||||||
|
|
||||||
|
val retract = RetractAll(Structure(Atom("a"), listOf(Variable("X"))))
|
||||||
|
val result = retract.satisfy(emptyMap()).toList()
|
||||||
|
|
||||||
|
assertEquals(1, result.size, "Expected 1 result")
|
||||||
|
assertTrue(result[0].isSuccess, "Expected success")
|
||||||
|
assertEquals(0, Program.db.predicates["a/1"]!!.clauses.size, "Expected 0 clauses")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `If Head refers to a predicate that is not defined, it is implicitly created as a dynamic predicate`() {
|
||||||
|
val predicateName = "idonotyetexist"
|
||||||
|
val predicateFunctor = "$predicateName/1"
|
||||||
|
|
||||||
|
assertFalse(predicateFunctor in Program.db.predicates, "Expected predicate to not exist before")
|
||||||
|
|
||||||
|
val retractAll = RetractAll(Structure(Atom(predicateName), listOf(Variable("X"))))
|
||||||
|
val result = retractAll.satisfy(emptyMap()).toList()
|
||||||
|
|
||||||
|
assertEquals(1, result.size, "Expected 1 result")
|
||||||
|
assertTrue(result[0].isSuccess, "Expected success")
|
||||||
|
|
||||||
|
assertTrue(predicateFunctor in Program.db.predicates, "Expected predicate to exist after")
|
||||||
|
assertTrue(Program.db.predicates[predicateFunctor]!!.dynamic, "Expected predicate to be dynamic")
|
||||||
|
}
|
||||||
}
|
}
|
Reference in a new issue