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.logic.equivalent class TermsGrammarTests { private lateinit var parser: Grammar @BeforeEach fun setup() { parser = TermsGrammar() as Grammar } @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, "Expected atom '$name'") } @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) assertTrue( equivalent(Structure(Atom("f"), listOf(Atom("a"), Variable("X"))), result, emptyMap()), "Expected atom 'f(a, X)'" ) } @Test fun `parse nested compound term f(a, g(b))`() { val input = "f(a, g(b))" val result = parser.parseToEnd(input) Assertions.assertTrue( equivalent( Structure(Atom("f"), listOf(Atom("a"), Structure(Atom("g"), listOf(Atom("b"))))), result, emptyMap() ), "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) Assertions.assertTrue( equivalent( Structure(Atom("f"), listOf(Atom("a"), Structure(Atom("g"), listOf(Variable("X"))))), result, emptyMap() ), "Expected atom 'f(a, g(X))'" ) } @ParameterizedTest @ValueSource(ints = [-987654321, -543, -21, -1, 0, 1, 5, 12, 345, 123456789]) fun `parse integer`(number: Int) { val input = number.toString() val result = parser.parseToEnd(input) Assertions.assertEquals(Integer(number), result, "Expected integer '$number'") } @Test fun `parse float`() { val input = "42.0" val result = parser.parseToEnd(input) Assertions.assertTrue( equivalent(Float(42.0f), result, emptyMap()), "Expected float '42.0'" ) } @Test fun `parse negative float`() { val input = "-42.0" val result = parser.parseToEnd(input) 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) } } @Nested class `Operator precedence` { private lateinit var parser: Grammar @BeforeEach fun setup() { parser = TermsGrammar() as Grammar } @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" ) } } }