Skip to content

Commit 2de119c

Browse files
committed
feat: Implement the core Virtual Machine for ProXPL, including bytecode interpretation, stack management, and runtime error handling.
1 parent 2297429 commit 2de119c

2 files changed

Lines changed: 148 additions & 0 deletions

File tree

include/vm.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,12 @@ Value pop(VM* vm);
5656
Value peek(VM* vm, int distance);
5757
bool isFalsey(Value value);
5858
void defineNative(VM* vm, const char* name, NativeFn function);
59+
bool bindMethod(struct ObjClass *klass, struct ObjString *name, VM *vm);
60+
void defineMethod(struct ObjString *name, VM *vm);
61+
void closeUpvalues(VM *vm, Value *last);
62+
struct ObjUpvalue *captureUpvalue(Value *local, VM *vm);
63+
bool invokeFromClass(struct ObjClass *klass, struct ObjString *name, int argCount, VM *vm);
64+
bool invoke(struct ObjString *name, int argCount, VM *vm);
65+
bool callValue(Value callee, int argCount, VM *vm);
5966

6067
#endif // PROX_VM_H

src/runtime/vm.c

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,148 @@ static void concatenate(VM* pvm) {
107107
push(pvm, OBJ_VAL(result));
108108
}
109109

110+
111+
void closeUpvalues(VM *vm, Value *last) {
112+
while (vm->openUpvalues != NULL &&
113+
vm->openUpvalues->location >= last) {
114+
ObjUpvalue *upvalue = vm->openUpvalues;
115+
upvalue->closed = *upvalue->location;
116+
upvalue->location = &upvalue->closed;
117+
vm->openUpvalues = upvalue->next;
118+
}
119+
}
120+
121+
ObjUpvalue *captureUpvalue(Value *local, VM *vm) {
122+
ObjUpvalue *prevUpvalue = NULL;
123+
ObjUpvalue *upvalue = vm->openUpvalues;
124+
while (upvalue != NULL && upvalue->location > local) {
125+
prevUpvalue = upvalue;
126+
upvalue = upvalue->next;
127+
}
128+
129+
if (upvalue != NULL && upvalue->location == local) {
130+
return upvalue;
131+
}
132+
133+
ObjUpvalue *createdUpvalue = newUpvalue(local);
134+
createdUpvalue->next = upvalue;
135+
136+
if (prevUpvalue == NULL) {
137+
vm->openUpvalues = createdUpvalue;
138+
} else {
139+
prevUpvalue->next = createdUpvalue;
140+
}
141+
142+
return createdUpvalue;
143+
}
144+
145+
void defineMethod(ObjString *name, VM *vm) {
146+
Value method = peek(vm, 0);
147+
ObjClass *klass = AS_CLASS(peek(vm, 1));
148+
tableSet(&klass->methods, name, method);
149+
pop(vm);
150+
}
151+
152+
bool bindMethod(struct ObjClass *klass, ObjString *name, VM *vm) {
153+
Value method;
154+
if (!tableGet(&klass->methods, name, &method)) {
155+
runtimeError(vm, "Undefined property '%s'.", name->chars);
156+
return false;
157+
}
158+
159+
ObjBoundMethod *bound = newBoundMethod(peek(vm, 0), AS_CLOSURE(method));
160+
pop(vm);
161+
push(vm, OBJ_VAL(bound));
162+
return true;
163+
}
164+
165+
bool call(ObjClosure *closure, int argCount, VM *vm) {
166+
if (argCount != closure->function->arity) {
167+
runtimeError(vm, "Expected %d arguments but got %d.",
168+
closure->function->arity, argCount);
169+
return false;
170+
}
171+
172+
if (vm->frameCount == 64) {
173+
runtimeError(vm, "Stack overflow.");
174+
return false;
175+
}
176+
177+
CallFrame *frame = &vm->frames[vm->frameCount++];
178+
frame->closure = closure;
179+
frame->ip = closure->function->chunk.code;
180+
frame->slots = vm->stackTop - argCount - 1;
181+
return true;
182+
}
183+
184+
bool callValue(Value callee, int argCount, VM *vm) {
185+
if (IS_OBJ(callee)) {
186+
switch (OBJ_TYPE(callee)) {
187+
case OBJ_BOUND_METHOD: {
188+
ObjBoundMethod *bound = AS_BOUND_METHOD(callee);
189+
vm->stackTop[-argCount - 1] = bound->receiver;
190+
return call(bound->method, argCount, vm);
191+
}
192+
case OBJ_CLASS: {
193+
ObjClass *klass = AS_CLASS(callee);
194+
vm->stackTop[-argCount - 1] = OBJ_VAL(newInstance(klass));
195+
Value initializer;
196+
if (tableGet(&klass->methods, copyString("init", 4), &initializer)) {
197+
return call(AS_CLOSURE(initializer), argCount, vm);
198+
} else if (argCount != 0) {
199+
runtimeError(vm, "Expected 0 arguments but got %d.", argCount);
200+
return false;
201+
}
202+
return true;
203+
}
204+
case OBJ_CLOSURE:
205+
return call(AS_CLOSURE(callee), argCount, vm);
206+
case OBJ_NATIVE: {
207+
NativeFn native = AS_NATIVE(callee);
208+
Value result = native(argCount, vm->stackTop - argCount);
209+
vm->stackTop -= argCount + 1;
210+
push(vm, result);
211+
return true;
212+
}
213+
default:
214+
break; // Non-callable object type
215+
}
216+
}
217+
runtimeError(vm, "Can only call functions and classes.");
218+
return false;
219+
}
220+
221+
bool invokeFromClass(struct ObjClass *klass, ObjString *name,
222+
int argCount, VM *vm) {
223+
Value method;
224+
if (!tableGet(&klass->methods, name, &method)) {
225+
runtimeError(vm, "Undefined property '%s'.", name->chars);
226+
return false;
227+
}
228+
return call(AS_CLOSURE(method), argCount, vm);
229+
}
230+
231+
bool invoke(ObjString *name, int argCount, VM *vm) {
232+
Value receiver = peek(vm, argCount);
233+
234+
if (!IS_INSTANCE(receiver)) {
235+
runtimeError(vm, "Only instances have methods.");
236+
return false;
237+
}
238+
239+
struct ObjInstance *instance = AS_INSTANCE(receiver);
240+
241+
Value value;
242+
if (tableGet(&instance->fields, name, &value)) {
243+
vm->stackTop[-argCount - 1] = value;
244+
return callValue(value, argCount, vm);
245+
}
246+
247+
return invokeFromClass(instance->klass, name, argCount, vm);
248+
}
249+
110250
static InterpretResult run(VM *vm) {
251+
111252
CallFrame* frame = &vm->frames[vm->frameCount - 1];
112253

113254
#define READ_BYTE() (*frame->ip++)

0 commit comments

Comments
 (0)