From 67e5660346ceaa2bf07120ff0a4595a9c4c23940 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Wed, 23 Oct 2013 22:50:04 -0700 Subject: [PATCH] Start working on class definitions. --- example/hello.wren | 1 + src/compiler.c | 47 ++++++++++++++++--- src/compiler.h | 2 +- src/main.c | 2 +- src/vm.c | 111 +++++++++++++++++++++++++++++++-------------- src/vm.h | 89 ++++++++++++++++++++++++------------ 6 files changed, 181 insertions(+), 71 deletions(-) diff --git a/example/hello.wren b/example/hello.wren index 93e6a73d..68628bf7 100644 --- a/example/hello.wren +++ b/example/hello.wren @@ -1,3 +1,4 @@ +class Foo {} var a = 123 var b = 345 a diff --git a/src/compiler.c b/src/compiler.c index 41690444..823d8eee 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -32,6 +32,8 @@ typedef enum TOKEN_EQEQ, TOKEN_BANGEQ, + TOKEN_CLASS, + TOKEN_META, TOKEN_VAR, TOKEN_NAME, @@ -73,7 +75,7 @@ typedef struct Token previous; // The block being compiled. - Block* block; + ObjBlock* block; int numCodes; // Symbol table for declared local variables in the current block. @@ -111,7 +113,7 @@ static void makeToken(Compiler* compiler, TokenType type); static void emit(Compiler* compiler, Code code); static void error(Compiler* compiler, const char* format, ...); -Block* compile(VM* vm, const char* source, size_t sourceLength) +ObjBlock* compile(VM* vm, const char* source, size_t sourceLength) { Compiler compiler; compiler.vm = vm; @@ -133,7 +135,7 @@ Block* compile(VM* vm, const char* source, size_t sourceLength) // Read the first token. advance(&compiler); - compiler.block = malloc(sizeof(Block)); + compiler.block = makeBlock(); // TODO(bob): Hack! make variable sized. compiler.block->bytecode = malloc(sizeof(Code) * 1024); @@ -147,6 +149,8 @@ Block* compile(VM* vm, const char* source, size_t sourceLength) { statement(&compiler); + consume(&compiler, TOKEN_LINE); + if (match(&compiler, TOKEN_EOF)) break; // Discard the result of the previous expression. @@ -162,6 +166,36 @@ Block* compile(VM* vm, const char* source, size_t sourceLength) void statement(Compiler* compiler) { + if (match(compiler, TOKEN_CLASS)) + { + consume(compiler, TOKEN_NAME); + + // TODO(bob): Copied from below. Unify. + int local = addSymbol(&compiler->locals, + compiler->source + compiler->previous.start, + compiler->previous.end - compiler->previous.start); + + if (local == -1) + { + error(compiler, "Local variable is already defined."); + } + + // Create the empty class. + emit(compiler, CODE_CLASS); + + // Store it in its name. + emit(compiler, CODE_STORE_LOCAL); + emit(compiler, local); + + // Compile the method definitions. + consume(compiler, TOKEN_LEFT_BRACE); + + // TODO(bob): Definitions! + + consume(compiler, TOKEN_RIGHT_BRACE); + return; + } + if (match(compiler, TOKEN_VAR)) { consume(compiler, TOKEN_NAME); @@ -187,7 +221,6 @@ void statement(Compiler* compiler) // Statement expression. expression(compiler); - consume(compiler, TOKEN_LINE); } void expression(Compiler* compiler) @@ -245,6 +278,7 @@ void primary(Compiler* compiler) void number(Compiler* compiler, Token* token) { char* end; + // TODO(bob): Parse actual double! long value = strtol(compiler->source + token->start, &end, 10); // TODO(bob): Check errno == ERANGE here. if (end == compiler->source + token->start) @@ -255,8 +289,7 @@ void number(Compiler* compiler, Token* token) // Define a constant for the literal. // TODO(bob): See if constant with same value already exists. - // TODO(bob): Handle truncation! - Value constant = makeNum((int)value); + Value constant = (Value)makeNum((double)value); compiler->block->constants[compiler->block->numConstants++] = constant; @@ -413,6 +446,8 @@ void readName(Compiler* compiler) TokenType type = TOKEN_NAME; + if (isKeyword(compiler, "class")) type = TOKEN_CLASS; + if (isKeyword(compiler, "meta")) type = TOKEN_META; if (isKeyword(compiler, "var")) type = TOKEN_VAR; makeToken(compiler, type); diff --git a/src/compiler.h b/src/compiler.h index 160d218f..807d11fc 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -3,6 +3,6 @@ #include "vm.h" -Block* compile(VM* vm, const char* source, size_t sourceLength); +ObjBlock* compile(VM* vm, const char* source, size_t sourceLength); #endif diff --git a/src/main.c b/src/main.c index 2943aa9a..5c8b3ebc 100644 --- a/src/main.c +++ b/src/main.c @@ -26,7 +26,7 @@ int main(int argc, const char * argv[]) size_t length; char* source = readFile(argv[1], &length); VM* vm = newVM(); - Block* block = compile(vm, source, length); + ObjBlock* block = compile(vm, source, length); Value value = interpret(vm, block); printValue(value); printf("\n"); diff --git a/src/vm.c b/src/vm.c index c71330fd..c5182e13 100644 --- a/src/vm.c +++ b/src/vm.c @@ -11,7 +11,7 @@ typedef struct int ip; // The block being executed. - Block* block; + ObjBlock* block; // Index of the stack slot that contains the first local for this block. int locals; @@ -26,7 +26,7 @@ typedef struct int numFrames; } Fiber; -static void callBlock(Fiber* fiber, Block* block, int locals); +static void callBlock(Fiber* fiber, ObjBlock* block, int locals); static void push(Fiber* fiber, Value value); static Value pop(Fiber* fiber); static Value primitiveNumAbs(Value number); @@ -36,12 +36,10 @@ VM* newVM() VM* vm = malloc(sizeof(VM)); initSymbolTable(&vm->symbols); - for (int i = 0; i < MAX_SYMBOLS; i++) - { - vm->numClass.methods[i] = NULL; - } - - vm->numClass.methods[ensureSymbol(&vm->symbols, "abs", 3)] = primitiveNumAbs; + vm->numClass = makeClass(); + int symbol = ensureSymbol(&vm->symbols, "abs", 3); + vm->numClass->methods[symbol].type = METHOD_PRIMITIVE; + vm->numClass->methods[symbol].primitive = primitiveNumAbs; return vm; } @@ -52,14 +50,35 @@ void freeVM(VM* vm) free(vm); } -Value makeNum(int number) +ObjClass* makeClass() { - Value value = malloc(sizeof(Obj)); - value->type = OBJ_INT; - value->flags = 0; - value->value = number; + ObjClass* obj = malloc(sizeof(ObjClass)); + obj->obj.type = OBJ_CLASS; + obj->obj.flags = 0; - return value; + for (int i = 0; i < MAX_SYMBOLS; i++) + { + obj->methods[i].type = METHOD_NONE; + } + + return obj; +} + +ObjBlock* makeBlock() +{ + ObjBlock* block = malloc(sizeof(ObjBlock)); + block->obj.type = OBJ_NUM; + block->obj.flags = 0; + return block; +} + +ObjNum* makeNum(double number) +{ + ObjNum* num = malloc(sizeof(ObjNum)); + num->obj.type = OBJ_NUM; + num->obj.flags = 0; + num->value = number; + return num; } void initSymbolTable(SymbolTable* symbols) @@ -115,7 +134,7 @@ int findSymbol(SymbolTable* symbols, const char* name, size_t length) return -1; } -Value interpret(VM* vm, Block* block) +Value interpret(VM* vm, ObjBlock* block) { Fiber fiber; fiber.stackSize = 0; @@ -137,21 +156,34 @@ Value interpret(VM* vm, Block* block) break; } + case CODE_CLASS: + push(&fiber, (Value)makeClass()); + printf("push class at %d\n", fiber.stackSize - 1); + break; + case CODE_LOAD_LOCAL: { int local = frame->block->bytecode[frame->ip++]; push(&fiber, fiber.stack[frame->locals + local]); + printf("load local %d to %d\n", local, fiber.stackSize - 1); break; } case CODE_STORE_LOCAL: { int local = frame->block->bytecode[frame->ip++]; - fiber.stack[frame->locals + local] = pop(&fiber); + printf("store local %d from %d\n", local, fiber.stackSize - 1); + fiber.stack[frame->locals + local] = fiber.stack[fiber.stackSize - 1]; break; } + case CODE_DUP: + push(&fiber, fiber.stack[fiber.stackSize - 1]); + printf("dup %d\n", fiber.stackSize - 1); + break; + case CODE_POP: + printf("pop %d\n", fiber.stackSize - 1); pop(&fiber); break; @@ -163,19 +195,24 @@ Value interpret(VM* vm, Block* block) int symbol = frame->block->bytecode[frame->ip++]; // TODO(bob): Support classes for other object types. - Class* classObj = &vm->numClass; + ObjClass* classObj = vm->numClass; + Method* method = &classObj->methods[symbol]; + switch (method->type) + { + case METHOD_NONE: + // TODO(bob): Should return nil or suspend fiber or something. + printf("No method.\n"); + exit(1); + break; - Primitive primitive = classObj->methods[symbol]; - if (primitive) - { - Value result = primitive(receiver); - push(&fiber, result); - } - else - { - // TODO(bob): Should return nil or suspend fiber or something. - printf("No method.\n"); - exit(1); + case METHOD_PRIMITIVE: + push(&fiber, method->primitive(receiver)); + break; + + case METHOD_BLOCK: + // TODO(bob): Should pass in correct index for locals. + callBlock(&fiber, method->block, fiber.stackSize); + break; } break; } @@ -200,13 +237,21 @@ void printValue(Value value) { switch (value->type) { - case OBJ_INT: - printf("%d", value->value); + case OBJ_NUM: + printf("%f", ((ObjNum*)value)->value); + break; + + case OBJ_BLOCK: + printf("[block]"); + break; + + case OBJ_CLASS: + printf("[class]"); break; } } -void callBlock(Fiber* fiber, Block* block, int locals) +void callBlock(Fiber* fiber, ObjBlock* block, int locals) { fiber->frames[fiber->numFrames].block = block; fiber->frames[fiber->numFrames].ip = 0; @@ -227,8 +272,8 @@ Value pop(Fiber* fiber) Value primitiveNumAbs(Value number) { - int value = number->value; + double value = ((ObjNum*)number)->value; if (value < 0) value = -value; - return makeNum(value); + return (Value)makeNum(value); } diff --git a/src/vm.h b/src/vm.h index 9df48e0e..fd6cd159 100644 --- a/src/vm.h +++ b/src/vm.h @@ -4,10 +4,12 @@ // TODO(bob): Make these externally controllable. #define STACK_SIZE 1024 #define MAX_CALL_FRAMES 256 - +#define MAX_SYMBOLS 256 typedef enum { - OBJ_INT + OBJ_NUM, + OBJ_BLOCK, + OBJ_CLASS } ObjType; typedef enum @@ -16,23 +18,66 @@ typedef enum FLAG_MARKED = 0x01, } ObjFlags; -typedef struct sObj { +typedef struct +{ ObjType type; ObjFlags flags; - - union { - /* OBJ_INT */ - int value; - }; } Obj; typedef Obj* Value; +typedef struct +{ + Obj obj; + double value; +} ObjNum; + +typedef struct +{ + Obj obj; + unsigned char* bytecode; + Value* constants; + int numConstants; + int numLocals; +} ObjBlock; + +typedef Value (*Primitive)(Value receiver); + +typedef enum +{ + METHOD_NONE, + METHOD_PRIMITIVE, + METHOD_BLOCK +} MethodType; + +typedef struct +{ + MethodType type; + union + { + Primitive primitive; + ObjBlock* block; + }; +} Method; + +typedef struct +{ + Obj obj; + // TODO(bob): Hack. Probably don't want to use this much space. + Method methods[MAX_SYMBOLS]; +} ObjClass; + typedef enum { CODE_CONSTANT, // Load the constant at index [arg]. + CODE_CLASS, + // Define a new empty class and push it. + + CODE_DUP, + // Push a copy of the top of stack. + CODE_POP, // Pop and discard the top of stack. @@ -40,7 +85,7 @@ typedef enum // Pushes the value in local slot [arg]. CODE_STORE_LOCAL, - // Pops and stores the value in local slot [arg]. + // Stores the top of stack in local slot [arg]. Does not pop it. CODE_CALL, // Invoke the method with symbol [arg]. @@ -50,18 +95,6 @@ typedef enum } Code; -typedef struct -{ - unsigned char* bytecode; - Value* constants; - int numConstants; - int numLocals; -} Block; - -#define MAX_SYMBOLS 256 - -typedef Value (*Primitive)(Value receiver); - typedef struct { // TODO(bob): Make this dynamically sized. @@ -69,22 +102,18 @@ typedef struct int count; } SymbolTable; -typedef struct -{ - // TODO(bob): Hack. Probably don't want to use this much space. - Primitive methods[MAX_SYMBOLS]; -} Class; - typedef struct { SymbolTable symbols; - Class numClass; + ObjClass* numClass; } VM; VM* newVM(); void freeVM(VM* vm); -Value makeNum(int number); +ObjClass* makeClass(); +ObjBlock* makeBlock(); +ObjNum* makeNum(double number); // Initializes the symbol table. void initSymbolTable(SymbolTable* symbols); @@ -104,7 +133,7 @@ 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); -Value interpret(VM* vm, Block* block); +Value interpret(VM* vm, ObjBlock* block); void printValue(Value value);