This commit is contained in:
Tibo De Peuter 2025-05-07 22:26:02 +02:00
parent 752c278cb0
commit 8bda3c5af4
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
15 changed files with 361 additions and 114 deletions

View file

@ -6,7 +6,6 @@ import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import parser.grammars.TermsGrammar
import prolog.ast.arithmetic.Integer
import prolog.ast.logic.Fact
import prolog.ast.logic.Rule
import prolog.ast.terms.*
import prolog.builtins.*
@ -612,7 +611,7 @@ class PreprocessorTests {
Atom("declaration/1")
)
)
val expected = Dynamic(FunctorInfo.of("declaration/1"))
val expected = Dynamic(Functor.of("declaration/1"))
val result = preprocessor.preprocess(input)

View file

@ -2,7 +2,6 @@ package parser.grammars
import com.github.h0tk3y.betterParse.grammar.Grammar
import com.github.h0tk3y.betterParse.grammar.parseToEnd
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
@ -14,8 +13,7 @@ import prolog.ast.logic.Rule
import prolog.ast.terms.CompoundTerm
import prolog.ast.terms.Structure
import prolog.ast.terms.Variable
import prolog.builtins.Conjunction
import prolog.ast.terms.FunctorInfo
import prolog.ast.terms.Functor
class LogicGrammarTests {
private lateinit var parser: Grammar<List<Clause>>
@ -95,13 +93,13 @@ class LogicGrammarTests {
assertTrue(rule.head is Structure, "Expected head to be a structure")
val head = rule.head as Structure
assertEquals(FunctorInfo.of("parent/2"), head.functor, "Expected functor 'parent/2'")
assertEquals(Functor.of("parent/2"), head.functor, "Expected functor 'parent/2'")
assertEquals(Variable("X"), head.arguments[0], "Expected first argument 'X'")
assertEquals(Variable("Y"), head.arguments[1], "Expected second argument 'Y'")
assertTrue(rule.body is Structure, "Expected body to be a structure")
val body = rule.body as Structure
assertEquals(FunctorInfo.of("father/2"), body.functor, "Expected functor 'father/2'")
assertEquals(Functor.of("father/2"), body.functor, "Expected functor 'father/2'")
assertEquals(Variable("X"), body.arguments[0], "Expected first argument 'X'")
assertEquals(Variable("Y"), body.arguments[1], "Expected second argument 'Y'")
}
@ -126,12 +124,12 @@ class LogicGrammarTests {
assertEquals(1, result.size, "Expected 1 rule")
val rule = result[0] as Rule
assertEquals(FunctorInfo.of("guest/2"), rule.head.functor, "Expected functor 'guest/2'")
assertEquals(FunctorInfo.of(",/2"), (rule.body as CompoundTerm).functor, "Expected functor ',/2'")
assertEquals(Functor.of("guest/2"), rule.head.functor, "Expected functor 'guest/2'")
assertEquals(Functor.of(",/2"), (rule.body as CompoundTerm).functor, "Expected functor ',/2'")
val l1 = (rule.body as CompoundTerm).arguments[0] as CompoundTerm
assertEquals(FunctorInfo.of(",/2"), l1.functor, "Expected functor ',/2'")
assertEquals(Functor.of(",/2"), l1.functor, "Expected functor ',/2'")
val l2 = l1.arguments[0] as CompoundTerm
assertEquals(FunctorInfo.of("invited/2"), l2.functor, "Expected functor 'invited/2'")
assertEquals(Functor.of("invited/2"), l2.functor, "Expected functor 'invited/2'")
}
@Test
@ -158,6 +156,6 @@ class LogicGrammarTests {
assertEquals(1, result.size, "Expected 1 rule")
assertTrue(result[0] is Rule, "Expected a rule")
val rule = result[0] as Rule
assertEquals(FunctorInfo.of("/0"), rule.head.functor, "Expected a constraint")
assertEquals(Functor.of("/0"), rule.head.functor, "Expected a constraint")
}
}

View file

