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 import prolog.ast.lists.List.Empty import prolog.ast.lists.List.Cons 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) } @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 @BeforeEach fun setup() { parser = TermsGrammar() as Grammar } @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" ) } } @Nested class Lists { private lateinit var parser: Grammar @BeforeEach fun setup() { parser = TermsGrammar() as Grammar } @Test fun `parse empty list`() { val input = "[]" val result = parser.parseToEnd(input) assertEquals(Empty, result, "Expected empty list") } @Test fun `parse non-empty list`() { val input = "[a, b, c]" val result = parser.parseToEnd(input) assertEquals( Cons(Atom("a"), Cons(Atom("b"), Cons(Atom("c"), Empty))), result, "Expected non-empty list" ) } @Test fun `parse nested lists`() { val input = "[a, [b, c], d]" val result = parser.parseToEnd(input) assertEquals( Cons(Atom("a"), Cons(Cons(Atom("b"), Cons(Atom("c"), Empty)), Cons(Atom("d"), Empty))), result, "Expected nested lists" ) } } }