feat: Cut

This commit is contained in:
Tibo De Peuter 2025-04-15 15:37:05 +02:00
parent 6469dd6ced
commit 229a8bbc3c
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
7 changed files with 294 additions and 81 deletions

View file

@ -0,0 +1,201 @@
package prolog.builtins
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import prolog.Program
import prolog.ast.logic.Fact
import prolog.ast.logic.Rule
import prolog.ast.terms.Atom
import prolog.ast.terms.CompoundTerm
import prolog.ast.terms.Integer
import prolog.ast.terms.Variable
class ControlOperatorsTests {
@BeforeEach
fun setUp() {
Program.clear()
}
@Test
fun `simple cut program`() {
// First an example without cut
Program.load(
listOf(
Fact(CompoundTerm(Atom("foo"), listOf(Atom("a")))),
Fact(CompoundTerm(Atom("foo"), listOf(Atom("b")))),
)
)
val goal = CompoundTerm(Atom("foo"), listOf(Variable("X")))
var result = Program.query(goal).toList()
assertEquals(2, result.size, "Expected 2 results")
// Now with cut
Program.clear()
Program.load(
listOf(
Rule(CompoundTerm(Atom("foo"), listOf(Atom("a"))), Cut()),
Fact(CompoundTerm(Atom("foo"), listOf(Atom("b")))),
)
)
result = Program.query(goal).toList()
assertEquals(1, result.size, "Expected 1 result")
}
@Test
fun `cut with pruning`() {
// First an example without cut
Program.load(
listOf(
Fact(CompoundTerm(Atom("a"), listOf(Integer(1)))),
Fact(CompoundTerm(Atom("a"), listOf(Integer(2)))),
Fact(CompoundTerm(Atom("b"), listOf(Integer(1)))),
Fact(CompoundTerm(Atom("b"), listOf(Integer(2)))),
Rule(
CompoundTerm(Atom("foo"), listOf(Variable("X"), Variable("Y"))),
Conjunction(
CompoundTerm(Atom("a"), listOf(Variable("X"))),
CompoundTerm(Atom("b"), listOf(Variable("Y")))
)
)
)
)
val goal = CompoundTerm(Atom("foo"), listOf(Variable("X"), Variable("Y")))
/* ?- foo(X, Y).
* X = Y, Y = 1 ;
* X = 1,
* Y = 2 ;
* X = 2,
* Y = 1 ;
* X = Y, Y = 2. */
var result = Program.query(goal).toList()
assertEquals(4, result.size, "Expected 4 results")
// Now with cut in the middle
Program.clear()
Program.load(
listOf(
Fact(CompoundTerm(Atom("a"), listOf(Integer(1)))),
Fact(CompoundTerm(Atom("a"), listOf(Integer(2)))),
Fact(CompoundTerm(Atom("b"), listOf(Integer(1)))),
Fact(CompoundTerm(Atom("b"), listOf(Integer(2)))),
Rule(
CompoundTerm(Atom("foo"), listOf(Variable("X"), Variable("Y"))),
Conjunction(
CompoundTerm(Atom("a"), listOf(Variable("X"))),
Conjunction(
Cut(),
CompoundTerm(Atom("b"), listOf(Variable("Y")))
)
)
)
)
)
/*
?- foo(X, Y).
X = Y, Y = 1 ;
X = 1,
Y = 2.
*/
result = Program.query(goal).toList()
assertEquals(2, result.size, "Expected 2 results")
// Now with cut at the end
Program.clear()
Program.load(
listOf(
Fact(CompoundTerm(Atom("a"), listOf(Integer(1)))),
Fact(CompoundTerm(Atom("a"), listOf(Integer(2)))),
Fact(CompoundTerm(Atom("b"), listOf(Integer(1)))),
Fact(CompoundTerm(Atom("b"), listOf(Integer(2)))),
Rule(
CompoundTerm(Atom("foo"), listOf(Variable("X"), Variable("Y"))),
Conjunction(
CompoundTerm(Atom("a"), listOf(Variable("X"))),
Conjunction(
CompoundTerm(Atom("b"), listOf(Variable("Y"))),
Cut()
)
)
)
)
)
/*
?- foo(X, Y).
X = Y, Y = 1 ;
*/
result = Program.query(goal).toList()
assertEquals(1, result.size, "Expected 1 result")
}
@Test
fun not_atom() {
val success = Fact(Atom("a"))
Program.load(listOf(success))
val goal = Atom("a")
val notGoal = Not(goal)
val result1 = Program.query(goal)
val result2 = Program.query(notGoal)
assertTrue(result1.any(), "Expected query to succeed")
assertFalse(result2.any(), "Expected query to fail")
}
@Test
fun not_compound() {
val success = Fact(Atom("f"))
val failure = Fact(Atom("g"))
Program.load(listOf(success, failure))
val goal = Atom("f")
val notGoal = Not(Atom("g"))
val result1 = Program.query(goal)
val result2 = Program.query(notGoal)
assertTrue(result1.any(), "Expected query to succeed")
assertFalse(result2.any(), "Expected query to fail")
}
@Test
fun fail_should_cause_fails() {
val success = Fact(Atom("a"))
val failure = Fact(Atom("b"))
Program.load(listOf(success, failure))
val goal = Atom("a")
val failGoal = Fail
val result1 = Program.query(goal)
val result2 = Program.query(failGoal)
assertTrue(result1.any(), "Expected query to succeed")
assertFalse(result2.any(), "Expected query to fail")
}
}

View file

@ -1,66 +0,0 @@
package prolog.builtins
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import prolog.Program
import prolog.ast.logic.Fact
import prolog.ast.terms.Atom
class ControlTest {
@BeforeEach
fun setUp() {
Program.clear()
}
@Test
fun not_atom() {
val success = Fact(Atom("a"))
Program.load(listOf(success))
val goal = Atom("a")
val notGoal = Not(goal)
val result1 = Program.query(goal)
val result2 = Program.query(notGoal)
assertTrue(result1.any(), "Expected query to succeed")
assertFalse(result2.any(), "Expected query to fail")
}
@Test
fun not_compound() {
val success = Fact(Atom("f"))
val failure = Fact(Atom("g"))
Program.load(listOf(success, failure))
val goal = Atom("f")
val notGoal = Not(Atom("g"))
val result1 = Program.query(goal)
val result2 = Program.query(notGoal)
assertTrue(result1.any(), "Expected query to succeed")
assertFalse(result2.any(), "Expected query to fail")
}
@Test
fun fail_should_cause_fails() {
val success = Fact(Atom("a"))
val failure = Fact(Atom("b"))
Program.load(listOf(success, failure))
val goal = Atom("a")
val failGoal = Fail
val result1 = Program.query(goal)
val result2 = Program.query(failGoal)
assertTrue(result1.any(), "Expected query to succeed")
assertFalse(result2.any(), "Expected query to fail")
}
}