Skip to content

Commit 368aee7

Browse files
authored
Refactor VM implementation with error handling
1 parent b9a7c8c commit 368aee7

File tree

1 file changed

+156
-73
lines changed

1 file changed

+156
-73
lines changed

src/runtime/vm.c

Lines changed: 156 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,118 @@
1+
/* src/runtime/vm.c */
2+
#include <stdarg.h>
13
#include <stdio.h>
4+
#include <string.h>
5+
#include <time.h>
26

3-
#include "common.h"
4-
#include "debug.h"
5-
#include "vm.h"
7+
#include "../include/common.h"
8+
#include "../include/compiler.h"
9+
#include "../include/debug.h"
10+
#include "../include/object.h"
11+
#include "../include/memory.h"
12+
#include "../include/vm.h"
13+
#include "../include/table.h"
614

7-
void initVM(VM *vm) { vm->stackTop = vm->stack; }
15+
// Helper to reset the stack
16+
static void resetStack(VM* vm) {
17+
vm->stackTop = vm->stack;
18+
}
19+
20+
// Runtime Error Helper
21+
static void runtimeError(VM* vm, const char* format, ...) {
22+
va_list args;
23+
va_start(args, format);
24+
vfprintf(stderr, format, args);
25+
va_end(args);
26+
fputs("\n", stderr);
27+
28+
size_t instruction = vm->ip - vm->chunk->code - 1;
29+
int line = vm->chunk->lines[instruction];
30+
fprintf(stderr, "[line %d] in script\n", line);
31+
resetStack(vm);
32+
}
833

