wrap up closure implementation from chapter 25
This commit is contained in:
parent
076da0bc1e
commit
78022fb701
7
chunk.h
7
chunk.h
@ -43,6 +43,13 @@ typedef enum {
|
||||
OP_JUMP,
|
||||
OP_LOOP,
|
||||
OP_CALL,
|
||||
OP_CLOSURE,
|
||||
OP_CLOSURE_LONG,
|
||||
OP_GET_UPVALUE,
|
||||
OP_GET_UPVALUE_LONG,
|
||||
OP_SET_UPVALUE,
|
||||
OP_SET_UPVALUE_LONG,
|
||||
OP_CLOSE_UPVALUE,
|
||||
} KrkOpCode;
|
||||
|
||||
/**
|
||||
|
84
compiler.c
84
compiler.c
@ -40,8 +40,14 @@ typedef struct {
|
||||
typedef struct {
|
||||
KrkToken name;
|
||||
ssize_t depth;
|
||||
int isCaptured;
|
||||
} Local;
|
||||
|
||||
typedef struct {
|
||||
size_t index;
|
||||
int isLocal;
|
||||
} Upvalue;
|
||||
|
||||
typedef enum {
|
||||
TYPE_FUNCTION,
|
||||
TYPE_MODULE,
|
||||
@ -55,6 +61,7 @@ typedef struct Compiler {
|
||||
Local locals[MAX_LOCALS];
|
||||
size_t localCount;
|
||||
size_t scopeDepth;
|
||||
Upvalue upvalues[MAX_LOCALS];
|
||||
} Compiler;
|
||||
|
||||
Parser parser;
|
||||
@ -64,6 +71,9 @@ static KrkChunk * currentChunk() {
|
||||
return ¤t->function->chunk;
|
||||
}
|
||||
|
||||
#define EMIT_CONSTANT_OP(opc, arg) do { if (arg < 256) { emitBytes(opc, arg); } \
|
||||
else { emitBytes(opc ## _LONG, arg >> 16); emitBytes(arg >> 8, arg); } } while (0)
|
||||
|
||||
static void initCompiler(Compiler * compiler, FunctionType type) {
|
||||
compiler->enclosing = current;
|
||||
compiler->function = NULL;
|
||||
@ -81,6 +91,7 @@ static void initCompiler(Compiler * compiler, FunctionType type) {
|
||||
local->depth = 0;
|
||||
local->name.start = "";
|
||||
local->name.length = 0;
|
||||
local->isCaptured = 0;
|
||||
}
|
||||
|
||||
static void parsePrecedence(Precedence precedence);
|
||||
@ -327,7 +338,11 @@ static void endScope() {
|
||||
current->scopeDepth--;
|
||||
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--;
|
||||
}
|
||||
}
|
||||
@ -377,7 +392,14 @@ static void function(FunctionType type, int blockWidth) {
|
||||
block(blockWidth);
|
||||
|
||||
KrkFunction * function = endCompiler();
|
||||
emitConstant(OBJECT_VAL(function));
|
||||
size_t ind = krk_addConstant(currentChunk(), OBJECT_VAL(function));
|
||||
EMIT_CONSTANT_OP(OP_CLOSURE, ind);
|
||||
|
||||
for (size_t i = 0; i < function->upvalueCount; ++i) {
|
||||
/* TODO: if the maximum count changes, fix the sizes for this */
|
||||
emitByte(compiler.upvalues[i].isLocal ? 1 : 0);
|
||||
emitByte(compiler.upvalues[i].index);
|
||||
}
|
||||
}
|
||||
|
||||
static void markInitialized() {
|
||||
@ -594,28 +616,57 @@ static void codepoint(int canAssign) {
|
||||
}
|
||||
*/
|
||||
|
||||
#define EMIT_CONSTANT_OP(opc, arg) do { if (arg < 256) { emitBytes(opc, arg); } \
|
||||
else { emitBytes(opc ## _LONG, arg >> 16); emitBytes(arg >> 8, arg); } } while (0)
|
||||
static size_t addUpvalue(Compiler * compiler, ssize_t index, int isLocal) {
|
||||
size_t upvalueCount = compiler->function->upvalueCount;
|
||||
for (size_t i = 0; i < upvalueCount; ++i) {
|
||||
Upvalue * upvalue = &compiler->upvalues[i];
|
||||
if (upvalue->index == index && upvalue->isLocal == isLocal) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
if (upvalueCount == MAX_LOCALS) {
|
||||
error("Too many closure variables in function.");
|
||||
return 0;
|
||||
}
|
||||
compiler->upvalues[upvalueCount].isLocal = isLocal;
|
||||
compiler->upvalues[upvalueCount].index = index;
|
||||
return compiler->function->upvalueCount++;
|
||||
}
|
||||
|
||||
static ssize_t resolveUpvalue(Compiler * compiler, KrkToken * name) {
|
||||
if (compiler->enclosing == NULL) return -1;
|
||||
ssize_t local = resolveLocal(compiler->enclosing, name);
|
||||
if (local != -1) {
|
||||
compiler->enclosing->locals[local].isCaptured = 1;
|
||||
return addUpvalue(compiler, local, 1);
|
||||
}
|
||||
ssize_t upvalue = resolveUpvalue(compiler->enclosing, name);
|
||||
if (upvalue != -1) {
|
||||
return addUpvalue(compiler, upvalue, 0);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define DO_VARIABLE(opset,opget) do { \
|
||||
if (canAssign && match(TOKEN_EQUAL)) { \
|
||||
expression(); \
|
||||
EMIT_CONSTANT_OP(opset, arg); \
|
||||
} else { \
|
||||
EMIT_CONSTANT_OP(opget, arg); \
|
||||
} } while (0)
|
||||
|
||||
static void namedVariable(KrkToken name, int canAssign) {
|
||||
ssize_t arg = resolveLocal(current, &name);
|
||||
if (arg != -1) {
|
||||
if (canAssign && match(TOKEN_EQUAL)) {
|
||||
expression();
|
||||
EMIT_CONSTANT_OP(OP_SET_LOCAL, arg);
|
||||
} else {
|
||||
EMIT_CONSTANT_OP(OP_GET_LOCAL, arg);
|
||||
}
|
||||
DO_VARIABLE(OP_SET_LOCAL, OP_GET_LOCAL);
|
||||
} else if ((arg = resolveUpvalue(current, &name)) != -1) {
|
||||
DO_VARIABLE(OP_SET_UPVALUE, OP_GET_UPVALUE);
|
||||
} else {
|
||||
arg = identifierConstant(&name);
|
||||
if (canAssign && match(TOKEN_EQUAL)) {
|
||||
expression();
|
||||
EMIT_CONSTANT_OP(OP_SET_GLOBAL, arg);
|
||||
} else {
|
||||
EMIT_CONSTANT_OP(OP_GET_GLOBAL, arg);
|
||||
}
|
||||
DO_VARIABLE(OP_SET_GLOBAL, OP_GET_GLOBAL);
|
||||
}
|
||||
}
|
||||
#undef DO_VARIABLE
|
||||
|
||||
static void variable(int canAssign) {
|
||||
namedVariable(parser.previous, canAssign);
|
||||
@ -722,6 +773,7 @@ static void addLocal(KrkToken name) {
|
||||
Local * local = ¤t->locals[current->localCount++];
|
||||
local->name = name;
|
||||
local->depth = -1;
|
||||
local->isCaptured = 0;
|
||||
}
|
||||
|
||||
static void declareVariable() {
|
||||
|
25
debug.c
25
debug.c
@ -11,16 +11,18 @@ void krk_disassembleChunk(KrkChunk * chunk, const char * name) {
|
||||
}
|
||||
|
||||
#define SIMPLE(opc) case opc: fprintf(stderr, "%s\n", #opc); return offset + 1;
|
||||
#define CONSTANT(opc) case opc: { size_t constant = chunk->code[offset + 1]; \
|
||||
#define CONSTANT(opc,more) case opc: { size_t constant = chunk->code[offset + 1]; \
|
||||
fprintf(stderr, "%-16s %4d '", #opc, (int)constant); \
|
||||
krk_printValue(stderr, chunk->constants.values[constant]); \
|
||||
fprintf(stderr,"' (type=%s)\n", typeName(chunk->constants.values[constant])); \
|
||||
more; \
|
||||
return offset + 2; } \
|
||||
case opc ## _LONG: { size_t constant = (chunk->code[offset + 1] << 16) | \
|
||||
(chunk->code[offset + 2] << 8) | (chunk->code[offset + 3]); \
|
||||
fprintf(stderr, "%-16s %4d '", #opc "_LONG", (int)constant); \
|
||||
krk_printValue(stderr, chunk->constants.values[constant]); \
|
||||
fprintf(stderr,"' (type=%s)\n", typeName(chunk->constants.values[constant])); \
|
||||
more; \
|
||||
return offset + 4; }
|
||||
#define OPERANDB(opc) case opc: { uint32_t operand = chunk->code[offset + 1]; \
|
||||
fprintf(stderr, "%-16s %4d\n", #opc, (int)operand); \
|
||||
@ -35,6 +37,15 @@ void krk_disassembleChunk(KrkChunk * chunk, const char * name) {
|
||||
fprintf(stderr, "%-16s %4d -> %d\n", #opc, (int)offset, (int)(offset + 3 sign jump)); \
|
||||
return offset + 3; }
|
||||
|
||||
#define CLOSURE_MORE \
|
||||
KrkFunction * function = AS_FUNCTION(chunk->constants.values[constant]); \
|
||||
for (size_t j = 0; j < function->upvalueCount; ++j) { \
|
||||
int isLocal = chunk->code[offset++]; \
|
||||
int index = chunk->code[offset++]; \
|
||||
fprintf(stderr, "%04d | %s %d\n", \
|
||||
(int)offset - 2, isLocal ? "local" : "upvalue", index); \
|
||||
}
|
||||
|
||||
size_t krk_disassembleInstruction(KrkChunk * chunk, size_t offset) {
|
||||
fprintf(stderr, "%04u ", (unsigned int)offset);
|
||||
if (offset > 0 && chunk->lines[offset] == chunk->lines[offset - 1]) {
|
||||
@ -60,17 +71,21 @@ size_t krk_disassembleInstruction(KrkChunk * chunk, size_t offset) {
|
||||
SIMPLE(OP_LESS)
|
||||
SIMPLE(OP_PRINT)
|
||||
SIMPLE(OP_POP)
|
||||
CONSTANT(OP_DEFINE_GLOBAL)
|
||||
CONSTANT(OP_CONSTANT)
|
||||
CONSTANT(OP_GET_GLOBAL)
|
||||
CONSTANT(OP_SET_GLOBAL)
|
||||
CONSTANT(OP_DEFINE_GLOBAL,(void)0)
|
||||
CONSTANT(OP_CONSTANT,(void)0)
|
||||
CONSTANT(OP_GET_GLOBAL,(void)0)
|
||||
CONSTANT(OP_SET_GLOBAL,(void)0)
|
||||
CONSTANT(OP_CLOSURE, CLOSURE_MORE)
|
||||
OPERANDL(OP_SET_LOCAL)
|
||||
OPERANDL(OP_GET_LOCAL)
|
||||
OPERANDL(OP_SET_UPVALUE)
|
||||
OPERANDL(OP_GET_UPVALUE)
|
||||
JUMP(OP_JUMP,+)
|
||||
JUMP(OP_JUMP_IF_FALSE,+)
|
||||
JUMP(OP_JUMP_IF_TRUE,+)
|
||||
JUMP(OP_LOOP,-)
|
||||
OPERANDB(OP_CALL)
|
||||
SIMPLE(OP_CLOSE_UPVALUE)
|
||||
default:
|
||||
fprintf(stderr, "Unknown opcode: %02x\n", opcode);
|
||||
return offset + 1;
|
||||
|
10
memory.c
10
memory.c
@ -29,6 +29,16 @@ static void freeObject(KrkObj * object) {
|
||||
FREE(KrkNative, object);
|
||||
break;
|
||||
}
|
||||
case OBJ_CLOSURE: {
|
||||
KrkClosure * closure = (KrkClosure*)object;
|
||||
FREE_ARRAY(KrkUpvalue*,closure->upvalues,closure->upvalueCount);
|
||||
FREE(KrkClosure, object);
|
||||
break;
|
||||
}
|
||||
case OBJ_UPVALUE: {
|
||||
FREE(KrkUpvalue, object);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
27
object.c
27
object.c
@ -70,12 +70,19 @@ void krk_printObject(FILE * f, KrkValue value) {
|
||||
case OBJ_NATIVE:
|
||||
fprintf(f, "<native bind>");
|
||||
break;
|
||||
case OBJ_CLOSURE:
|
||||
fprintf(f, "<closure <def %s>>", AS_CLOSURE(value)->function->name->chars);
|
||||
break;
|
||||
case OBJ_UPVALUE:
|
||||
fprintf(f, "<upvalue>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
KrkFunction * newFunction() {
|
||||
KrkFunction * function = ALLOCATE_OBJECT(KrkFunction, OBJ_FUNCTION);
|
||||
function->arity = 0;
|
||||
function->upvalueCount = 0;
|
||||
function->name = NULL;
|
||||
krk_initChunk(&function->chunk);
|
||||
return function;
|
||||
@ -86,3 +93,23 @@ KrkNative * newNative(NativeFn function) {
|
||||
native->function = function;
|
||||
return native;
|
||||
}
|
||||
|
||||
KrkClosure * newClosure(KrkFunction * function) {
|
||||
KrkUpvalue ** upvalues = ALLOCATE(KrkUpvalue*, function->upvalueCount);
|
||||
for (size_t i = 0; i < function->upvalueCount; ++i) {
|
||||
upvalues[i] = NULL;
|
||||
}
|
||||
KrkClosure * closure = ALLOCATE_OBJECT(KrkClosure, OBJ_CLOSURE);
|
||||
closure->function = function;
|
||||
closure->upvalues = upvalues;
|
||||
closure->upvalueCount = function->upvalueCount;
|
||||
return closure;
|
||||
}
|
||||
|
||||
KrkUpvalue * newUpvalue(KrkValue * slot) {
|
||||
KrkUpvalue * upvalue = ALLOCATE_OBJECT(KrkUpvalue, OBJ_UPVALUE);
|
||||
upvalue->location = slot;
|
||||
upvalue->next = NULL;
|
||||
upvalue->closed = NONE_VAL();
|
||||
return upvalue;
|
||||
}
|
||||
|
24
object.h
24
object.h
@ -14,11 +14,15 @@
|
||||
#define AS_FUNCTION(value) ((KrkFunction *)AS_OBJECT(value))
|
||||
#define IS_NATIVE(value) isObjType(value, OBJ_NATIVE)
|
||||
#define AS_NATIVE(value) (((KrkNative *)AS_OBJECT(value))->function)
|
||||
#define IS_CLOSURE(value) isObjType(value, OBJ_CLOSURE)
|
||||
#define AS_CLOSURE(value) ((KrkClosure *)AS_OBJECT(value))
|
||||
|
||||
typedef enum {
|
||||
OBJ_FUNCTION,
|
||||
OBJ_NATIVE,
|
||||
OBJ_CLOSURE,
|
||||
OBJ_STRING,
|
||||
OBJ_UPVALUE,
|
||||
} ObjType;
|
||||
|
||||
struct Obj {
|
||||
@ -33,13 +37,29 @@ struct ObjString {
|
||||
uint32_t hash;
|
||||
};
|
||||
|
||||
typedef struct KrkUpvalue {
|
||||
KrkObj obj;
|
||||
KrkValue * location;
|
||||
KrkValue closed;
|
||||
struct KrkUpvalue * next;
|
||||
} KrkUpvalue;
|
||||
|
||||
typedef struct {
|
||||
KrkObj obj;
|
||||
int arity;
|
||||
size_t upvalueCount;
|
||||
KrkChunk chunk;
|
||||
KrkString * name;
|
||||
} KrkFunction;
|
||||
|
||||
typedef struct {
|
||||
KrkObj obj;
|
||||
KrkFunction * function;
|
||||
KrkUpvalue ** upvalues;
|
||||
size_t upvalueCount;
|
||||
} KrkClosure;
|
||||
|
||||
|
||||
typedef KrkValue (*NativeFn)(int argCount, KrkValue* args);
|
||||
typedef struct {
|
||||
KrkObj obj;
|
||||
@ -56,3 +76,7 @@ extern void krk_printObject(FILE * f, KrkValue value);
|
||||
|
||||
extern KrkFunction * newFunction();
|
||||
extern KrkNative * newNative(NativeFn function);
|
||||
|
||||
extern KrkClosure * newClosure(KrkFunction * function);
|
||||
|
||||
extern KrkUpvalue * newUpvalue(KrkValue * slot);
|
||||
|
22
test.krk
22
test.krk
@ -64,3 +64,25 @@ result = sleep(0.5)
|
||||
print "Call to sleep returned: " + result
|
||||
|
||||
function("something else")
|
||||
|
||||
if True:
|
||||
let a = 1
|
||||
def f():
|
||||
print a
|
||||
let b = 2
|
||||
def g():
|
||||
print b
|
||||
let c = 3
|
||||
def h():
|
||||
print c
|
||||
|
||||
def outer():
|
||||
let x = "outside"
|
||||
def inner():
|
||||
print x
|
||||
return inner
|
||||
|
||||
print "Function is defined, creating it..."
|
||||
let closure = outer()
|
||||
print "And executing the result..."
|
||||
closure()
|
||||
|
96
vm.c
96
vm.c
@ -14,6 +14,7 @@ KrkVM vm;
|
||||
static void resetStack() {
|
||||
vm.stackTop = vm.stack;
|
||||
vm.frameCount = 0;
|
||||
vm.openUpvalues = NULL;
|
||||
}
|
||||
|
||||
static void runtimeError(const char * fmt, ...) {
|
||||
@ -25,7 +26,7 @@ static void runtimeError(const char * fmt, ...) {
|
||||
|
||||
for (int i = vm.frameCount - 1; i >= 0; i--) {
|
||||
CallFrame * frame = &vm.frames[i];
|
||||
KrkFunction * function = frame->function;
|
||||
KrkFunction * function = frame->closure->function;
|
||||
size_t instruction = frame->ip - function->chunk.code - 1;
|
||||
fprintf(stderr, "[line %d] in ", (int)function->chunk.lines[instruction]);
|
||||
if (function->name == NULL) {
|
||||
@ -86,9 +87,9 @@ static KrkValue krk_sleep(int argc, KrkValue argv[]) {
|
||||
return BOOLEAN_VAL(1);
|
||||
}
|
||||
|
||||
static int call(KrkFunction * function, int argCount) {
|
||||
if (argCount != function->arity) {
|
||||
runtimeError("Wrong number of arguments (%d expected, got %d)", function->arity, argCount);
|
||||
static int call(KrkClosure * closure, int argCount) {
|
||||
if (argCount != closure->function->arity) {
|
||||
runtimeError("Wrong number of arguments (%d expected, got %d)", closure->function->arity, argCount);
|
||||
return 0;
|
||||
}
|
||||
if (vm.frameCount == FRAMES_MAX) {
|
||||
@ -96,8 +97,8 @@ static int call(KrkFunction * function, int argCount) {
|
||||
return 0;
|
||||
}
|
||||
CallFrame * frame = &vm.frames[vm.frameCount++];
|
||||
frame->function = function;
|
||||
frame->ip = function->chunk.code;
|
||||
frame->closure = closure;
|
||||
frame->ip = closure->function->chunk.code;
|
||||
frame->slots = vm.stackTop - argCount - 1;
|
||||
return 1;
|
||||
}
|
||||
@ -105,8 +106,8 @@ static int call(KrkFunction * function, int argCount) {
|
||||
static int callValue(KrkValue callee, int argCount) {
|
||||
if (IS_OBJECT(callee)) {
|
||||
switch (OBJECT_TYPE(callee)) {
|
||||
case OBJ_FUNCTION:
|
||||
return call(AS_FUNCTION(callee), argCount);
|
||||
case OBJ_CLOSURE:
|
||||
return call(AS_CLOSURE(callee), argCount);
|
||||
case OBJ_NATIVE: {
|
||||
NativeFn native = AS_NATIVE(callee);
|
||||
KrkValue result = native(argCount, vm.stackTop - argCount);
|
||||
@ -122,6 +123,35 @@ static int callValue(KrkValue callee, int argCount) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static KrkUpvalue * captureUpvalue(KrkValue * local) {
|
||||
KrkUpvalue * prevUpvalue = NULL;
|
||||
KrkUpvalue * upvalue = vm.openUpvalues;
|
||||
while (upvalue != NULL && upvalue->location > local) {
|
||||
prevUpvalue = upvalue;
|
||||
upvalue = upvalue->next;
|
||||
}
|
||||
if (upvalue && upvalue->location == local) {
|
||||
return upvalue;
|
||||
}
|
||||
KrkUpvalue * createdUpvalue = newUpvalue(local);
|
||||
createdUpvalue->next = upvalue;
|
||||
if (prevUpvalue == NULL) {
|
||||
vm.openUpvalues = createdUpvalue;
|
||||
} else {
|
||||
prevUpvalue->next = createdUpvalue;
|
||||
}
|
||||
return createdUpvalue;
|
||||
}
|
||||
|
||||
static void closeUpvalues(KrkValue * last) {
|
||||
while (vm.openUpvalues != NULL && vm.openUpvalues->location >= last) {
|
||||
KrkUpvalue * upvalue = vm.openUpvalues;
|
||||
upvalue->closed = *upvalue->location;
|
||||
upvalue->location = &upvalue->closed;
|
||||
vm.openUpvalues = upvalue->next;
|
||||
}
|
||||
}
|
||||
|
||||
void krk_initVM() {
|
||||
resetStack();
|
||||
vm.objects = NULL;
|
||||
@ -151,6 +181,9 @@ const char * typeName(KrkValue value) {
|
||||
if (value.type == VAL_FLOATING) return "Floating";
|
||||
if (value.type == VAL_OBJECT) {
|
||||
if (IS_STRING(value)) return "String";
|
||||
if (IS_FUNCTION(value)) return "Function";
|
||||
if (IS_NATIVE(value)) return "Native";
|
||||
if (IS_CLOSURE(value)) return "Closure";
|
||||
return "(Unspecified Object)";
|
||||
}
|
||||
return "???";
|
||||
@ -233,7 +266,7 @@ static void addObjects() {
|
||||
|
||||
#define READ_BYTE() (*frame->ip++)
|
||||
#define BINARY_OP(op) { KrkValue b = krk_pop(); KrkValue a = krk_pop(); krk_push(op(a,b)); break; }
|
||||
#define READ_CONSTANT(s) (frame->function->chunk.constants.values[readBytes(frame,s)])
|
||||
#define READ_CONSTANT(s) (frame->closure->function->chunk.constants.values[readBytes(frame,s)])
|
||||
#define READ_STRING(s) AS_STRING(READ_CONSTANT(s))
|
||||
|
||||
static size_t readBytes(CallFrame * frame, int num) {
|
||||
@ -264,8 +297,8 @@ static KrkValue run() {
|
||||
fprintf(stderr, " ]");
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
krk_disassembleInstruction(&frame->function->chunk,
|
||||
(size_t)(frame->ip - frame->function->chunk.code));
|
||||
krk_disassembleInstruction(&frame->closure->function->chunk,
|
||||
(size_t)(frame->ip - frame->closure->function->chunk.code));
|
||||
#endif
|
||||
uint8_t opcode;
|
||||
switch ((opcode = READ_BYTE())) {
|
||||
@ -276,6 +309,7 @@ static KrkValue run() {
|
||||
}
|
||||
case OP_RETURN: {
|
||||
KrkValue result = krk_pop();
|
||||
closeUpvalues(frame->slots);
|
||||
vm.frameCount--;
|
||||
if (vm.frameCount == 0) {
|
||||
krk_pop();
|
||||
@ -312,7 +346,7 @@ static KrkValue run() {
|
||||
case OP_CONSTANT_LONG:
|
||||
case OP_CONSTANT: {
|
||||
size_t index = readBytes(frame, opcode == OP_CONSTANT ? 1 : 3);
|
||||
KrkValue constant = frame->function->chunk.constants.values[index];
|
||||
KrkValue constant = frame->closure->function->chunk.constants.values[index];
|
||||
krk_push(constant);
|
||||
break;
|
||||
}
|
||||
@ -389,6 +423,38 @@ static KrkValue run() {
|
||||
frame = &vm.frames[vm.frameCount - 1];
|
||||
break;
|
||||
}
|
||||
case OP_CLOSURE_LONG:
|
||||
case OP_CLOSURE: {
|
||||
KrkFunction * function = AS_FUNCTION(READ_CONSTANT((opcode == OP_CLOSURE ? 1 : 3)));
|
||||
KrkClosure * closure = newClosure(function);
|
||||
krk_push(OBJECT_VAL(closure));
|
||||
for (size_t i = 0; i < closure->upvalueCount; ++i) {
|
||||
int isLocal = READ_BYTE();
|
||||
int index = READ_BYTE();
|
||||
if (isLocal) {
|
||||
closure->upvalues[i] = captureUpvalue(frame->slots + index);
|
||||
} else {
|
||||
closure->upvalues[i] = frame->closure->upvalues[index];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OP_GET_UPVALUE_LONG:
|
||||
case OP_GET_UPVALUE: {
|
||||
int slot = readBytes(frame, (opcode == OP_GET_UPVALUE) ? 1 : 3);
|
||||
krk_push(*frame->closure->upvalues[slot]->location);
|
||||
break;
|
||||
}
|
||||
case OP_SET_UPVALUE_LONG:
|
||||
case OP_SET_UPVALUE: {
|
||||
int slot = readBytes(frame, (opcode == OP_SET_UPVALUE) ? 1 : 3);
|
||||
*frame->closure->upvalues[slot]->location = krk_peek(0);
|
||||
break;
|
||||
}
|
||||
case OP_CLOSE_UPVALUE:
|
||||
closeUpvalues(vm.stackTop - 1);
|
||||
krk_pop();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -400,7 +466,11 @@ int krk_interpret(const char * src) {
|
||||
KrkFunction * function = krk_compile(src);
|
||||
|
||||
krk_push(OBJECT_VAL(function));
|
||||
callValue(OBJECT_VAL(function), 0);
|
||||
KrkClosure * closure = newClosure(function);
|
||||
krk_pop();
|
||||
|
||||
krk_push(OBJECT_VAL(closure));
|
||||
callValue(OBJECT_VAL(closure), 0);
|
||||
|
||||
KrkValue result = run();
|
||||
return IS_NONE(result);
|
||||
|
3
vm.h
3
vm.h
@ -8,7 +8,7 @@
|
||||
#define FRAMES_MAX 64
|
||||
|
||||
typedef struct {
|
||||
KrkFunction * function;
|
||||
KrkClosure * closure;
|
||||
uint8_t * ip;
|
||||
KrkValue * slots;
|
||||
} CallFrame;
|
||||
@ -21,6 +21,7 @@ typedef struct {
|
||||
KrkValue * stackTop;
|
||||
KrkTable globals;
|
||||
KrkTable strings;
|
||||
KrkUpvalue * openUpvalues;
|
||||
KrkObj * objects;
|
||||
} KrkVM;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user