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.
277 lines
6.8 KiB
Kotlin
277 lines
6.8 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 whileStatement(): Stmt {
|
|
consume(LEFT_PAREN, "Expect '(' after 'while'.")
|
|
val condition = expression()
|
|
consume(RIGHT_PAREN, "Expect ')' after condition.")
|
|
val body = statement()
|
|
|
|
return While(condition, body)
|
|
}
|
|
|
|
private fun statement(): Stmt {
|
|
return when {
|
|
match(IF) -> ifStatement()
|
|
match(PRINT) -> printStatement()
|
|
match(WHILE) -> whileStatement()
|
|
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()
|
|
} |