Quoted atoms

This commit is contained in:
Tibo De Peuter 2025-04-30 12:08:36 +02:00
parent 1e087c8339
commit 43b364044e
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
5 changed files with 65 additions and 32 deletions

View file

@ -7,7 +7,6 @@ import com.github.h0tk3y.betterParse.combinators.unaryMinus
import com.github.h0tk3y.betterParse.combinators.use import com.github.h0tk3y.betterParse.combinators.use
import com.github.h0tk3y.betterParse.grammar.parser import com.github.h0tk3y.betterParse.grammar.parser
import com.github.h0tk3y.betterParse.parser.Parser import com.github.h0tk3y.betterParse.parser.Parser
import prolog.ast.arithmetic.ArithmeticOperator
import prolog.ast.arithmetic.Expression import prolog.ast.arithmetic.Expression
import prolog.ast.arithmetic.Float import prolog.ast.arithmetic.Float
import prolog.ast.arithmetic.Integer import prolog.ast.arithmetic.Integer
@ -15,9 +14,16 @@ import prolog.ast.logic.LogicOperand
import prolog.ast.terms.* import prolog.ast.terms.*
open class TermsGrammar : Tokens() { open class TermsGrammar : Tokens() {
// Basic named terms // Basic named terms
protected val variable: Parser<Variable> by variableToken use { Variable(text) } protected val variable: Parser<Variable> by variableToken use { Variable(text) }
protected val atom: Parser<Atom> by nameToken use { Atom(text) } protected val simpleAtom: Parser<Atom> by nameToken use { Atom(text) }
protected val quotedAtom: Parser<Atom> by (dummy
or ticked
or doubleTicked
or backTicked
) use { Atom(text.substring(1, text.length - 1)) }
protected val atom: Parser<Atom> by (quotedAtom or simpleAtom)
protected val compound: Parser<Structure> by (atom * -leftParenthesis * separated( protected val compound: Parser<Structure> by (atom * -leftParenthesis * separated(
parser(::term), parser(::term),
comma, comma,

View file

@ -30,5 +30,10 @@ abstract class Tokens : Grammar<Any>() {
protected val singleLineComment: Token by regexToken("%[^\\n]*", ignore = true) protected val singleLineComment: Token by regexToken("%[^\\n]*", ignore = true)
protected val multiLineComment: Token by regexToken("/\\*.*?\\*/", ignore = true) protected val multiLineComment: Token by regexToken("/\\*.*?\\*/", ignore = true)
protected val ticked: Token by regexToken("'[^']*'")
protected val doubleTicked: Token by regexToken("\"[^\"]*\"")
protected val backTicked: Token by regexToken("`[^`]*`")
// Helper
protected val dummy by token { _, _ -> -1 } use { throw IllegalStateException("This parser should not be used") } protected val dummy by token { _, _ -> -1 } use { throw IllegalStateException("This parser should not be used") }
} }

View file

@ -7,15 +7,16 @@ import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import parser.grammars.TermsGrammar import parser.grammars.TermsGrammar
import prolog.ast.terms.Atom import prolog.ast.terms.Atom
import prolog.ast.terms.CompoundTerm
import prolog.ast.terms.Operator import prolog.ast.terms.Operator
import prolog.ast.terms.Structure import prolog.ast.terms.Structure
class OperatorParserTests { class OperatorParserTests {
class OperatorParser: TermsGrammar() { class OperatorParser: TermsGrammar() {
override val rootParser: Parser<Operator> by operator override val rootParser: Parser<CompoundTerm> by operator
} }
private var parser = OperatorParser() as Grammar<Operator> private var parser = OperatorParser() as Grammar<CompoundTerm>
@Test @Test
fun `parse conjunction`() { fun `parse conjunction`() {

View file

@ -3,6 +3,7 @@ package parser.grammars
import com.github.h0tk3y.betterParse.grammar.Grammar import com.github.h0tk3y.betterParse.grammar.Grammar
import com.github.h0tk3y.betterParse.grammar.parseToEnd import com.github.h0tk3y.betterParse.grammar.parseToEnd
import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test import org.junit.jupiter.api.Test
import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.ParameterizedTest
@ -37,9 +38,9 @@ class LogicGrammarTests {
fun `parse simple fact`(input: String) { fun `parse simple fact`(input: String) {
val result = parser.parseToEnd(input) val result = parser.parseToEnd(input)
Assertions.assertEquals(1, result.size, "Expected 1 fact") assertEquals(1, result.size, "Expected 1 fact")
Assertions.assertTrue(result[0] is Fact, "Expected a fact") assertTrue(result[0] is Fact, "Expected a fact")
Assertions.assertEquals(input, "${result[0].toString()}.", "Expected fact to be '$input'") assertEquals(input, "${result[0].toString()}.", "Expected fact to be '$input'")
} }
@ParameterizedTest @ParameterizedTest
@ -52,9 +53,9 @@ class LogicGrammarTests {
fun `parse multiple facts`(input: String) { fun `parse multiple facts`(input: String) {
val result = parser.parseToEnd(input) val result = parser.parseToEnd(input)
Assertions.assertEquals(2, result.size, "Expected 2 facts") assertEquals(2, result.size, "Expected 2 facts")
Assertions.assertTrue(result[0] is Fact, "Expected a fact") assertTrue(result[0] is Fact, "Expected a fact")
Assertions.assertTrue(result[1] is Fact, "Expected a fact") assertTrue(result[1] is Fact, "Expected a fact")
} }
@Test @Test
@ -63,9 +64,9 @@ class LogicGrammarTests {
val result = parser.parseToEnd(input) val result = parser.parseToEnd(input)
Assertions.assertEquals(1, result.size, "Expected 1 rule") assertEquals(1, result.size, "Expected 1 rule")
Assertions.assertTrue(result[0] is Rule, "Expected a rule") assertTrue(result[0] is Rule, "Expected a rule")
Assertions.assertEquals("a :- b", result[0].toString()) assertEquals("a :- b", result[0].toString())
} }
@ParameterizedTest @ParameterizedTest
@ -76,8 +77,8 @@ class LogicGrammarTests {
fun `parse simple rule`(input: String) { fun `parse simple rule`(input: String) {
val result = parser.parseToEnd(input) val result = parser.parseToEnd(input)
Assertions.assertEquals(1, result.size, "Expected 1 rule") assertEquals(1, result.size, "Expected 1 rule")
Assertions.assertTrue(result[0] is Rule, "Expected a rule") assertTrue(result[0] is Rule, "Expected a rule")
} }
@Test @Test
@ -86,22 +87,22 @@ class LogicGrammarTests {
val result = parser.parseToEnd(input) val result = parser.parseToEnd(input)
Assertions.assertEquals(1, result.size, "Expected 1 rule") assertEquals(1, result.size, "Expected 1 rule")
Assertions.assertTrue(result[0] is Rule, "Expected a rule") assertTrue(result[0] is Rule, "Expected a rule")
val rule = result[0] as Rule val rule = result[0] as Rule
Assertions.assertTrue(rule.head is Structure, "Expected head to be a structure") assertTrue(rule.head is Structure, "Expected head to be a structure")
val head = rule.head as Structure val head = rule.head as Structure
Assertions.assertEquals("parent/2", head.functor, "Expected functor 'parent/2'") assertEquals("parent/2", head.functor, "Expected functor 'parent/2'")
Assertions.assertEquals(Variable("X"), head.arguments[0], "Expected first argument 'X'") assertEquals(Variable("X"), head.arguments[0], "Expected first argument 'X'")
Assertions.assertEquals(Variable("Y"), head.arguments[1], "Expected second argument 'Y'") assertEquals(Variable("Y"), head.arguments[1], "Expected second argument 'Y'")
Assertions.assertTrue(rule.body is Structure, "Expected body to be a structure") assertTrue(rule.body is Structure, "Expected body to be a structure")
val body = rule.body as Structure val body = rule.body as Structure
Assertions.assertEquals("father/2", body.functor, "Expected functor 'father/2'") assertEquals("father/2", body.functor, "Expected functor 'father/2'")
Assertions.assertEquals(Variable("X"), body.arguments[0], "Expected first argument 'X'") assertEquals(Variable("X"), body.arguments[0], "Expected first argument 'X'")
Assertions.assertEquals(Variable("Y"), body.arguments[1], "Expected second argument 'Y'") assertEquals(Variable("Y"), body.arguments[1], "Expected second argument 'Y'")
} }
@Test @Test
@ -110,10 +111,10 @@ class LogicGrammarTests {
val result = parser.parseToEnd(input) val result = parser.parseToEnd(input)
Assertions.assertEquals(1, result.size, "Expected 1 rule") assertEquals(1, result.size, "Expected 1 rule")
Assertions.assertInstanceOf(Rule::class.java, result[0], "Expected a rule") assertInstanceOf(Rule::class.java, result[0], "Expected a rule")
val rule = result[0] as Rule val rule = result[0] as Rule
Assertions.assertInstanceOf(Conjunction::class.java, rule.body, "Expected body to be a conjunction") assertInstanceOf(CompoundTerm::class.java, rule.body, "Expected body to be a compound term")
} }
@Test @Test
@ -122,10 +123,10 @@ class LogicGrammarTests {
val result = parser.parseToEnd(input) val result = parser.parseToEnd(input)
Assertions.assertEquals(1, result.size, "Expected 1 rule") assertEquals(1, result.size, "Expected 1 rule")
val rule = result[0] as Rule val rule = result[0] as Rule
Assertions.assertTrue(rule.body is Conjunction, "Expected body to be a conjunction") assertInstanceOf(CompoundTerm::class.java, rule.body, "Expected body to be a conjunction")
val conjunction = rule.body as Conjunction val conjunction = rule.body as CompoundTerm
Assertions.assertEquals("invited/2", (conjunction.left as CompoundTerm).functor, "Expected functor 'invited/2'") assertEquals("invited/2", (conjunction.arguments[0] as CompoundTerm).functor, "Expected functor 'invited/2'")
} }
} }

View file

@ -31,6 +31,26 @@ class TermsGrammarTests {
Assertions.assertEquals(Atom(name), result, "Expected atom '$name'") Assertions.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)
Assertions.assertEquals(Atom(expected), result, "Expected atom")
}
@ParameterizedTest @ParameterizedTest
@ValueSource(strings = ["X", "X1", "X_1"]) @ValueSource(strings = ["X", "X1", "X_1"])
fun `parse variable`(name: String) { fun `parse variable`(name: String) {