mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-11 22:28:45 +01:00
Start getting superclass constructors working.
This also means the metaclass inheritance hierarchy parallels the regular inheritance chain so that the subclass can find the superclass constructor.
This commit is contained in:
@ -1342,7 +1342,7 @@ static void super_(Compiler* compiler, bool allowAssignment)
|
||||
emit(compiler, CODE_LOAD_LOCAL);
|
||||
emit(compiler, 0);
|
||||
|
||||
// TODO: Super operator and constructor calls.
|
||||
// TODO: Super operator calls.
|
||||
consume(compiler, TOKEN_DOT, "Expect '.' after 'super'.");
|
||||
|
||||
// Compile the superclass call.
|
||||
@ -1596,11 +1596,40 @@ void method(Compiler* compiler, Code instruction, bool isConstructor,
|
||||
|
||||
int symbol = ensureSymbol(&compiler->parser->vm->methods, name, length);
|
||||
|
||||
consume(compiler, TOKEN_LEFT_BRACE, "Expect '{' to begin method body.");
|
||||
if (isConstructor)
|
||||
{
|
||||
// See if there is a superclass constructor call, which comes before the
|
||||
// opening '{'.
|
||||
if (match(compiler, TOKEN_SUPER))
|
||||
{
|
||||
// Push a copy of the class onto the stack so it can be the receiver for
|
||||
// the superclass constructor call.
|
||||
emit(&methodCompiler, CODE_LOAD_LOCAL);
|
||||
emit(&methodCompiler, 0);
|
||||
|
||||
// If this is a constructor, the first thing is does is create the new
|
||||
// instance.
|
||||
if (isConstructor) emit(&methodCompiler, CODE_NEW);
|
||||
consume(compiler, TOKEN_DOT, "Expect '.' after 'super'.");
|
||||
|
||||
// Compile the superclass call.
|
||||
// TODO(bob): What if there turns out to not be a superclass constructor
|
||||
// that matches this?
|
||||
namedCall(&methodCompiler, false, CODE_SUPER_0);
|
||||
|
||||
// The superclass call will return the new instance, so store it back
|
||||
// into slot 0 to replace the receiver with the new object.
|
||||
emit(&methodCompiler, CODE_STORE_LOCAL);
|
||||
emit(&methodCompiler, 0);
|
||||
|
||||
// Then remove it from the stack.
|
||||
emit(&methodCompiler, CODE_POP);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, just create the new instance.
|
||||
emit(&methodCompiler, CODE_NEW);
|
||||
}
|
||||
}
|
||||
|
||||
consume(compiler, TOKEN_LEFT_BRACE, "Expect '{' to begin method body.");
|
||||
|
||||
finishBlock(&methodCompiler);
|
||||
// TODO: Single-expression methods that implicitly return the result.
|
||||
|
||||
@ -54,7 +54,20 @@ ObjClass* wrenNewClass(WrenVM* vm, ObjClass* superclass, int numFields)
|
||||
// Make the metaclass.
|
||||
// TODO: What is the metaclass's metaclass?
|
||||
// TODO: Handle static fields.
|
||||
ObjClass* metaclass = newClass(vm, NULL, vm->classClass, 0);
|
||||
// The metaclass inheritance chain mirrors the class's inheritance chain
|
||||
// except that when the latter bottoms out at "Object", the metaclass one
|
||||
// bottoms out at "Class".
|
||||
ObjClass* metaclassSuperclass;
|
||||
if (superclass == vm->objectClass)
|
||||
{
|
||||
metaclassSuperclass = vm->classClass;
|
||||
}
|
||||
else
|
||||
{
|
||||
metaclassSuperclass = superclass->metaclass;
|
||||
}
|
||||
|
||||
ObjClass* metaclass = newClass(vm, NULL, metaclassSuperclass, 0);
|
||||
|
||||
// Make sure it isn't collected when we allocate the metaclass.
|
||||
PinnedObj pinned;
|
||||
|
||||
25
test/constructor/superclass.wren
Normal file
25
test/constructor/superclass.wren
Normal file
@ -0,0 +1,25 @@
|
||||
class A {
|
||||
this new(arg) {
|
||||
io.write("A.new " + arg)
|
||||
}
|
||||
}
|
||||
|
||||
class B is A {
|
||||
this otherName(arg1, arg2) super.new(arg2) {
|
||||
io.write("B.otherName " + arg1)
|
||||
}
|
||||
}
|
||||
|
||||
class C is B {
|
||||
this create super.otherName("one", "two") {
|
||||
io.write("C.create")
|
||||
}
|
||||
}
|
||||
|
||||
var c = C.create
|
||||
// expect: A.new two
|
||||
// expect: B.otherName one
|
||||
// expect: C.create
|
||||
io.write(c is A) // expect: true
|
||||
io.write(c is B) // expect: true
|
||||
io.write(c is C) // expect: true
|
||||
@ -2,12 +2,14 @@ class Foo {
|
||||
methodOnFoo { io.write("foo") }
|
||||
method(a) { io.write("foo") }
|
||||
method(a, b, c) { io.write("foo") }
|
||||
override { io.write("foo") }
|
||||
}
|
||||
|
||||
class Bar is Foo {
|
||||
methodOnBar { io.write("bar") }
|
||||
method(a, b) { io.write("bar") }
|
||||
method(a, b, c, d) { io.write("bar") }
|
||||
override { io.write("bar") }
|
||||
}
|
||||
|
||||
var bar = Bar.new
|
||||
@ -19,9 +21,6 @@ bar.method(1) // expect: foo
|
||||
bar.method(1, 2) // expect: bar
|
||||
bar.method(1, 2, 3) // expect: foo
|
||||
bar.method(1, 2, 3, 4) // expect: bar
|
||||
bar.override // expect: bar
|
||||
|
||||
// TODO: Overriding.
|
||||
// TODO: Private fields.
|
||||
// TODO: Super (or inner) calls.
|
||||
// TODO: Grammar for what expressions can follow "is".
|
||||
// TODO: Prevent extending built-in types.
|
||||
|
||||
23
test/inheritance/inherit_static_methods.wren
Normal file
23
test/inheritance/inherit_static_methods.wren
Normal file
@ -0,0 +1,23 @@
|
||||
class Foo {
|
||||
static methodOnFoo { io.write("foo") }
|
||||
static method(a) { io.write("foo") }
|
||||
static method(a, b, c) { io.write("foo") }
|
||||
static override { io.write("foo") }
|
||||
}
|
||||
|
||||
class Bar is Foo {
|
||||
static methodOnBar { io.write("bar") }
|
||||
static method(a, b) { io.write("bar") }
|
||||
static method(a, b, c, d) { io.write("bar") }
|
||||
static override { io.write("bar") }
|
||||
}
|
||||
|
||||
Bar.methodOnFoo // expect: foo
|
||||
Bar.methodOnBar // expect: bar
|
||||
|
||||
// Methods with different arity do not shadow each other.
|
||||
Bar.method(1) // expect: foo
|
||||
Bar.method(1, 2) // expect: bar
|
||||
Bar.method(1, 2, 3) // expect: foo
|
||||
Bar.method(1, 2, 3, 4) // expect: bar
|
||||
Bar.override // expect: bar
|
||||
Reference in New Issue
Block a user