This repository has been archived on 2025-09-23. You can view files and clone it, but you cannot make any changes to it's state, such as pushing and creating new issues, pull requests or comments.
2025LogProg-project-GhentPr.../tests/interpreter/PreprocessorTests.kt
2025-05-07 20:22:14 +02:00

622 lines
23 KiB
Kotlin

package interpreter
import com.github.h0tk3y.betterParse.grammar.parseToEnd
import org.junit.jupiter.api.Assertions.*
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.*
class PreprocessorTests {
val preprocessor = OpenPreprocessor()
companion object {
val preprocessor = OpenPreprocessor()
fun test(tests: Map<Term, Term>) {
for ((input, expected) in tests) {
val result = preprocessor.preprocess(input)
assertEquals(expected, result, "Expected preprocessed")
assertEquals(expected::class, result::class, "Expected same class")
}
}
}
@Test
fun `can preprocess anonymous variable`() {
val input = Variable("_")
val result = preprocessor.preprocess(input)
assertInstanceOf(AnonymousVariable::class.java, result, "Expected anonymous variable")
assertTrue((result as Variable).name.matches("_\\d+".toRegex()), "Expected anonymous variable name")
}
@Test
fun `multiple anonymous variables should be unique`() {
val input = CompoundTerm(Atom("foo"), listOf(Variable("_"), Variable("_")))
val result = preprocessor.preprocess(input)
assertInstanceOf(CompoundTerm::class.java, result, "Expected compound term")
assertEquals(2, (result as CompoundTerm).arguments.size, "Expected two terms")
for (argument in result.arguments) {
assertTrue(
(argument as Variable).name.matches("_\\d+".toRegex()),
"Expected anonymous variable name, but got ${argument.name}"
)
}
val first = result.arguments[0] as Variable
val second = result.arguments[1] as Variable
assertNotEquals(first.name, second.name, "Expected different anonymous variable names")
}
@Test
fun `can preprocess nested anonymous variables`() {
val input = TermsGrammar().parseToEnd("name(character(Name, _, _, _))") as Term
val result = preprocessor.preprocess(input)
assertInstanceOf(CompoundTerm::class.java, result, "Expected compound term")
assertEquals(1, (result as CompoundTerm).arguments.size, "Expected one term")
assertInstanceOf(CompoundTerm::class.java, result.arguments[0], "Expected compound term")
val inner = result.arguments[0] as CompoundTerm
assertEquals(4, inner.arguments.size, "Expected four terms")
for (argument in inner.arguments) {
if ((argument as Variable).name != "Name") {
assertTrue(
(argument as Variable).name.matches("_\\d+".toRegex()),
"Expected anonymous variable name, but got ${argument.name}"
)
}
}
}
@Nested
class `Arithmetic operators` {
@Test
fun `evaluates to different`() {
test(
mapOf(
Atom("=\\=") to Atom("=\\="),
CompoundTerm(Atom("=\\="), emptyList()) to CompoundTerm(Atom("=\\="), emptyList()),
Atom("EvaluatesToDifferent") to Atom("EvaluatesToDifferent"),
CompoundTerm(Atom("EvaluatesToDifferent"), emptyList()) to CompoundTerm(
Atom("EvaluatesToDifferent"),
emptyList()
),
CompoundTerm(Atom("=\\="), listOf(Atom("a"))) to CompoundTerm(
Atom("=\\="),
listOf(Atom("a"))
),
CompoundTerm(Atom("=\\="), listOf(Integer(1))) to CompoundTerm(
Atom("=\\="),
listOf(Integer(1))
),
CompoundTerm(Atom("=\\="), listOf(Atom("=\\="))) to CompoundTerm(
Atom("=\\="),
listOf(Atom("=\\="))
),
CompoundTerm(Atom("=\\="), listOf(Integer(1), Integer(2))) to EvaluatesToDifferent(
Integer(1), Integer(2)
)
)
)
}
@Test
fun `evaluates to`() {
test(
mapOf(
Atom("=:=") to Atom("=:="),
CompoundTerm(Atom("=:="), emptyList()) to CompoundTerm(Atom("=:="), emptyList()),
Atom("EvaluatesTo") to Atom("EvaluatesTo"),
CompoundTerm(Atom("EvaluatesTo"), emptyList()) to CompoundTerm(
Atom("EvaluatesTo"),
emptyList()
),
CompoundTerm(Atom("=:="), listOf(Atom("a"))) to CompoundTerm(
Atom("=:="),
listOf(Atom("a"))
),
CompoundTerm(Atom("=:="), listOf(Atom("=:="))) to CompoundTerm(
Atom("=:="),
listOf(Atom("=:="))
),
CompoundTerm(Atom("=:="), listOf(Integer(1), Integer(2))) to EvaluatesTo(
Integer(1), Integer(2)
)
)
)
}
@Test
fun `is`() {
test(
mapOf(
Atom("is") to Atom("is"),
CompoundTerm(Atom("is"), emptyList()) to CompoundTerm(Atom("is"), emptyList()),
Atom("Is") to Atom("Is"),
CompoundTerm(Atom("Is"), emptyList()) to CompoundTerm(Atom("Is"), emptyList()),
CompoundTerm(Atom("is"), listOf(Atom("a"))) to CompoundTerm(
Atom("is"),
listOf(Atom("a"))
),
CompoundTerm(Atom("is"), listOf(Integer(1))) to CompoundTerm(
Atom("is"),
listOf(Integer(1))
),
CompoundTerm(Atom("is"), listOf(Atom("is"))) to CompoundTerm(
Atom("is"),
listOf(Atom("is"))
),
CompoundTerm(Atom("is"), listOf(Integer(1), Integer(2))) to Is(
Integer(1), Integer(2)
)
)
)
}
@Test
fun `negate and subtract`() {
test(
mapOf(
Atom("-") to Atom("-"),
CompoundTerm(Atom("-"), emptyList()) to CompoundTerm(Atom("-"), emptyList()),
Atom("Negate") to Atom("Negate"),
CompoundTerm(Atom("Negate"), emptyList()) to CompoundTerm(
Atom("Negate"),
emptyList()
),
CompoundTerm(Atom("-"), listOf(Atom("a"))) to CompoundTerm(
Atom("-"),
listOf(Atom("a"))
),
CompoundTerm(Atom("-"), listOf(Integer(1))) to Negate(Integer(1)),
CompoundTerm(Atom("-"), listOf(Atom("-"))) to CompoundTerm(
Atom("-"),
listOf(Atom("-"))
),
CompoundTerm(Atom("-"), listOf(Integer(1), Integer(2))) to Subtract(
Integer(1), Integer(2)
),
CompoundTerm(Atom("-"), listOf(Atom("1"), Atom("2"))) to CompoundTerm(
Atom("-"),
listOf(Atom("1"), Atom("2"))
),
CompoundTerm(Atom("-"), listOf(Integer(1), Integer(2), Integer(3))) to CompoundTerm(
Atom("-"),
listOf(Integer(1), Integer(2), Integer(3))
)
)
)
}
@Test
fun `positive and add`() {
test(
mapOf(
Atom("+") to Atom("+"),
CompoundTerm(Atom("+"), emptyList()) to CompoundTerm(Atom("+"), emptyList()),
Atom("Positive") to Atom("Positive"),
CompoundTerm(Atom("Positive"), emptyList()) to CompoundTerm(
Atom("Positive"),
emptyList()
),
CompoundTerm(Atom("+"), listOf(Atom("a"))) to CompoundTerm(
Atom("+"),
listOf(Atom("a"))
),
CompoundTerm(Atom("+"), listOf(Integer(1))) to Positive(Integer(1)),
CompoundTerm(Atom("+"), listOf(Atom("+"))) to CompoundTerm(
Atom("+"),
listOf(Atom("+"))
),
CompoundTerm(Atom("+"), listOf(Integer(1), Integer(2))) to Add(
Integer(1), Integer(2)
),
CompoundTerm(Atom("+"), listOf(Atom("1"), Atom("2"))) to CompoundTerm(
Atom("+"),
listOf(Atom("1"), Atom("2"))
),
CompoundTerm(Atom("+"), listOf(Integer(1), Integer(2), Integer(3))) to CompoundTerm(
Atom("+"),
listOf(Integer(1), Integer(2), Integer(3))
)
)
)
}
@Test
fun multiply() {
test(
mapOf(
Atom("*") to Atom("*"),
CompoundTerm(Atom("*"), emptyList()) to CompoundTerm(Atom("*"), emptyList()),
Atom("Multiply") to Atom("Multiply"),
CompoundTerm(Atom("Multiply"), emptyList()) to CompoundTerm(
Atom("Multiply"),
emptyList()
),
CompoundTerm(Atom("*"), listOf(Atom("a"))) to CompoundTerm(
Atom("*"),
listOf(Atom("a"))
),
CompoundTerm(Atom("*"), listOf(Integer(1))) to CompoundTerm(Atom("*"), listOf(Integer(1))),
CompoundTerm(Atom("*"), listOf(Atom("*"))) to CompoundTerm(
Atom("*"),
listOf(Atom("*"))
),
CompoundTerm(Atom("*"), listOf(Integer(1), Integer(2))) to Multiply(
Integer(1), Integer(2)
),
CompoundTerm(Atom("*"), listOf(Atom("1"), Atom("2"))) to CompoundTerm(
Atom("*"),
listOf(Atom("1"), Atom("2"))
),
CompoundTerm(Atom("*"), listOf(Integer(1), Integer(2), Integer(3))) to CompoundTerm(
Atom("*"),
listOf(Integer(1), Integer(2), Integer(3))
)
)
)
}
@Test
fun divide() {
test(
mapOf(
Atom("/") to Atom("/"),
CompoundTerm(Atom("/"), emptyList()) to CompoundTerm(Atom("/"), emptyList()),
Atom("Divide") to Atom("Divide"),
CompoundTerm(Atom("Divide"), emptyList()) to CompoundTerm(
Atom("Divide"),
emptyList()
),
CompoundTerm(Atom("/"), listOf(Atom("a"))) to CompoundTerm(
Atom("/"),
listOf(Atom("a"))
),
CompoundTerm(Atom("/"), listOf(Integer(1))) to CompoundTerm(Atom("/"), listOf(Integer(1))),
CompoundTerm(Atom("/"), listOf(Atom("/"))) to CompoundTerm(
Atom("/"),
listOf(Atom("/"))
),
CompoundTerm(Atom("/"), listOf(Integer(1), Integer(2))) to Divide(
Integer(1), Integer(2)
),
CompoundTerm(Atom("/"), listOf(Atom("1"), Atom("2"))) to CompoundTerm(
Atom("/"),
listOf(Atom("1"), Atom("2"))
),
CompoundTerm(Atom("/"), listOf(Integer(1), Integer(2), Integer(3))) to CompoundTerm(
Atom("/"),
listOf(Integer(1), Integer(2), Integer(3))
)
)
)
}
@Test
fun between() {
test(
mapOf(
Atom("between") to Atom("between"),
CompoundTerm(Atom("between"), emptyList()) to CompoundTerm(
Atom("between"),
emptyList()
),
Atom("Between") to Atom("Between"),
CompoundTerm(Atom("Between"), emptyList()) to CompoundTerm(
Atom("Between"),
emptyList()
),
CompoundTerm(Atom("between"), listOf(Atom("a"))) to CompoundTerm(
Atom("between"),
listOf(Atom("a"))
),
CompoundTerm(Atom("between"), listOf(Integer(1))) to CompoundTerm(
Atom("between"),
listOf(Integer(1))
),
CompoundTerm(Atom("between"), listOf(Atom("between"))) to CompoundTerm(
Atom("between"),
listOf(Atom("between"))
),
CompoundTerm(Atom("between"), listOf(Integer(1), Integer(2))) to CompoundTerm(
Atom("between"),
listOf(Integer(1), Integer(2))
),
CompoundTerm(Atom("between"), listOf(Integer(1), Integer(2), Integer(3))) to Between(
Integer(1), Integer(2), Integer(3)
),
)
)
}
@Test
fun `fun combinations`() {
/*
* [X - 1] is [(1 + 2) * ((12 / 3) - 0)]
* should return
* X = 13
*/
val sum_ = CompoundTerm(Atom("+"), listOf(Integer(1), Integer(2)))
val sum = Add(Integer(1), Integer(2))
val div_ = CompoundTerm(Atom("/"), listOf(Integer(12), Integer(3)))
val div = Divide(Integer(12), Integer(3))
val sub_ = CompoundTerm(Atom("-"), listOf(div_, Integer(0)))
val sub = Subtract(div, Integer(0))
val right_ = CompoundTerm(Atom("*"), listOf(sum_, sub_))
val right = Multiply(sum, sub)
val left_ = CompoundTerm(Atom("-"), listOf(Variable("X"), Integer(1)))
val left = Subtract(Variable("X"), Integer(1))
val expr_ = CompoundTerm(Atom("is"), listOf(left_, right_))
val expr = Is(left, right)
val result = OpenPreprocessor().preprocess(expr_)
assertEquals(expr, result)
assertEquals(Is::class, result::class)
val `is` = result as Is
assertEquals(left, `is`.number)
assertEquals(Subtract::class, `is`.number::class)
assertEquals(right, `is`.expr)
assertEquals(Multiply::class, `is`.expr::class)
val multiply = `is`.expr as Multiply
assertEquals(sum, multiply.expr1)
assertEquals(Add::class, multiply.expr1::class)
}
}
@Nested
class `Control operators` {
private var preprocessor = OpenPreprocessor()
@Test
fun fail() {
test(
mapOf(
Atom("fail") to Fail,
CompoundTerm(Atom("fail"), emptyList()) to Fail,
Atom("Fail") to Atom("Fail"),
CompoundTerm(Atom("Fail"), emptyList()) to CompoundTerm(Atom("Fail"), emptyList()),
CompoundTerm(Atom("fail"), listOf(Atom("a"))) to CompoundTerm(Atom("fail"), listOf(Atom("a"))),
CompoundTerm(Atom("fail"), listOf(Atom("fail"))) to CompoundTerm(Atom("fail"), listOf(Fail))
)
)
}
@Test
fun `true`() {
test(
mapOf(
Atom("true") to True,
CompoundTerm(Atom("true"), emptyList()) to True,
Atom("True") to Atom("True"),
CompoundTerm(Atom("True"), emptyList()) to CompoundTerm(Atom("True"), emptyList()),
CompoundTerm(Atom("true"), listOf(Atom("a"))) to CompoundTerm(Atom("true"), listOf(Atom("a"))),
CompoundTerm(Atom("true"), listOf(Atom("true"))) to CompoundTerm(Atom("true"), listOf(True))
)
)
}
@Test
fun cut() {
test(
mapOf(
Atom("!") to Cut(),
CompoundTerm(Atom("!"), emptyList()) to Cut(),
CompoundTerm(Atom("!"), listOf(Atom("a"))) to CompoundTerm(Atom("!"), listOf(Atom("a"))),
CompoundTerm(Atom("!"), listOf(Atom("!"))) to CompoundTerm(Atom("!"), listOf(Cut()))
)
)
}
@Test
fun conjunction() {
test(
mapOf(
CompoundTerm(Atom(","), listOf(Atom("a"), Atom("b"))) to Conjunction(Atom("a"), Atom("b")),
CompoundTerm(Atom(","), listOf(Atom("a"), Atom("b"), Atom("c"))) to CompoundTerm(
Atom(","),
listOf(Atom("a"), Atom("b"), Atom("c"))
),
// Nested conjunctions
CompoundTerm(
Atom(","),
listOf(Atom("a"), CompoundTerm(Atom(","), listOf(Atom("b"), Atom("c"))))
) to Conjunction(Atom("a"), Conjunction(Atom("b"), Atom("c"))),
)
)
}
@Test
fun disjunction() {
test(
mapOf(
CompoundTerm(Atom(";"), listOf(Atom("a"), Atom("b"))) to Disjunction(Atom("a"), Atom("b")),
CompoundTerm(Atom(";"), listOf(Atom("a"), Atom("b"), Atom("c"))) to CompoundTerm(
Atom(";"),
listOf(Atom("a"), Atom("b"), Atom("c"))
),
// Nested disjunctions
CompoundTerm(
Atom(";"),
listOf(Atom("a"), CompoundTerm(Atom(";"), listOf(Atom("b"), Atom("c"))))
) to Disjunction(Atom("a"), Disjunction(Atom("b"), Atom("c"))),
)
)
}
@Test
fun not() {
test(
mapOf(
CompoundTerm(Atom("\\+"), listOf(Atom("a"))) to Not(Atom("a")),
CompoundTerm(Atom("\\+"), listOf(Atom("a"), Atom("b"))) to CompoundTerm(
Atom("\\+"),
listOf(Atom("a"), Atom("b"))
),
// Nested not
CompoundTerm(
Atom("foo"),
listOf(
Atom("bar"),
CompoundTerm(Atom("\\+"), listOf(CompoundTerm(Atom("\\+"), listOf(Atom("baz")))))
)
) to CompoundTerm(Atom("foo"), listOf(Atom("bar"), Not(Not(Atom("baz"))))),
)
)
}
@Test
fun `is`() {
test(
mapOf(
CompoundTerm(Atom("is"), listOf(Variable("T"), Integer(1))) to Is(Variable("T"), Integer(1)),
CompoundTerm(Atom("is"), listOf(Variable("T"), Add(Variable("HP"), Integer(5)))) to Is(
Variable("T"),
Add(Variable("HP"), Integer(5))
),
CompoundTerm(Atom("is"), listOf(Variable("T"), Subtract(Variable("HP"), Integer(5)))) to Is(
Variable("T"),
Subtract(Variable("HP"), Integer(5))
),
)
)
}
}
@Nested
class `Database operators` {
private val preprocessor = OpenPreprocessor()
@Test
fun `assert(fact)`() {
val input = Structure(
Atom("assert"), listOf(
Structure(
Atom(":-"), listOf(
Atom("a"),
Atom("b")
)
)
)
)
val expected = Assert(
Rule(
Atom("a"),
Atom("b")
)
)
val result = preprocessor.preprocess(input)
assertEquals(expected, result)
}
@Test
fun `asserta(fact)`() {
val input = Structure(
Atom("asserta"), listOf(
Structure(
Atom(":-"), listOf(
Atom("a"),
Atom("b")
)
)
)
)
val expected = AssertA(
Rule(
Atom("a"),
Atom("b")
)
)
val result = preprocessor.preprocess(input)
assertEquals(expected, result)
}
@Test
fun `assertz(fact)`() {
val input = Structure(
Atom("assertz"), listOf(
Structure(
Atom(":-"), listOf(
Atom("a"),
Atom("b")
)
)
)
)
val expected = AssertZ(
Rule(
Atom("a"),
Atom("b")
)
)
val result = preprocessor.preprocess(input)
assertEquals(expected, result)
}
@Test
fun `retract(atom)`() {
val input = Structure(
Atom("retract"), listOf(
Atom("a")
)
)
val expected = Retract(Atom("a"))
val result = preprocessor.preprocess(input)
assertEquals(expected, result)
}
@Test
fun `retract(compund with variable)`() {
val input = Structure(
Atom("retract"), listOf(
CompoundTerm(Atom("a"), listOf(Variable("X")))
)
)
val expected = Retract(CompoundTerm(Atom("a"), listOf(Variable("X"))))
val result = preprocessor.preprocess(input)
assertEquals(expected, result)
}
@Test
fun `dynamic declaration`() {
val input = Structure(
Atom("dynamic"), listOf(
Atom("declaration/1")
)
)
val expected = Dynamic(FunctorInfo.of("declaration/1"))
val result = preprocessor.preprocess(input)
assertEquals(expected, result)
}
}
}