package fr.celticinfo.lox import fr.celticinfo.lox.TokenType.* class Interpreter: ExprVisitor, StmtVisitor{ var globals = Environment() private var environment = globals constructor() { globals.define("clock", object : LoxCallable { override fun arity(): Int { return 0 } override fun call(interpreter: Interpreter, arguments: List): Any? { return System.currentTimeMillis().toDouble() / 1000.0 } override fun toString(): String { return "" } }) } fun interpret(statements: List) { try { for (statement in statements) { execute(statement) } } catch (error: RuntimeError) { Lox.runtimeError(error) throw error } } private fun execute(stmt: Stmt?) { stmt?.accept(this) } override fun visitBlock(stmt: Block) { executeBlock(stmt.statements, Environment(environment)) } fun executeBlock(statements: List, environment: Environment) { val previous = this.environment try { this.environment = environment for (statement in statements) { execute(statement) } } finally { this.environment = previous } } private fun evaluate(expr: Expr?): Any? { return expr?.accept(this) } override fun visitExpression(stmt: Expression) { evaluate(stmt.expression) } override fun visitFunction(stmt: Function) { val function = LoxFunction(stmt, environment) environment.define(stmt.name.lexeme, function) } override fun visitIf(stmt: If) { if (isTruthy(evaluate(stmt.condition))) { execute(stmt.thenBranch) } else { stmt.elseBranch?.let { execute(it) } } } override fun visitWhile(stmt: While) { while (isTruthy(evaluate(stmt.condition))) { execute(stmt.body) } } override fun visitPrint(stmt: Print) { val value = evaluate(stmt.expression) println(stringify(value)) } override fun visitReturn(stmt: Return) { val value = stmt.value?.let { evaluate(it) } throw LoxReturn(value) } override fun visitVar(stmt: Var) { val value = evaluate(stmt.initializer) environment.define(stmt.name.lexeme, value) } override fun visitAssign(expr: Assign): Any? { val value = evaluate(expr.value) environment.assign(expr.name, value) return value } override fun visitBinary(expr: Binary): Any? { val left = evaluate(expr.left) val right = evaluate(expr.right) return when (expr.operator.type) { MINUS -> { checkNumberOperands(expr.operator, left, right) left as Double - right as Double } SLASH -> { checkNumberOperands(expr.operator, left, right) if (right == 0.0) { throw RuntimeError(expr.operator, "Division by zero") } left as Double / right as Double } STAR -> { checkNumberOperands(expr.operator, left, right) left as Double * right as Double } PLUS -> { if (left is Double && right is Double) { left + right } else if (left is String && right is String) { left + right } else { throw RuntimeError(expr.operator, "Operands must be two numbers or two strings") } } GREATER -> { checkNumberOperands(expr.operator, left, right) left as Double > right as Double } GREATER_EQUAL -> { checkNumberOperands(expr.operator, left, right) left as Double >= right as Double } LESS -> { checkNumberOperands(expr.operator, left, right) (left as Double) < right as Double } LESS_EQUAL -> { checkNumberOperands(expr.operator, left, right) left as Double <= right as Double } BANG_EQUAL -> return !isEqual(left, right) EQUAL_EQUAL -> return isEqual(left, right) else -> null } } override fun visitCall(expr: Call): Any? { val callee = evaluate(expr.callee) val arguments = expr.arguments.map { evaluate(it) } if (callee !is LoxCallable) { throw RuntimeError(expr.paren, "Can only call functions and classes") } if (arguments.size != callee.arity()) { throw RuntimeError(expr.paren, "Expected ${callee.arity()} arguments but got ${arguments.size}") } return callee.call(this, arguments) } override fun visitGrouping(expr: Grouping): Any? { return evaluate(expr.expression) } override fun visitLiteral(expr: Literal): Any? { return expr.value } override fun visitLogical(expr: Logical): Any? { val left = evaluate(expr.left) if (expr.operator.type == OR) { if (isTruthy(left)) return left } else { if (!isTruthy(left)) return left } return evaluate(expr.right) } override fun visitUnary(expr: Unary): Any? { val right = evaluate(expr.right) return when (expr.operator.type) { MINUS -> { checkNumberOperand(expr.operator, right) -(right as Double) } BANG -> !isTruthy(right) else -> null } } override fun visitVariable(expr: Variable): Any? { return environment.get(expr.name) } private fun isTruthy(obj: Any?): Boolean { return when (obj) { null -> false is Boolean -> obj else -> true } } private fun isEqual(left: Any?, right: Any?): Boolean { return if (left == null && right == null) { true } else if (left == null) { false } else { left == right } } private fun checkNumberOperand(operator: Token, right: Any?) { if (right !is Double) { throw RuntimeError(operator, "Operand must be a number") } } private fun checkNumberOperands(operator: Token, left: Any?, right: Any?) { if (left !is Double || right !is Double) { 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() } } }