diff --git a/src/main/ExprGenerator.ws.kts b/src/main/ExprGenerator.ws.kts index 44a92f7..795bab2 100644 --- a/src/main/ExprGenerator.ws.kts +++ b/src/main/ExprGenerator.ws.kts @@ -42,6 +42,7 @@ fun defineAst(baseName: String, types: List) { val exprTypes = listOf( "Assign : Token name, Expr value", "Binary : Expr left, Token operator, Expr right", + "Call : Expr callee, Token paren, List arguments", "Grouping : Expr expression", "Literal : Any? value", "Logical : Expr left, Token operator, Expr right", diff --git a/src/main/fr/celticinfo/lox/Expr.kt b/src/main/fr/celticinfo/lox/Expr.kt index 3f75a65..5212681 100644 --- a/src/main/fr/celticinfo/lox/Expr.kt +++ b/src/main/fr/celticinfo/lox/Expr.kt @@ -6,6 +6,7 @@ package fr.celticinfo.lox interface ExprVisitor { fun visitAssign(expr: Assign): R fun visitBinary(expr: Binary): R + fun visitCall(expr: Call): R fun visitGrouping(expr: Grouping): R fun visitLiteral(expr: Literal): R fun visitLogical(expr: Logical): R @@ -37,6 +38,16 @@ data class Binary( } } +data class Call( + val callee: Expr, + val paren: Token, + val arguments: List +) : Expr() { + override fun accept(visitor: ExprVisitor): R { + return visitor.visitCall(this) + } +} + data class Grouping( val expression: Expr ) : Expr() { diff --git a/src/main/fr/celticinfo/lox/Interpreter.kt b/src/main/fr/celticinfo/lox/Interpreter.kt index 5fbb400..c85efc7 100644 --- a/src/main/fr/celticinfo/lox/Interpreter.kt +++ b/src/main/fr/celticinfo/lox/Interpreter.kt @@ -125,6 +125,22 @@ class Interpreter: ExprVisitor, StmtVisitor{ } } + 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) } diff --git a/src/main/fr/celticinfo/lox/LoxCallable.kt b/src/main/fr/celticinfo/lox/LoxCallable.kt new file mode 100644 index 0000000..9fbea39 --- /dev/null +++ b/src/main/fr/celticinfo/lox/LoxCallable.kt @@ -0,0 +1,6 @@ +package fr.celticinfo.lox + +interface LoxCallable { + fun call(interpreter: Interpreter, arguments: List): Any? + fun arity(): Int +} \ No newline at end of file diff --git a/src/main/fr/celticinfo/lox/Parser.kt b/src/main/fr/celticinfo/lox/Parser.kt index fd5e1aa..7774195 100644 --- a/src/main/fr/celticinfo/lox/Parser.kt +++ b/src/main/fr/celticinfo/lox/Parser.kt @@ -238,7 +238,7 @@ class Parser(private val tokens: List) { return Unary(operator, right) } - return primary() + return call() } private fun match(vararg types: TokenType): Boolean { @@ -251,6 +251,36 @@ class Parser(private val tokens: List) { return false } + private fun call(): Expr { + var expr = primary() + + while (true) { + if (match(LEFT_PAREN)) { + expr = finishCall(expr) + } else { + break + } + } + + return expr + } + + private fun finishCall(callee: Expr): Expr { + val arguments: MutableList = ArrayList() + if (!check(RIGHT_PAREN)) { + do { + if (arguments.size >= 255) { + error(peek(), "Cannot have more than 255 arguments.") + } + arguments.add(expression()) + } while (match(COMMA)) + } + + val paren = consume(RIGHT_PAREN, "Expect ')' after arguments.") + + return Call(callee, paren, arguments) + } + private fun primary(): Expr { when { match(FALSE) -> return Literal(false)