Added simple Expr visitor

main
oabrivard 2 years ago
parent 076e5fce4e
commit b52de314f0

@ -1,3 +1,5 @@
import java.util.*
val types = listOf(
"Binary : Expr left, Token operator, Expr right",
"Grouping : Expr expression",
@ -6,10 +8,23 @@ val types = listOf(
)
println("package fr.celticinfo.lox")
println()
println("sealed class Expr {")
println("interface ExprVisitor<R> {")
for (type in types) {
val parts = type.split(":")
val name = parts[0].trim()
println(" fun visit$name(${name.lowercase(Locale.getDefault())}: $name): R")
}
println("}")
println()
println("""/**
* The Expr class represents the different types of expressions that can be parsed by the Parser.
*/""".trimIndent())
println("sealed class Expr {")
println(" abstract fun <R> accept(visitor: ExprVisitor<R>): R")
println("}")
for (type in types) {
val parts = type.split(":")
val name = parts[0].trim()
@ -23,5 +38,9 @@ for (type in types) {
val sep = if (field == fields.last()) "" else ","
println(" val $name: $type$sep")
}
println(") : Expr()")
println(") : Expr() {")
println(" override fun <R> accept(visitor: ExprVisitor<R>): R {")
println(" return visitor.visit$name(this)")
println(" }")
println("}")
}

@ -0,0 +1,34 @@
package fr.celticinfo.lox
class AstPrinter : ExprVisitor<String> {
fun print(expr: Expr): String {
return expr.accept(this)
}
override fun visitBinary(binary: Binary): String {
return parenthesize(binary.operator.lexeme, binary.left, binary.right)
}
override fun visitGrouping(grouping: Grouping): String {
return parenthesize("group", grouping.expression)
}
override fun visitLiteral(literal: Literal): String {
return literal.value?.toString() ?: "nil"
}
override fun visitUnary(unary: Unary): String {
return parenthesize(unary.operator.lexeme, unary.right)
}
private fun parenthesize(name: String, vararg exprs: Expr): String {
val builder = StringBuilder()
builder.append("(").append(name)
for (expr in exprs) {
builder.append(" ")
builder.append(expr.accept(this))
}
builder.append(")")
return builder.toString()
}
}

@ -1,26 +1,48 @@
package fr.celticinfo.lox
interface ExprVisitor<R> {
fun visitBinary(binary: Binary): R
fun visitGrouping(grouping: Grouping): R
fun visitLiteral(literal: Literal): R
fun visitUnary(unary: Unary): R
}
/**
* The Expr class represents the different types of expressions that can be parsed by the Parser.
*/
sealed class Expr {
abstract fun <R> accept(visitor: ExprVisitor<R>): R
}
data class Binary(
val left: Expr,
val operator: Token,
val right: Expr
) : Expr()
val left: Expr, val operator: Token, val right: Expr
) : Expr() {
override fun <R> accept(visitor: ExprVisitor<R>): R {
return visitor.visitBinary(this)
}
}
data class Grouping(
val expression: Expr
) : Expr()
) : Expr() {
override fun <R> accept(visitor: ExprVisitor<R>): R {
return visitor.visitGrouping(this)
}
}
data class Literal(
val value: Any
) : Expr()
) : Expr() {
override fun <R> accept(visitor: ExprVisitor<R>): R {
return visitor.visitLiteral(this)
}
}
data class Unary(
val operator: Token,
val right: Expr
) : Expr()
val operator: Token, val right: Expr
) : Expr() {
override fun <R> accept(visitor: ExprVisitor<R>): R {
return visitor.visitUnary(this)
}
}

@ -0,0 +1,35 @@
package fr.celticinfo.loxext
import fr.celticinfo.lox.*
class RpnPrinter : ExprVisitor<String> {
fun print(expr: Expr): String {
return expr.accept(this)
}
override fun visitBinary(binary: Binary): String {
return stack(binary.operator.lexeme, binary.left, binary.right)
}
override fun visitGrouping(grouping: Grouping): String {
return stack("", grouping.expression)
}
override fun visitLiteral(literal: Literal): String {
return literal.value?.toString() ?: "nil"
}
override fun visitUnary(unary: Unary): String {
return stack(unary.operator.lexeme, unary.right)
}
private fun stack(name: String, vararg exprs: Expr): String {
val builder = StringBuilder()
for (expr in exprs) {
builder.append(expr.accept(this))
if (name.isNotEmpty()) builder.append(" ")
}
builder.append(name)
return builder.toString()
}
}

@ -0,0 +1,50 @@
package fr.celticinfo.lox
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
class AstPrinterTest {
private val astPrinter = AstPrinter()
@Test
fun print() {
val expression: Expr = Binary(
Unary(
Token(TokenType.MINUS, "-", null, 1), Literal(123)
), Token(TokenType.STAR, "*", null, 1), Grouping(
Literal(45.67)
)
)
assertEquals("(* (- 123) (group 45.67))", astPrinter.print(expression))
}
@Test
fun visitBinary() {
val expression: Expr = Binary(
Literal(1), Token(TokenType.PLUS, "+", null, 1), Literal(2)
)
assertEquals("(+ 1 2)", astPrinter.print(expression))
}
@Test
fun visitGrouping() {
val expression: Expr = Grouping(
Literal(123)
)
assertEquals("(group 123)", astPrinter.print(expression))
}
@Test
fun visitLiteral() {
val expression: Expr = Literal(123)
assertEquals("123", astPrinter.print(expression))
}
@Test
fun visitUnary() {
val expression: Expr = Unary(
Token(TokenType.MINUS, "-", null, 1), Literal(123)
)
assertEquals("(- 123)", astPrinter.print(expression))
}
}

@ -0,0 +1,70 @@
package fr.celticinfo.loxext
import org.junit.jupiter.api.Test
import kotlin.test.assertEquals
import fr.celticinfo.lox.*
class RpnPrinterTest {
private val rpnPrinter = RpnPrinter()
@Test
fun print() {
val expression: Expr = Binary(
Unary(
Token(TokenType.MINUS, "-", null, 1), Literal(123)
), Token(TokenType.STAR, "*", null, 1), Grouping(
Literal(45.67)
)
)
assertEquals("123 - 45.67 *", rpnPrinter.print(expression))
}
@Test
fun testComplexExpr() {
val expression: Expr = Binary(
Grouping(
Binary(
Literal(1), Token(TokenType.PLUS, "+", null, 1), Literal(2)
)
),
Token(TokenType.STAR, "*", null, 1),
Grouping(
Binary(
Literal(4), Token(TokenType.MINUS, "-", null, 1), Literal(3)
)
)
)
assertEquals("1 2 + 4 3 - *", rpnPrinter.print(expression))
}
@Test
fun visitBinary() {
val expression: Expr = Binary(
Literal(1), Token(TokenType.PLUS, "+", null, 1), Literal(2)
)
assertEquals("1 2 +", rpnPrinter.print(expression))
}
@Test
fun visitGrouping() {
val expression: Expr = Grouping(
Literal(123)
)
assertEquals("123", rpnPrinter.print(expression))
}
@Test
fun visitLiteral() {
val expression: Expr = Literal(123)
assertEquals("123", rpnPrinter.print(expression))
}
@Test
fun visitUnary() {
val expression: Expr = Unary(
Token(TokenType.MINUS, "-", null, 1), Literal(123)
)
assertEquals("123 -", rpnPrinter.print(expression))
}
}
Loading…
Cancel
Save