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/parser/grammars/TermsGrammarTests.kt
2025-05-02 13:28:00 +02:00

355 lines
No EOL
10 KiB
Kotlin

package parser.grammars
import com.github.h0tk3y.betterParse.grammar.Grammar
import com.github.h0tk3y.betterParse.grammar.parseToEnd
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Nested
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
import prolog.ast.arithmetic.Integer
import prolog.ast.terms.Atom
import prolog.ast.terms.Structure
import prolog.ast.terms.Term
import prolog.ast.terms.Variable
import prolog.builtins.Is
import prolog.logic.equivalent
class TermsGrammarTests {
private lateinit var parser: Grammar<Term>
@BeforeEach
fun setup() {
parser = TermsGrammar() as Grammar<Term>
}
@ParameterizedTest
@ValueSource(strings = ["a", "foo", "foo1", "fooBar", "foo_bar"])
fun `parse atom`(name: String) {
val result = parser.parseToEnd(name)
assertEquals(Atom(name), result, "Expected atom '$name'")
}
@ParameterizedTest
@ValueSource(
strings = [
"'tick'",
"\"doubleTick\"",
"`backTick`",
"'i have spaces'",
"`i have a 'quote' inside`",
"'I have Cases'",
"'I, h@v3 many (!!!) special characters?! {}'"
]
)
fun `Parse a quoted atom`(input: String) {
val result = parser.parseToEnd(input)
val expected = input.substring(1, input.length - 1)
assertEquals(Atom(expected), result, "Expected atom")
}
@ParameterizedTest
@ValueSource(strings = ["X", "X1", "X_1"])
fun `parse variable`(name: String) {
val result = parser.parseToEnd(name)
assertEquals(Variable(name), result)
}
@Test
fun `parse anonymous variable`() {
val input = "_"
val result = parser.parseToEnd(input)
assertEquals(Variable("_"), result, "Expected anonymous variable")
}
@Test
fun `empty compound term`() {
val input = "f()"
val result = parser.parseToEnd(input)
assertEquals(Structure(Atom("f"), emptyList()), result, "Expected atom 'f'")
}
@Test
fun `parse compound term f(a)`() {
val input = "f(a)"
val result = parser.parseToEnd(input)
assertEquals(Structure(Atom("f"), listOf(Atom("a"))), result, "Expected atom 'f(a)'")
}
@Test
fun `parse compound term f(a, b)`() {
val input = "f(a, b)"
val result = parser.parseToEnd(input)
assertEquals(
Structure(Atom("f"), listOf(Atom("a"), Atom("b"))),
result,
"Expected atom 'f(a, b)'"
)
}
@Test
fun `parse compound term with variable f(a, X)`() {
val input = "f(a, X)"
val result = parser.parseToEnd(input)
assertEquals(
Structure(Atom("f"), listOf(Atom("a"), Variable("X"))),
result,
"Expected atom 'f(a, X)'"
)
}
@Test
fun `parse compound term with var and int`() {
val input = "check_identical(A, 13)"
val result = parser.parseToEnd(input)
assertEquals(
Structure(Atom("check_identical"), listOf(Variable("A"), Integer(13))),
result,
"Expected atom 'check_identical(A, 13)'"
)
}
@Test
fun `parse nested compound term f(a, g(b))`() {
val input = "f(a, g(b))"
val result = parser.parseToEnd(input)
assertEquals(
Structure(Atom("f"), listOf(Atom("a"), Structure(Atom("g"), listOf(Atom("b"))))),
result,
"Expected atom 'f(a, g(b))'"
)
}
@Test
fun `parse compound term with variable f(a, g(X))`() {
val input = "f(a, g(X))"
val result = parser.parseToEnd(input)
assertEquals(
Structure(Atom("f"), listOf(Atom("a"), Structure(Atom("g"), listOf(Variable("X"))))),
result,
"Expected atom 'f(a, g(X))'"
)
}
@Test
fun `parse nested compound term with variables`() {
val input = "hit(character(Name, Class, Level, HP), character(Name, Class, Level, T))"
val result = parser.parseToEnd(input)
assertEquals(
Structure(
Atom("hit"),
listOf(
Structure(Atom("character"), listOf(Variable("Name"), Variable("Class"), Variable("Level"), Variable("HP"))),
Structure(Atom("character"), listOf(Variable("Name"), Variable("Class"), Variable("Level"), Variable("T")))
)
),
result,
"Expected atom 'hit(character(Name, Class, Level, HP), character(Name, Class, Level, T))'"
)
}
@Test
fun `parse compound term with anonymous variables`() {
val input = "f(a, _, g(X))"
val result = parser.parseToEnd(input)
assertEquals(
Structure(Atom("f"), listOf(Atom("a"), Variable("_"), Structure(Atom("g"), listOf(Variable("X"))))),
result,
"Expected atom 'f(a, _, g(X))'"
)
}
@ParameterizedTest
@ValueSource(ints = [0, 1, 5, 12, 345, 123456789])
fun `parse positive integer`(number: Int) {
val input = number.toString()
val result = parser.parseToEnd(input)
assertEquals(Integer(number), result, "Expected integer '$number'")
}
@ParameterizedTest
@ValueSource(ints = [-987654321, -543, -21, -1])
fun `parse negative integer`(number: Int) {
val input = number.toString()
val result = parser.parseToEnd(input)
assertEquals(Structure(Atom("-"), listOf(Integer(0 - number))), result, "Expected integer '$number'")
}
@Test
fun `parse float`() {
val input = "42.0"
val result = parser.parseToEnd(input)
assertEquals(Float(42.0f), result, "Expected float '42.0'")
}
@Test
fun `parse negative float`() {
val input = "-42.0"
val result = parser.parseToEnd(input)
assertEquals(Structure(Atom("-"), listOf(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) }
}
@Nested
class `Operators and precedence` {
private lateinit var parser: Grammar<Term>
@BeforeEach
fun setup() {
parser = TermsGrammar() as Grammar<Term>
}
@Test
fun `can parse equivalent`() {
val input = "X == Y"
val result = parser.parseToEnd(input)
assertEquals(
Structure(Atom("=="), listOf(Variable("X"), Variable("Y"))),
result,
"Expected equivalent operator"
)
}
@Test
fun `can parse cut`() {
val input = "!"
val result = parser.parseToEnd(input)
assertEquals(Atom("!"), result, "Expected cut operator")
}
@Test
fun `can parse 'is'`() {
val input = "T is 1"
val result = parser.parseToEnd(input)
assertEquals(
Structure(Atom("is"), listOf(Variable("T"), Integer(1))),
result
)
}
@Test
fun `can parse 'is' with addition`() {
val input = "T is 1 + 2"
val result = parser.parseToEnd(input)
assertEquals(
Structure(Atom("is"), listOf(Variable("T"), Structure(Atom("+"), listOf(Integer(1), Integer(2))))),
result
)
}
@ParameterizedTest
@ValueSource(strings = ["+", "-", "*", "/"])
fun `can parse with spaces`(operator: String) {
val input = "1 $operator 2"
val result = parser.parseToEnd(input)
assertEquals(
Structure(Atom(operator), listOf(Integer(1), Integer(2))),
result,
"Expected operator '$operator'"
)
}
@ParameterizedTest
@ValueSource(strings = ["+", "-", "*", "/"])
fun `can parse without spaces`(operator: String) {
val input = "1${operator}2"
val result = parser.parseToEnd(input)
assertEquals(
Structure(Atom(operator), listOf(Integer(1), Integer(2))),
result,
"Expected operator '$operator' without spaces"
)
}
@Test
fun `parse addition and multiplication`() {
val input = "1 + 2 * 3"
val result = parser.parseToEnd(input)
assertEquals(
Structure(Atom("+"), listOf(Integer(1), Structure(Atom("*"), listOf(Integer(2), Integer(3))))),
result,
"Expected addition and multiplication"
)
}
@Test
fun `parse multiplication and addition`() {
val input = "1 * 2 + 3"
val result = parser.parseToEnd(input)
assertEquals(
Structure(Atom("+"), listOf(Structure(Atom("*"), listOf(Integer(1), Integer(2))), Integer(3))),
result,
"Expected multiplication and addition"
)
}
@Test
fun `complex expression`() {
val input = "1 + 2 * 3 - 4 / 5"
val result = parser.parseToEnd(input)
assertEquals(
Structure(
Atom("-"),
listOf(
Structure(Atom("+"), listOf(Integer(1), Structure(Atom("*"), listOf(Integer(2), Integer(3))))),
Structure(Atom("/"), listOf(Integer(4), Integer(5)))
)
),
result,
"Expected complex expression"
)
}
}
}