Rework parsing structure

This commit is contained in:
Tibo De Peuter 2025-04-27 19:31:29 +02:00
parent a4ec29f084
commit b9f419a59d
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
17 changed files with 246 additions and 278 deletions

View file

@ -0,0 +1,131 @@
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.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.ValueSource
import prolog.ast.logic.Clause
import prolog.ast.logic.Fact
import prolog.ast.logic.Rule
import prolog.ast.terms.CompoundTerm
import prolog.ast.terms.Structure
import prolog.ast.terms.Variable
import prolog.builtins.Conjunction
class LogicGrammarTests {
private lateinit var parser: Grammar<List<Clause>>
@BeforeEach
fun setup() {
parser = LogicGrammar() as Grammar<List<Clause>>
}
@ParameterizedTest
@ValueSource(strings = [
"john.",
"mary.",
"jimmy.",
"male(john).",
"male(jimmy).",
"female(mary).",
"not(not(true)).",
"not(a, not(b, c), d, not(not(a)))."
])
fun `parse simple fact`(input: String) {
val result = parser.parseToEnd(input)
Assertions.assertEquals(1, result.size, "Expected 1 fact")
Assertions.assertTrue(result[0] is Fact, "Expected a fact")
Assertions.assertEquals(input, "${result[0].toString()}.", "Expected fact to be '$input'")
}
@ParameterizedTest
@ValueSource(strings = [
"john. mary.",
"likes(john, mary). likes(mary, john).",
"belgium. capital(belgium, brussels).",
"plus(1, 2, 3). plus(3, 4, 7).",
])
fun `parse multiple facts`(input: String) {
val result = parser.parseToEnd(input)
Assertions.assertEquals(2, result.size, "Expected 2 facts")
Assertions.assertTrue(result[0] is Fact, "Expected a fact")
Assertions.assertTrue(result[1] is Fact, "Expected a fact")
}
@Test
fun `simplest rule`() {
val input = "a :- b."
val result = parser.parseToEnd(input)
Assertions.assertEquals(1, result.size, "Expected 1 rule")
Assertions.assertTrue(result[0] is Rule, "Expected a rule")
Assertions.assertEquals("a :- b", result[0].toString())
}
@ParameterizedTest
@ValueSource(strings = [
"parent(X, Y) :- father(X, Y).",
"parent(X, Y) :- mother(X, Y)."
])
fun `parse simple rule`(input: String) {
val result = parser.parseToEnd(input)
Assertions.assertEquals(1, result.size, "Expected 1 rule")
Assertions.assertTrue(result[0] is Rule, "Expected a rule")
}
@Test
fun `parse rule with very verbose checks`() {
val input = "parent(X, Y) :- father(X, Y)."
val result = parser.parseToEnd(input)
Assertions.assertEquals(1, result.size, "Expected 1 rule")
Assertions.assertTrue(result[0] is Rule, "Expected a rule")
val rule = result[0] as Rule
Assertions.assertTrue(rule.head is Structure, "Expected head to be a structure")
val head = rule.head as Structure
Assertions.assertEquals("parent/2", head.functor, "Expected functor 'parent/2'")
Assertions.assertEquals(Variable("X"), head.arguments[0], "Expected first argument 'X'")
Assertions.assertEquals(Variable("Y"), head.arguments[1], "Expected second argument 'Y'")
Assertions.assertTrue(rule.body is Structure, "Expected body to be a structure")
val body = rule.body as Structure
Assertions.assertEquals("father/2", body.functor, "Expected functor 'father/2'")
Assertions.assertEquals(Variable("X"), body.arguments[0], "Expected first argument 'X'")
Assertions.assertEquals(Variable("Y"), body.arguments[1], "Expected second argument 'Y'")
}
@Test
fun `parse rule with conjunction`() {
val input = "father(X, Y) :- parent(X, Y), male(X)."
val result = parser.parseToEnd(input)
Assertions.assertEquals(1, result.size, "Expected 1 rule")
Assertions.assertInstanceOf(Rule::class.java, result[0], "Expected a rule")
val rule = result[0] as Rule
Assertions.assertInstanceOf(Conjunction::class.java, rule.body, "Expected body to be a conjunction")
}
@Test
fun `parse rule with nested conjunction`() {
val input = "guest(X, Y) :- invited(Y, X), has_time(X), not(sick(Y))."
val result = parser.parseToEnd(input)
Assertions.assertEquals(1, result.size, "Expected 1 rule")
val rule = result[0] as Rule
Assertions.assertTrue(rule.body is Conjunction, "Expected body to be a conjunction")
val conjunction = rule.body as Conjunction
Assertions.assertEquals("invited/2", (conjunction.left as CompoundTerm).functor, "Expected functor 'invited/2'")
}
}

View file

@ -0,0 +1,155 @@
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.BeforeEach
import org.junit.jupiter.api.Test
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<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)
Assertions.assertEquals(Atom(name), result, "Expected atom '$name'")
}
@ParameterizedTest
@ValueSource(strings = ["X", "X1", "X_1"])
fun `parse variable`(name: String) {
val result = parser.parseToEnd(name)
Assertions.assertEquals(Variable(name), result, "Expected atom '$name'")
}
@Test
fun `empty compound term`() {
val input = "f()"
val result = parser.parseToEnd(input)
Assertions.assertTrue(
equivalent(Structure(Atom("f"), emptyList()), result, emptyMap()),
"Expected atom 'f'"
)
}
@Test
fun `parse compound term f(a)`() {
val input = "f(a)"
val result = parser.parseToEnd(input)
Assertions.assertTrue(
equivalent(Structure(Atom("f"), listOf(Atom("a"))), result, emptyMap()),
"Expected atom 'f(a)'"
)
}
@Test
fun `parse compound term f(a, b)`() {
val input = "f(a, b)"
val result = parser.parseToEnd(input)
Assertions.assertTrue(
equivalent(Structure(Atom("f"), listOf(Atom("a"), Atom("b"))), result, emptyMap()),
"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)
Assertions.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)
Assertions.assertTrue(
equivalent(Float(-42.0f), result, emptyMap()),
"Expected float '-42.0'"
)
}
}