diff --git a/clox/src/compiler.c b/clox/src/compiler.c index 31a7937..a773e97 100644 --- a/clox/src/compiler.c +++ b/clox/src/compiler.c @@ -51,7 +51,7 @@ typedef struct { bool isLocal; } Upvalue; -typedef enum { TYPE_FUNCTION, TYPE_SCRIPT } FunctionType; +typedef enum { TYPE_FUNCTION, TYPE_METHOD, TYPE_SCRIPT } FunctionType; typedef struct Compiler { struct Compiler *enclosing; @@ -64,10 +64,15 @@ typedef struct Compiler { int scopeDepth; } Compiler; +typedef struct ClassCompiler { + struct ClassCompiler *enclosing; +} ClassCompiler; + static int resolveUpvalue(Compiler *, Token *); Parser parser; Compiler *current = NULL; +ClassCompiler *currentClass = NULL; Chunk *compilingChunk; static Chunk *currentChunk() { return ¤t->function->chunk; } @@ -200,8 +205,13 @@ static void initCompiler(Compiler *compiler, FunctionType type) { Local *local = ¤t->locals[current->localCount++]; local->depth = 0; local->isCaptured = false; - local->name.start = ""; - local->name.length = 0; + if (type != TYPE_FUNCTION) { + local->name.start = "this"; + local->name.length = 4; + } else { + local->name.start = ""; + local->name.length = 0; + } } static ObjFunction *endCompiler() { @@ -378,7 +388,7 @@ static void method() { consume(TOKEN_IDENTIFIER, "Expect method name."); uint8_t constant = identifierConstant(&parser.previous); - FunctionType type = TYPE_FUNCTION; + FunctionType type = TYPE_METHOD; function(type); emitBytes(OP_METHOD, constant); } @@ -453,6 +463,10 @@ static void classDeclaration() { emitBytes(OP_CLASS, nameConstant); defineVariable(nameConstant); + ClassCompiler classCompiler; + classCompiler.enclosing = currentClass; + currentClass = &classCompiler; + namedVariable(className, false); consume(TOKEN_LEFT_BRACE, "Expect '{' before class body."); while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) { @@ -460,6 +474,8 @@ static void classDeclaration() { } consume(TOKEN_RIGHT_BRACE, "Expect '}' after class body."); emitByte(OP_POP); + + currentClass = currentClass->enclosing; } static void markInitialized() { @@ -686,6 +702,15 @@ static void variable(bool canAssign) { namedVariable(parser.previous, canAssign); } +static void this_(bool canAssign) { + if (currentClass == NULL) { + error("Can't use 'this' outside of a class."); + return; + } + + variable(false); +} + static void unary(bool canAssign) { TokenType operatorType = parser.previous.type; @@ -745,7 +770,7 @@ ParseRule rules[] = { [TOKEN_PRINT] = {NULL, NULL, PREC_NONE}, [TOKEN_RETURN] = {NULL, NULL, PREC_NONE}, [TOKEN_SUPER] = {NULL, NULL, PREC_NONE}, - [TOKEN_THIS] = {NULL, NULL, PREC_NONE}, + [TOKEN_THIS] = {this_, NULL, PREC_NONE}, [TOKEN_TRUE] = {literal, NULL, PREC_NONE}, [TOKEN_VAR] = {NULL, NULL, PREC_NONE}, [TOKEN_WHILE] = {NULL, NULL, PREC_NONE}, diff --git a/clox/src/vm.c b/clox/src/vm.c index 4b0fe48..6b15a3a 100644 --- a/clox/src/vm.c +++ b/clox/src/vm.c @@ -110,6 +110,7 @@ static bool callValue(Value callee, int argCount) { switch (OBJ_TYPE(callee)) { case OBJ_BOUND_METHOD: { ObjBoundMethod *bound = AS_BOUND_METHOD(callee); + vm.stackTop[-argCount - 1] = bound->receiver; return call(bound->method, argCount); } case OBJ_CLASS: {