forked from Mirror/wren
Start sketching out inheritance.
This commit is contained in:
@ -1201,8 +1201,17 @@ void definition(Compiler* compiler)
|
||||
// Create a variable to store the class in.
|
||||
int symbol = declareVariable(compiler);
|
||||
|
||||
// Create the empty class.
|
||||
emit(compiler, CODE_CLASS);
|
||||
// Load the superclass (if there is one).
|
||||
if (match(compiler, TOKEN_IS))
|
||||
{
|
||||
parsePrecedence(compiler, 0, PREC_CALL);
|
||||
emit(compiler, CODE_SUBCLASS);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create the empty class.
|
||||
emit(compiler, CODE_CLASS);
|
||||
}
|
||||
|
||||
// Store it in its name.
|
||||
defineVariable(compiler, symbol);
|
||||
|
||||
@ -228,6 +228,7 @@ DEF_PRIMITIVE(io_write)
|
||||
}
|
||||
|
||||
static const char* CORE_LIB =
|
||||
"class Object {}\n"
|
||||
"class Bool {}\n"
|
||||
"class Class {}\n"
|
||||
"class Function {}\n"
|
||||
@ -273,6 +274,8 @@ void loadCore(VM* vm)
|
||||
PRIMITIVE(vm->numClass, "== ", num_eqeq);
|
||||
PRIMITIVE(vm->numClass, "!= ", num_bangeq);
|
||||
|
||||
vm->objectClass = AS_CLASS(findGlobal(vm, "Object"));
|
||||
|
||||
vm->stringClass = AS_CLASS(findGlobal(vm, "String"));
|
||||
PRIMITIVE(vm->stringClass, "contains ", string_contains);
|
||||
PRIMITIVE(vm->stringClass, "count", string_count);
|
||||
@ -284,7 +287,7 @@ void loadCore(VM* vm)
|
||||
ObjClass* ioClass = AS_CLASS(findGlobal(vm, "IO"));
|
||||
PRIMITIVE(ioClass, "write ", io_write);
|
||||
|
||||
ObjClass* unsupportedClass = newClass(vm);
|
||||
ObjClass* unsupportedClass = newClass(vm, vm->objectClass);
|
||||
|
||||
// TODO(bob): Make this a distinct object type.
|
||||
vm->unsupported = (Value)newInstance(vm, unsupportedClass);
|
||||
|
||||
59
src/vm.c
59
src/vm.c
@ -265,11 +265,13 @@ Value newBool(VM* vm, int value)
|
||||
return obj;
|
||||
}
|
||||
|
||||
static ObjClass* newSingleClass(VM* vm)
|
||||
static ObjClass* newSingleClass(VM* vm, ObjClass* metaclass,
|
||||
ObjClass* superclass)
|
||||
{
|
||||
ObjClass* obj = allocate(vm, sizeof(ObjClass));
|
||||
initObj(vm, &obj->obj, OBJ_CLASS);
|
||||
obj->metaclass = NULL;
|
||||
obj->metaclass = metaclass;
|
||||
obj->superclass = superclass;
|
||||
|
||||
for (int i = 0; i < MAX_SYMBOLS; i++)
|
||||
{
|
||||
@ -279,18 +281,29 @@ static ObjClass* newSingleClass(VM* vm)
|
||||
return obj;
|
||||
}
|
||||
|
||||
ObjClass* newClass(VM* vm)
|
||||
ObjClass* newClass(VM* vm, ObjClass* superclass)
|
||||
{
|
||||
ObjClass* classObj = newSingleClass(vm);
|
||||
// Make the metaclass.
|
||||
// TODO(bob): What is the metaclass's metaclass and superclass?
|
||||
ObjClass* metaclass = newSingleClass(vm, NULL, NULL);
|
||||
|
||||
// Make sure this isn't collected when we allocate the metaclass.
|
||||
pinObj(vm, (Value)classObj);
|
||||
// Make sure it isn't collected when we allocate the metaclass.
|
||||
pinObj(vm, (Value)metaclass);
|
||||
|
||||
// Make its metaclass.
|
||||
// TODO(bob): What is the metaclass's metaclass?
|
||||
classObj->metaclass = newSingleClass(vm);
|
||||
ObjClass* classObj = newSingleClass(vm, metaclass, superclass);
|
||||
|
||||
unpinObj(vm, (Value)classObj);
|
||||
unpinObj(vm, (Value)metaclass);
|
||||
|
||||
// Inherit methods from its superclass (unless it's Object, which has none).
|
||||
// TODO(bob): If we want BETA-style inheritance, we'll need to do this after
|
||||
// the subclass has defined its methods.
|
||||
if (superclass != NULL)
|
||||
{
|
||||
for (int i = 0; i < MAX_SYMBOLS; i++)
|
||||
{
|
||||
classObj->methods[i] = superclass->methods[i];
|
||||
}
|
||||
}
|
||||
|
||||
return classObj;
|
||||
}
|
||||
@ -461,6 +474,10 @@ void dumpCode(VM* vm, ObjFn* fn)
|
||||
case CODE_CLASS:
|
||||
printf("CLASS\n");
|
||||
break;
|
||||
|
||||
case CODE_SUBCLASS:
|
||||
printf("SUBCLASS\n");
|
||||
break;
|
||||
|
||||
case CODE_METACLASS:
|
||||
printf("METACLASS\n");
|
||||
@ -608,7 +625,8 @@ Value interpret(VM* vm, ObjFn* fn)
|
||||
{
|
||||
CallFrame* frame = &fiber->frames[fiber->numFrames - 1];
|
||||
|
||||
switch (frame->fn->bytecode[frame->ip++])
|
||||
Code instruction = frame->fn->bytecode[frame->ip++];
|
||||
switch (instruction)
|
||||
{
|
||||
case CODE_CONSTANT:
|
||||
PUSH(frame->fn->constants[READ_ARG()]);
|
||||
@ -619,8 +637,23 @@ Value interpret(VM* vm, ObjFn* fn)
|
||||
case CODE_TRUE: PUSH(newBool(vm, 1)); break;
|
||||
|
||||
case CODE_CLASS:
|
||||
case CODE_SUBCLASS:
|
||||
{
|
||||
ObjClass* classObj = newClass(vm);
|
||||
int isSubclass = instruction == CODE_SUBCLASS;
|
||||
|
||||
ObjClass* superclass;
|
||||
if (isSubclass)
|
||||
{
|
||||
// TODO(bob): Handle the superclass not being a class object!
|
||||
superclass = AS_CLASS(POP());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Implicit Object superclass.
|
||||
superclass = vm->objectClass;
|
||||
}
|
||||
|
||||
ObjClass* classObj = newClass(vm, superclass);
|
||||
|
||||
// Define a "new" method on the metaclass.
|
||||
// TODO(bob): Can this be inherited?
|
||||
@ -696,7 +729,7 @@ Value interpret(VM* vm, ObjFn* fn)
|
||||
case CODE_CALL_10:
|
||||
{
|
||||
// Add one for the implicit receiver argument.
|
||||
int numArgs = frame->fn->bytecode[frame->ip - 1] - CODE_CALL_0 + 1;
|
||||
int numArgs = instruction - CODE_CALL_0 + 1;
|
||||
int symbol = READ_ARG();
|
||||
|
||||
Value receiver = fiber->stack[fiber->stackSize - numArgs];
|
||||
|
||||
7
src/vm.h
7
src/vm.h
@ -87,6 +87,7 @@ typedef struct sObjClass
|
||||
{
|
||||
Obj obj;
|
||||
struct sObjClass* metaclass;
|
||||
struct sObjClass* superclass;
|
||||
// TODO(bob): Hack. Probably don't want to use this much space.
|
||||
Method methods[MAX_SYMBOLS];
|
||||
} ObjClass;
|
||||
@ -127,6 +128,9 @@ typedef enum
|
||||
// Define a new empty class and push it.
|
||||
CODE_CLASS,
|
||||
|
||||
// Pop a superclass off the stack, then push a new class that extends it.
|
||||
CODE_SUBCLASS,
|
||||
|
||||
// Push the metaclass of the class on the top of the stack. Does not discard
|
||||
// the class.
|
||||
CODE_METACLASS,
|
||||
@ -196,6 +200,7 @@ struct sVM
|
||||
ObjClass* fnClass;
|
||||
ObjClass* nullClass;
|
||||
ObjClass* numClass;
|
||||
ObjClass* objectClass;
|
||||
ObjClass* stringClass;
|
||||
|
||||
// The singleton values.
|
||||
@ -264,7 +269,7 @@ Value newBool(VM* vm, int value);
|
||||
ObjFn* newFunction(VM* vm);
|
||||
|
||||
// Creates a new class object.
|
||||
ObjClass* newClass(VM* vm);
|
||||
ObjClass* newClass(VM* vm, ObjClass* superclass);
|
||||
|
||||
// Creates a new instance of the given [classObj].
|
||||
ObjInstance* newInstance(VM* vm, ObjClass* classObj);
|
||||
|
||||
26
test/class_inheritance.wren
Normal file
26
test/class_inheritance.wren
Normal file
@ -0,0 +1,26 @@
|
||||
class Foo {
|
||||
methodOnFoo { io.write("foo") }
|
||||
method(a) { io.write("foo") }
|
||||
method(a, b, c) { 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") }
|
||||
}
|
||||
|
||||
var bar = Bar.new
|
||||
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
|
||||
|
||||
// TODO(bob): Overriding (or BETA-style refining?).
|
||||
// TODO(bob): Private fields.
|
||||
// TODO(bob): Super (or inner) calls.
|
||||
// TODO(bob): Grammar for what expressions can follow "is".
|
||||
Reference in New Issue
Block a user