You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

268 lines
6.5 KiB
Kotlin

package fr.celticinfo.lox
import fr.celticinfo.lox.TokenType.*
/**
* The Parser class is responsible for parsing the tokens produced by the Scanner into an abstract syntax tree (AST).
* It is a recursive descent parser that uses a series of methods to parse different parts of the grammar.
*/
class Parser(private val tokens: List<Token>) {
private var current = 0
fun parse(): List<Stmt?> {
val statements: MutableList<Stmt?> = ArrayList()
while (!isAtEnd()) {
statements.add(declaration())
}
return statements
}
private fun declaration(): Stmt? {
return try {
when {
match(VAR) -> varDeclaration()
else -> statement()
}
} catch (error: ParseError) {
synchronize()
return null
}
}
private fun varDeclaration(): Stmt {
val name = consume(IDENTIFIER, "Expect variable name.")
var initializer: Expr? = null
if (match(EQUAL)) {
initializer = expression()
}
consume(SEMICOLON, "Expect ';' after variable declaration.")
return Var(name, initializer)
}
private fun statement(): Stmt {
return when {
match(IF) -> ifStatement()
match(PRINT) -> printStatement()
match(LEFT_BRACE) -> Block(blockStatement())
else -> expressionStatement()
}
}
private fun ifStatement(): Stmt {
consume(LEFT_PAREN, "Expect '(' after 'if'.")
val condition = expression()
consume(RIGHT_PAREN, "Expect ')' after if condition.")
val thenBranch = statement()
var elseBranch: Stmt? = null
if (match(ELSE)) {
elseBranch = statement()
}
return If(condition, thenBranch, elseBranch)
}
private fun printStatement(): Stmt {
val value = expression()
consume(SEMICOLON, "Expect ';' after value.")
return Print(value)
}
private fun expressionStatement(): Stmt {
val value = expression()
consume(SEMICOLON, "Expect ';' after expression.")
return Expression(value)
}
private fun blockStatement(): List<Stmt?> {
val statements: MutableList<Stmt?> = ArrayList()
while (!check(RIGHT_BRACE) && !isAtEnd()) {
statements.add(declaration())
}
consume(RIGHT_BRACE, "Expect '}' after block.")
return statements
}
private fun expression(): Expr {
return assignment()
}
private fun assignment(): Expr {
val expr = or()
if (match(EQUAL)) {
val equals = previous()
val value = assignment()
if (expr is Variable) {
val name = expr.name
return Assign(name, value)
}
error(equals, "Invalid assignment target.")
}
return expr
}
private fun or(): Expr {
var expr = and()
while (match(OR)) {
val operator = previous()
val right = and()
expr = Logical(expr, operator, right)
}
return expr
}
private fun and(): Expr {
var expr = equality()
while (match(AND)) {
val operator = previous()
val right = equality()
expr = Logical(expr, operator, right)
}
return expr
}
private fun equality(): Expr {
var expr = comparison()
while (match(BANG_EQUAL, EQUAL_EQUAL)) {
val operator = previous()
val right = comparison()
expr = Binary(expr, operator, right)
}
return expr
}
private fun comparison(): Expr {
var expr = term()
while (match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)) {
val operator = previous()
val right = term()
expr = Binary(expr, operator, right)
}
return expr
}
private fun term(): Expr {
var expr = factor()
while (match(MINUS, PLUS)) {
val operator = previous()
val right = factor()
expr = Binary(expr, operator, right)
}
return expr
}
private fun factor(): Expr {
var expr = unary()
while (match(SLASH, STAR)) {
val operator = previous()
val right = unary()
expr = Binary(expr, operator, right)
}
return expr
}
private fun unary(): Expr {
if (match(BANG, MINUS)) {
val operator = previous()
val right = unary()
return Unary(operator, right)
}
return primary()
}
private fun match(vararg types: TokenType): Boolean {
for (type in types) {
if (check(type)) {
advance()
return true
}
}
return false
}
private fun primary(): Expr {
when {
match(FALSE) -> return Literal(false)
match(TRUE) -> return Literal(true)
match(NIL) -> return Literal(null)
match(NUMBER, STRING) -> return Literal(previous().literal)
match(IDENTIFIER) -> return Variable(previous())
match(LEFT_PAREN) -> {
val expr = expression()
consume(RIGHT_PAREN, "Expect ')' after expression.")
return Grouping(expr)
}
else -> throw error(peek(), "Expect expression.")
}
}
private fun check(type: TokenType): Boolean {
if (isAtEnd()) return false
return peek().type == type
}
private fun advance(): Token {
if (!isAtEnd()) current++
return previous()
}
private fun isAtEnd(): Boolean {
return peek().type == EOF
}
private fun peek(): Token {
return tokens[current]
}
private fun previous(): Token {
return tokens[current - 1]
}
private fun consume(type: TokenType, message: String): Token {
if (check(type)) return advance()
throw error(peek(), message)
}
private fun error(token: Token, message: String): ParseError {
Lox.error(token, message)
return ParseError()
}
private fun synchronize() {
advance()
while (!isAtEnd()) {
if (previous().type == SEMICOLON) return
when (peek().type) {
CLASS, FUN, VAR, FOR, IF, WHILE, PRINT, RETURN -> return
else -> advance()
}
}
}
class ParseError: RuntimeException()
}