aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Tom Willemse2022-06-02 22:27:43 -0700
committerGravatar Tom Willemse2022-06-02 22:27:43 -0700
commit57ed9226c06b5fbe016e62857fe9662c51da3dfd (patch)
treef8bdae71c33f92ba1e105612ee848fcf168d3777
parente74cbddb0463e93f5d9742accc20bbed027d0b9b (diff)
downloadcrafting-interpreters-57ed9226c06b5fbe016e62857fe9662c51da3dfd.tar.gz
crafting-interpreters-57ed9226c06b5fbe016e62857fe9662c51da3dfd.zip
Chapter 25.3 & 25.4
-rw-r--r--clox/src/chunk.h1
-rw-r--r--clox/src/compiler.c10
-rw-r--r--clox/src/debug.c2
-rw-r--r--clox/src/memory.c5
-rw-r--r--clox/src/object.c18
-rw-r--r--clox/src/object.h11
-rw-r--r--clox/src/vm.c58
-rw-r--r--clox/src/vm.h1
8 files changed, 105 insertions, 1 deletions
diff --git a/clox/src/chunk.h b/clox/src/chunk.h
index fe585de..e992c04 100644
--- a/clox/src/chunk.h
+++ b/clox/src/chunk.h
@@ -32,6 +32,7 @@ typedef enum {
OP_LOOP,
OP_CALL,
OP_CLOSURE,
+ OP_CLOSE_UPVALUE,
OP_RETURN,
} OpCode;
diff --git a/clox/src/compiler.c b/clox/src/compiler.c
index 6e98b5e..75ed697 100644
--- a/clox/src/compiler.c
+++ b/clox/src/compiler.c
@@ -42,6 +42,7 @@ typedef struct {
typedef struct {
Token name;
int depth;
+ bool isCaptured;
} Local;
typedef struct {
@@ -197,6 +198,7 @@ static void initCompiler(Compiler *compiler, FunctionType type) {
Local *local = &current->locals[current->localCount++];
local->depth = 0;
+ local->isCaptured = false;
local->name.start = "";
local->name.length = 0;
}
@@ -224,7 +226,11 @@ static void endScope() {
while (current->localCount > 0 &&
current->locals[current->localCount - 1].depth > current->scopeDepth) {
- emitByte(OP_POP);
+ if (current->locals[current->localCount - 1].isCaptured) {
+ emitByte(OP_CLOSE_UPVALUE);
+ } else {
+ emitByte(OP_POP);
+ }
current->localCount--;
}
}
@@ -740,6 +746,7 @@ static int resolveUpvalue(Compiler *compiler, Token *name) {
int local = resolveLocal(compiler->enclosing, name);
if (local != -1) {
+ compiler->enclosing->locals[local].isCaptured = true;
return addUpvalue(compiler, (uint8_t)local, true);
}
@@ -760,6 +767,7 @@ static void addLocal(Token name) {
Local *local = &current->locals[current->localCount++];
local->name = name;
local->depth = -1;
+ local->isCaptured = false;
}
static void declareVariable() {
diff --git a/clox/src/debug.c b/clox/src/debug.c
index f175030..750fdb9 100644
--- a/clox/src/debug.c
+++ b/clox/src/debug.c
@@ -118,6 +118,8 @@ int disassembleInstruction(Chunk *chunk, int offset) {
return offset;
}
+ case OP_CLOSE_UPVALUE:
+ return simpleInstruction("OP_CLOSE_UPVALUE", offset);
case OP_RETURN:
return simpleInstruction("OP_RETURN", offset);
default:
diff --git a/clox/src/memory.c b/clox/src/memory.c
index 3124bb6..1db1c61 100644
--- a/clox/src/memory.c
+++ b/clox/src/memory.c
@@ -18,6 +18,8 @@ void *reallocate(void *pointer, size_t oldSize, size_t newSize) {
static void freeObject(Obj *object) {
switch (object->type) {
case OBJ_CLOSURE: {
+ ObjClosure *closure = (ObjClosure *)object;
+ FREE_ARRAY(ObjUpvalue *, closure->upvalues, closure->upvalueCount);
FREE(ObjClosure, object);
break;
}
@@ -36,6 +38,9 @@ static void freeObject(Obj *object) {
FREE(ObjString, object);
break;
}
+ case OBJ_UPVALUE:
+ FREE(ObjUpvalue, object);
+ break;
}
}
diff --git a/clox/src/object.c b/clox/src/object.c
index 8d26d8b..31550ae 100644
--- a/clox/src/object.c
+++ b/clox/src/object.c
@@ -20,8 +20,15 @@ static Obj *allocateObject(size_t size, ObjType type) {
}
ObjClosure *newClosure(ObjFunction *function) {
+ ObjUpvalue **upvalues = ALLOCATE(ObjUpvalue *, function->upvalueCount);
+ for (int i = 0; i < function->upvalueCount; i++) {
+ upvalues[i] = NULL;
+ }
+
ObjClosure *closure = ALLOCATE_OBJ(ObjClosure, OBJ_CLOSURE);
closure->function = function;
+ closure->upvalues = upvalues;
+ closure->upvalueCount = function->upvalueCount;
return closure;
}
@@ -81,6 +88,14 @@ ObjString *copyString(const char *chars, int length) {
return allocateString(heapChars, length, hash);
}
+ObjUpvalue *newUpvalue(Value *slot) {
+ ObjUpvalue *upvalue = ALLOCATE_OBJ(ObjUpvalue, OBJ_UPVALUE);
+ upvalue->closed = NIL_VAL;
+ upvalue->location = slot;
+ upvalue->next = NULL;
+ return upvalue;
+}
+
static void printFunction(ObjFunction *function) {
if (function->name == NULL) {
printf("<script>");
@@ -103,5 +118,8 @@ void printObject(Value value) {
case OBJ_STRING:
printf("%s", AS_CSTRING(value));
break;
+ case OBJ_UPVALUE:
+ printf("upvalue");
+ break;
}
}
diff --git a/clox/src/object.h b/clox/src/object.h
index f650b30..e6b842d 100644
--- a/clox/src/object.h
+++ b/clox/src/object.h
@@ -23,6 +23,7 @@ typedef enum {
OBJ_FUNCTION,
OBJ_NATIVE,
OBJ_STRING,
+ OBJ_UPVALUE,
} ObjType;
struct Obj {
@@ -52,9 +53,18 @@ struct ObjString {
uint32_t hash;
};
+typedef struct ObjUpvalue {
+ Obj obj;
+ Value *location;
+ Value closed;
+ struct ObjUpvalue *next;
+} ObjUpvalue;
+
typedef struct {
Obj obj;
ObjFunction *function;
+ ObjUpvalue **upvalues;
+ int upvalueCount;
} ObjClosure;
ObjClosure *newClosure(ObjFunction *function);
@@ -62,6 +72,7 @@ ObjFunction *newFunction();
ObjNative *newNative(NativeFn function);
ObjString *takeString(char *chars, int length);
ObjString *copyString(const char *chars, int length);
+ObjUpvalue *newUpvalue(Value *slot);
void printObject(Value value);
static inline bool isObjType(Value value, ObjType type) {
diff --git a/clox/src/vm.c b/clox/src/vm.c
index b728ed5..b11a52a 100644
--- a/clox/src/vm.c
+++ b/clox/src/vm.c
@@ -19,6 +19,7 @@ static Value clockNative(int argCount, Value *args) {
static void resetStack() {
vm.stackTop = vm.stack;
vm.frameCount = 0;
+ vm.openUpvalues = NULL;
}
static void runtimeError(const char *format, ...) {
@@ -118,6 +119,39 @@ static bool callValue(Value callee, int argCount) {
return false;
}
+static ObjUpvalue *captureUpvalue(Value *local) {
+ ObjUpvalue *prevUpvalue = NULL;
+ ObjUpvalue *upvalue = vm.openUpvalues;
+ while (upvalue != NULL && upvalue->location > local) {
+ prevUpvalue = upvalue;
+ upvalue = upvalue->next;
+ }
+
+ if (upvalue != NULL && upvalue->location == local) {
+ return upvalue;
+ }
+
+ ObjUpvalue *createdUpvalue = newUpvalue(local);
+ createdUpvalue->next = upvalue;
+
+ if (prevUpvalue == NULL) {
+ vm.openUpvalues = createdUpvalue;
+ } else {
+ prevUpvalue->next = createdUpvalue;
+ }
+
+ return createdUpvalue;
+}
+
+static void closeUpvalues(Value *last) {
+ while (vm.openUpvalues != NULL && vm.openUpvalues->location >= last) {
+ ObjUpvalue *upvalue = vm.openUpvalues;
+ upvalue->closed = *upvalue->location;
+ upvalue->location = &upvalue->closed;
+ vm.openUpvalues = upvalue->next;
+ }
+}
+
static bool isFalsey(Value value) {
return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
}
@@ -225,6 +259,16 @@ static InterpretResult run() {
}
break;
}
+ case OP_GET_UPVALUE: {
+ uint8_t slot = READ_BYTE();
+ push(*frame->closure->upvalues[slot]->location);
+ break;
+ }
+ case OP_SET_UPVALUE: {
+ uint8_t slot = READ_BYTE();
+ *frame->closure->upvalues[slot]->location = peek(0);
+ break;
+ }
case OP_EQUAL: {
Value b = pop();
Value a = pop();
@@ -303,10 +347,24 @@ static InterpretResult run() {
ObjFunction *function = AS_FUNCTION(READ_CONSTANT());
ObjClosure *closure = newClosure(function);
push(OBJ_VAL(closure));
+ for (int i = 0; i < closure->upvalueCount; i++) {
+ uint8_t isLocal = READ_BYTE();
+ uint8_t index = READ_BYTE();
+ if (isLocal) {
+ closure->upvalues[i] = captureUpvalue(frame->slots + index);
+ } else {
+ closure->upvalues[i] = frame->closure->upvalues[index];
+ }
+ }
break;
}
+ case OP_CLOSE_UPVALUE:
+ closeUpvalues(vm.stackTop - 1);
+ pop();
+ break;
case OP_RETURN: {
Value result = pop();
+ closeUpvalues(frame->slots);
vm.frameCount--;
if (vm.frameCount == 0) {
pop();
diff --git a/clox/src/vm.h b/clox/src/vm.h
index ce14d0c..08706a3 100644
--- a/clox/src/vm.h
+++ b/clox/src/vm.h
@@ -23,6 +23,7 @@ typedef struct {
Value *stackTop;
Table globals;
Table strings;
+ ObjUpvalue *openUpvalues;
Obj *objects;
} VM;