@ -1,20 +1,24 @@
package prolog.builtins
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertInstanceOf
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import prolog.ast.Database.Program
import prolog.ast.arithmetic.Integer
import prolog.ast.logic.Fact
import prolog.ast.logic.Rule
import prolog.ast.terms.CompoundTerm
import prolog.ast.terms.AnonymousVariable
import prolog.ast.terms.Atom
import prolog.ast.terms.Structure
import prolog.ast.terms.Variable
class AnalysingAndConstructionOperatorsTests {
class AnalysisOperatorsTests {
@Test
fun `functor(foo, foo, 0)`() {
val functor = Functor(Atom("foo"), Atom("foo"), Integer(0))
val functor = FunctorOp(Atom("foo"), Atom("foo"), Integer(0))
val result = functor.satisfy(emptyMap()).toList()
@ -25,7 +29,7 @@ class AnalysingAndConstructionOperatorsTests {
@Test
fun `functor(foo(X), foo, Y)`() {
val functor = Functor(
val functor = FunctorOp(
Structure(Atom("foo"), listOf(Variable("X"))),
Atom("foo"),
Variable("Y")
@ -43,7 +47,7 @@ class AnalysingAndConstructionOperatorsTests {
@Test
fun `functor(foo, X, Y)`() {
val atom = Atom("foo")
val functor = Functor(atom, Variable("X"), Variable("Y"))
val functor = FunctorOp(atom, Variable("X"), Variable("Y"))
val result = functor.satisfy(emptyMap()).toList()
@ -57,7 +61,7 @@ class AnalysingAndConstructionOperatorsTests {
@Test
fun `functor(X, foo, 1)`() {
val functor = Functor(Variable("X"), Atom("foo"), Integer(1))
val functor = FunctorOp(Variable("X"), Atom("foo"), Integer(1))
val result = functor.satisfy(emptyMap()).toList()
@ -74,7 +78,7 @@ class AnalysingAndConstructionOperatorsTests {
@Test
fun `functor(foo(a), foo, 0)`() {
val functor = Functor(Structure(Atom("foo"), listOf(Atom("a"))), Atom("foo"), Integer(0))
val functor = FunctorOp(Structure(Atom("foo"), listOf(Atom("a"))), Atom("foo"), Integer(0))
val result = functor.satisfy(emptyMap()).toList()
@ -83,7 +87,7 @@ class AnalysingAndConstructionOperatorsTests {
@Test
fun `functor(foo(X), foo, 0)`() {
val functor = Functor(Structure(Atom("foo"), listOf(Variable("X"))), Atom("foo"), Integer(0))
val functor = FunctorOp(Structure(Atom("foo"), listOf(Variable("X"))), Atom("foo"), Integer(0))
val result = functor.satisfy(emptyMap()).toList()
@ -92,7 +96,7 @@ class AnalysingAndConstructionOperatorsTests {
@Test
fun `functor(X, Y, 1)`() {
val functor = Functor(Variable("X"), Variable("Y"), Integer(1))
val functor = FunctorOp(Variable("X"), Variable("Y"), Integer(1))
val exception = assertThrows<Exception> {
functor.satisfy(emptyMap()).toList()
@ -233,4 +237,168 @@ class AnalysingAndConstructionOperatorsTests {
}
assertEquals("Domain error: not_less_than_zero", exception.message)
}
}
@Nested
class `Clause operator` {
@BeforeEach
fun setup() {
Program.reset()
}
@Test
fun `clause fact atom without variables`() {
Program.load(listOf(Fact(Atom("foo"))))
val result = ClauseOp(
Atom("foo"),
True
).satisfy(emptyMap()).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertTrue(subs.isEmpty(), "Expected empty substitutions")
}
@Test
fun `clause fact compound without variables`() {
Program.load(
listOf(
Fact(CompoundTerm(Atom("foo"), listOf(Atom("a"), Atom("b"))))
)
)
val result = ClauseOp(
CompoundTerm(Atom("foo"), listOf(Atom("a"), Atom("b"))),
True
).satisfy(emptyMap()).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertTrue(subs.isEmpty(), "Expected empty substitutions")
}
@Test
fun `clause rule without variables`() {
Program.load(listOf(Rule(Atom("foo"), Atom("bar"))))
val result = ClauseOp(
Atom("foo"),
Atom("bar")
).satisfy(emptyMap()).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertTrue(subs.isEmpty(), "Expected empty substitutions")
}
@Test
fun `clause fact variable body`() {
Program.load(listOf(Fact(Atom("foo"))))
val variable = Variable("Term")
val result = ClauseOp(
Atom("foo"),
variable
).satisfy(emptyMap()).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertEquals(1, subs.size, "Expected 1 substitution")
assertEquals(True, subs[variable])
}
@Test
fun `clause fact with variable head`() {
Program.load(
listOf(
Fact(CompoundTerm(Atom("foo"), listOf(Atom("a"))))
)
)
val result = ClauseOp(
CompoundTerm(Atom("foo"), listOf(Variable("X"))),
True
).satisfy(emptyMap()).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertEquals(1, subs.size, "Expected 1 substitution")
assertEquals(Atom("a"), subs[Variable("X")])
}
@Test
fun `clause rule with variable body`() {
Program.load(listOf(Rule(Atom("foo"), Atom("bar"))))
val result = ClauseOp(
Atom("foo"),
Variable("Term")
).satisfy(emptyMap()).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertEquals(1, subs.size, "Expected 1 substitution")
assertEquals(Atom("bar"), subs[Variable("Term")])
}
@Test
fun `clause fact variable value with backtracking`() {
Program.load(
listOf(
Fact(CompoundTerm(Atom("bar"), listOf(Atom("d")))),
Fact(CompoundTerm(Atom("bar"), listOf(Atom("e")))),
Fact(CompoundTerm(Atom("foo"), listOf(Atom("a")))),
Fact(CompoundTerm(Atom("foo"), listOf(Atom("b")))),
Fact(CompoundTerm(Atom("foo"), listOf(Atom("c")))),
Rule(
CompoundTerm(Atom("foo"), listOf(Variable("X"))),
CompoundTerm(Atom("bar"), listOf(Variable("X")))
)
)
)
val x = Variable("X")
val term = Variable("Term")
val result = ClauseOp(
CompoundTerm(Atom("foo"), listOf(x)),
term
).satisfy(emptyMap()).toList()
assertEquals(4, result.size, "Expected 4 results")
assertTrue(result[0].isSuccess, "Expected success")
val subs1 = result[0].getOrNull()!!
assertEquals(2, subs1.size, "Expected 2 substitutions")
assertEquals(Atom("a"), subs1[x], "Expected a")
assertEquals(True, subs1[term], "Expected True")
assertTrue(result[1].isSuccess, "Expected success")
val subs2 = result[1].getOrNull()!!
assertEquals(2, subs2.size, "Expected 2 substitutions")
assertEquals(Atom("b"), subs2[x], "Expected b")
assertEquals(True, subs2[term], "Expected True")
assertTrue(result[2].isSuccess, "Expected success")
val subs3 = result[2].getOrNull()!!
assertEquals(2, subs3.size, "Expected 2 substitutions")
assertEquals(Atom("c"), subs3[x], "Expected c")
assertEquals(True, subs3[term], "Expected True")
assertTrue(result[3].isSuccess, "Expected success")
val subs4 = result[3].getOrNull()!!
assertEquals(1, subs4.size, "Expected 2 substitutions")
assertEquals(
CompoundTerm(Atom("bar"), listOf(Variable("X"))),
subs4[term],
"Expected bar(X)"
)
}
}
}

View file

@ -14,7 +14,7 @@ import prolog.ast.logic.Fact
import prolog.ast.logic.Predicate
import prolog.ast.logic.Rule
import prolog.ast.terms.Atom
import prolog.ast.terms.FunctorInfo
import prolog.ast.terms.Functor
import prolog.ast.terms.Structure
import prolog.ast.terms.Variable
@ -39,7 +39,7 @@ class DatabaseOperatorsTests {
createAssert(fact).satisfy(emptyMap())
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
assertEquals(fact, Program.db.predicates[FunctorInfo.of("a/_")]!!.clauses[0])
assertEquals(fact, Program.db.predicates[Functor.of("a/_")]!!.clauses[0])
}
@Test
@ -48,7 +48,7 @@ class DatabaseOperatorsTests {
createAssert(fact).satisfy(emptyMap())
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
assertEquals(fact, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses[0])
assertEquals(fact, Program.db.predicates[Functor.of("a/1")]!!.clauses[0])
}
@Test
@ -60,7 +60,7 @@ class DatabaseOperatorsTests {
createAssert(rule).satisfy(emptyMap())
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
assertEquals(rule, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses[0])
assertEquals(rule, Program.db.predicates[Functor.of("a/1")]!!.clauses[0])
}
}
@ -91,8 +91,8 @@ class DatabaseOperatorsTests {
AssertA(rule2).satisfy(emptyMap())
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
assertEquals(rule2, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses[0])
assertEquals(rule1, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses[1])
assertEquals(rule2, Program.db.predicates[Functor.of("a/1")]!!.clauses[0])
assertEquals(rule1, Program.db.predicates[Functor.of("a/1")]!!.clauses[1])
}
}
@ -116,8 +116,8 @@ class DatabaseOperatorsTests {
AssertZ(rule2).satisfy(emptyMap())
assertEquals(1, Program.db.predicates.size, "Expected 1 predicate")
assertEquals(rule1, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses[0])
assertEquals(rule2, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses[1])
assertEquals(rule1, Program.db.predicates[Functor.of("a/1")]!!.clauses[0])
assertEquals(rule2, Program.db.predicates[Functor.of("a/1")]!!.clauses[1])
}
}
@ -308,20 +308,20 @@ class DatabaseOperatorsTests {
val control = Program.query(Structure(Atom("a"), listOf(Variable("X")))).toList()
assertEquals(3, control.size, "Expected 3 results")
assertEquals(3, Program.db.predicates[FunctorInfo.of("a/1")]!!.clauses.size, "Expected 3 clauses")
assertEquals(3, Program.db.predicates[Functor.of("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[FunctorInfo.of("a/1")]!!.clauses.size, "Expected 0 clauses")
assertEquals(0, Program.db.predicates[Functor.of("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 = FunctorInfo.of("$predicateName/1")
val predicateFunctor = Functor.of("$predicateName/1")
assertFalse(predicateFunctor in Program.db.predicates, "Expected predicate to not exist before")