M zlox/src/chunk.h => zlox/src/chunk.h +2 -0
@@ 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,
M zlox/src/common.h => zlox/src/common.h +2 -0
@@ 8,4 8,6 @@
#define DEBUG_PRINT_CODE
#define DEBUG_TRACE_EXECUTION
+#define UINT8_COUNT (UINT8_MAX + 1)
+
#endif
M zlox/src/compiler.c => zlox/src/compiler.c +119 -3
@@ 10,6 10,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
typedef struct {
Token current;
@@ 40,7 41,19 @@ typedef struct {
Precedence precedence;
} ParseRule;
+typedef struct {
+ Token name;
+ int depth;
+} Local;
+
+typedef struct {
+ Local locals[UINT8_COUNT];
+ int localCount;
+ int scopeDepth;
+} Compiler;
+
Parser parser;
+Compiler *current = NULL;
Chunk *compilingChunk;
static Chunk *currentChunk() {
@@ 129,6 142,12 @@ static void emitConstant(Value value) {
emitBytes(OP_CONSTANT, makeConstant(value));
}
+static void initCompiler(Compiler *compiler) {
+ compiler->localCount = 0;
+ compiler->scopeDepth = 0;
+ current = compiler;
+}
+
static void endCompiler() {
emitReturn();
#ifdef DEBUG_PRINT_CODE
@@ 138,6 157,20 @@ static void endCompiler() {
#endif
}
+static void beginScope() {
+ current->scopeDepth++;
+}
+
+static void endScope() {
+ current->scopeDepth--;
+
+ while (current->localCount > 0 &&
+ current->locals[current->localCount - 1].depth > current->scopeDepth) {
+ emitByte(OP_POP);
+ current->localCount--;
+ }
+}
+
static void expression();
static void statement();
static void declaration();
@@ 192,14 225,70 @@ static uint8_t identifierConstant(Token *name) {
return makeConstant(OBJ_VAL(copyString(name->start, name->length)));
}
+static bool identifiersEqual(Token *a, Token *b) {
+ if (a->length != b->length) return false;
+ 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.");
+ return;
+ }
+
+ Local *local = ¤t->locals[current->localCount++];
+ local->name = name;
+ local->depth = -1;
+}
+
+static void declareVariable() {
+ if (current->scopeDepth == 0) return;
+
+ Token *name = &parser.previous;
+ for (int i = current->localCount - 1; i >= 0; i--) {
+ Local *local = ¤t->locals[i];
+ if (local->depth != -1 && local->depth < current->scopeDepth) {
+ break;
+ }
+
+ if (identifiersEqual(name, &local->name)) {
+ error("Already a variable with this name in this scope.");
+ }
+ }
+ addLocal(*name);
+}
+
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, (uint8_t)arg);
} else {
- emitBytes(OP_GET_GLOBAL, arg);
+ emitBytes(getOp, (uint8_t)arg);
}
}
@@ 288,10 377,23 @@ static void parsePrecedence(Precedence precedence) {
static uint8_t parseVariable(const char *errorMessage) {
consume(TOKEN_IDENTIFIER, errorMessage);
+
+ declareVariable();
+ if (current->scopeDepth > 0) return 0;
+
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;
+ }
+
emitBytes(OP_DEFINE_GLOBAL, global);
}
@@ 303,6 405,14 @@ static void expression() {
parsePrecedence(PREC_ASSIGNMENT);
}
+static void block() {
+ while (!check(TOKEN_RIGHT_BRACE) && !check(TOKEN_EOF)) {
+ declaration();
+ }
+
+ consume(TOKEN_RIGHT_BRACE, "Expect '}' after block.");
+}
+
static void varDeclaration() {
uint8_t global = parseVariable("Expect variable name.");
@@ 366,6 476,10 @@ static void declaration() {
static void statement() {
if (match(TOKEN_PRINT)) {
printStatement();
+ } else if (match(TOKEN_LEFT_BRACE)) {
+ beginScope();
+ block();
+ endScope();
} else {
expressionStatement();
}
@@ 373,6 487,8 @@ static void statement() {
bool compile(const char *source, Chunk *chunk) {
initScanner(source);
+ Compiler compiler;
+ initCompiler(&compiler);
compilingChunk = chunk;
parser.hadError = false;
M zlox/src/debug.c => zlox/src/debug.c +10 -0
@@ 11,6 11,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;
+}
+
void disassembleChunk(Chunk *chunk, const char *name) {
printf("== %s ==\n", name);
@@ 48,6 54,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:
M zlox/src/vm.c => zlox/src/vm.c +12 -0
@@ 104,6 104,18 @@ static InterpretResult run() {
case OP_TRUE: push(BOOL_VAL(true)); break;
case OP_FALSE: push(BOOL_VAL(false)); break;
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();