Chapter 25.3 & 25.4
This commit is contained in:
parent
e74cbddb04
commit
57ed9226c0
8 changed files with 105 additions and 1 deletions
|
@ -32,6 +32,7 @@ typedef enum {
|
||||||
OP_LOOP,
|
OP_LOOP,
|
||||||
OP_CALL,
|
OP_CALL,
|
||||||
OP_CLOSURE,
|
OP_CLOSURE,
|
||||||
|
OP_CLOSE_UPVALUE,
|
||||||
OP_RETURN,
|
OP_RETURN,
|
||||||
} OpCode;
|
} OpCode;
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@ typedef struct {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Token name;
|
Token name;
|
||||||
int depth;
|
int depth;
|
||||||
|
bool isCaptured;
|
||||||
} Local;
|
} Local;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -197,6 +198,7 @@ static void initCompiler(Compiler *compiler, FunctionType type) {
|
||||||
|
|
||||||
Local *local = ¤t->locals[current->localCount++];
|
Local *local = ¤t->locals[current->localCount++];
|
||||||
local->depth = 0;
|
local->depth = 0;
|
||||||
|
local->isCaptured = false;
|
||||||
local->name.start = "";
|
local->name.start = "";
|
||||||
local->name.length = 0;
|
local->name.length = 0;
|
||||||
}
|
}
|
||||||
|
@ -224,7 +226,11 @@ static void endScope() {
|
||||||
|
|
||||||
while (current->localCount > 0 &&
|
while (current->localCount > 0 &&
|
||||||
current->locals[current->localCount - 1].depth > current->scopeDepth) {
|
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--;
|
current->localCount--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -740,6 +746,7 @@ static int resolveUpvalue(Compiler *compiler, Token *name) {
|
||||||
|
|
||||||
int local = resolveLocal(compiler->enclosing, name);
|
int local = resolveLocal(compiler->enclosing, name);
|
||||||
if (local != -1) {
|
if (local != -1) {
|
||||||
|
compiler->enclosing->locals[local].isCaptured = true;
|
||||||
return addUpvalue(compiler, (uint8_t)local, true);
|
return addUpvalue(compiler, (uint8_t)local, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -760,6 +767,7 @@ static void addLocal(Token name) {
|
||||||
Local *local = ¤t->locals[current->localCount++];
|
Local *local = ¤t->locals[current->localCount++];
|
||||||
local->name = name;
|
local->name = name;
|
||||||
local->depth = -1;
|
local->depth = -1;
|
||||||
|
local->isCaptured = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void declareVariable() {
|
static void declareVariable() {
|
||||||
|
|
|
@ -118,6 +118,8 @@ int disassembleInstruction(Chunk *chunk, int offset) {
|
||||||
|
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
case OP_CLOSE_UPVALUE:
|
||||||
|
return simpleInstruction("OP_CLOSE_UPVALUE", offset);
|
||||||
case OP_RETURN:
|
case OP_RETURN:
|
||||||
return simpleInstruction("OP_RETURN", offset);
|
return simpleInstruction("OP_RETURN", offset);
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -18,6 +18,8 @@ void *reallocate(void *pointer, size_t oldSize, size_t newSize) {
|
||||||
static void freeObject(Obj *object) {
|
static void freeObject(Obj *object) {
|
||||||
switch (object->type) {
|
switch (object->type) {
|
||||||
case OBJ_CLOSURE: {
|
case OBJ_CLOSURE: {
|
||||||
|
ObjClosure *closure = (ObjClosure *)object;
|
||||||
|
FREE_ARRAY(ObjUpvalue *, closure->upvalues, closure->upvalueCount);
|
||||||
FREE(ObjClosure, object);
|
FREE(ObjClosure, object);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +38,9 @@ static void freeObject(Obj *object) {
|
||||||
FREE(ObjString, object);
|
FREE(ObjString, object);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OBJ_UPVALUE:
|
||||||
|
FREE(ObjUpvalue, object);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,15 @@ static Obj *allocateObject(size_t size, ObjType type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjClosure *newClosure(ObjFunction *function) {
|
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);
|
ObjClosure *closure = ALLOCATE_OBJ(ObjClosure, OBJ_CLOSURE);
|
||||||
closure->function = function;
|
closure->function = function;
|
||||||
|
closure->upvalues = upvalues;
|
||||||
|
closure->upvalueCount = function->upvalueCount;
|
||||||
return closure;
|
return closure;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +88,14 @@ ObjString *copyString(const char *chars, int length) {
|
||||||
return allocateString(heapChars, length, hash);
|
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) {
|
static void printFunction(ObjFunction *function) {
|
||||||
if (function->name == NULL) {
|
if (function->name == NULL) {
|
||||||
printf("<script>");
|
printf("<script>");
|
||||||
|
@ -103,5 +118,8 @@ void printObject(Value value) {
|
||||||
case OBJ_STRING:
|
case OBJ_STRING:
|
||||||
printf("%s", AS_CSTRING(value));
|
printf("%s", AS_CSTRING(value));
|
||||||
break;
|
break;
|
||||||
|
case OBJ_UPVALUE:
|
||||||
|
printf("upvalue");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ typedef enum {
|
||||||
OBJ_FUNCTION,
|
OBJ_FUNCTION,
|
||||||
OBJ_NATIVE,
|
OBJ_NATIVE,
|
||||||
OBJ_STRING,
|
OBJ_STRING,
|
||||||
|
OBJ_UPVALUE,
|
||||||
} ObjType;
|
} ObjType;
|
||||||
|
|
||||||
struct Obj {
|
struct Obj {
|
||||||
|
@ -52,9 +53,18 @@ struct ObjString {
|
||||||
uint32_t hash;
|
uint32_t hash;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct ObjUpvalue {
|
||||||
|
Obj obj;
|
||||||
|
Value *location;
|
||||||
|
Value closed;
|
||||||
|
struct ObjUpvalue *next;
|
||||||
|
} ObjUpvalue;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Obj obj;
|
Obj obj;
|
||||||
ObjFunction *function;
|
ObjFunction *function;
|
||||||
|
ObjUpvalue **upvalues;
|
||||||
|
int upvalueCount;
|
||||||
} ObjClosure;
|
} ObjClosure;
|
||||||
|
|
||||||
ObjClosure *newClosure(ObjFunction *function);
|
ObjClosure *newClosure(ObjFunction *function);
|
||||||
|
@ -62,6 +72,7 @@ ObjFunction *newFunction();
|
||||||
ObjNative *newNative(NativeFn function);
|
ObjNative *newNative(NativeFn function);
|
||||||
ObjString *takeString(char *chars, int length);
|
ObjString *takeString(char *chars, int length);
|
||||||
ObjString *copyString(const char *chars, int length);
|
ObjString *copyString(const char *chars, int length);
|
||||||
|
ObjUpvalue *newUpvalue(Value *slot);
|
||||||
void printObject(Value value);
|
void printObject(Value value);
|
||||||
|
|
||||||
static inline bool isObjType(Value value, ObjType type) {
|
static inline bool isObjType(Value value, ObjType type) {
|
||||||
|
|
|
@ -19,6 +19,7 @@ static Value clockNative(int argCount, Value *args) {
|
||||||
static void resetStack() {
|
static void resetStack() {
|
||||||
vm.stackTop = vm.stack;
|
vm.stackTop = vm.stack;
|
||||||
vm.frameCount = 0;
|
vm.frameCount = 0;
|
||||||
|
vm.openUpvalues = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void runtimeError(const char *format, ...) {
|
static void runtimeError(const char *format, ...) {
|
||||||
|
@ -118,6 +119,39 @@ static bool callValue(Value callee, int argCount) {
|
||||||
return false;
|
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) {
|
static bool isFalsey(Value value) {
|
||||||
return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
|
return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
|
||||||
}
|
}
|
||||||
|
@ -225,6 +259,16 @@ static InterpretResult run() {
|
||||||
}
|
}
|
||||||
break;
|
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: {
|
case OP_EQUAL: {
|
||||||
Value b = pop();
|
Value b = pop();
|
||||||
Value a = pop();
|
Value a = pop();
|
||||||
|
@ -303,10 +347,24 @@ static InterpretResult run() {
|
||||||
ObjFunction *function = AS_FUNCTION(READ_CONSTANT());
|
ObjFunction *function = AS_FUNCTION(READ_CONSTANT());
|
||||||
ObjClosure *closure = newClosure(function);
|
ObjClosure *closure = newClosure(function);
|
||||||
push(OBJ_VAL(closure));
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
case OP_CLOSE_UPVALUE:
|
||||||
|
closeUpvalues(vm.stackTop - 1);
|
||||||
|
pop();
|
||||||
|
break;
|
||||||
case OP_RETURN: {
|
case OP_RETURN: {
|
||||||
Value result = pop();
|
Value result = pop();
|
||||||
|
closeUpvalues(frame->slots);
|
||||||
vm.frameCount--;
|
vm.frameCount--;
|
||||||
if (vm.frameCount == 0) {
|
if (vm.frameCount == 0) {
|
||||||
pop();
|
pop();
|
||||||
|
|
|
@ -23,6 +23,7 @@ typedef struct {
|
||||||
Value *stackTop;
|
Value *stackTop;
|
||||||
Table globals;
|
Table globals;
|
||||||
Table strings;
|
Table strings;
|
||||||
|
ObjUpvalue *openUpvalues;
|
||||||
Obj *objects;
|
Obj *objects;
|
||||||
} VM;
|
} VM;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue