Rework parsing structure
This commit is contained in:
parent
a4ec29f084
commit
b9f419a59d
17 changed files with 246 additions and 278 deletions
|
@ -1,17 +0,0 @@
|
||||||
package better_parser
|
|
||||||
|
|
||||||
import com.github.h0tk3y.betterParse.grammar.Grammar
|
|
||||||
import com.github.h0tk3y.betterParse.grammar.parseToEnd
|
|
||||||
import prolog.Program
|
|
||||||
import prolog.ast.logic.Clause
|
|
||||||
import prolog.ast.terms.Atom
|
|
||||||
|
|
||||||
class PrologParser {
|
|
||||||
private val parser: Grammar<List<Clause>> = SimpleSourceParser() as Grammar<List<Clause>>
|
|
||||||
|
|
||||||
public fun parse(input: String) {
|
|
||||||
val clauses: List<Clause> = parser.parseToEnd(input)
|
|
||||||
|
|
||||||
Program.load(clauses)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
package better_parser
|
|
||||||
|
|
||||||
import com.github.h0tk3y.betterParse.combinators.*
|
|
||||||
import com.github.h0tk3y.betterParse.grammar.Grammar
|
|
||||||
import com.github.h0tk3y.betterParse.lexer.literalToken
|
|
||||||
import com.github.h0tk3y.betterParse.lexer.regexToken
|
|
||||||
import com.github.h0tk3y.betterParse.parser.Parser
|
|
||||||
import prolog.ast.logic.Fact
|
|
||||||
import prolog.ast.arithmetic.Integer
|
|
||||||
import prolog.ast.arithmetic.Float
|
|
||||||
import prolog.ast.logic.Clause
|
|
||||||
import prolog.ast.logic.LogicOperand
|
|
||||||
import prolog.ast.logic.Rule
|
|
||||||
import prolog.ast.terms.*
|
|
||||||
import prolog.builtins.Conjunction
|
|
||||||
import prolog.builtins.Disjunction
|
|
||||||
|
|
||||||
class PrologSourceParser : Grammar<List<Clause>>() {
|
|
||||||
// Define the tokens
|
|
||||||
private val atom by regexToken("[a-z][a-zA-Z0-9_]*")
|
|
||||||
private val variable by regexToken("[A-Z][a-zA-Z0-9_]*")
|
|
||||||
private val number by regexToken("-?[0-9]+(\\.[0-9]+)?")
|
|
||||||
private val whitespace by regexToken("\\s+", ignore = true)
|
|
||||||
|
|
||||||
private val comma by literalToken(",")
|
|
||||||
private val semicolon by literalToken(";")
|
|
||||||
private val neck by literalToken(":-")
|
|
||||||
private val lparen by literalToken("(")
|
|
||||||
private val rparen by literalToken(")")
|
|
||||||
private val dot by literalToken(".")
|
|
||||||
|
|
||||||
private val atomParser by atom use { Atom(text) }
|
|
||||||
private val variableParser by variable use { Variable(text) }
|
|
||||||
private val intParser by number use { Integer(text.toInt()) }
|
|
||||||
private val floatParser by number use { Float(text.toFloat()) }
|
|
||||||
private val numberParser by (intParser or floatParser)
|
|
||||||
private val compoundTermParser by (atomParser and skip(lparen) and separated(
|
|
||||||
atomParser or variableParser,
|
|
||||||
comma
|
|
||||||
) and skip(rparen)) use {
|
|
||||||
CompoundTerm(t1, t2.terms)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val termParser: Parser<Term> by (numberParser or variableParser or compoundTermParser or atomParser)
|
|
||||||
|
|
||||||
private val logicOperandParser: Parser<LogicOperand> by (termParser or compoundTermParser or atomParser) map {
|
|
||||||
it as LogicOperand
|
|
||||||
}
|
|
||||||
|
|
||||||
private val conjunctionParser: Parser<Conjunction> by (logicOperandParser and comma and logicOperandParser) use {
|
|
||||||
Conjunction(t1, t3)
|
|
||||||
}
|
|
||||||
private val disjunctionParser: Parser<Disjunction> by (logicOperandParser and semicolon and logicOperandParser) use {
|
|
||||||
Disjunction(t1, t3)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val operatorParser: Parser<Operator> by (conjunctionParser or disjunctionParser)
|
|
||||||
|
|
||||||
private val headParser by (compoundTermParser or atomParser)
|
|
||||||
private val bodyParser by (operatorParser or compoundTermParser or atomParser)
|
|
||||||
|
|
||||||
private val factParser by (headParser and dot) use { Fact(t1 as Head) }
|
|
||||||
private val ruleParser by (headParser and neck and bodyParser and dot) use {
|
|
||||||
Rule(t1 as Head, t3 as Body)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val clauseParser: Parser<Clause> by (factParser or ruleParser)
|
|
||||||
|
|
||||||
override val rootParser: Parser<List<Clause>> by zeroOrMore(clauseParser)
|
|
||||||
}
|
|
|
@ -1,67 +0,0 @@
|
||||||
package better_parser
|
|
||||||
|
|
||||||
import com.github.h0tk3y.betterParse.combinators.*
|
|
||||||
import com.github.h0tk3y.betterParse.grammar.Grammar
|
|
||||||
import com.github.h0tk3y.betterParse.grammar.parser
|
|
||||||
import com.github.h0tk3y.betterParse.lexer.Token
|
|
||||||
import com.github.h0tk3y.betterParse.lexer.literalToken
|
|
||||||
import com.github.h0tk3y.betterParse.lexer.regexToken
|
|
||||||
import com.github.h0tk3y.betterParse.lexer.token
|
|
||||||
import com.github.h0tk3y.betterParse.parser.Parser
|
|
||||||
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
|
|
||||||
|
|
||||||
open class SimplePrologParser : Grammar<Any>() {
|
|
||||||
// Prolog tokens
|
|
||||||
protected val nameToken: Token by regexToken("[a-z][a-zA-Z0-9_]*")
|
|
||||||
protected val variableToken: Token by regexToken("[A-Z][a-zA-Z0-9_]*")
|
|
||||||
|
|
||||||
// Arithmetic tokens
|
|
||||||
private val floatToken: Token by regexToken("-?[1-9][0-9]*\\.[0-9]+")
|
|
||||||
private val integerToken: Token by regexToken("-?([1-9][0-9]*|0)")
|
|
||||||
|
|
||||||
// Special tokens
|
|
||||||
protected val neck by literalToken(":-")
|
|
||||||
protected val comma: Token by literalToken(",")
|
|
||||||
protected val leftParenthesis: Token by literalToken("(")
|
|
||||||
protected val rightParenthesis: Token by literalToken(")")
|
|
||||||
protected val dot by literalToken(".")
|
|
||||||
|
|
||||||
// Ignored tokens
|
|
||||||
protected val whitespace: Token by regexToken("\\s+", ignore = true)
|
|
||||||
protected val singleLineComment: Token by regexToken("%[^\\n]*", ignore = true)
|
|
||||||
protected val multiLineComment: Token by regexToken("/\\*.*?\\*/", ignore = true)
|
|
||||||
|
|
||||||
protected val dummy by token { _, _ -> -1 } use { throw IllegalStateException("This parser should not be used") }
|
|
||||||
|
|
||||||
// Prolog parsers
|
|
||||||
protected val variable: Parser<Variable> by variableToken use { Variable(text) }
|
|
||||||
protected val atom: Parser<Atom> by nameToken use { Atom(text) }
|
|
||||||
protected val compound: Parser<Structure> by (atom and skip(leftParenthesis) and separated(
|
|
||||||
parser(::term),
|
|
||||||
comma,
|
|
||||||
acceptZero = true
|
|
||||||
) and skip(rightParenthesis)) use {
|
|
||||||
Structure(t1, t2.terms)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arithmetic parsers
|
|
||||||
private val int: Parser<Integer> by integerToken use { Integer(text.toInt()) }
|
|
||||||
private val float: Parser<Float> by floatToken use {
|
|
||||||
Float(text.toFloat())
|
|
||||||
}
|
|
||||||
|
|
||||||
protected val term: Parser<Term> by (dummy
|
|
||||||
or float
|
|
||||||
or int
|
|
||||||
or variable
|
|
||||||
or compound
|
|
||||||
or atom
|
|
||||||
) map { it }
|
|
||||||
|
|
||||||
override val rootParser: Parser<Any> = term
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
package better_parser
|
|
||||||
|
|
||||||
import com.github.h0tk3y.betterParse.combinators.times
|
|
||||||
import com.github.h0tk3y.betterParse.combinators.unaryMinus
|
|
||||||
import com.github.h0tk3y.betterParse.combinators.use
|
|
||||||
import com.github.h0tk3y.betterParse.grammar.parseToEnd
|
|
||||||
import com.github.h0tk3y.betterParse.parser.Parser
|
|
||||||
import prolog.ast.logic.LogicOperand
|
|
||||||
import prolog.builtins.Query
|
|
||||||
|
|
||||||
class SimpleReplParser(val debug: Boolean = false) : SimpleSourceParser() {
|
|
||||||
override val rootParser: Parser<Query> by (body * -dot) use { Query(this as LogicOperand) }
|
|
||||||
|
|
||||||
fun parse(input: String): Query {
|
|
||||||
if (debug) {
|
|
||||||
println("Parsing input: $input")
|
|
||||||
}
|
|
||||||
|
|
||||||
val query = parseToEnd(input) as Query
|
|
||||||
|
|
||||||
if (debug) {
|
|
||||||
println("Parsed query: $query")
|
|
||||||
}
|
|
||||||
|
|
||||||
return query
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
package better_parser
|
|
||||||
|
|
||||||
import com.github.h0tk3y.betterParse.combinators.*
|
|
||||||
import com.github.h0tk3y.betterParse.grammar.parser
|
|
||||||
import com.github.h0tk3y.betterParse.lexer.literalToken
|
|
||||||
import com.github.h0tk3y.betterParse.parser.Parser
|
|
||||||
import prolog.ast.arithmetic.ArithmeticOperator
|
|
||||||
import prolog.ast.logic.*
|
|
||||||
import prolog.ast.terms.*
|
|
||||||
import prolog.builtins.Conjunction
|
|
||||||
import prolog.builtins.Disjunction
|
|
||||||
|
|
||||||
open class SimpleSourceParser : SimplePrologParser() {
|
|
||||||
protected val simpleLogicOperand: Parser<LogicOperand> by (dummy
|
|
||||||
or compound
|
|
||||||
or atom
|
|
||||||
)
|
|
||||||
protected val logicOperand: Parser<LogicOperand> by (dummy
|
|
||||||
or parser(::operator)
|
|
||||||
or simpleLogicOperand
|
|
||||||
)
|
|
||||||
|
|
||||||
protected val arithmeticOperator: Parser<ArithmeticOperator> by dummy
|
|
||||||
protected val logicOperator: Parser<LogicOperator> by (simpleLogicOperand * comma * logicOperand) use {
|
|
||||||
Conjunction(t1, t3)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected val operator: Parser<Operator> by (arithmeticOperator or logicOperator) use { this as Operator }
|
|
||||||
|
|
||||||
protected val head: Parser<Head> by (dummy
|
|
||||||
or compound
|
|
||||||
or atom
|
|
||||||
)
|
|
||||||
protected val body: Parser<Body> by (dummy
|
|
||||||
or operator
|
|
||||||
or head
|
|
||||||
) use { this as Body }
|
|
||||||
|
|
||||||
// ----
|
|
||||||
|
|
||||||
private val rule: Parser<Rule> by (head * -neck * body) use { Rule(t1, t2) }
|
|
||||||
private val fact: Parser<Fact> by head use { Fact(this) }
|
|
||||||
|
|
||||||
private val clause: Parser<Clause> by ((rule or fact) * -dot)
|
|
||||||
private val clauses: Parser<List<Clause>> by zeroOrMore(clause)
|
|
||||||
|
|
||||||
override val rootParser: Parser<Any> by clauses
|
|
||||||
}
|
|
11
src/parser/Parser.kt
Normal file
11
src/parser/Parser.kt
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
interface Parser {
|
||||||
|
/**
|
||||||
|
* Parses the input string and returns the parsed result.
|
||||||
|
*
|
||||||
|
* @param input The input string to parse.
|
||||||
|
* @return The parsed result, which is the AST of the input.
|
||||||
|
*/
|
||||||
|
fun parse(input: String): Any
|
||||||
|
}
|
9
src/parser/ReplParser.kt
Normal file
9
src/parser/ReplParser.kt
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import prolog.builtins.Query
|
||||||
|
|
||||||
|
class ReplParser: Parser {
|
||||||
|
override fun parse(input: String): Query {
|
||||||
|
TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
12
src/parser/ScriptParser.kt
Normal file
12
src/parser/ScriptParser.kt
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import com.github.h0tk3y.betterParse.grammar.Grammar
|
||||||
|
import com.github.h0tk3y.betterParse.grammar.parseToEnd
|
||||||
|
import parser.grammars.LogicGrammar
|
||||||
|
import prolog.ast.logic.Clause
|
||||||
|
|
||||||
|
class ScriptParser: Parser {
|
||||||
|
private val grammar: Grammar<List<Clause>> = LogicGrammar() as Grammar<List<Clause>>
|
||||||
|
|
||||||
|
override fun parse(input: String): List<Clause> = grammar.parseToEnd(input)
|
||||||
|
}
|
22
src/parser/grammars/LogicGrammar.kt
Normal file
22
src/parser/grammars/LogicGrammar.kt
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package parser.grammars
|
||||||
|
|
||||||
|
import com.github.h0tk3y.betterParse.combinators.oneOrMore
|
||||||
|
import com.github.h0tk3y.betterParse.combinators.or
|
||||||
|
import com.github.h0tk3y.betterParse.combinators.separated
|
||||||
|
import com.github.h0tk3y.betterParse.combinators.times
|
||||||
|
import com.github.h0tk3y.betterParse.combinators.unaryMinus
|
||||||
|
import com.github.h0tk3y.betterParse.combinators.use
|
||||||
|
import com.github.h0tk3y.betterParse.parser.Parser
|
||||||
|
import prolog.ast.logic.Clause
|
||||||
|
import prolog.ast.logic.Fact
|
||||||
|
import prolog.ast.logic.Rule
|
||||||
|
|
||||||
|
class LogicGrammar : TermsGrammar() {
|
||||||
|
protected val rule: Parser<Rule> by (head * -neck * body) use { Rule(t1, t2) }
|
||||||
|
protected val fact: Parser<Fact> by head use { Fact(this) }
|
||||||
|
|
||||||
|
protected val clause: Parser<Clause> by ((rule or fact) * -dot)
|
||||||
|
protected val clauses: Parser<List<Clause>> by oneOrMore(clause)
|
||||||
|
|
||||||
|
override val rootParser: Parser<Any> by clauses
|
||||||
|
}
|
74
src/parser/grammars/TermsGrammar.kt
Normal file
74
src/parser/grammars/TermsGrammar.kt
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
package parser.grammars
|
||||||
|
|
||||||
|
import com.github.h0tk3y.betterParse.combinators.or
|
||||||
|
import com.github.h0tk3y.betterParse.combinators.separated
|
||||||
|
import com.github.h0tk3y.betterParse.combinators.times
|
||||||
|
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.Float
|
||||||
|
import prolog.ast.arithmetic.Integer
|
||||||
|
import prolog.ast.logic.LogicOperand
|
||||||
|
import prolog.ast.logic.LogicOperator
|
||||||
|
import prolog.ast.terms.Atom
|
||||||
|
import prolog.ast.terms.Body
|
||||||
|
import prolog.ast.terms.Head
|
||||||
|
import prolog.ast.terms.Operator
|
||||||
|
import prolog.ast.terms.Structure
|
||||||
|
import prolog.ast.terms.Term
|
||||||
|
import prolog.ast.terms.Variable
|
||||||
|
import prolog.builtins.Conjunction
|
||||||
|
|
||||||
|
open class TermsGrammar : Tokens() {
|
||||||
|
// Basic named terms
|
||||||
|
protected val variable: Parser<Variable> by variableToken use { Variable(text) }
|
||||||
|
protected val atom: Parser<Atom> by nameToken use { Atom(text) }
|
||||||
|
protected val compound: Parser<Structure> by (atom * -leftParenthesis * separated(
|
||||||
|
parser(::term),
|
||||||
|
comma,
|
||||||
|
acceptZero = true
|
||||||
|
) * -rightParenthesis) use {
|
||||||
|
Structure(t1, t2.terms)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basic arithmetic
|
||||||
|
protected val int: Parser<Integer> by integerToken use { Integer(text.toInt()) }
|
||||||
|
protected val float: Parser<Float> by floatToken use { Float(text.toFloat()) }
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
protected val simpleLogicOperand: Parser<LogicOperand> by (dummy
|
||||||
|
or compound
|
||||||
|
or atom
|
||||||
|
)
|
||||||
|
protected val logicOperand: Parser<LogicOperand> by (dummy
|
||||||
|
or parser(::operator)
|
||||||
|
or simpleLogicOperand
|
||||||
|
)
|
||||||
|
protected val logicOperator: Parser<LogicOperator> by (simpleLogicOperand * -comma * logicOperand) use {
|
||||||
|
Conjunction(t1, t2)
|
||||||
|
}
|
||||||
|
protected val operator: Parser<Operator> by (dummy
|
||||||
|
or logicOperator
|
||||||
|
)
|
||||||
|
|
||||||
|
// Parts
|
||||||
|
protected val head: Parser<Head> by (dummy
|
||||||
|
or compound
|
||||||
|
or atom
|
||||||
|
)
|
||||||
|
protected val body: Parser<Body> by (dummy
|
||||||
|
or operator
|
||||||
|
or head
|
||||||
|
) use { this as Body }
|
||||||
|
|
||||||
|
protected val term: Parser<Term> by (dummy
|
||||||
|
or float
|
||||||
|
or int
|
||||||
|
or variable
|
||||||
|
or compound
|
||||||
|
or atom
|
||||||
|
)
|
||||||
|
|
||||||
|
override val rootParser: Parser<Any> by term
|
||||||
|
}
|
32
src/parser/grammars/Tokens.kt
Normal file
32
src/parser/grammars/Tokens.kt
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
package parser.grammars
|
||||||
|
|
||||||
|
import com.github.h0tk3y.betterParse.combinators.use
|
||||||
|
import com.github.h0tk3y.betterParse.grammar.Grammar
|
||||||
|
import com.github.h0tk3y.betterParse.lexer.Token
|
||||||
|
import com.github.h0tk3y.betterParse.lexer.literalToken
|
||||||
|
import com.github.h0tk3y.betterParse.lexer.regexToken
|
||||||
|
import com.github.h0tk3y.betterParse.lexer.token
|
||||||
|
|
||||||
|
abstract class Tokens : Grammar<Any>() {
|
||||||
|
// Prolog tokens
|
||||||
|
protected val nameToken: Token by regexToken("[a-z][a-zA-Z0-9_]*")
|
||||||
|
protected val variableToken: Token by regexToken("[A-Z][a-zA-Z0-9_]*")
|
||||||
|
|
||||||
|
// Arithmetic tokens
|
||||||
|
protected val floatToken: Token by regexToken("-?[1-9][0-9]*\\.[0-9]+")
|
||||||
|
protected val integerToken: Token by regexToken("-?([1-9][0-9]*|0)")
|
||||||
|
|
||||||
|
// Special tokens
|
||||||
|
protected val neck by literalToken(":-")
|
||||||
|
protected val comma: Token by literalToken(",")
|
||||||
|
protected val leftParenthesis: Token by literalToken("(")
|
||||||
|
protected val rightParenthesis: Token by literalToken(")")
|
||||||
|
protected val dot by literalToken(".")
|
||||||
|
|
||||||
|
// Ignored tokens
|
||||||
|
protected val whitespace: Token by regexToken("\\s+", ignore = true)
|
||||||
|
protected val singleLineComment: Token by regexToken("%[^\\n]*", ignore = true)
|
||||||
|
protected val multiLineComment: Token by regexToken("/\\*.*?\\*/", ignore = true)
|
||||||
|
|
||||||
|
protected val dummy by token { _, _ -> -1 } use { throw IllegalStateException("This parser should not be used") }
|
||||||
|
}
|
36
tests/parser/ScriptParserTests.kt
Normal file
36
tests/parser/ScriptParserTests.kt
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Assertions.assertInstanceOf
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import prolog.ast.logic.Fact
|
||||||
|
import prolog.ast.terms.Atom
|
||||||
|
import prolog.logic.equivalent
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
class ScriptParserTests {
|
||||||
|
private lateinit var parser: ScriptParser
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setup() {
|
||||||
|
parser = ScriptParser()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `parse single atom`() {
|
||||||
|
val input = """
|
||||||
|
a.
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
val result = parser.parse(input)
|
||||||
|
val expected = Fact(Atom("a"))
|
||||||
|
|
||||||
|
assertEquals(1, result.size, "Should return one result")
|
||||||
|
assertInstanceOf(Fact::class.java, result[0], "Result should be a fact")
|
||||||
|
assertTrue(
|
||||||
|
equivalent(expected.head, result[0].head, emptyMap()),
|
||||||
|
"Expected fact 'a'"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
package better_parser
|
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.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
|
||||||
|
@ -14,14 +14,13 @@ import prolog.ast.terms.CompoundTerm
|
||||||
import prolog.ast.terms.Structure
|
import prolog.ast.terms.Structure
|
||||||
import prolog.ast.terms.Variable
|
import prolog.ast.terms.Variable
|
||||||
import prolog.builtins.Conjunction
|
import prolog.builtins.Conjunction
|
||||||
import prolog.builtins.Disjunction
|
|
||||||
|
|
||||||
class SimpleSourcePrologParserTests {
|
class LogicGrammarTests {
|
||||||
private lateinit var parser: Grammar<List<Clause>>
|
private lateinit var parser: Grammar<List<Clause>>
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setup() {
|
fun setup() {
|
||||||
parser = SimpleSourceParser() as Grammar<List<Clause>>
|
parser = LogicGrammar() as Grammar<List<Clause>>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -38,9 +37,9 @@ class SimpleSourcePrologParserTests {
|
||||||
fun `parse simple fact`(input: String) {
|
fun `parse simple fact`(input: String) {
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertEquals(1, result.size, "Expected 1 fact")
|
Assertions.assertEquals(1, result.size, "Expected 1 fact")
|
||||||
assertTrue(result[0] is Fact, "Expected a fact")
|
Assertions.assertTrue(result[0] is Fact, "Expected a fact")
|
||||||
assertEquals(input, "${result[0].toString()}.", "Expected fact to be '$input'")
|
Assertions.assertEquals(input, "${result[0].toString()}.", "Expected fact to be '$input'")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -53,9 +52,9 @@ class SimpleSourcePrologParserTests {
|
||||||
fun `parse multiple facts`(input: String) {
|
fun `parse multiple facts`(input: String) {
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertEquals(2, result.size, "Expected 2 facts")
|
Assertions.assertEquals(2, result.size, "Expected 2 facts")
|
||||||
assertTrue(result[0] is Fact, "Expected a fact")
|
Assertions.assertTrue(result[0] is Fact, "Expected a fact")
|
||||||
assertTrue(result[1] is Fact, "Expected a fact")
|
Assertions.assertTrue(result[1] is Fact, "Expected a fact")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -64,9 +63,9 @@ class SimpleSourcePrologParserTests {
|
||||||
|
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertEquals(1, result.size, "Expected 1 rule")
|
Assertions.assertEquals(1, result.size, "Expected 1 rule")
|
||||||
assertTrue(result[0] is Rule, "Expected a rule")
|
Assertions.assertTrue(result[0] is Rule, "Expected a rule")
|
||||||
assertEquals("a :- b", result[0].toString())
|
Assertions.assertEquals("a :- b", result[0].toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -77,8 +76,8 @@ class SimpleSourcePrologParserTests {
|
||||||
fun `parse simple rule`(input: String) {
|
fun `parse simple rule`(input: String) {
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertEquals(1, result.size, "Expected 1 rule")
|
Assertions.assertEquals(1, result.size, "Expected 1 rule")
|
||||||
assertTrue(result[0] is Rule, "Expected a rule")
|
Assertions.assertTrue(result[0] is Rule, "Expected a rule")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -87,22 +86,22 @@ class SimpleSourcePrologParserTests {
|
||||||
|
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertEquals(1, result.size, "Expected 1 rule")
|
Assertions.assertEquals(1, result.size, "Expected 1 rule")
|
||||||
assertTrue(result[0] is Rule, "Expected a rule")
|
Assertions.assertTrue(result[0] is Rule, "Expected a rule")
|
||||||
|
|
||||||
val rule = result[0] as Rule
|
val rule = result[0] as Rule
|
||||||
|
|
||||||
assertTrue(rule.head is Structure, "Expected head to be a structure")
|
Assertions.assertTrue(rule.head is Structure, "Expected head to be a structure")
|
||||||
val head = rule.head as Structure
|
val head = rule.head as Structure
|
||||||
assertEquals("parent/2", head.functor, "Expected functor 'parent/2'")
|
Assertions.assertEquals("parent/2", head.functor, "Expected functor 'parent/2'")
|
||||||
assertEquals(Variable("X"), head.arguments[0], "Expected first argument 'X'")
|
Assertions.assertEquals(Variable("X"), head.arguments[0], "Expected first argument 'X'")
|
||||||
assertEquals(Variable("Y"), head.arguments[1], "Expected second argument 'Y'")
|
Assertions.assertEquals(Variable("Y"), head.arguments[1], "Expected second argument 'Y'")
|
||||||
|
|
||||||
assertTrue(rule.body is Structure, "Expected body to be a structure")
|
Assertions.assertTrue(rule.body is Structure, "Expected body to be a structure")
|
||||||
val body = rule.body as Structure
|
val body = rule.body as Structure
|
||||||
assertEquals("father/2", body.functor, "Expected functor 'father/2'")
|
Assertions.assertEquals("father/2", body.functor, "Expected functor 'father/2'")
|
||||||
assertEquals(Variable("X"), body.arguments[0], "Expected first argument 'X'")
|
Assertions.assertEquals(Variable("X"), body.arguments[0], "Expected first argument 'X'")
|
||||||
assertEquals(Variable("Y"), body.arguments[1], "Expected second argument 'Y'")
|
Assertions.assertEquals(Variable("Y"), body.arguments[1], "Expected second argument 'Y'")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -111,10 +110,10 @@ class SimpleSourcePrologParserTests {
|
||||||
|
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertEquals(1, result.size, "Expected 1 rule")
|
Assertions.assertEquals(1, result.size, "Expected 1 rule")
|
||||||
assertInstanceOf(Rule::class.java, result[0], "Expected a rule")
|
Assertions.assertInstanceOf(Rule::class.java, result[0], "Expected a rule")
|
||||||
val rule = result[0] as Rule
|
val rule = result[0] as Rule
|
||||||
assertInstanceOf(Conjunction::class.java, rule.body, "Expected body to be a conjunction")
|
Assertions.assertInstanceOf(Conjunction::class.java, rule.body, "Expected body to be a conjunction")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -123,10 +122,10 @@ class SimpleSourcePrologParserTests {
|
||||||
|
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertEquals(1, result.size, "Expected 1 rule")
|
Assertions.assertEquals(1, result.size, "Expected 1 rule")
|
||||||
val rule = result[0] as Rule
|
val rule = result[0] as Rule
|
||||||
assertTrue(rule.body is Conjunction, "Expected body to be a conjunction")
|
Assertions.assertTrue(rule.body is Conjunction, "Expected body to be a conjunction")
|
||||||
val conjunction = rule.body as Conjunction
|
val conjunction = rule.body as Conjunction
|
||||||
assertEquals("invited/2", (conjunction.left as CompoundTerm).functor, "Expected functor 'invited/2'")
|
Assertions.assertEquals("invited/2", (conjunction.left as CompoundTerm).functor, "Expected functor 'invited/2'")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
package better_parser
|
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.assertEquals
|
import org.junit.jupiter.api.Assertions
|
||||||
import org.junit.jupiter.api.Assertions.assertTrue
|
|
||||||
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
|
||||||
|
@ -16,12 +15,12 @@ import prolog.ast.terms.Term
|
||||||
import prolog.ast.terms.Variable
|
import prolog.ast.terms.Variable
|
||||||
import prolog.logic.equivalent
|
import prolog.logic.equivalent
|
||||||
|
|
||||||
class SimplePrologPrologParserTests {
|
class TermsGrammarTests {
|
||||||
private lateinit var parser: Grammar<Term>
|
private lateinit var parser: Grammar<Term>
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun setup() {
|
fun setup() {
|
||||||
parser = SimplePrologParser() as Grammar<Term>
|
parser = TermsGrammar() as Grammar<Term>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -29,7 +28,7 @@ class SimplePrologPrologParserTests {
|
||||||
fun `parse atom`(name: String) {
|
fun `parse atom`(name: String) {
|
||||||
val result = parser.parseToEnd(name)
|
val result = parser.parseToEnd(name)
|
||||||
|
|
||||||
assertEquals(Atom(name), result, "Expected atom '$name'")
|
Assertions.assertEquals(Atom(name), result, "Expected atom '$name'")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
@ -37,7 +36,7 @@ class SimplePrologPrologParserTests {
|
||||||
fun `parse variable`(name: String) {
|
fun `parse variable`(name: String) {
|
||||||
val result = parser.parseToEnd(name)
|
val result = parser.parseToEnd(name)
|
||||||
|
|
||||||
assertEquals(Variable(name), result, "Expected atom '$name'")
|
Assertions.assertEquals(Variable(name), result, "Expected atom '$name'")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -46,7 +45,7 @@ class SimplePrologPrologParserTests {
|
||||||
|
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertTrue(
|
Assertions.assertTrue(
|
||||||
equivalent(Structure(Atom("f"), emptyList()), result, emptyMap()),
|
equivalent(Structure(Atom("f"), emptyList()), result, emptyMap()),
|
||||||
"Expected atom 'f'"
|
"Expected atom 'f'"
|
||||||
)
|
)
|
||||||
|
@ -58,7 +57,7 @@ class SimplePrologPrologParserTests {
|
||||||
|
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertTrue(
|
Assertions.assertTrue(
|
||||||
equivalent(Structure(Atom("f"), listOf(Atom("a"))), result, emptyMap()),
|
equivalent(Structure(Atom("f"), listOf(Atom("a"))), result, emptyMap()),
|
||||||
"Expected atom 'f(a)'"
|
"Expected atom 'f(a)'"
|
||||||
)
|
)
|
||||||
|
@ -70,7 +69,7 @@ class SimplePrologPrologParserTests {
|
||||||
|
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertTrue(
|
Assertions.assertTrue(
|
||||||
equivalent(Structure(Atom("f"), listOf(Atom("a"), Atom("b"))), result, emptyMap()),
|
equivalent(Structure(Atom("f"), listOf(Atom("a"), Atom("b"))), result, emptyMap()),
|
||||||
"Expected atom 'f(a, b)'"
|
"Expected atom 'f(a, b)'"
|
||||||
)
|
)
|
||||||
|
@ -82,7 +81,7 @@ class SimplePrologPrologParserTests {
|
||||||
|
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertTrue(
|
Assertions.assertTrue(
|
||||||
equivalent(Structure(Atom("f"), listOf(Atom("a"), Variable("X"))), result, emptyMap()),
|
equivalent(Structure(Atom("f"), listOf(Atom("a"), Variable("X"))), result, emptyMap()),
|
||||||
"Expected atom 'f(a, X)'"
|
"Expected atom 'f(a, X)'"
|
||||||
)
|
)
|
||||||
|
@ -94,7 +93,7 @@ class SimplePrologPrologParserTests {
|
||||||
|
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertTrue(
|
Assertions.assertTrue(
|
||||||
equivalent(
|
equivalent(
|
||||||
Structure(Atom("f"), listOf(Atom("a"), Structure(Atom("g"), listOf(Atom("b"))))),
|
Structure(Atom("f"), listOf(Atom("a"), Structure(Atom("g"), listOf(Atom("b"))))),
|
||||||
result,
|
result,
|
||||||
|
@ -110,7 +109,7 @@ class SimplePrologPrologParserTests {
|
||||||
|
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertTrue(
|
Assertions.assertTrue(
|
||||||
equivalent(
|
equivalent(
|
||||||
Structure(Atom("f"), listOf(Atom("a"), Structure(Atom("g"), listOf(Variable("X"))))),
|
Structure(Atom("f"), listOf(Atom("a"), Structure(Atom("g"), listOf(Variable("X"))))),
|
||||||
result,
|
result,
|
||||||
|
@ -127,7 +126,7 @@ class SimplePrologPrologParserTests {
|
||||||
|
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertEquals(Integer(number), result, "Expected integer '$number'")
|
Assertions.assertEquals(Integer(number), result, "Expected integer '$number'")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -136,7 +135,7 @@ class SimplePrologPrologParserTests {
|
||||||
|
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertTrue(
|
Assertions.assertTrue(
|
||||||
equivalent(Float(42.0f), result, emptyMap()),
|
equivalent(Float(42.0f), result, emptyMap()),
|
||||||
"Expected float '42.0'"
|
"Expected float '42.0'"
|
||||||
)
|
)
|
||||||
|
@ -148,7 +147,7 @@ class SimplePrologPrologParserTests {
|
||||||
|
|
||||||
val result = parser.parseToEnd(input)
|
val result = parser.parseToEnd(input)
|
||||||
|
|
||||||
assertTrue(
|
Assertions.assertTrue(
|
||||||
equivalent(Float(-42.0f), result, emptyMap()),
|
equivalent(Float(-42.0f), result, emptyMap()),
|
||||||
"Expected float '-42.0'"
|
"Expected float '-42.0'"
|
||||||
)
|
)
|
|
@ -6,4 +6,7 @@ parent(mary, jimmy).
|
||||||
father(X, Y) :- parent(X, Y), male(X).
|
father(X, Y) :- parent(X, Y), male(X).
|
||||||
mother(X, Y) :- parent(X, Y), female(X).
|
mother(X, Y) :- parent(X, Y), female(X).
|
||||||
|
|
||||||
kan_goed_koken(miriam).
|
:- write(hello),
|
||||||
|
nl.
|
||||||
|
|
||||||
|
:- write(hello2).
|
Reference in a new issue