From 32f8c412c79b78f7bea69ea6c3b7f96b228cce69 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Thu, 24 Oct 2013 21:32:17 -0700 Subject: [PATCH] Method calls. --- example/hello.wren | 11 ++--- src/compiler.c | 29 +++++++++---- src/vm.c | 101 +++++++++++++++++++++++++++++++++++++++++---- src/vm.h | 21 +++++++++- 4 files changed, 139 insertions(+), 23 deletions(-) diff --git a/example/hello.wren b/example/hello.wren index 79d3cee8..6b7095a9 100644 --- a/example/hello.wren +++ b/example/hello.wren @@ -1,6 +1,7 @@ -var a = { - var b = 234 - 3 - b +class Foo { + bar { + 123 + } } -a.call +var a = Foo.new +a.bar diff --git a/src/compiler.c b/src/compiler.c index 2bb2d138..3be31775 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -97,6 +97,7 @@ typedef struct sCompiler static ObjBlock* compileBlock(Parser* parser, Compiler* parent, TokenType endToken); +static int addConstant(Compiler* compiler, Value constant); // Grammar: static void statement(Compiler* compiler); @@ -185,6 +186,12 @@ ObjBlock* compileBlock(Parser* parser, Compiler* parent, TokenType endToken) return parser->hasError ? NULL : compiler.block; } +int addConstant(Compiler* compiler, Value constant) +{ + compiler->block->constants[compiler->block->numConstants++] = constant; + return compiler->block->numConstants - 1; +} + void statement(Compiler* compiler) { if (match(compiler, TOKEN_CLASS)) @@ -215,13 +222,20 @@ void statement(Compiler* compiler) { // Method name. consume(compiler, TOKEN_NAME); - //int symbol = internSymbol(compiler); + int symbol = internSymbol(compiler); consume(compiler, TOKEN_LEFT_BRACE); - // TODO(bob): Parse body. - consume(compiler, TOKEN_RIGHT_BRACE); - + ObjBlock* method = compileBlock(compiler->parser, compiler, + TOKEN_RIGHT_BRACE); consume(compiler, TOKEN_LINE); + + // Add the block to the constant table. + int constant = addConstant(compiler, (Value)method); + + // Compile the code to define the method it. + emit(compiler, CODE_METHOD); + emit(compiler, symbol); + emit(compiler, constant); } return; @@ -335,14 +349,11 @@ void number(Compiler* compiler) } // Define a constant for the literal. - // TODO(bob): See if constant with same value already exists. - Value constant = (Value)makeNum((double)value); - - compiler->block->constants[compiler->block->numConstants++] = constant; + int constant = addConstant(compiler, (Value)makeNum((double)value)); // Compile the code to load the constant. emit(compiler, CODE_CONSTANT); - emit(compiler, compiler->block->numConstants - 1); + emit(compiler, constant); } TokenType peek(Compiler* compiler) diff --git a/src/vm.c b/src/vm.c index 67b84d83..6702f787 100644 --- a/src/vm.c +++ b/src/vm.c @@ -36,7 +36,8 @@ typedef struct static void callBlock(Fiber* fiber, ObjBlock* block, int firstLocal); static void push(Fiber* fiber, Value value); static Value pop(Fiber* fiber); -static Value primitive_num_abs(Value block); +static Value primitive_metaclass_new(Value receiver); +static Value primitive_num_abs(Value receiver); VM* newVM() { @@ -66,7 +67,7 @@ void freeVM(VM* vm) free(vm); } -ObjClass* makeClass() +ObjClass* makeSingleClass() { ObjClass* obj = malloc(sizeof(ObjClass)); obj->obj.type = OBJ_CLASS; @@ -80,6 +81,17 @@ ObjClass* makeClass() return obj; } +ObjClass* makeClass() +{ + ObjClass* classObj = makeSingleClass(); + + // Make its metaclass. + // TODO(bob): What is the metaclass's metaclass? + classObj->metaclass = makeSingleClass(); + + return classObj; +} + ObjBlock* makeBlock() { ObjBlock* block = malloc(sizeof(ObjBlock)); @@ -97,6 +109,16 @@ ObjNum* makeNum(double number) return num; } +ObjInstance* makeInstance(ObjClass* classObj) +{ + ObjInstance* instance = malloc(sizeof(ObjInstance)); + instance->obj.type = OBJ_INSTANCE; + instance->obj.flags = 0; + instance->classObj = classObj; + + return instance; +} + void initSymbolTable(SymbolTable* symbols) { symbols->count = 0; @@ -150,6 +172,11 @@ int findSymbol(SymbolTable* symbols, const char* name, size_t length) return -1; } +const char* getSymbolName(SymbolTable* symbols, int symbol) +{ + return symbols->names[symbol]; +} + Value interpret(VM* vm, ObjBlock* block) { Fiber fiber; @@ -176,9 +203,37 @@ Value interpret(VM* vm, ObjBlock* block) } case CODE_CLASS: - push(&fiber, (Value)makeClass()); + { + ObjClass* classObj = makeClass(); + + // Define a "new" method on the metaclass. + // TODO(bob): Can this be inherited? + int newSymbol = ensureSymbol(&vm->symbols, "new", strlen("new")); + printf("define new %d\n", newSymbol); + classObj->metaclass->methods[newSymbol].type = METHOD_PRIMITIVE; + classObj->metaclass->methods[newSymbol].primitive = + primitive_metaclass_new; + + push(&fiber, (Value)classObj); printf("push class at %d\n", fiber.stackSize - 1); break; + } + + case CODE_METHOD: + { + int symbol = frame->block->bytecode[frame->ip++]; + int constant = frame->block->bytecode[frame->ip++]; + ObjClass* classObj = (ObjClass*)fiber.stack[fiber.stackSize - 1]; + + ObjBlock* body = (ObjBlock*)frame->block->constants[constant]; + classObj->methods[symbol].type = METHOD_BLOCK; + classObj->methods[symbol].block = body; + + printf("define method %d using constant %d on ", symbol, constant); + printValue((Value)classObj); + printf("\n"); + break; + } case CODE_LOAD_LOCAL: { @@ -222,14 +277,22 @@ Value interpret(VM* vm, ObjBlock* block) break; case OBJ_CLASS: - classObj = vm->classClass; + classObj = ((ObjClass*)receiver)->metaclass; break; case OBJ_NUM: classObj = vm->numClass; break; + + case OBJ_INSTANCE: + classObj = ((ObjInstance*)receiver)->classObj; + break; } + printf("call %d on ", symbol); + printValue(receiver); + printf("\n"); + Method* method = &classObj->methods[symbol]; switch (method->type) { @@ -262,11 +325,23 @@ Value interpret(VM* vm, ObjBlock* block) fiber.numFrames--; // If we are returning from the top-level block, just return the value. - if (fiber.numFrames == 0) return result; + if (fiber.numFrames == 0) + { + printf("done with result "); + printValue(result); + printf("\n"); + return result; + } // Store the result of the block in the first slot, which is where the // caller expects it. + printf("return and store result "); + printValue(result); + printf(" in %d\n", frame->locals); fiber.stack[frame->locals] = result; + + // Discard the stack slots for the locals. + fiber.stackSize = frame->locals + 1; } } } @@ -274,6 +349,7 @@ Value interpret(VM* vm, ObjBlock* block) void printValue(Value value) { + // TODO(bob): Do more useful stuff here. switch (value->type) { case OBJ_NUM: @@ -287,6 +363,10 @@ void printValue(Value value) case OBJ_CLASS: printf("[class]"); break; + + case OBJ_INSTANCE: + printf("[instance]"); + break; } } @@ -318,9 +398,16 @@ Value pop(Fiber* fiber) return fiber->stack[--fiber->stackSize]; } -Value primitive_num_abs(Value number) +Value primitive_metaclass_new(Value receiver) { - double value = ((ObjNum*)number)->value; + ObjClass* classObj = (ObjClass*)receiver; + // TODO(bob): Invoke initializer method. + return (Value)makeInstance(classObj); +} + +Value primitive_num_abs(Value receiver) +{ + double value = ((ObjNum*)receiver)->value; if (value < 0) value = -value; return (Value)makeNum(value); diff --git a/src/vm.h b/src/vm.h index ebbc913e..c1aeae6e 100644 --- a/src/vm.h +++ b/src/vm.h @@ -9,7 +9,8 @@ typedef enum { OBJ_NUM, OBJ_BLOCK, - OBJ_CLASS + OBJ_CLASS, + OBJ_INSTANCE } ObjType; typedef enum @@ -61,13 +62,21 @@ typedef struct }; } Method; -typedef struct +typedef struct sObjClass { Obj obj; + struct sObjClass* metaclass; // TODO(bob): Hack. Probably don't want to use this much space. Method methods[MAX_SYMBOLS]; } ObjClass; +typedef struct +{ + Obj obj; + ObjClass* classObj; + // TODO(bob): Fields. +} ObjInstance; + typedef enum { CODE_CONSTANT, @@ -76,6 +85,10 @@ typedef enum CODE_CLASS, // Define a new empty class and push it. + CODE_METHOD, + // Add a method for symbol [arg1] with body stored in constant [arg2] to the + // class on the top of stack. Does not modify the stack. + CODE_DUP, // Push a copy of the top of stack. @@ -118,6 +131,7 @@ void freeVM(VM* vm); ObjClass* makeClass(); ObjBlock* makeBlock(); ObjNum* makeNum(double number); +ObjInstance* makeInstance(ObjClass* classObj); // Initializes the symbol table. void initSymbolTable(SymbolTable* symbols); @@ -137,6 +151,9 @@ int ensureSymbol(SymbolTable* symbols, const char* name, size_t length); // Looks up name in the symbol table. Returns its index if found or -1 if not. int findSymbol(SymbolTable* symbols, const char* name, size_t length); +// Given an index in the symbol table, returns its name. +const char* getSymbolName(SymbolTable* symbols, int symbol); + Value interpret(VM* vm, ObjBlock* block); void printValue(Value value);