diff --git a/clox/src/chunk.h b/clox/src/chunk.h index 2807d30..7777ad6 100644 --- a/clox/src/chunk.h +++ b/clox/src/chunk.h @@ -10,6 +10,8 @@ typedef enum { OP_TRUE, OP_FALSE, OP_POP, + OP_GET_LOCAL, + OP_SET_LOCAL, OP_GET_GLOBAL, OP_DEFINE_GLOBAL, OP_SET_GLOBAL, diff --git a/clox/src/compiler.c b/clox/src/compiler.c index 98de087..a964b79 100644 --- a/clox/src/compiler.c +++ b/clox/src/compiler.c @@ -170,6 +170,7 @@ static void parsePrecedence(Precedence precedence); static uint8_t parseVariable(const char *name); static void defineVariable(uint8_t global); static uint8_t identifierConstant(Token *name); +static int resolveLocal(Compiler *compiler, Token *name); static void binary(bool canAssign) { TokenType operatorType = parser.previous.type; @@ -321,13 +322,23 @@ static void string(bool canAssign) { } static void namedVariable(Token name, bool canAssign) { - uint8_t arg = identifierConstant(&name); + uint8_t getOp, setOp; + int arg = resolveLocal(current, &name); + + if (arg != -1) { + getOp = OP_GET_LOCAL; + setOp = OP_SET_LOCAL; + } else { + arg = identifierConstant(&name); + getOp = OP_GET_GLOBAL; + setOp = OP_SET_GLOBAL; + } if (canAssign && match(TOKEN_EQUAL)) { expression(); - emitBytes(OP_SET_GLOBAL, arg); + emitBytes(setOp, arg); } else { - emitBytes(OP_GET_GLOBAL, arg); + emitBytes(getOp, arg); } } @@ -434,6 +445,20 @@ static bool identifiersEqual(Token *a, Token *b) { return memcmp(a->start, b->start, a->length) == 0; } +static int resolveLocal(Compiler *compiler, Token *name) { + for (int i = compiler->localCount - 1; i >= 0; i--) { + Local *local = &compiler->locals[i]; + if (identifiersEqual(name, &local->name)) { + if (local->depth == -1) { + error("Can't read local variable in its own initializer."); + } + return i; + } + } + + return -1; +} + static void addLocal(Token name) { if (current->localCount == UINT8_COUNT) { error("Too many local variables in function."); @@ -442,7 +467,7 @@ static void addLocal(Token name) { Local *local = ¤t->locals[current->localCount++]; local->name = name; - local->depth = current->scopeDepth; + local->depth = -1; } static void declareVariable() { @@ -474,8 +499,13 @@ static uint8_t parseVariable(const char *errorMessage) { return identifierConstant(&parser.previous); } +static void markInitialized() { + current->locals[current->localCount - 1].depth = current->scopeDepth; +} + static void defineVariable(uint8_t global) { if (current->scopeDepth > 0) { + markInitialized(); return; } diff --git a/clox/src/debug.c b/clox/src/debug.c index 61deb52..5a3cedc 100644 --- a/clox/src/debug.c +++ b/clox/src/debug.c @@ -24,6 +24,12 @@ static int simpleInstruction(const char *name, int offset) { return offset + 1; } +static int byteInstruction(const char *name, Chunk *chunk, int offset) { + uint8_t slot = chunk->code[offset + 1]; + printf("%-16s %4d\n", name, slot); + return offset + 2; +} + int disassembleInstruction(Chunk *chunk, int offset) { printf("%04d ", offset); if (offset > 0 && chunk->lines[offset] == chunk->lines[offset - 1]) { @@ -44,6 +50,10 @@ int disassembleInstruction(Chunk *chunk, int offset) { return simpleInstruction("OP_FALSE", offset); case OP_POP: return simpleInstruction("OP_POP", offset); + case OP_GET_LOCAL: + return byteInstruction("OP_GET_LOCAL", chunk, offset); + case OP_SET_LOCAL: + return byteInstruction("OP_SET_LOCAL", chunk, offset); case OP_GET_GLOBAL: return constantInstruction("OP_GET_GLOBAL", chunk, offset); case OP_DEFINE_GLOBAL: diff --git a/clox/src/vm.c b/clox/src/vm.c index 80e11c3..d869527 100644 --- a/clox/src/vm.c +++ b/clox/src/vm.c @@ -115,6 +115,16 @@ static InterpretResult run() { case OP_POP: pop(); break; + case OP_GET_LOCAL: { + uint8_t slot = READ_BYTE(); + push(vm.stack[slot]); + break; + } + case OP_SET_LOCAL: { + uint8_t slot = READ_BYTE(); + vm.stack[slot] = peek(0); + break; + } case OP_GET_GLOBAL: { ObjString *name = READ_STRING(); Value value;