Checkpoint
This commit is contained in:
parent
973365e2ec
commit
3724ac72f9
13 changed files with 659 additions and 29 deletions
|
@ -19,6 +19,8 @@ import prolog.ast.terms.Term
|
|||
import prolog.ast.terms.Variable
|
||||
import prolog.builtins.Is
|
||||
import prolog.logic.equivalent
|
||||
import prolog.ast.lists.List.Empty
|
||||
import prolog.ast.lists.List.Cons
|
||||
|
||||
class TermsGrammarTests {
|
||||
private lateinit var parser: Grammar<Term>
|
||||
|
@ -352,4 +354,49 @@ class TermsGrammarTests {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class Lists {
|
||||
private lateinit var parser: Grammar<Term>
|
||||
|
||||
@BeforeEach
|
||||
fun setup() {
|
||||
parser = TermsGrammar() as Grammar<Term>
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parse empty list`() {
|
||||
val input = "[]"
|
||||
|
||||
val result = parser.parseToEnd(input)
|
||||
|
||||
assertEquals(Empty, result, "Expected empty list")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parse non-empty list`() {
|
||||
val input = "[a, b, c]"
|
||||
|
||||
val result = parser.parseToEnd(input)
|
||||
|
||||
assertEquals(
|
||||
Cons(Atom("a"), Cons(Atom("b"), Cons(Atom("c"), Empty))),
|
||||
result,
|
||||
"Expected non-empty list"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `parse nested lists`() {
|
||||
val input = "[a, [b, c], d]"
|
||||
|
||||
val result = parser.parseToEnd(input)
|
||||
|
||||
assertEquals(
|
||||
Cons(Atom("a"), Cons(Cons(Atom("b"), Cons(Atom("c"), Empty)), Cons(Atom("d"), Empty))),
|
||||
result,
|
||||
"Expected nested lists"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,13 +7,13 @@ 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.lists.List
|
||||
import prolog.ast.lists.List.Cons
|
||||
import prolog.ast.lists.List.Empty
|
||||
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
|
||||
import prolog.ast.terms.*
|
||||
import prolog.logic.equivalent
|
||||
|
||||
class AnalysisOperatorsTests {
|
||||
@Test
|
||||
|
@ -401,4 +401,246 @@ class AnalysisOperatorsTests {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class `Univ operator` {
|
||||
@Test
|
||||
fun `univ without variables`() {
|
||||
val univ = Univ(Atom("foo"), Cons(Atom("foo"), Empty))
|
||||
|
||||
val result = univ.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 `univ with variable list`() {
|
||||
val list = Variable("List")
|
||||
val univ = Univ(
|
||||
CompoundTerm(Atom("foo"), listOf(Atom("a"), Atom("b"))),
|
||||
list
|
||||
)
|
||||
val expected = Cons(Atom("foo"), Cons(Atom("a"), Cons(Atom("b"), Empty)))
|
||||
|
||||
val result = univ.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(expected, subs[list], "Expected List to be $expected")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `univ with variable in compound`() {
|
||||
val list = Variable("List")
|
||||
val univ = Univ(
|
||||
CompoundTerm(Atom("foo"), listOf(Variable("X"))),
|
||||
list
|
||||
)
|
||||
val expected = Cons(Atom("foo"), Cons(Variable("X"), Empty))
|
||||
|
||||
val result = univ.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(expected, subs[list], "Expected List to be $expected")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `univ with var foo`() {
|
||||
val univ = Univ(
|
||||
Variable("Term"),
|
||||
Cons(Atom("foo"), Empty)
|
||||
)
|
||||
|
||||
val result = univ.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("foo"), subs[Variable("Term")], "Expected Term to be foo")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `univ with var foo(a)`() {
|
||||
val univ = Univ(
|
||||
Variable("Term"),
|
||||
Cons(Atom("foo"), Cons(Atom("a"), Empty))
|
||||
)
|
||||
|
||||
val result = univ.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(
|
||||
Structure(Atom("foo"), listOf(Atom("a"))),
|
||||
subs[Variable("Term")],
|
||||
"Expected Term to be foo(a)"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `univ with var foo(X)`() {
|
||||
val univ = Univ(
|
||||
Variable("Term"),
|
||||
Cons(Atom("foo"), Cons(Variable("X"), Empty))
|
||||
)
|
||||
|
||||
val result = univ.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(
|
||||
Structure(Atom("foo"), listOf(Variable("X"))),
|
||||
subs[Variable("Term")],
|
||||
"Expected Term to be foo(X)"
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `univ with var foo(a, b(X), c)`() {
|
||||
val univ = Univ(
|
||||
Variable("Term"),
|
||||
Cons(
|
||||
Atom("foo"),
|
||||
Cons(Atom("a"), Cons(CompoundTerm(Atom("b"), listOf(Variable("X"))), Cons(Atom("c"), Empty)))
|
||||
)
|
||||
)
|
||||
val expected = CompoundTerm(
|
||||
Atom("foo"),
|
||||
listOf(Atom("a"), CompoundTerm(Atom("b"), listOf(Variable("X"))), Atom("c"))
|
||||
)
|
||||
|
||||
val result = univ.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(expected, subs[Variable("Term")], "Expected Term to be $expected")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `univ with unified var should return substitution`() {
|
||||
val univ = Univ(
|
||||
CompoundTerm(
|
||||
Atom("foo"), listOf(
|
||||
Atom("a"),
|
||||
CompoundTerm(Atom("bar"), listOf(Variable("X"))),
|
||||
Atom("c")
|
||||
)
|
||||
),
|
||||
Cons(
|
||||
Atom("foo"),
|
||||
Cons(Atom("a"), Cons(CompoundTerm(Atom("bar"), listOf(Atom("b"))), Cons(Atom("c"), Empty)))
|
||||
)
|
||||
)
|
||||
|
||||
val result = univ.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("b"), subs[Variable("X")], "Expected X to be b")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `univ of incompatible structures should fail`() {
|
||||
val univ = Univ(
|
||||
CompoundTerm(Atom("foo"), listOf(Atom("a"))),
|
||||
Cons(Atom("bar"), Empty)
|
||||
)
|
||||
|
||||
val result = univ.satisfy(emptyMap()).toList()
|
||||
|
||||
assertEquals(0, result.size, "Expected 0 results")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `univ with two vars should throw`() {
|
||||
val univ = Univ(Variable("X"), Variable("Y"))
|
||||
|
||||
val exception = assertThrows<Exception> {
|
||||
univ.satisfy(emptyMap()).toList()
|
||||
}
|
||||
assertEquals("Arguments are not sufficiently instantiated", exception.message)
|
||||
}
|
||||
|
||||
class OpenUniv(term: Term, list: Term) : Univ(term, list) {
|
||||
public override fun listToTerm(list: List): Term = super.listToTerm(list)
|
||||
public override fun termToList(term: Term): List = super.termToList(term)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a to term`() {
|
||||
val univ = OpenUniv(Atom(""), Empty)
|
||||
|
||||
val originalList = Cons(Atom("a"), Empty)
|
||||
val originalTerm = Atom("a")
|
||||
|
||||
val term = univ.listToTerm(originalList)
|
||||
assertEquals(originalTerm, term, "Expected term to be a")
|
||||
|
||||
val list = univ.termToList(term)
|
||||
assertEquals(originalList, list, "Expected list to be [a]")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `foo, bar to compound`() {
|
||||
val univ = OpenUniv(Atom(""), Empty)
|
||||
|
||||
val originalList = Cons(Atom("foo"), Cons(Atom("bar"), Empty))
|
||||
val originalTerm = CompoundTerm(Atom("foo"), listOf(Atom("bar")))
|
||||
|
||||
val term = univ.listToTerm(originalList)
|
||||
assertEquals(originalTerm, term)
|
||||
|
||||
val list = univ.termToList(term)
|
||||
assertEquals(originalList, list)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `foo, bar, baz to compound`() {
|
||||
val univ = OpenUniv(Atom(""), Empty)
|
||||
|
||||
val originalList = Cons(Atom("foo"), Cons(Atom("bar"), Cons(Atom("baz"), Empty)))
|
||||
val originalTerm = CompoundTerm(Atom("foo"), listOf(Atom("bar"), Atom("baz")))
|
||||
|
||||
val term = univ.listToTerm(originalList)
|
||||
assertEquals(originalTerm, term)
|
||||
|
||||
val list = univ.termToList(term)
|
||||
assertEquals(originalList, list)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `foo, bar, (baz, bar) to compound with list`() {
|
||||
val univ = OpenUniv(Atom(""), Empty)
|
||||
|
||||
val originalList =
|
||||
Cons(Atom("foo"), Cons(Atom("bar"), Cons(Cons(Atom("baz"), Cons(Atom("bar"), Empty)), Empty)))
|
||||
val originalTerm = CompoundTerm(
|
||||
Atom("foo"),
|
||||
listOf(Atom("bar"), Cons(Atom("baz"), Cons(Atom("bar"), Empty)))
|
||||
)
|
||||
|
||||
val term = univ.listToTerm(originalList)
|
||||
assertTrue(equivalent(originalTerm, term, emptyMap()), "Expected term to be foo(bar, [baz, bar]))")
|
||||
|
||||
val list = univ.termToList(term)
|
||||
assertTrue(equivalent(originalList, list, emptyMap()), "Expected list to be [foo, bar, [baz, bar]]")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
60
tests/prolog/builtins/ListOperatorsTests.kt
Normal file
60
tests/prolog/builtins/ListOperatorsTests.kt
Normal file
|
@ -0,0 +1,60 @@
|
|||
package prolog.builtins
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.Test
|
||||
import prolog.ast.lists.List.Cons
|
||||
import prolog.ast.lists.List.Empty
|
||||
import prolog.ast.terms.Atom
|
||||
|
||||
class ListOperatorsTests {
|
||||
@Test
|
||||
fun `size empty list is 0`() {
|
||||
assertEquals(0, Empty.size.value, "Expected size of empty list to be 0")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `size of singleton is 1`() {
|
||||
val list = Cons(Atom("a"), Empty)
|
||||
|
||||
assertEquals(1, list.size.value, "Expected size of singleton list to be 1")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `size of list with five elements is 5`() {
|
||||
val list = Cons(Atom("a"), Cons(Atom("b"), Cons(Atom("c"), Cons(Atom("d"), Cons(Atom("e"), Empty)))))
|
||||
|
||||
assertEquals(5, list.size.value, "Expected size of list with five elements to be 5")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `member(a, list of a)`() {
|
||||
val atom = Atom("a")
|
||||
val list = Cons(atom, Empty)
|
||||
|
||||
val member = Member(atom, list)
|
||||
|
||||
val result = member.satisfy(emptyMap()).toList()
|
||||
|
||||
assertEquals(1, result.size, "Expected one solution")
|
||||
assertTrue(result[0].isSuccess, "Expected success")
|
||||
assertTrue(result[0].getOrNull()!!.isEmpty(), "Expected empty substitution map")
|
||||
}
|
||||
|
||||
@Disabled("Not required functionality")
|
||||
@Test
|
||||
fun `member with variable in list`() {
|
||||
val atom = Atom("a")
|
||||
val variable = Atom("X")
|
||||
val list = Cons(variable, Empty)
|
||||
|
||||
val member = Member(atom, list)
|
||||
|
||||
val result = member.satisfy(emptyMap()).toList()
|
||||
|
||||
assertEquals(1, result.size, "Expected one solution")
|
||||
assertTrue(result[0].isSuccess, "Expected success")
|
||||
assertEquals(atom, result[0].getOrNull()!![variable], "Expected variable to be unified with atom")
|
||||
}
|
||||
}
|
|
@ -10,13 +10,16 @@ import prolog.ast.terms.Variable
|
|||
import prolog.builtins.Add
|
||||
import org.junit.jupiter.api.Disabled
|
||||
import org.junit.jupiter.api.Nested
|
||||
import prolog.ast.lists.List
|
||||
import prolog.ast.lists.List.Empty
|
||||
import prolog.ast.lists.List.Cons
|
||||
|
||||
/*
|
||||
* Based on: https://en.wikipedia.org/wiki/Unification_%28computer_science%29#Examples_of_syntactic_unification_of_first-order_terms
|
||||
*/
|
||||
class UnificationTests {
|
||||
@Nested
|
||||
class `unify` {
|
||||
class `unify logic` {
|
||||
@Test
|
||||
fun identical_atoms_unify() {
|
||||
val atom1 = Atom("a")
|
||||
|
@ -330,7 +333,7 @@ class UnificationTests {
|
|||
}
|
||||
|
||||
@Nested
|
||||
class `applySubstitution` {
|
||||
class `applySubstitution logic` {
|
||||
@Test
|
||||
fun `apply substitution without sub`() {
|
||||
val term = Variable("X")
|
||||
|
@ -367,4 +370,67 @@ class UnificationTests {
|
|||
assertEquals(Integer(35), result)
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
class `equivalent logic` {
|
||||
@Test
|
||||
fun `empty lists are equivalent`() {
|
||||
val eq = equivalent(Empty, Empty, emptyMap())
|
||||
assertTrue(eq, "Empty lists should be equivalent")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `singletons are equivalent`() {
|
||||
val atom = Atom("a")
|
||||
val list1 = Cons(atom, Empty)
|
||||
val list2 = Cons(atom, Empty)
|
||||
|
||||
val eq = equivalent(list1, list2, emptyMap())
|
||||
|
||||
assertTrue(eq, "Singleton lists should be equivalent")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `singleton and empty list are not equivalent`() {
|
||||
val atom = Atom("a")
|
||||
val list1 = Cons(atom, Empty)
|
||||
val list2 = Empty
|
||||
|
||||
var eq = equivalent(list1, list2, emptyMap())
|
||||
assertFalse(eq, "Singleton and empty lists should not be equivalent")
|
||||
|
||||
eq = equivalent(list2, list1, emptyMap())
|
||||
assertFalse(eq, "Empty and singleton lists should not be equivalent")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `identical lists are equivalent`() {
|
||||
val list1 = Cons(Atom("a"), Cons(Atom("b"), Empty))
|
||||
val list2 = Cons(Atom("a"), Cons(Atom("b"), Empty))
|
||||
|
||||
var eq = equivalent(list1, list2, emptyMap())
|
||||
assertTrue(eq, "Identical lists should be equivalent")
|
||||
|
||||
eq = equivalent(list2, list1, emptyMap())
|
||||
assertTrue(eq, "Identical lists should be equivalent")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `identical nested lists are equivalent`() {
|
||||
val list1 = Cons(Atom("foo"), Cons(Atom("bar"), Cons(Cons(Atom("baz"), Cons(Atom("bar"), Empty)), Empty)))
|
||||
val list2 = Cons(Atom("foo"), Cons(Atom("bar"), Cons(Cons(Atom("baz"), Cons(Atom("bar"), Empty)), Empty)))
|
||||
|
||||
var eq = equivalent(list1, list2, emptyMap())
|
||||
assertTrue(eq, "Identical nested lists should be equivalent")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `lists with different nests are not equivalent`() {
|
||||
val list1 = Cons(Atom("foo"), Cons(Atom("bar"), Cons(Cons(Atom("bar"), Cons(Atom("baz"), Empty)), Empty)))
|
||||
val list2 = Cons(Atom("foo"), Cons(Atom("bar"), Cons(Cons(Atom("baz"), Cons(Atom("bar"), Empty)), Empty)))
|
||||
|
||||
var eq = equivalent(list1, list2, emptyMap())
|
||||
assertFalse(eq, "Lists with different nests should not be equivalent")
|
||||
}
|
||||
}
|
||||
}
|
Reference in a new issue