Checkpoint

This commit is contained in:
Tibo De Peuter 2025-05-01 17:13:35 +02:00
parent 43b364044e
commit 9db1c66781
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
34 changed files with 746 additions and 194 deletions

53
tests/compare.sh Normal file
View file

@ -0,0 +1,53 @@
#!/usr/bin/env bash
# This script is expected to be run from the root of the project.
# Paths to the two implementations
GPL="src/gpl"
SPL="swipl"
GPL_FLAGS=("--debug")
SPL_FLAGS=("--quiet" "-t" "'true'")
# Directory containing test files
TEST_DIR="examples"
# Temporary files for storing outputs
GPL_OUT=$(mktemp)
SPL_OUT=$(mktemp)
# Flag to track if all tests pass
PASSED=0
FAILED=0
# Iterate over all test files in the test directory
#for TESTFILE in $(find ${TEST_DIR} -type f); do
files=("examples/program.pl" "examples/basics/disjunction.pl" "examples/basics/fraternity.pl")
for TESTFILE in "${files[@]}"; do
# Run both programs with the test file
"${SPL}" "${SPL_FLAGS[@]}" "$TESTFILE" > "${SPL_OUT}" 2>&1
"${GPL}" "${GPL_FLAGS[@]}" -s "$TESTFILE" > "${GPL_OUT}" 2>&1
# Compare the outputs
if diff -q "$SPL_OUT" "$GPL_OUT" > /dev/null; then
PASSED=$((PASSED + 1))
else
echo "Test failed! Outputs differ for $TESTFILE"
printf "\nTest:\n%s\n" "$(cat "$TESTFILE")"
printf "\nExpected:\n%s\n" "$(cat "$SPL_OUT")"
printf "\nGot:\n%s\n" "$(cat "$GPL_OUT")"
echo "-----------------------------------------"
FAILED=$((FAILED + 1))
fi
done
# Clean up temporary files
rm "$SPL_OUT" "$GPL_OUT"
# Final result, summary
if [ $FAILED -eq 0 ]; then
echo "All tests passed!"
else
printf "Tests passed: %d\nTests failed: %d\n" "$PASSED" "$FAILED"
exit 1
fi

4
tests/e2e/myClass.kt Normal file
View file

@ -0,0 +1,4 @@
package e2e
class myClass {
}

View file

@ -12,8 +12,8 @@ import prolog.builtins.*
class PreprocessorTests {
class OpenPreprocessor : Preprocessor() {
public override fun preprocess(input: Term): Term {
return super.preprocess(input)
public override fun preprocess(term: Term, nested: Boolean): Term {
return super.preprocess(term, nested)
}
}

View file

@ -16,8 +16,6 @@ class SourceFileReaderTests {
val reader = FileLoader()
reader.readFile(inputFile)
println(Program.predicates)
}
@Test
@ -26,7 +24,5 @@ class SourceFileReaderTests {
val reader = FileLoader()
reader.readFile(inputFile)
println(Program.predicates)
}
}

View file

@ -26,4 +26,17 @@ class OperatorParserTests {
assertEquals(Structure(Atom(","), listOf(Atom("a"), Atom("b"))), result, "Expected atom 'a, b'")
}
class BodyParser : TermsGrammar() {
override val rootParser: Parser<Any> by body
}
@Test
fun `parse equality`() {
val input = "a = b"
val result = BodyParser().parseToEnd(input)
assertEquals(Structure(Atom("="), listOf(Atom("a"), Atom("b"))), result, "Expected atom 'a = b'")
}
}

View file

@ -129,4 +129,16 @@ class LogicGrammarTests {
val conjunction = rule.body as CompoundTerm
assertEquals("invited/2", (conjunction.arguments[0] as CompoundTerm).functor, "Expected functor 'invited/2'")
}
@Test
fun `parse constraints`() {
val input = ":- a."
val result = parser.parseToEnd(input)
assertEquals(1, result.size, "Expected 1 rule")
assertTrue(result[0] is Rule, "Expected a rule")
val rule = result[0] as Rule
assertEquals("/_", rule.head.functor, "Expected a constraint")
}
}

