Add constructors and initializers

main
Olivier Abrivard 1 year ago
parent b84650ee61
commit 2fec77db1f

@ -50,7 +50,7 @@ class Interpreter: ExprVisitor<Any?>, StmtVisitor<Unit>{
environment.define(stmt.name.lexeme, null) environment.define(stmt.name.lexeme, null)
val methods = stmt.methods.associate { method -> val methods = stmt.methods.associate { method ->
val function = LoxFunction(method, environment) val function = LoxFunction(method, environment, method.name.lexeme == "init")
method.name.lexeme to function method.name.lexeme to function
} }

@ -1,5 +1,8 @@
package fr.celticinfo.lox package fr.celticinfo.lox
/**
* The LoxCallable interface represents a callable entity in the Lox language.
*/
interface LoxCallable { interface LoxCallable {
fun call(interpreter: Interpreter, arguments: List<Any?>): Any? fun call(interpreter: Interpreter, arguments: List<Any?>): Any?
fun arity(): Int fun arity(): Int

@ -1,8 +1,13 @@
package fr.celticinfo.lox package fr.celticinfo.lox
/**
* The LoxClass class represents a class in the Lox language.
*/
class LoxClass(private val name: String, private val methods: Map<String, LoxFunction>) : LoxCallable { class LoxClass(private val name: String, private val methods: Map<String, LoxFunction>) : LoxCallable {
override fun call(interpreter: Interpreter, arguments: List<Any?>): Any? { override fun call(interpreter: Interpreter, arguments: List<Any?>): Any? {
val instance = LoxInstance(this) val instance = LoxInstance(this)
val initializer = findMethod("init")
initializer?.bind(instance)?.call(interpreter, arguments)
return instance return instance
} }
@ -10,7 +15,7 @@ class LoxClass(private val name: String, private val methods: Map<String, LoxFun
return methods[name] return methods[name]
} }
override fun arity() = 0 override fun arity() = findMethod("init")?.arity() ?: 0
override fun toString() = name override fun toString() = name
} }

@ -1,14 +1,18 @@
package fr.celticinfo.lox package fr.celticinfo.lox
/**
* The LoxFunction class represents a function in the Lox language.
*/
class LoxFunction( class LoxFunction(
private val declaration: Function, private val declaration: Function,
private val closure: Environment private val closure: Environment,
private val isInitializer: Boolean = false
) : LoxCallable { ) : LoxCallable {
fun bind(instance: LoxInstance): LoxFunction { fun bind(instance: LoxInstance): LoxFunction {
val environment = Environment(closure) val environment = Environment(closure)
environment.define("this", instance) environment.define("this", instance)
return LoxFunction(declaration, environment) return LoxFunction(declaration, environment, isInitializer)
} }
override fun call(interpreter: Interpreter, arguments: List<Any?>): Any? { override fun call(interpreter: Interpreter, arguments: List<Any?>): Any? {
@ -22,8 +26,16 @@ class LoxFunction(
interpreter.executeBlock(declaration.body, environment) interpreter.executeBlock(declaration.body, environment)
null null
} catch (returnValue: LoxReturn) { } catch (returnValue: LoxReturn) {
if (isInitializer) {
return closure.getAt(0, "this")
}
returnValue.value returnValue.value
} }
if (isInitializer) {
return closure.getAt(0, "this")
}
} }
override fun arity() = declaration.params.size override fun arity() = declaration.params.size

@ -30,7 +30,7 @@ class Resolver(private val interpreter: Interpreter) : ExprVisitor<Unit>, StmtVi
scopes.last()["this"] = true scopes.last()["this"] = true
for (method in stmt.methods) { for (method in stmt.methods) {
val declaration = FunctionType.METHOD val declaration = if (method.name.lexeme == "init") FunctionType.INITIALIZER else FunctionType.METHOD
resolveFunction(method, declaration) resolveFunction(method, declaration)
} }
endScope() endScope()
@ -63,7 +63,12 @@ class Resolver(private val interpreter: Interpreter) : ExprVisitor<Unit>, StmtVi
Lox.error(stmt.keyword, "Cannot return from top-level code.") Lox.error(stmt.keyword, "Cannot return from top-level code.")
} }
stmt.value?.let { resolve(it) } if (stmt.value != null) {
if (currentFunctionType == FunctionType.INITIALIZER) {
Lox.error(stmt.keyword, "Cannot return a value from an initializer.")
}
resolve(stmt.value)
}
} }
override fun visitVar(stmt: Var) { override fun visitVar(stmt: Var) {
@ -197,6 +202,7 @@ class Resolver(private val interpreter: Interpreter) : ExprVisitor<Unit>, StmtVi
enum class FunctionType { enum class FunctionType {
NONE, NONE,
FUNCTION, FUNCTION,
INITIALIZER,
METHOD METHOD
} }

@ -947,4 +947,46 @@ var a = "global";
System.setOut(standardOut) System.setOut(standardOut)
} }
} }
@Test
fun `valid code with constructor`() {
val standardOut = System.out
val outputStreamCaptor = ByteArrayOutputStream()
System.setOut(PrintStream(outputStreamCaptor))
try {
val code = """
class Cake {
init(flavor) {
this.flavor = flavor;
}
taste() {
var adjective = "delicious";
print "The " + this.flavor + " cake is " + adjective + "!";
}
}
var cake = Cake("German chocolate");
cake.taste();
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val statements = parser.parse()
assertEquals(3, statements.size)
val interpreter = Interpreter()
val resolver = Resolver(interpreter)
resolver.resolve(statements)
interpreter.interpret(statements)
val output = outputStreamCaptor.toString().trim()
assertEquals("The German chocolate cake is delicious!", output)
} finally {
System.setOut(standardOut)
}
}
} }

@ -336,4 +336,32 @@ class ParserTest {
assertTrue(statements[2] is Expression) assertTrue(statements[2] is Expression)
assertTrue(statements[3] is Expression) assertTrue(statements[3] is Expression)
} }
@Test
fun `valid code with constructor`() {
val code = """
class Cake {
init(flavor) {
this.flavor = flavor;
}
taste() {
var adjective = "delicious";
print "The " + this.flavor + " cake is " + adjective + "!";
}
}
var cake = Cake("German chocolate");
cake.taste();
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val statements = parser.parse()
assertEquals(3,statements.size)
assertTrue(statements[0] is ClassStmt)
assertTrue(statements[1] is Var)
assertTrue(statements[2] is Expression)
}
} }
Loading…
Cancel
Save