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) { private var current = 0 fun parse(): List { val statements: MutableList = 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 { val statements: MutableList = 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() }