View file

@ -2,9 +2,12 @@ package parser.grammars
import com.github.h0tk3y.betterParse.grammar.Grammar
import com.github.h0tk3y.betterParse.grammar.parseToEnd
import com.github.h0tk3y.betterParse.parser.Parser
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertDoesNotThrow
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import prolog.ast.arithmetic.Float
@ -14,6 +17,7 @@ import prolog.ast.terms.Structure
import prolog.ast.terms.Term
import prolog.ast.terms.Variable
import prolog.logic.equivalent
import kotlin.test.assertEquals
class TermsGrammarTests {
private lateinit var parser: Grammar<Term>
@ -167,9 +171,12 @@ class TermsGrammarTests {
val result = parser.parseToEnd(input)
Assertions.assertTrue(
equivalent(Float(-42.0f), result, emptyMap()),
"Expected float '-42.0'"
)
assertEquals(Float(-42.0f), result, "Expected float '-42.0'")
}
@ParameterizedTest
@ValueSource(strings = ["got_an_a(Student)", "grade(Student, Grade)"])
fun `parse unification`(input: String) {
assertDoesNotThrow { parser.parseToEnd(input) }
}
}

View file

@ -2,6 +2,7 @@ package prolog
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 prolog.ast.logic.Fact
import prolog.ast.logic.Rule
@ -108,9 +109,9 @@ class EvaluationTests {
val parent = Rule(
Structure(Atom("parent"), listOf(variable1, variable2)),
/* :- */ Disjunction(
Structure(Atom("father"), listOf(variable1, variable2)),
/* ; */
Structure(Atom("mother"), listOf(variable1, variable2))
Structure(Atom("father"), listOf(variable1, variable2)),
/* ; */
Structure(Atom("mother"), listOf(variable1, variable2))
)
)
@ -212,7 +213,182 @@ class EvaluationTests {
assertEquals(expectedResults.size, actualResults.size, "Number of results should match")
for (i in expectedResults.indices) {
assertEquals(expectedResults[i].size, actualResults[i].getOrNull()!!.size, "Substitution size should match")
assertTrue(expectedResults[i].all { actualResults[i].getOrNull()!![it.key]?.let { it1 -> equivalent(it.value, it1, emptyMap()) } ?: false }, "Substitution values should match")
assertTrue(expectedResults[i].all {
actualResults[i].getOrNull()!![it.key]?.let { it1 ->
equivalent(
it.value,
it1,
emptyMap()
)
} ?: false
}, "Substitution values should match")
}
}
}
@Test
fun `likes(alice, pizza)`() {
val fact = Fact(Structure(Atom("likes"), listOf(Atom("alice"), Atom("pizza"))))
val goal = Structure(Atom("likes"), listOf(Atom("alice"), Atom("pizza")))
Program.load(listOf(fact))
val result = Program.query(goal).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertEquals(0, subs.size, "Expected no substitutions")
}
@Test
fun `likes(Person, pizza)`() {
val fact = Fact(Structure(Atom("likes"), listOf(Atom("alice"), Atom("pizza"))))
val goal = Structure(Atom("likes"), listOf(Variable("Person"), Atom("pizza")))
Program.load(listOf(fact))
val result = Program.query(goal).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("alice"), subs[Variable("Person")], "Expected Person to be alice")
}
@Test
fun `likes_food(alice)`() {
val fact = Fact(Structure(Atom("likes"), listOf(Atom("alice"), Atom("pizza"))))
val rule = Rule(
Structure(Atom("likes_food"), listOf(Variable("Person"))),
Structure(Atom("likes"), listOf(Variable("Person"), Atom("pizza")))
)
val goal = Structure(Atom("likes_food"), listOf(Atom("alice")))
Program.load(listOf(fact, rule))
val result = Program.query(goal).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertEquals(0, subs.size, "Expected no substitutions")
}
@Test
fun `likes_food(Person)`() {
val fact = Fact(Structure(Atom("likes"), listOf(Atom("alice"), Atom("pizza"))))
val rule = Rule(
Structure(Atom("likes_food"), listOf(Variable("Person"))),
Structure(Atom("likes"), listOf(Variable("Person"), Atom("pizza")))
)
val goal = Structure(Atom("likes"), listOf(Variable("X"), Atom("pizza")))
Program.load(listOf(fact, rule))
val result = Program.query(goal).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("alice"), subs[Variable("X")], "Expected Person to be alice")
}
@Test
fun `requires querying exact`() {
val fact1 = Fact(Atom("a"))
val fact2 = Fact(Atom("b"))
val rule1 = Rule(
Atom("c"),
Conjunction(
Atom("a"),
Atom("b")
)
)
Program.load(listOf(fact1, fact2, rule1))
val result = Program.query(Atom("c")).toList()
assertEquals(1, result.size, "Expected 1 result")
}
@Test
fun `requires querying with variable`() {
val fact1 = Fact(Atom("a"))
val fact2 = Fact(Atom("b"))
val rule1 = Rule(
Structure(Atom("has fact"), listOf(Variable("X"))),
Variable("X")
)
Program.load(listOf(fact1, fact2, rule1))
val result = Program.query(Structure(Atom("has fact"), listOf(Atom("a")))).toList()
assertEquals(1, result.size, "Expected 1 result")
assertTrue(result[0].isSuccess, "Expected success")
val subs = result[0].getOrNull()!!
assertEquals(0, subs.size, "Expected no substitutions")
}
@Nested
class `requires querying with filled variable` {
@BeforeEach
fun setup() {
val fact1 = Fact(Structure(Atom("likes"), listOf(Atom("alice"), Atom("pizza"))))
val fact2 = Fact(Structure(Atom("likes"), listOf(Atom("alice"), Atom("pasta"))))
val fact3 = Fact(Structure(Atom("likes"), listOf(Atom("bob"), Atom("pasta"))))
val rule1 = Rule(
Structure(Atom("likes_italian_food"), listOf(Variable("Person"))),
Disjunction(
Structure(Atom("likes"), listOf(Variable("Person"), Atom("pizza"))),
Structure(Atom("likes"), listOf(Variable("Person"), Atom("pasta")))
)
)
Program.clear()
Program.load(listOf(fact1, fact2, fact3, rule1))
}
@Test
fun `likes_italian_food(alice)`() {
val result = Program.query(Structure(Atom("likes_italian_food"), listOf(Atom("alice")))).toList()
assertEquals(2, result.size, "Expected 2 results")
assertTrue(result[0].isSuccess, "Expected success")
val subs1 = result[0].getOrNull()!!
assertEquals(0, subs1.size, "Expected no substitutions")
assertTrue(result[1].isSuccess, "Expected success")
val subs2 = result[1].getOrNull()!!
assertEquals(0, subs2.size, "Expected no substitutions")
}
@Test
fun `likes_italian_food(X)`() {
val result = Program.query(Structure(Atom("likes_italian_food"), listOf(Variable("X")))).toList()
assertEquals(3, result.size, "Expected 3 results")
assertTrue(result[0].isSuccess, "Expected success")
val subs3 = result[0].getOrNull()!!
assertEquals(1, subs3.size, "Expected 1 substitution, especially without 'Person'")
assertEquals(Atom("alice"), subs3[Variable("X")], "Expected alice")
assertTrue(result[1].isSuccess, "Expected success")
val subs4 = result[1].getOrNull()!!
assertEquals(1, subs4.size, "Expected 1 substitution, especially without 'Person'")
assertEquals(Atom("alice"), subs4[Variable("X")], "Expected alice")
assertTrue(result[2].isSuccess, "Expected success")
val subs5 = result[2].getOrNull()!!
assertEquals(1, subs5.size, "Expected 1 substitution, especially without 'Person'")
assertEquals(Atom("bob"), subs5[Variable("X")], "Expected bob")
}
}
}

View file

@ -0,0 +1,109 @@
package prolog.logic
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertTrue
import prolog.ast.terms.Atom
import prolog.ast.terms.Structure
import prolog.ast.terms.Variable
class TermsTests {
@Test
fun `rename vars in atom`() {
val term = Atom("a")
val start = 0
val (end, subs) = numbervars(term, start)
assertEquals(start, end, "Expected end to still be at start")
assertTrue(subs.isEmpty(), "Expected no substitutions")
}
@Test
fun `rename vars in var`() {
val term = Variable("X")
val start = 0
val (end, subs) = numbervars(term, start)
assertEquals(start + 1, end, "Expected end to be incremented by 1")
assertEquals(1, subs.size, "Expected one substitution")
assertTrue(subs.containsKey(term), "Expected subs to contain the original term")
assertEquals(Variable("X($start)"), subs[term], "Expected subs to contain the new term")
}
@Test
fun `rename vars in compound term without vars`() {
val term = Structure(Atom("f"), listOf(Atom("a"), Atom("b")))
val start = 0
val (end, subs) = numbervars(term, start)
assertEquals(start, end, "Expected end to still be at start")
assertTrue(subs.isEmpty(), "Expected no substitutions")
}
@Test
fun `rename vars in compound term`() {
val term = Structure(Atom("f"), listOf(Variable("X"), Variable("Y")))
val start = 0
val (end, subs) = numbervars(term, start)
assertEquals(start + 2, end, "Expected end to be incremented by 2")
assertEquals(2, subs.size, "Expected two substitutions")
assertTrue(subs.containsKey(term.arguments[0]), "Expected subs to contain the first original term")
assertEquals(Variable("X($start)"), subs[term.arguments[0]], "Expected subs to contain the new term")
assertTrue(subs.containsKey(term.arguments[1]), "Expected subs to contain the second original term")
assertEquals(Variable("Y(${start + 1})"), subs[term.arguments[1]], "Expected subs to contain the new term")
}
@Test
fun `renaming identical vars should keep the same name`() {
val term = Structure(Atom("f"), listOf(Variable("X"), Variable("X")))
val start = 0
val (end, subs) = numbervars(term, start)
assertEquals(start + 1, end, "Expected end to be incremented by 1")
assertEquals(1, subs.size, "Expected one substitution")
assertTrue(subs.containsKey(term.arguments[0]), "Expected subs to contain the first original term")
assertEquals(Variable("X($start)"), subs[term.arguments[0]], "Expected subs to contain the new term")
assertTrue(subs.containsKey(term.arguments[1]), "Expected subs to contain the second original term")
assertEquals(Variable("X($start)"), subs[term.arguments[1]], "Expected subs to contain the new term")
}
@Test
fun `renaming identical vars should keep the same name in nested terms`() {
val term = Structure(Atom("f"), listOf(Variable("X"), Structure(Atom("g"), listOf(Variable("X")))))
val start = 0
val (end, subs) = numbervars(term, start)
assertEquals(start + 1, end, "Expected end to be incremented by 1")
assertEquals(1, subs.size, "Expected one substitution")
assertTrue(subs.containsKey(Variable("X")), "Expected subs to contain the variable")
assertEquals(Variable("X($start)"), subs[term.arguments[0]], "Expected subs to contain the new term")
}
@Test
fun `renaming multiple times`() {
val variable = Variable("X")
val term = Structure(Atom("f"), listOf(variable))
val start = 0
val (end1, subs1) = numbervars(term, start, emptyMap())
assertEquals(start + 1, end1, "Expected end to be incremented by 1")
assertEquals(1, subs1.size, "Expected one substitution")
assertTrue(subs1.containsKey(variable), "Expected subs to contain the variable")
assertEquals(Variable("X($start)"), subs1[variable], "Expected subs to contain the new term")
val (end2, subs2) = numbervars(term, end1, subs1)
assertEquals(start + 2, end2, "Expected end to be incremented by 2")
assertEquals(1, subs2.size, "Expected one substitution")
assertTrue(subs2.containsKey(variable), "Expected subs to contain the variable")
assertEquals(Variable("X($end1)"), subs2[variable], "Expected subs to contain the new term")
}
}