From 43b364044e0ce561a820e98412d4634c863f82a8 Mon Sep 17 00:00:00 2001 From: Tibo De Peuter Date: Wed, 30 Apr 2025 12:08:36 +0200 Subject: [PATCH] Quoted atoms --- src/parser/grammars/TermsGrammar.kt | 10 +++- src/parser/grammars/Tokens.kt | 5 ++ tests/parser/OperatorParserTests.kt | 5 +- tests/parser/grammars/LogicGrammarTests.kt | 57 +++++++++++----------- tests/parser/grammars/TermsGrammarTests.kt | 20 ++++++++ 5 files changed, 65 insertions(+), 32 deletions(-) diff --git a/src/parser/grammars/TermsGrammar.kt b/src/parser/grammars/TermsGrammar.kt index c57ab22..9bac47c 100644 --- a/src/parser/grammars/TermsGrammar.kt +++ b/src/parser/grammars/TermsGrammar.kt @@ -7,7 +7,6 @@ import com.github.h0tk3y.betterParse.combinators.unaryMinus import com.github.h0tk3y.betterParse.combinators.use import com.github.h0tk3y.betterParse.grammar.parser import com.github.h0tk3y.betterParse.parser.Parser -import prolog.ast.arithmetic.ArithmeticOperator import prolog.ast.arithmetic.Expression import prolog.ast.arithmetic.Float import prolog.ast.arithmetic.Integer @@ -15,9 +14,16 @@ import prolog.ast.logic.LogicOperand import prolog.ast.terms.* open class TermsGrammar : Tokens() { + // Basic named terms protected val variable: Parser by variableToken use { Variable(text) } - protected val atom: Parser by nameToken use { Atom(text) } + protected val simpleAtom: Parser by nameToken use { Atom(text) } + protected val quotedAtom: Parser by (dummy + or ticked + or doubleTicked + or backTicked + ) use { Atom(text.substring(1, text.length - 1)) } + protected val atom: Parser by (quotedAtom or simpleAtom) protected val compound: Parser by (atom * -leftParenthesis * separated( parser(::term), comma, diff --git a/src/parser/grammars/Tokens.kt b/src/parser/grammars/Tokens.kt index 49afbee..14c5be9 100644 --- a/src/parser/grammars/Tokens.kt +++ b/src/parser/grammars/Tokens.kt @@ -30,5 +30,10 @@ abstract class Tokens : Grammar() { protected val singleLineComment: Token by regexToken("%[^\\n]*", 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") } } \ No newline at end of file diff --git a/tests/parser/OperatorParserTests.kt b/tests/parser/OperatorParserTests.kt index e15e89d..8cfdb95 100644 --- a/tests/parser/OperatorParserTests.kt +++ b/tests/parser/OperatorParserTests.kt @@ -7,15 +7,16 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import parser.grammars.TermsGrammar import prolog.ast.terms.Atom +import prolog.ast.terms.CompoundTerm import prolog.ast.terms.Operator import prolog.ast.terms.Structure class OperatorParserTests { class OperatorParser: TermsGrammar() { - override val rootParser: Parser by operator + override val rootParser: Parser by operator } - private var parser = OperatorParser() as Grammar + private var parser = OperatorParser() as Grammar @Test fun `parse conjunction`() { diff --git a/tests/parser/grammars/LogicGrammarTests.kt b/tests/parser/grammars/LogicGrammarTests.kt index 5365e9f..9afb439 100644 --- a/tests/parser/grammars/LogicGrammarTests.kt +++ b/tests/parser/grammars/LogicGrammarTests.kt @@ -3,6 +3,7 @@ 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.* import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest @@ -37,9 +38,9 @@ class LogicGrammarTests { 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'") + assertEquals(1, result.size, "Expected 1 fact") + assertTrue(result[0] is Fact, "Expected a fact") + assertEquals(input, "${result[0].toString()}.", "Expected fact to be '$input'") } @ParameterizedTest @@ -52,9 +53,9 @@ class LogicGrammarTests { 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") + assertEquals(2, result.size, "Expected 2 facts") + assertTrue(result[0] is Fact, "Expected a fact") + assertTrue(result[1] is Fact, "Expected a fact") } @Test @@ -63,9 +64,9 @@ class LogicGrammarTests { 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()) + assertEquals(1, result.size, "Expected 1 rule") + assertTrue(result[0] is Rule, "Expected a rule") + assertEquals("a :- b", result[0].toString()) } @ParameterizedTest @@ -76,8 +77,8 @@ class LogicGrammarTests { 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") + assertEquals(1, result.size, "Expected 1 rule") + assertTrue(result[0] is Rule, "Expected a rule") } @Test @@ -86,22 +87,22 @@ class LogicGrammarTests { val result = parser.parseToEnd(input) - Assertions.assertEquals(1, result.size, "Expected 1 rule") - Assertions.assertTrue(result[0] is Rule, "Expected a rule") + assertEquals(1, result.size, "Expected 1 rule") + 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") + 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'") + assertEquals("parent/2", head.functor, "Expected functor 'parent/2'") + assertEquals(Variable("X"), head.arguments[0], "Expected first argument 'X'") + 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 - 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'") + assertEquals("father/2", body.functor, "Expected functor 'father/2'") + assertEquals(Variable("X"), body.arguments[0], "Expected first argument 'X'") + assertEquals(Variable("Y"), body.arguments[1], "Expected second argument 'Y'") } @Test @@ -110,10 +111,10 @@ class LogicGrammarTests { val result = parser.parseToEnd(input) - Assertions.assertEquals(1, result.size, "Expected 1 rule") - Assertions.assertInstanceOf(Rule::class.java, result[0], "Expected a rule") + assertEquals(1, result.size, "Expected 1 rule") + 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") + assertInstanceOf(CompoundTerm::class.java, rule.body, "Expected body to be a compound term") } @Test @@ -122,10 +123,10 @@ class LogicGrammarTests { 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 - 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'") + assertInstanceOf(CompoundTerm::class.java, rule.body, "Expected body to be a conjunction") + val conjunction = rule.body as CompoundTerm + assertEquals("invited/2", (conjunction.arguments[0] as CompoundTerm).functor, "Expected functor 'invited/2'") } } \ No newline at end of file diff --git a/tests/parser/grammars/TermsGrammarTests.kt b/tests/parser/grammars/TermsGrammarTests.kt index 339c7c2..203eac2 100644 --- a/tests/parser/grammars/TermsGrammarTests.kt +++ b/tests/parser/grammars/TermsGrammarTests.kt @@ -31,6 +31,26 @@ class TermsGrammarTests { 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 @ValueSource(strings = ["X", "X1", "X_1"]) fun `parse variable`(name: String) {