Checkpoint
This commit is contained in:
parent
39c3af4ba5
commit
da21d890fb
39 changed files with 1166 additions and 48 deletions
26
tests/prolog/EvaluationTest.kt
Normal file
26
tests/prolog/EvaluationTest.kt
Normal file
|
@ -0,0 +1,26 @@
|
|||
package prolog
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
import prolog.components.Program
|
||||
import prolog.components.expressions.Fact
|
||||
import prolog.components.terms.Atom
|
||||
|
||||
class EvaluationTest {
|
||||
@Test
|
||||
fun successful_single_clause_program_query() {
|
||||
val fact = Fact(Atom("a"))
|
||||
Program.load(listOf(fact))
|
||||
|
||||
assertTrue(Program.query(Atom("a")))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun failing_single_clause_program_query() {
|
||||
val fact = Fact(Atom("a"))
|
||||
Program.load(listOf(fact))
|
||||
|
||||
assertFalse(Program.query(Atom("b")))
|
||||
}
|
||||
}
|
246
tests/prolog/UnifyTest.kt
Normal file
246
tests/prolog/UnifyTest.kt
Normal file
|
@ -0,0 +1,246 @@
|
|||
package prolog
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.Test
|
||||
import prolog.components.terms.Atom
|
||||
import prolog.components.terms.Structure
|
||||
import prolog.components.terms.Variable
|
||||
|
||||
/*
|
||||
* Based on: https://en.wikipedia.org/wiki/Unification_%28computer_science%29#Examples_of_syntactic_unification_of_first-order_terms
|
||||
*/
|
||||
class UnifyTest {
|
||||
@Test
|
||||
fun identical_atoms_unify() {
|
||||
val atom1 = Atom("a")
|
||||
val atom2 = Atom("a")
|
||||
|
||||
val result = unify(atom1, atom2)
|
||||
|
||||
assertTrue(result.isPresent, "Identical atoms should unify")
|
||||
assertEquals(0, result.get().size, "No substitutions should be made")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun different_atoms_do_not_unify() {
|
||||
val atom1 = Atom("a")
|
||||
val atom2 = Atom("b")
|
||||
|
||||
val result = unify(atom1, atom2)
|
||||
|
||||
assertFalse(result.isPresent, "Different atoms should not unify")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun identical_variables_unify() {
|
||||
val variable1 = Variable("X")
|
||||
val variable2 = Variable("X")
|
||||
|
||||
val result = unify(variable1, variable2)
|
||||
|
||||
assertTrue(result.isPresent, "Identical variables should unify")
|
||||
assertEquals(0, result.get().size, "No substitutions should be made")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun variable_unifies_with_atom() {
|
||||
val variable = Variable("X")
|
||||
val atom = Atom("a")
|
||||
|
||||
val result = unify(atom, variable)
|
||||
|
||||
assertTrue(result.isPresent, "Variable should unify with atom")
|
||||
assertEquals(1, result.get().size, "There should be one substitution")
|
||||
assertEquals(atom, variable.alias().get(), "Variable should be substituted with atom")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun variables_alias_when_unified() {
|
||||
val variable1 = Variable("X")
|
||||
val variable2 = Variable("Y")
|
||||
|
||||
val result = unify(variable1, variable2)
|
||||
|
||||
assertTrue(result.isPresent)
|
||||
assertEquals(1, result.get().size)
|
||||
assertEquals(variable2, variable1.alias().get(), "Variable 1 should alias to variable 2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun identical_compound_terms_unify() {
|
||||
val structure1 = Structure(Atom("f"), listOf(Atom("a"), Atom("b")))
|
||||
val structure2 = Structure(Atom("f"), listOf(Atom("a"), Atom("b")))
|
||||
|
||||
val result = unify(structure1, structure2)
|
||||
|
||||
assertTrue(result.isPresent, "Identical compound terms should unify")
|
||||
assertEquals(0, result.get().size, "No substitutions should be made")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun compound_terms_with_different_arguments_do_not_unify() {
|
||||
val structure1 = Structure(Atom("f"), listOf(Atom("a"), Atom("b")))
|
||||
val structure2 = Structure(Atom("f"), listOf(Atom("a"), Atom("c")))
|
||||
|
||||
val result = unify(structure1, structure2)
|
||||
|
||||
assertFalse(result.isPresent, "Different compound terms should not unify")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun compound_terms_with_different_functors_do_not_unify() {
|
||||
val structure1 = Structure(Atom("f"), listOf(Atom("a"), Atom("b")))
|
||||
val structure2 = Structure(Atom("g"), listOf(Atom("a"), Atom("b")))
|
||||
|
||||
val result = unify(structure1, structure2)
|
||||
|
||||
assertFalse(result.isPresent, "Compound terms with different functors should not unify")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun variable_unifies_with_compound_term() {
|
||||
val variable = Variable("X")
|
||||
val structure = Structure(Atom("f"), listOf(Atom("a"), Atom("b")))
|
||||
|
||||
val result = unify(variable, structure)
|
||||
|
||||
assertTrue(result.isPresent, "Variable should unify with compound term")
|
||||
assertEquals(1, result.get().size, "There should be one substitution")
|
||||
assertEquals(structure, variable.alias().get(), "Variable should be substituted with compound term")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun compound_term_with_variable_unifies_with_part() {
|
||||
val variable = Variable("X")
|
||||
val structure1 = Structure(Atom("f"), listOf(Atom("a"), variable))
|
||||
val structure2 = Structure(Atom("f"), listOf(Atom("a"), Atom("b")))
|
||||
|
||||
val result = unify(structure1, structure2)
|
||||
|
||||
assertTrue(result.isPresent, "Compound term with variable should unify with part")
|
||||
assertEquals(1, result.get().size, "There should be one substitution")
|
||||
assertEquals(Atom("b"), variable.alias().get(), "Variable should be substituted with atom")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun compound_terms_with_variable_arguments_lists_alias_variables() {
|
||||
val variable1 = Variable("X")
|
||||
val variable2 = Variable("Y")
|
||||
|
||||
val structure1 = Structure(Atom("f"), listOf(variable1))
|
||||
val structure2 = Structure(Atom("f"), listOf(variable2))
|
||||
|
||||
val result = unify(structure1, structure2)
|
||||
|
||||
assertTrue(result.isPresent, "Compound terms with variable arguments should unify")
|
||||
assertEquals(1, result.get().size, "There should be one substitution")
|
||||
assertEquals(variable2, variable1.alias().get(), "Variable 1 should alias to variable 2")
|
||||
}
|
||||
|
||||
/**
|
||||
* f(X) = f(Y, Z)
|
||||
*/
|
||||
@Test
|
||||
fun compound_terms_with_different_arity_do_not_unify() {
|
||||
val structure1 = Structure(Atom("f"), listOf(Variable("X")))
|
||||
val structure2 = Structure(Atom("f"), listOf(Variable("Y"), Variable("Z")))
|
||||
|
||||
val result = unify(structure1, structure2)
|
||||
|
||||
assertFalse(result.isPresent, "Compound terms with different arity should not unify")
|
||||
}
|
||||
|
||||
/**
|
||||
* f(g(X)) = f(Y)
|
||||
*/
|
||||
@Test
|
||||
fun nested_compound_terms_with_variables_unify() {
|
||||
val variable2 = Variable("Y")
|
||||
|
||||
val structure1 = Structure(Atom("f"), listOf(Structure(Atom("g"), listOf(Variable("X")))))
|
||||
val structure2 = Structure(Atom("f"), listOf(variable2))
|
||||
|
||||
val result = unify(structure1, structure2)
|
||||
|
||||
assertTrue(result.isPresent, "Nested compound terms with variables should unify")
|
||||
assertEquals(1, result.get().size, "There should be one substitution")
|
||||
assertEquals(Structure(Atom("g"), listOf(Variable("X"))), variable2.alias().get(), "Variable should be substituted with compound term")
|
||||
}
|
||||
|
||||
/**
|
||||
* f(g(X), X) = f(Y, a)
|
||||
*/
|
||||
@Test
|
||||
fun compound_terms_with_more_variables() {
|
||||
val variable1 = Variable("X")
|
||||
val variable2 = Variable("Y")
|
||||
|
||||
val structure1 = Structure(Atom("f"), listOf(Structure(Atom("g"), listOf(variable1)), variable1))
|
||||
val structure2 = Structure(Atom("f"), listOf(variable2, Atom("a")))
|
||||
|
||||
val result = unify(structure1, structure2)
|
||||
|
||||
assertTrue(result.isPresent, "Compound terms with more variables should unify")
|
||||
assertEquals(2, result.get().size, "There should be two substitutions")
|
||||
|
||||
assertEquals(Atom("a"), variable1.alias().get(), "Variable 1 should be substituted with atom")
|
||||
assertEquals(Structure(Atom("g"), listOf(Atom("a"))), variable2.alias().get(), "Variable 2 should be substituted with compound term")
|
||||
}
|
||||
|
||||
/**
|
||||
* ?- X = f(X).
|
||||
* X = f(f(X)).
|
||||
*/
|
||||
@Test
|
||||
fun recursive_unification() {
|
||||
val variable1 = Variable("X")
|
||||
val structure2 = Structure(Atom("f"), listOf(Variable("X")))
|
||||
|
||||
val result = unify(variable1, structure2)
|
||||
|
||||
assertTrue(result.isPresent, "Recursive unification should succeed")
|
||||
assertEquals(1, result.get().size, "There should be one substitution")
|
||||
assertEquals(structure2, variable1.alias().get(), "Variable should be substituted with compound term")
|
||||
}
|
||||
|
||||
/**
|
||||
* ?- X = bar, Y = bar, X = Y.
|
||||
*/
|
||||
@Test
|
||||
fun multiple_unification() {
|
||||
val variable1 = Variable("X")
|
||||
val variable2 = Variable("Y")
|
||||
val atom = Atom("bar")
|
||||
|
||||
variable1.bind(atom)
|
||||
variable2.bind(atom)
|
||||
|
||||
val result = unify(variable1, variable2)
|
||||
|
||||
assertTrue(result.isPresent, "Multiple unification should succeed")
|
||||
assertEquals(0, result.get().size, "No substitutions should be made")
|
||||
}
|
||||
|
||||
/**
|
||||
* ?- a = a().
|
||||
* false.
|
||||
*/
|
||||
@Test
|
||||
fun atom_with_different_arity() {
|
||||
val atom1 = Atom("a")
|
||||
val structure2 = Structure(Atom("a"), emptyList())
|
||||
|
||||
val result = unify(atom1, structure2)
|
||||
|
||||
assertFalse(result.isPresent, "Atom with different arity should not unify")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun temp() {
|
||||
val atom1 = Atom("a")
|
||||
val atom2 = Atom("a")
|
||||
|
||||
val result = unify(atom1, atom2)
|
||||
assertTrue(result.isPresent, "Identical atoms should unify")
|
||||
}
|
||||
}
|
43
tests/prolog/builtins/TermAnalysisConstructionTest.kt
Normal file
43
tests/prolog/builtins/TermAnalysisConstructionTest.kt
Normal file
|
@ -0,0 +1,43 @@
|
|||
package prolog.builtins
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import prolog.components.terms.Atom
|
||||
import prolog.components.terms.Structure
|
||||
|
||||
/**
|
||||
* Based on [Predicates for analyzing/constructing terms](https://github.com/dtonhofer/prolog_notes/blob/master/swipl_notes/about_term_analysis_and_construction/term_analysis_construction.png)
|
||||
*
|
||||
* Notes by [David Tonhofer](https://github.com/dtonhofer)
|
||||
*/
|
||||
class TermAnalysisConstructionTest {
|
||||
@Test
|
||||
fun atomic_term_properties() {
|
||||
val atom = Atom("foo")
|
||||
|
||||
assertTrue(atomic(atom))
|
||||
assertFalse(compound(atom))
|
||||
|
||||
assertTrue(functor(atom, Atom("foo"), 0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun compound_arity_0_properties() {
|
||||
val structure = Structure(Atom("foo"), emptyList())
|
||||
|
||||
assertFalse(atomic(structure))
|
||||
assertTrue(compound(structure))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun compound_arity_1_properties() {
|
||||
val structure = Structure(Atom("foo"), listOf(Atom("bar")))
|
||||
|
||||
assertFalse(atomic(structure))
|
||||
assertTrue(compound(structure))
|
||||
|
||||
assertTrue(functor(structure, Atom("foo"), 1))
|
||||
}
|
||||
}
|
251
tests/prolog/builtins/VerificationBuiltinsTest.kt
Normal file
251
tests/prolog/builtins/VerificationBuiltinsTest.kt
Normal file
|
@ -0,0 +1,251 @@
|
|||
package prolog.builtins
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.Test
|
||||
import prolog.components.terms.Atom
|
||||
import prolog.components.terms.Structure
|
||||
import prolog.components.terms.Variable
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class BuiltinsTest {
|
||||
@Test
|
||||
fun unbound_variable_is_var() {
|
||||
val variable = Variable("X")
|
||||
assertTrue(variable(variable))
|
||||
assertTrue(variable.alias().isEmpty)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun variable_bound_to_atom_is_not_var() {
|
||||
val variable = Variable("X")
|
||||
|
||||
assertTrue(variable(variable))
|
||||
|
||||
val atom = Atom("a")
|
||||
variable.bind(atom)
|
||||
|
||||
assertFalse(variable(variable))
|
||||
assertEquals(atom, variable.alias().get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun variable_bound_to_compound_term_is_not_var() {
|
||||
val variable = Variable("X")
|
||||
|
||||
assertTrue(variable(variable))
|
||||
|
||||
val structure = Structure(Atom("a"), listOf(Atom("b")))
|
||||
variable.bind(structure)
|
||||
|
||||
assertFalse(variable(variable))
|
||||
assertEquals(structure, variable.alias().get())
|
||||
}
|
||||
|
||||
/**
|
||||
* ?- X = X, var(X).
|
||||
* true.
|
||||
*/
|
||||
@Test
|
||||
fun variable_bound_to_itself_is_var() {
|
||||
val variable = Variable("X")
|
||||
variable.bind(variable)
|
||||
assertTrue(variable(variable))
|
||||
}
|
||||
|
||||
/**
|
||||
* ?- X = Y, var(X).
|
||||
* X = Y.
|
||||
*/
|
||||
@Test
|
||||
fun variable_bound_to_another_variable_is_var() {
|
||||
val variable1 = Variable("X")
|
||||
val variable2 = Variable("Y")
|
||||
variable1.bind(variable2)
|
||||
assertTrue(variable(variable1))
|
||||
assertEquals(variable2, variable1.alias().get())
|
||||
}
|
||||
|
||||
/**
|
||||
* ?- Y = a, X = Y, var(X).
|
||||
* false.
|
||||
*/
|
||||
@Test
|
||||
fun variable_bound_to_bound_variable_is_not_var() {
|
||||
val variable1 = Variable("X")
|
||||
val variable2 = Variable("Y")
|
||||
variable2.bind(Atom("a"))
|
||||
variable1.bind(variable2)
|
||||
assertFalse(variable(variable1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun atom_is_not_var() {
|
||||
val atom = Atom("a")
|
||||
assertFalse(variable(atom))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun compound_term_is_not_var() {
|
||||
val structure = Structure(Atom("a"), listOf(Atom("b")))
|
||||
assertFalse(variable(structure))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun compound_term_with_var_is_not_var() {
|
||||
val structure = Structure(Atom("a"), listOf(Variable("X")))
|
||||
assertFalse(variable(structure))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unbound_variable_is_not_nonvar() {
|
||||
val variable = Variable("X")
|
||||
assertFalse(nonvariable(variable))
|
||||
}
|
||||
|
||||
/**
|
||||
* ?- A = a, nonvar(A).
|
||||
* A = a.
|
||||
*/
|
||||
@Test
|
||||
fun variable_bound_to_atom_is_nonvar() {
|
||||
val variable = Variable("X")
|
||||
|
||||
assertFalse(nonvariable(variable))
|
||||
|
||||
val atom = Atom("a")
|
||||
variable.bind(atom)
|
||||
|
||||
assertTrue(nonvariable(variable))
|
||||
assertEquals(atom, variable.alias().get())
|
||||
}
|
||||
|
||||
/**
|
||||
* ?- A = f(b), nonvar(A).
|
||||
* A = f(b).
|
||||
*/
|
||||
@Test
|
||||
fun variable_bound_to_compound_term_is_nonvar() {
|
||||
val variable = Variable("X")
|
||||
|
||||
assertFalse(nonvariable(variable))
|
||||
|
||||
val structure = Structure(Atom("a"), listOf(Atom("b")))
|
||||
variable.bind(structure)
|
||||
|
||||
assertTrue(nonvariable(variable))
|
||||
assertEquals(structure, variable.alias().get())
|
||||
}
|
||||
|
||||
/**
|
||||
* ?- X = X, nonvar(X).
|
||||
* false.
|
||||
*/
|
||||
@Test
|
||||
fun variable_bound_to_itself_is_not_nonvar() {
|
||||
val variable = Variable("X")
|
||||
variable.bind(variable)
|
||||
assertFalse(nonvariable(variable))
|
||||
}
|
||||
|
||||
/**
|
||||
* ?- X = Y, nonvar(X).
|
||||
* false.
|
||||
*/
|
||||
@Test
|
||||
fun variable_bound_to_another_variable_is_not_nonvar() {
|
||||
val variable1 = Variable("X")
|
||||
val variable2 = Variable("Y")
|
||||
variable1.bind(variable2)
|
||||
assertFalse(nonvariable(variable1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun atom_is_nonvar() {
|
||||
val atom = Atom("a")
|
||||
assertTrue(nonvariable(atom))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun compound_term_is_nonvar() {
|
||||
val structure = Structure(Atom("a"), listOf(Atom("b")))
|
||||
assertTrue(nonvariable(structure))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun compound_term_with_var_is_nonvar() {
|
||||
val structure = Structure(Atom("a"), listOf(Variable("X")))
|
||||
assertTrue(nonvariable(structure))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun unbound_variable_is_not_compound() {
|
||||
val variable = Variable("X")
|
||||
assertFalse(compound(variable))
|
||||
}
|
||||
|
||||
/**
|
||||
* ?- A = a, compound(A).
|
||||
* false.
|
||||
*/
|
||||
@Test
|
||||
fun bound_variable_to_atom_is_not_compound() {
|
||||
val variable = Variable("X")
|
||||
val atom = Atom("a")
|
||||
variable.bind(atom)
|
||||
assertFalse(compound(variable))
|
||||
}
|
||||
|
||||
/**
|
||||
* ?- A = f(b), compound(A).
|
||||
* A = f(b).
|
||||
*/
|
||||
@Test
|
||||
fun bound_variable_to_compound_term_is_compound() {
|
||||
val variable = Variable("X")
|
||||
val structure = Structure(Atom("a"), listOf(Atom("b")))
|
||||
variable.bind(structure)
|
||||
assertTrue(compound(variable))
|
||||
}
|
||||
|
||||
/**
|
||||
* ?- Y = f(b), X = Y, compound(X).
|
||||
* Y = X, X = f(b).
|
||||
*/
|
||||
@Test
|
||||
fun variable_bound_to_bound_variable_is_compound() {
|
||||
val variable1 = Variable("X")
|
||||
val variable2 = Variable("Y")
|
||||
val structure = Structure(Atom("a"), listOf(Atom("b")))
|
||||
|
||||
variable2.bind(structure)
|
||||
variable1.bind(variable2)
|
||||
|
||||
assertTrue(compound(variable1))
|
||||
|
||||
assertEquals(structure, variable2.alias().get())
|
||||
assertEquals(variable2, variable1.alias().get())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun atom_is_not_compound() {
|
||||
val atom = Atom("a")
|
||||
assertFalse(compound(atom))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun compound_term_is_compound() {
|
||||
val structure = Structure(Atom("a"), listOf(Atom("b")))
|
||||
assertTrue(compound(structure))
|
||||
}
|
||||
|
||||
/**
|
||||
* ?- compound(a()).
|
||||
* true.
|
||||
*/
|
||||
@Test
|
||||
fun compound_term_with_no_arguments_is_compound() {
|
||||
val structure = Structure(Atom("a"), emptyList())
|
||||
assertTrue(compound(structure))
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
package prolog
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import org.junit.jupiter.api.Test
|
||||
import prolog.terms.Atom
|
||||
|
||||
class unifyTest {
|
||||
@Test
|
||||
fun identical_atoms_unify() {
|
||||
val atom1 = Atom("a")
|
||||
val atom2 = Atom("a")
|
||||
|
||||
val result = unify(atom1, atom2)
|
||||
|
||||
assertTrue(result.success)
|
||||
assertEquals(0, result.substitutions.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun different_atoms_do_not_unify() {
|
||||
val atom1 = Atom("a")
|
||||
val atom2 = Atom("b")
|
||||
|
||||
val result = unify(atom1, atom2)
|
||||
|
||||
assertFalse(result.success)
|
||||
assertEquals(0, result.substitutions.size)
|
||||
}
|
||||
}
|
Reference in a new issue