9-
void freeVM(VM *vm) {
10-
// Nothing to free yet
34+
// Initialize the VM
35+
void initVM(VM* vm) {
36+
resetStack(vm);
37+
vm->objects = NULL;
38+
initTable(&vm->globals);
39+
initTable(&vm->strings);
40+
41+
// You can register native functions here if you want them globally available
42+
// defineNative(vm, "clock", clockNative);
1143
}
1244

13-
void push(VM *vm, Value value) {
45+
// Free the VM resources
46+
void freeVM(VM* vm) {
47+
freeTable(&vm->globals);
48+
freeTable(&vm->strings);
49+
freeObjects(); // Frees the linked list of objects
50+
}
51+
52+
void push(VM* vm, Value value) {
1453
*vm->stackTop = value;
1554
vm->stackTop++;
1655
}
1756

18-
Value pop(VM *vm) {
57+
Value pop(VM* vm) {
1958
vm->stackTop--;
2059
return *vm->stackTop;
2160
}
2261

23-
static InterpretResult run(VM *vm) {
62+
static Value peek(VM* vm, int distance) {
63+
return vm->stackTop[-1 - distance];
64+
}
65+
66+
// Helper to check for false/nil
67+
static bool isFalsey(Value value) {
68+
return IS_NIL(value) || (IS_BOOL(value) && !AS_BOOL(value));
69+
}
70+
71+
// Concatenate two strings
72+
static void concatenate(VM* vm) {
73+
ObjString* b = AS_STRING(peek(vm, 0));
74+
ObjString* a = AS_STRING(peek(vm, 1));
75+
76+
int length = a->length + b->length;
77+
char* chars = ALLOCATE(char, length + 1);
78+
memcpy(chars, a->chars, a->length);
79+
memcpy(chars + a->length, b->chars, b->length);
80+
chars[length] = '\0';
81+
82+
ObjString* result = takeString(chars, length);
83+
pop(vm);
84+
pop(vm);
85+
push(vm, OBJ_VAL(result));
86+
}
87+
88+
// Define a native function
89+
void defineNative(VM* vm, const char* name, NativeFn function) {
90+
push(vm, OBJ_VAL(copyString(name, (int)strlen(name))));
91+
push(vm, OBJ_VAL(newNative(function)));
92+
tableSet(&vm->globals, AS_STRING(vm->stack[0]), vm->stack[1]);
93+
pop(vm);
94+
pop(vm);
95+
}
96+
97+
// Main execution loop
98+
static InterpretResult run(VM* vm) {
2499
#define READ_BYTE() (*vm->ip++)
25100
#define READ_CONSTANT() (vm->chunk->constants.values[READ_BYTE()])
26-
#define BINARY_OP(valueType, op) \
27-
do { \
28-
if (!IS_NUMBER((*(vm->stackTop - 1))) || \
29-
!IS_NUMBER((*(vm->stackTop - 2)))) { \
30-
printf("Operands must be numbers.\n"); \
31-
return INTERPRET_RUNTIME_ERROR; \
32-
} \
33-
double b = AS_NUMBER(pop(vm)); \
34-
double a = AS_NUMBER(pop(vm)); \
35-
push(vm, valueType(a op b)); \
36-
} while (false)
101+
#define BINARY_OP(valueType, op) \
102+
do { \
103+
if (!IS_NUMBER(peek(vm, 0)) || !IS_NUMBER(peek(vm, 1))) { \
104+
runtimeError(vm, "Operands must be numbers."); \
105+
return INTERPRET_RUNTIME_ERROR; \
106+
} \
107+
double b = AS_NUMBER(pop(vm)); \
108+
double a = AS_NUMBER(pop(vm)); \
109+
push(vm, valueType(a op b)); \
110+
} while (false)
37111

38112
for (;;) {
39113
#ifdef DEBUG_TRACE_EXECUTION
40114
printf(" ");
41-
for (Value *slot = vm->stack; slot < vm->stackTop; slot++) {
115+
for (Value* slot = vm->stack; slot < vm->stackTop; slot++) {
42116
printf("[ ");
43117
printValue(*slot);
44118
printf(" ]");
@@ -47,57 +121,52 @@ static InterpretResult run(VM *vm) {
47121
disassembleInstruction(vm->chunk, (int)(vm->ip - vm->chunk->code));
48122
#endif
49123

50-
u8 instruction;
124+
uint8_t instruction;
51125
switch (instruction = READ_BYTE()) {
52-
case OP_CONSTANT: {
53-
Value constant = READ_CONSTANT();
54-
push(vm, constant);
55-
break;
56-
}
57-
case OP_TRUE:
58-
push(vm, BOOL_VAL(true));
59-
break;
60-
case OP_FALSE:
61-
push(vm, BOOL_VAL(false));
62-
break;
63-
case OP_NULL:
64-
push(vm, NULL_VAL);
65-
break;
66-
case OP_POP:
67-
pop(vm);
68-
break;
69-
case OP_ADD:
70-
BINARY_OP(NUMBER_VAL, +);
71-
break;
72-
case OP_SUBTRACT:
73-
BINARY_OP(NUMBER_VAL, -);
74-
break;
75-
case OP_MULTIPLY:
76-
BINARY_OP(NUMBER_VAL, *);
77-
break;
78-
case OP_DIVIDE:
79-
BINARY_OP(NUMBER_VAL, /);
80-
break;
81-
case OP_NOT:
82-
push(vm, BOOL_VAL(IS_NULL(*(vm->stackTop - 1)) ||
83-
(IS_BOOL(*(vm->stackTop - 1)) &&
84-
!AS_BOOL(*(vm->stackTop - 1)))));
85-
break;
86-
case OP_NEGATE:
87-
if (!IS_NUMBER(*(vm->stackTop - 1))) {
88-
printf("Operand must be a number.\n");
89-
return INTERPRET_RUNTIME_ERROR;
126+
case OP_CONSTANT: {
127+
Value constant = READ_CONSTANT();
128+
push(vm, constant);
129+
break;
130+
}
131+
case OP_TRUE: push(vm, BOOL_VAL(true)); break;
132+
case OP_FALSE: push(vm, BOOL_VAL(false)); break;
133+
case OP_POP: pop(vm); break;
134+
case OP_NULL: push(vm, NULL_VAL); break;
135+
136+
case OP_ADD: {
137+
if (IS_STRING(peek(vm, 0)) && IS_STRING(peek(vm, 1))) {
138+
concatenate(vm);
139+
} else if (IS_NUMBER(peek(vm, 0)) && IS_NUMBER(peek(vm, 1))) {
140+
double b = AS_NUMBER(pop(vm));
141+
double a = AS_NUMBER(pop(vm));
142+
push(vm, NUMBER_VAL(a + b));
143+
} else {
144+
runtimeError(vm, "Operands must be two numbers or two strings.");
145+
return INTERPRET_RUNTIME_ERROR;
146+
}
147+
break;
148+
}
149+
case OP_SUBTRACT: BINARY_OP(NUMBER_VAL, -); break;
150+
case OP_MULTIPLY: BINARY_OP(NUMBER_VAL, *); break;
151+
case OP_DIVIDE: BINARY_OP(NUMBER_VAL, /); break;
152+
case OP_NOT:
153+
push(vm, BOOL_VAL(isFalsey(pop(vm))));
154+
break;
155+
case OP_NEGATE:
156+
if (!IS_NUMBER(peek(vm, 0))) {
157+
runtimeError(vm, "Operand must be a number.");
158+
return INTERPRET_RUNTIME_ERROR;
159+
}
160+
push(vm, NUMBER_VAL(-AS_NUMBER(pop(vm))));
161+
break;
162+
case OP_PRINT: {
163+
printValue(pop(vm));
164+
printf("\n");
165+
break;
166+
}
167+
case OP_RETURN: {
168+
return INTERPRET_OK;
90169
}
91-
push(vm, NUMBER_VAL(-AS_NUMBER(pop(vm))));
92-
break;
93-
case OP_PRINT: {
94-
printValue(pop(vm));
95-
printf("\n");
96-
break;
97-
}
98-
case OP_RETURN: {
99-
return INTERPRET_OK;
100-
}
101170
}
102171
}
103172

@@ -106,8 +175,22 @@ static InterpretResult run(VM *vm) {
106175
#undef BINARY_OP
107176
}
108177

109-
InterpretResult interpret(VM *vm, Chunk *chunk) {
110-
vm->chunk = chunk;
178+
// Compile and Run
179+
InterpretResult interpret(VM* vm, const char* source) {
180+
Chunk chunk;
181+
initChunk(&chunk);
182+
183+
if (!compile(source, &chunk)) {
184+
freeChunk(&chunk);
185+
return INTERPRET_COMPILE_ERROR;
186+
}
187+
188+
vm->chunk = &chunk;
111189
vm->ip = vm->chunk->code;
112-
return run(vm);
190+
191+
InterpretResult result = run(vm);
192+
193+
freeChunk(&chunk);
194+
return result;
113195
}
196+

0 commit comments

Comments
 (0)