REPL checkpoint
This commit is contained in:
parent
69c156024a
commit
1b3280a947
21 changed files with 503 additions and 34 deletions
3
src/Debug.kt
Normal file
3
src/Debug.kt
Normal file
|
@ -0,0 +1,3 @@
|
|||
data object Debug {
|
||||
val on: Boolean = true
|
||||
}
|
101
src/Main.kt
101
src/Main.kt
|
@ -1,9 +1,98 @@
|
|||
fun main() {
|
||||
for (i in 1..10) {
|
||||
println("Hello, Kotlin Command Line Utility!")
|
||||
}
|
||||
import better_parser.PrologParser
|
||||
import better_parser.SimpleReplParser
|
||||
import interpreter.SourceFileReader
|
||||
import prolog.Answer
|
||||
import prolog.Program
|
||||
import prolog.ast.logic.Fact
|
||||
import prolog.ast.logic.Rule
|
||||
import prolog.ast.terms.Atom
|
||||
import prolog.ast.terms.CompoundTerm
|
||||
import prolog.ast.terms.Variable
|
||||
import prolog.builtins.Conjunction
|
||||
|
||||
fun help(): String {
|
||||
println("Unknown command. Type 'h' for help.")
|
||||
println("Commands:")
|
||||
println(" ; - find next solution")
|
||||
println(" a - abort")
|
||||
println(" . - end query")
|
||||
println(" h - help")
|
||||
println(" exit - exit Prolog REPL")
|
||||
return ""
|
||||
}
|
||||
|
||||
fun testMe(): String {
|
||||
return "Hello, Kotlin Command Line Utility!"
|
||||
fun say(message: String) {
|
||||
println(message)
|
||||
}
|
||||
|
||||
fun prompt(message: String): String {
|
||||
print("$message ")
|
||||
var input: String = readlnOrNull() ?: help()
|
||||
while (input.isBlank()) {
|
||||
input = readlnOrNull() ?: help()
|
||||
}
|
||||
return input
|
||||
}
|
||||
|
||||
fun prettyResult(result: Answer): String {
|
||||
result.fold(
|
||||
onSuccess = {
|
||||
val subs = result.getOrNull()!!
|
||||
if (subs.isEmpty()) {
|
||||
return "true."
|
||||
}
|
||||
return subs.entries.joinToString(", ") { "${it.key} = ${it.value}" }
|
||||
},
|
||||
onFailure = {
|
||||
return "Failure: ${result.exceptionOrNull()!!}"
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val knownCommands = setOf(";", "a", ".")
|
||||
|
||||
fun main() {
|
||||
SourceFileReader().readFile("tests/better_parser/resources/parent.pl")
|
||||
|
||||
val parser = SimpleReplParser(debug = false)
|
||||
|
||||
say("Prolog REPL. Type 'exit' to quit.")
|
||||
|
||||
while (true) {
|
||||
val queryString = prompt("?-")
|
||||
|
||||
try {
|
||||
val query = parser.parse(queryString)
|
||||
val answers = query.satisfy(emptyMap())
|
||||
|
||||
if (answers.none()) {
|
||||
say("false.")
|
||||
} else {
|
||||
val iterator = answers.iterator()
|
||||
|
||||
var previous = iterator.next()
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
var command = prompt(prettyResult(previous))
|
||||
|
||||
while (command !in knownCommands) {
|
||||
say("Unknown action: $command (h for help)")
|
||||
command = prompt("Action?")
|
||||
}
|
||||
|
||||
when (command) {
|
||||
";" -> previous = iterator.next()
|
||||
"a" -> break
|
||||
"." -> break
|
||||
}
|
||||
}
|
||||
|
||||
say(prettyResult(previous))
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
println("Error: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
17
src/better_parser/PrologParser.kt
Normal file
17
src/better_parser/PrologParser.kt
Normal file
|
@ -0,0 +1,17 @@
|
|||
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)
|
||||
}
|
||||
}
|
70
src/better_parser/PrologSourceParser.kt
Normal file
70
src/better_parser/PrologSourceParser.kt
Normal file
|
@ -0,0 +1,70 @@
|
|||
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)
|
||||
}
|
|
@ -6,6 +6,7 @@ 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
|
||||
|
@ -16,28 +17,32 @@ import prolog.ast.terms.Variable
|
|||
|
||||
open class SimplePrologParser : Grammar<Any>() {
|
||||
// Prolog tokens
|
||||
private val nameToken: Token by regexToken("[a-z][a-zA-Z0-9_]*")
|
||||
private val variableToken: Token by regexToken("[A-Z][a-zA-Z0-9_]*")
|
||||
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
|
||||
private val comma: Token by literalToken(",")
|
||||
private val leftParenthesis: Token by literalToken("(")
|
||||
private val rightParenthesis: Token by literalToken(")")
|
||||
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
|
||||
private val whitespace: Token by regexToken("\\s+", ignore = true)
|
||||
private val singleLineComment: Token by regexToken("%[^\\n]*", ignore = true)
|
||||
private val multiLineComment: Token by regexToken("/\\*.*?\\*/", ignore = true)
|
||||
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
|
||||
private val atomParser: Parser<Atom> by nameToken use { Atom(text) }
|
||||
private val variableParser: Parser<Variable> by variableToken use { Variable(text) }
|
||||
private val structureParser: Parser<Structure> by (atomParser and skip(leftParenthesis) and separated(
|
||||
parser(this::termParser),
|
||||
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 {
|
||||
|
@ -45,17 +50,18 @@ open class SimplePrologParser : Grammar<Any>() {
|
|||
}
|
||||
|
||||
// Arithmetic parsers
|
||||
private val integerParser: Parser<Integer> by integerToken use { Integer(text.toInt()) }
|
||||
private val floatParser: Parser<Float> by floatToken use {
|
||||
private val int: Parser<Integer> by integerToken use { Integer(text.toInt()) }
|
||||
private val float: Parser<Float> by floatToken use {
|
||||
Float(text.toFloat())
|
||||
}
|
||||
|
||||
private val termParser: Parser<Term> by (floatParser
|
||||
or integerParser
|
||||
or variableParser
|
||||
or structureParser
|
||||
or atomParser
|
||||
protected val term: Parser<Term> by (dummy
|
||||
or float
|
||||
or int
|
||||
or variable
|
||||
or compound
|
||||
or atom
|
||||
) map { it }
|
||||
|
||||
override val rootParser: Parser<Any> = termParser
|
||||
override val rootParser: Parser<Any> = term
|
||||
}
|
27
src/better_parser/SimpleReplParser.kt
Normal file
27
src/better_parser/SimpleReplParser.kt
Normal file
|
@ -0,0 +1,27 @@
|
|||
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
|
||||
}
|
||||
}
|
48
src/better_parser/SimpleSourceParser.kt
Normal file
48
src/better_parser/SimpleSourceParser.kt
Normal file
|
@ -0,0 +1,48 @@
|
|||
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
|
||||
}
|
23
src/interpreter/SourceFileReader.kt
Normal file
23
src/interpreter/SourceFileReader.kt
Normal file
|
@ -0,0 +1,23 @@
|
|||
package interpreter
|
||||
|
||||
import better_parser.PrologParser
|
||||
|
||||
class SourceFileReader {
|
||||
private val parser = PrologParser()
|
||||
|
||||
fun readFile(filePath: String) {
|
||||
return try {
|
||||
val file = java.io.File(filePath)
|
||||
if (!file.exists()) {
|
||||
throw IllegalArgumentException("File not found: $filePath")
|
||||
}
|
||||
|
||||
val content = file.readText()
|
||||
|
||||
// Parse the content using SimpleSourceParser
|
||||
parser.parse(content)
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException("Error reading file: $filePath", e)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package prolog
|
||||
|
||||
import Debug
|
||||
import prolog.ast.logic.Clause
|
||||
import prolog.ast.logic.Predicate
|
||||
import prolog.ast.logic.Resolvent
|
||||
|
@ -12,13 +13,16 @@ typealias Database = Program
|
|||
* Prolog Program or database.
|
||||
*/
|
||||
object Program: Resolvent {
|
||||
private var predicates: Map<Functor, Predicate> = emptyMap()
|
||||
var predicates: Map<Functor, Predicate> = emptyMap()
|
||||
|
||||
init {
|
||||
setup()
|
||||
}
|
||||
|
||||
private fun setup() {
|
||||
if (Debug.on) {
|
||||
println("Setting up Prolog program...")
|
||||
}
|
||||
// Initialize the program with built-in predicates
|
||||
load(listOf(
|
||||
))
|
||||
|
|
|
@ -18,7 +18,7 @@ import prolog.logic.unifyLazy
|
|||
* @see [prolog.ast.terms.Variable]
|
||||
* @see [Predicate]
|
||||
*/
|
||||
abstract class Clause(private val head: Head, private val body: Body) : Resolvent {
|
||||
abstract class Clause(val head: Head, val body: Body) : Resolvent {
|
||||
val functor: Functor = head.functor
|
||||
|
||||
override fun solve (goal: Goal, subs: Substitutions): Answers = sequence {
|
||||
|
|
|
@ -39,6 +39,11 @@ class Predicate : Resolvent {
|
|||
*/
|
||||
fun add(clause: Clause) {
|
||||
require(clause.functor == functor) { "Clause functor does not match predicate functor" }
|
||||
|
||||
if (Debug.on) {
|
||||
println("Adding clause $clause to predicate $functor")
|
||||
}
|
||||
|
||||
clauses.add(clause)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package prolog.ast.terms
|
||||
|
||||
import prolog.ast.arithmetic.Expression
|
||||
|
||||
typealias Operand = Term
|
||||
|
||||
abstract class Operator(
|
||||
|
|
|
@ -39,7 +39,7 @@ class Cut() : Atom("!") {
|
|||
/**
|
||||
* Conjunction (and). True if both Goal1 and Goal2 are true.
|
||||
*/
|
||||
class Conjunction(private val left: LogicOperand, private val right: LogicOperand) :
|
||||
class Conjunction(val left: LogicOperand, private val right: LogicOperand) :
|
||||
LogicOperator(Atom(","), left, right) {
|
||||
override fun satisfy(subs: Substitutions): Answers = sequence {
|
||||
// Satisfy the left part first, which either succeeds or fails
|
||||
|
|
4
src/repl/Repl.kt
Normal file
4
src/repl/Repl.kt
Normal file
|
@ -0,0 +1,4 @@
|
|||
package repl
|
||||
|
||||
class Repl {
|
||||
}
|
Reference in a new issue