Checkpoint

This commit is contained in:
Tibo De Peuter 2025-04-17 17:49:53 +02:00
parent e749f8c6cb
commit 48f94c30df
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
15 changed files with 175 additions and 67 deletions

View file

@ -2,18 +2,37 @@ package parser
import lexer.Token
import lexer.TokenType
import parser.errors.ParsingError
import parser.errors.ParsingErrorType
import parser.state.ParserPosition
import prolog.ast.logic.Clause
import prolog.ast.logic.Fact
import prolog.ast.logic.Rule
import prolog.ast.terms.Atom
import prolog.ast.terms.Structure
import prolog.ast.terms.Term
class Parser(private val tokens: List<Token>) {
private var position: Int = 0
private val position: ParserPosition = ParserPosition(0)
fun parse(): List<Term> {
val terms = mutableListOf<Term>()
// TODO
while (hasNext()) {
terms.add(parseTerm())
position.save()
var term: Term? = null
while (term == null) {
// Try each parser rule in order
}
require(term != null) {
ParsingError(ParsingErrorType.UNEXPECTED_TOKEN, "Expected a term", position)
}
terms.add(term)
}
return terms
@ -21,6 +40,7 @@ class Parser(private val tokens: List<Token>) {
/**
* Matches the current token with any of the expected types.
* If it matches, it consumes the token and returns true.
*
* @param types The list of expected token types.
* @return True if the current token matches any of the expected types, false otherwise.
@ -46,71 +66,72 @@ class Parser(private val tokens: List<Token>) {
private fun hasNext(): Boolean {
// Check if the position is within the tokens list
// TODO Check for EOF instead?
return position < tokens.size
return position.offset < tokens.size
}
private fun peek(): Token {
// Peek should only be called if there is a next token
if (!hasNext()) {
throw Error("Unexpected end of input")
}
require(hasNext()) { "Unexpected end of input" }
return tokens[position]
return tokens[position.offset]
}
private fun next(): Token {
val token = peek()
position++
position.offset++
return token
}
private fun previous(): Token {
// Previous should only be called if there is a previous token
if (position == 0) {
throw Error("No previous token")
}
return tokens[position - 1]
require(0 < position.offset) { "No previous token" }
return tokens[position.offset - 1]
}
/* * * * * *
* Parsers *
* * * * * */
private fun parseTerm(): Term {
// TODO Variable
// TODO braced term
// TODO Integer Term
// TODO Float term
// TODO Compound term
// TODO Binary operator
// TODO Unary operator
// TODO list term
// TODO curly bracketed term
return parseAtom()
private fun parseWithTry(parseRule: () -> Term): Term {
try {
return parseRule()
} catch (e: Exception) {
throw ParsingError(ParsingErrorType.UNEXPECTED_TOKEN, "Unexpected token", position)
}
}
private fun parseClause(): Clause {
return try {
Fact(parseStructure())
} catch (e: Exception) {
Fact(parseAtom())
}
}
private fun parseStructure(): Structure {
val name = parseAtom()
val args = mutableListOf<Term>()
require(match(listOf(TokenType.PARENTHESIS_LEFT))) {
ParsingError(ParsingErrorType.UNEXPECTED_TOKEN, "Expected '(' after structure name", position)
}
// TODO Handle arguments
require(match(listOf(TokenType.PARENTHESIS_RIGHT))) {
ParsingError(ParsingErrorType.UNEXPECTED_TOKEN, "Expected ')' after structure arguments", position)
}
return Structure(name, args)
}
private fun parseAtom(): Atom {
// TODO empty list
// TODO empty braces
return Atom(parseLetterDigit())
// TODO graphic
// TODO quoted
// TODO double quoted
// TODO back quoted
// TODO semicolon
// TODO cut
}
private fun parseLetterDigit(): String {
// Check if the first character is a lowercase letter
if (match(listOf(TokenType.ALPHANUMERIC)) && previous().value[0].isLowerCase()) {
return previous().value
require(match(listOf(TokenType.ALPHANUMERIC)) && previous().value[0].isLowerCase()) {
ParsingError(ParsingErrorType.UNEXPECTED_TOKEN, "Expected lowercase letter", position)
}
// TODO How to fix?
return ""
return previous().value
}
}

View file

@ -0,0 +1,12 @@
package parser.errors
import parser.state.ParserPosition
class ParsingError(private val type: ParsingErrorType, override val message: String, private val position: ParserPosition) :
Throwable() {
override fun toString(): String {
return """
($position) ${type}: $message
""".trimIndent()
}
}

View file

@ -0,0 +1,7 @@
package parser.errors
enum class ParsingErrorType {
UNEXPECTED_TOKEN,
INTERNAL_ERROR,
}

View file

@ -0,0 +1,25 @@
package parser.state
import parser.errors.ParsingError
import parser.errors.ParsingErrorType
data class ParserPosition(var offset: Int) {
private val checkpoints: ArrayDeque<ParserPosition> = ArrayDeque()
fun save() {
checkpoints.addLast(this.copy())
}
fun reload() {
require(checkpoints.isNotEmpty()) {
ParsingError(ParsingErrorType.INTERNAL_ERROR, "No checkpoint to reload from", this)
}
val checkpoint = checkpoints.removeLast()
offset = checkpoint.offset
}
override fun toString(): String {
return "at $offset"
}
}