Created Parser class

main
oabrivard 2 years ago
parent 72b2edcf07
commit 8aa86a34ce

@ -38,9 +38,13 @@ class Lox {
private fun run(source: String) { private fun run(source: String) {
val scanner = Scanner(source) val scanner = Scanner(source)
val tokens = scanner.scanTokens() val tokens = scanner.scanTokens()
for (token in tokens) { val parser = Parser(tokens)
println(token) val expression = parser.parse()
}
// Stop if there was a syntax error.
if (hadError) return
println(AstPrinter().print(expression!!))
} }
companion object { companion object {
@ -48,6 +52,14 @@ class Lox {
report(line, "", s) report(line, "", s)
} }
fun error(token: Token, message: String) {
if (token.type == TokenType.EOF) {
report(token.line, " at end", message)
} else {
report(token.line, " at '${token.lexeme}'", message)
}
}
private fun report(line: Int, where: String, message: String) { private fun report(line: Int, where: String, message: String) {
System.err.println("[line $line] Error$where: $message") System.err.println("[line $line] Error$where: $message")
} }

@ -0,0 +1,151 @@
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(): Expr? {
return try {
expression()
} catch (error: ParseError) {
null
}
}
private fun expression(): Expr {
return equality()
}
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(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()
}

@ -0,0 +1,32 @@
package fr.celticinfo.lox
import fr.celticinfo.loxext.RpnPrinter
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.*
class ParserTest {
@Test
fun `validate parser`() {
val code = """
1 + 2 * 3 - 4 / 5
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val expr = parser.parse()
assertNotNull(expr)
assertEquals("1.0 2.0 3.0 * + 4.0 5.0 / -", RpnPrinter().print(expr!!))
}
@Test
fun `invalid code generates parsing error`() {
val code = """
1 + (2
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val expr = parser.parse()
assertNull(expr)
}
}
Loading…
Cancel
Save