Completed Expression evaluation

main
oabrivard 2 years ago
parent 35974645a4
commit 78c65bbf39

@ -3,6 +3,22 @@ package fr.celticinfo.lox
import fr.celticinfo.lox.TokenType.*
class Interpreter: ExprVisitor<Any?>{
fun interpret(expr: Expr): Any? {
return try {
val value = evaluate(expr)
println(stringify(value))
value
} catch (error: RuntimeError) {
Lox.runtimeError(error)
null
}
}
private fun evaluate(expr: Expr): Any? {
return expr.accept(this)
}
override fun visitBinary(binary: Binary): Any? {
val left = evaluate(binary.left)
val right = evaluate(binary.right)
@ -83,10 +99,6 @@ class Interpreter: ExprVisitor<Any?>{
}
}
private fun evaluate(expr: Expr): Any? {
return expr.accept(this)
}
private fun isEqual(left: Any?, right: Any?): Boolean {
return if (left == null && right == null) {
true
@ -108,4 +120,18 @@ class Interpreter: ExprVisitor<Any?>{
throw RuntimeError(operator, "Operands must be numbers")
}
}
private fun stringify(obj: Any?): String {
return when (obj) {
null -> "nil"
is Double -> {
var text = obj.toString()
if (text.endsWith(".0")) {
text = text.substring(0, text.length - 2)
}
text
}
else -> obj.toString()
}
}
}

@ -3,11 +3,9 @@ package fr.celticinfo.lox
import kotlin.system.exitProcess
fun main(args: Array<String>) {
val lox = Lox()
when (args.size) {
0 -> lox.runPrompt()
1 -> lox.runFile(args.first())
0 -> Lox.runPrompt()
1 -> Lox.runFile(args.first())
else -> {println("Usage: jlox [script]") ; exitProcess(64)}
}
}
@ -15,8 +13,10 @@ fun main(args: Array<String>) {
/**
* Lox is the main class that runs the Lox interpreter.
*/
class Lox {
object Lox {
private var hadError = false
private var hadRuntimeError = false
private val interpreter = Interpreter()
fun runPrompt() {
while (true) {
@ -33,6 +33,7 @@ class Lox {
run(String(bytes))
// Indicate an error in the exit code.
if (hadError) exitProcess(65)
if (hadRuntimeError) exitProcess(70)
}
private fun run(source: String) {
@ -44,10 +45,9 @@ class Lox {
// Stop if there was a syntax error.
if (hadError) return
println(AstPrinter().print(expression!!))
interpreter.interpret(expression!!)
}
companion object {
fun error(line: Int, s: String) {
report(line, "", s)
}
@ -60,8 +60,13 @@ class Lox {
}
}
fun runtimeError(error: RuntimeError) {
System.err.println("${error.message}\n[line ${error.token.line}]")
hadRuntimeError = true
}
private fun report(line: Int, where: String, message: String) {
System.err.println("[line $line] Error$where: $message")
}
hadError = true
}
}

@ -11,7 +11,13 @@ class Parser(private val tokens: List<Token>) {
fun parse(): Expr? {
return try {
expression()
val result = expression()
if (!isAtEnd()) {
throw error(peek(), "Expect end of expression.")
}
result
} catch (error: ParseError) {
null
}

@ -0,0 +1,46 @@
package fr.celticinfo.lox
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.Assertions.*
class InterpreterTest {
@Test
fun `validate interpreter`() {
val code = """
(1 + 2 * 3 - 5) / 2
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val expr = parser.parse()
val value = Interpreter().interpret(expr!!)
assertEquals(1.0, value)
}
@Test
fun `Division by zero should raise error`() {
val code = """
1 / 0
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val expr = parser.parse()
val value = Interpreter().interpret(expr!!)
assertNull(value)
}
@Test
fun `Invalid type raise error`() {
val code = """
1 + false
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val expr = parser.parse()
val value = Interpreter().interpret(expr!!)
assertNull(value)
}
}

@ -29,4 +29,16 @@ class ParserTest {
val expr = parser.parse()
assertNull(expr)
}
@Test
fun `invalid expression 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