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.Test import org.junit.jupiter.api.assertThrows import prolog.ast.arithmetic.Integer import prolog.ast.terms.AnonymousVariable import prolog.ast.terms.Atom import prolog.ast.terms.Structure import prolog.ast.terms.Variable class AnalysingAndConstructionOperatorsTests { @Test fun `functor(foo, foo, 0)`() { val functor = Functor(Atom("foo"), Atom("foo"), Integer(0)) val result = functor.satisfy(emptyMap()).toList() assertEquals(1, result.size, "Expected 1 result") assertTrue(result[0].isSuccess, "Expected success") assertTrue(result[0].getOrNull()!!.isEmpty(), "Expected empty substitutions") } @Test fun `functor(foo(X), foo, Y)`() { val functor = Functor( Structure(Atom("foo"), listOf(Variable("X"))), Atom("foo"), Variable("Y") ) val result = functor.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(Integer(1), subs[Variable("Y")]) } @Test fun `functor(foo, X, Y)`() { val atom = Atom("foo") val functor = Functor(atom, Variable("X"), Variable("Y")) val result = functor.satisfy(emptyMap()).toList() assertEquals(1, result.size, "Expected 1 result") assertTrue(result[0].isSuccess, "Expected success") val subs = result[0].getOrNull()!! assertEquals(2, subs.size, "Expected 2 substitutions") assertEquals(atom.functor.name, subs[Variable("X")]) assertEquals(atom.functor.arity, subs[Variable("Y")]) } @Test fun `functor(X, foo, 1)`() { val functor = Functor(Variable("X"), Atom("foo"), Integer(1)) val result = functor.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") assertInstanceOf(Structure::class.java, subs[Variable("X")]) val structure = subs[Variable("X")] as Structure assertEquals(Atom("foo"), structure.name) assertEquals(1, structure.arguments.size) assertInstanceOf(AnonymousVariable::class.java, structure.arguments[0]) } @Test fun `functor(foo(a), foo, 0)`() { val functor = Functor(Structure(Atom("foo"), listOf(Atom("a"))), Atom("foo"), Integer(0)) val result = functor.satisfy(emptyMap()).toList() assertTrue(result.isEmpty(), "Expected no results") } @Test fun `functor(foo(X), foo, 0)`() { val functor = Functor(Structure(Atom("foo"), listOf(Variable("X"))), Atom("foo"), Integer(0)) val result = functor.satisfy(emptyMap()).toList() assertTrue(result.isEmpty(), "Expected no results") } @Test fun `functor(X, Y, 1)`() { val functor = Functor(Variable("X"), Variable("Y"), Integer(1)) val exception = assertThrows { functor.satisfy(emptyMap()).toList() } assertEquals("Arguments are not sufficiently instantiated", exception.message) } @Test fun `arg without variables`() { val arg = Arg( Integer(1), Structure(Atom("f"), listOf(Atom("a"), Atom("b"), Atom("c"))), Atom("a") ) val result = arg.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 no substitutions") } @Test fun `arg with variable value`() { val arg = Arg( Integer(1), Structure(Atom("f"), listOf(Atom("a"), Atom("b"), Atom("c"))), Variable("Term") ) val result = arg.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("Term")]) } @Test fun `arg with variable arg`() { val arguments = listOf(Atom("a"), Atom("b"), Atom("c")) for (i in arguments.indices) { val arg = Arg( Variable("Arg"), Structure(Atom("f"), arguments), arguments[i] ) val result = arg.satisfy(emptyMap()).toList() assertEquals(1, result.size, "Expected 1 result for arg $i") assertTrue(result[0].isSuccess, "Expected success for arg $i") val subs = result[0].getOrNull()!! assertEquals(1, subs.size, "Expected 1 substitution for arg $i") assertEquals(Integer(i + 1), subs[Variable("Arg")], "Expected arg to be $i + 1") } } @Test fun `arg with backtracking`() { val arg = Arg( Variable("Position"), Structure(Atom("f"), listOf(Atom("a"), Atom("b"), Atom("c"))), Variable("Term") ) val result = arg.satisfy(emptyMap()).toList() assertEquals(3, result.size, "Expected 3 results") assertTrue(result[0].isSuccess, "Expected success") val subs1 = result[0].getOrNull()!! assertEquals(2, subs1.size, "Expected 2 substitutions") assertEquals(Integer(1), subs1[Variable("Position")]) assertEquals(Atom("a"), subs1[Variable("Term")]) assertTrue(result[1].isSuccess, "Expected success") val subs2 = result[1].getOrNull()!! assertEquals(2, subs2.size, "Expected 2 substitutions") assertEquals(Integer(2), subs2[Variable("Position")]) assertEquals(Atom("b"), subs2[Variable("Term")]) assertTrue(result[2].isSuccess, "Expected success") val subs3 = result[2].getOrNull()!! assertEquals(2, subs3.size, "Expected 2 substitutions") assertEquals(Integer(3), subs3[Variable("Position")]) assertEquals(Atom("c"), subs3[Variable("Term")]) } @Test fun `arg raises error if Arg is not compound`() { val arg = Arg(Integer(1), Atom("foo"), Variable("X")) val exception = assertThrows { arg.satisfy(emptyMap()).toList() } assertEquals("Type error: `structure' expected, found `foo' (atom)", exception.message) } @Test fun `arg fails silently if arg = 0`() { val arg = Arg( Integer(0), Structure(Atom("f"), listOf(Atom("a"), Atom("b"), Atom("c"))), Variable("Term") ) val result = arg.satisfy(emptyMap()).toList() assertTrue(result.isEmpty(), "Expected no results") } @Test fun `arg fails silently if arg gt arity`() { val arg = Arg( Integer(4), Structure(Atom("f"), listOf(Atom("a"), Atom("b"), Atom("c"))), Variable("Term") ) val result = arg.satisfy(emptyMap()).toList() assertTrue(result.isEmpty(), "Expected no results") } @Test fun `arg raises error if arg lt 0`() { val arg = Arg( Integer(-1), Structure(Atom("f"), listOf(Atom("a"), Atom("b"), Atom("c"))), Variable("Term") ) val exception = assertThrows { arg.satisfy(emptyMap()).toList() } assertEquals("Domain error: not_less_than_zero", exception.message) } }