aboutsummaryrefslogtreecommitdiffstats
path: root/src/bytecode/vm.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/bytecode/vm.h')
-rwxr-xr-xsrc/bytecode/vm.h396
1 files changed, 0 insertions, 396 deletions
diff --git a/src/bytecode/vm.h b/src/bytecode/vm.h
deleted file mode 100755
index d363958..0000000
--- a/src/bytecode/vm.h
+++ /dev/null
@@ -1,396 +0,0 @@
1#ifndef BDL_VM_H
2#define BDL_VM_H
3
4#include "types.h"
5#include "errors.h"
6#include "chunk.h"
7#include "ops.h"
8#include "debug.h"
9#include "hashtable.h"
10
11#define VM_FRAMES_CAP 64
12#define VM_STACK_CAP 1024
13
14typedef struct CallFrame {
15 // Current code being run.
16 Closure *closure;
17 // Return counter.
18 u8 *rp;
19 // Starting point of the locals for this procedure.
20 size_t stack_offset;
21} CallFrame;
22
23typedef struct VM {
24 CallFrame *frames;
25 // Stack.
26 Object *stack;
27 // Program counter.
28 u8 *pc;
29 // Global variables.
30 HashTable *globals;
31} VM;
32
33void vm_init(VM *vm);
34void vm_free(VM *vm);
35void vm_reset(VM *vm);
36void vm_interpret(VM *vm);
37
38void
39vm_init(VM *vm) {
40 *vm = (VM){0};
41 array_init(vm->frames, VM_FRAMES_CAP);
42 array_init(vm->stack, VM_STACK_CAP);
43 vm->globals = ht_init();
44}
45
46void
47vm_free(VM *vm) {
48 array_free(vm->frames);
49 array_free(vm->stack);
50 ht_free(vm->globals);
51}
52
53void
54vm_reset(VM *vm) {
55 vm_free(vm);
56 vm_init(vm);
57}
58
59// Helper macros for a more clear VM switch.
60#ifdef DEBUG
61#define STACK_TRACE() \
62 fprintf(stderr, "stack trace:\n"); \
63 for (ssize_t i = array_size(vm->frames) - 1; i >= 0 ; i--) { \
64 CallFrame frame = vm->frames[i]; \
65 Chunk *chunk = frame.closure->chunk; \
66 size_t instruction = vm->pc - chunk->code - 1; \
67 fprintf(stderr, "\t%-4ld -> ", i); \
68 fprintf(stderr, "%.*s",(int)array_size(chunk->name), chunk->name); \
69 fprintf(stderr, ":%ld:%ld\n", chunk->lines[instruction].line, chunk->lines[instruction].col); \
70 vm->pc = frame.rp; \
71 }
72#else
73#define STACK_TRACE()
74#endif
75
76#define RUNTIME_ERROR(ERR) \
77 error_push((Error){ \
78 .type = ERR_TYPE_RUNTIME, \
79 .value = (ERR), \
80 .line = chunk->lines[vm->pc - chunk->code - 1].line, \
81 .col = chunk->lines[vm->pc - chunk->code - 1].col, \
82 }); \
83 STACK_TRACE() \
84 return
85
86#define FIXNUM_ARITHMETIC_OP(OP) \
87 do { \
88 ssize_t n = AS_FIXNUM(array_pop(vm->stack)); \
89 size_t stack_size = array_size(vm->stack) - n; \
90 Object obj = array_peek(vm->stack, n - 1); \
91 if (!IS_FIXNUM(obj)) { \
92 RUNTIME_ERROR(ERR_WRONG_ARG_TYPE); \
93 return; \
94 } \
95 ssize_t acc = AS_FIXNUM(obj); \
96 while (n > 1) { \
97 obj = array_peek(vm->stack, n - 2); \
98 if (!IS_FIXNUM(obj)) { \
99 RUNTIME_ERROR(ERR_WRONG_ARG_TYPE); \
100 return; \
101 } \
102 ssize_t current = AS_FIXNUM(obj); \
103 acc = acc OP current; \
104 n--; \
105 } \
106 array_head(vm->stack)->size = stack_size; \
107 array_push(vm->stack, FIXNUM_VAL(acc)); \
108 } while (false)
109
110#define FIXNUM_COMPARE_OP(OP) \
111 do { \
112 ssize_t n = AS_FIXNUM(array_pop(vm->stack)); \
113 size_t stack_size = array_size(vm->stack) - n; \
114 Object obj = array_peek(vm->stack, n - 1); \
115 if (!IS_FIXNUM(obj)) { \
116 RUNTIME_ERROR(ERR_WRONG_ARG_TYPE); \
117 return; \
118 } \
119 ssize_t prev = AS_FIXNUM(obj); \
120 bool ret = true; \
121 while (n > 1) { \
122 obj = array_peek(vm->stack, n - 2); \
123 if (!IS_FIXNUM(obj)) { \
124 RUNTIME_ERROR(ERR_WRONG_ARG_TYPE); \
125 return; \
126 } \
127 ssize_t current = AS_FIXNUM(obj); \
128 ret = ret && (prev OP current); \
129 prev = current; \
130 n--; \
131 } \
132 array_head(vm->stack)->size = stack_size; \
133 array_push(vm->stack, BOOL_VAL(ret)); \
134 } while (false)
135
136#define LOGIC_OP(OP) \
137 do { \
138 ssize_t n = AS_FIXNUM(array_pop(vm->stack)); \
139 size_t stack_size = array_size(vm->stack) - n; \
140 Object obj = array_peek(vm->stack, n - 1); \
141 bool ret = IS_TRUE(obj); \
142 while (n > 1) { \
143 obj = array_peek(vm->stack, n - 2); \
144 bool current = IS_TRUE(obj); \
145 ret = ret OP current; \
146 n--; \
147 } \
148 array_head(vm->stack)->size = stack_size; \
149 array_push(vm->stack, BOOL_VAL(ret)); \
150 } while (false)
151
152void
153vm_interpret(VM *vm) {
154 CallFrame *frame = &vm->frames[0];
155 Chunk *chunk = frame->closure->chunk;
156 vm->pc = chunk->code;
157 frame->rp = NULL;
158
159 if (chunk->code == NULL || array_size(chunk->code) == 0) {
160 error_push((Error){
161 .type = ERR_TYPE_RUNTIME,
162 .value = ERR_EMPTY_CHUNK,
163 });
164 return;
165 }
166
167 while (true) {
168#ifdef DEBUG_TRACE_EXECUTION
169 printf("stack: [ ");
170 for (size_t i = 0; i < array_size(vm->stack); i++) {
171 object_display(vm->stack[i]);
172 if (i < array_size(vm->stack) - 1) {
173 printf(" | ");
174 }
175 }
176 printf(" ]\nop: ");
177 disassemble_instruction(chunk, (vm->pc - chunk->code));
178#endif
179 u8 instruction = *vm->pc++;
180 switch (instruction) {
181 case OP_LOCAL: {
182 ssize_t idx = AS_FIXNUM(array_pop(vm->stack));
183 array_push(vm->stack, vm->stack[frame->stack_offset + idx]);
184 } break;
185 case OP_CONSTANT: {
186 u8 constant = *vm->pc++;
187 Object obj = chunk->constants[constant];
188 array_push(vm->stack, obj);
189 } break;
190 case OP_CAPTURED: {
191 ssize_t idx = AS_FIXNUM(array_pop(vm->stack));
192 array_push(vm->stack, frame->closure->values[idx]);
193 } break;
194 case OP_CAPTURE_LOCAL: {
195 // This is a closure with captured variables. We need a copy
196 // of it that lives on the heap.
197 Object proc = array_pop(vm->stack);
198 proc = make_lambda(proc.closure->chunk);
199
200 ssize_t n_captured = AS_FIXNUM(array_pop(vm->stack));
201 for (ssize_t i = 0; i < n_captured; i++) {
202 Object value = array_pop(vm->stack);
203 array_push(proc.closure->values, value);
204 }
205 array_push(vm->stack, proc);
206 } break;
207 case OP_SET_CAPTURED: {
208 Object value = array_pop(vm->stack);
209 ssize_t idx = AS_FIXNUM(array_pop(vm->stack));
210 frame->closure->values[idx] = value;
211 } break;
212 case OP_DEF_LOCAL: {
213 Object value = array_pop(vm->stack);
214 ssize_t idx = AS_FIXNUM(array_pop(vm->stack));
215 vm->stack[frame->stack_offset + idx] = value;
216 } break;
217 case OP_SET_LOCAL: {
218 Object value = array_pop(vm->stack);
219 ssize_t idx = AS_FIXNUM(array_pop(vm->stack));
220 CallFrame frame = vm->frames[array_size(vm->frames) - 1];
221 vm->stack[frame.stack_offset + idx] = value;
222 } break;
223 case OP_DEF: {
224 Object value = array_pop(vm->stack);
225 Object name = array_pop(vm->stack);
226 ht_insert(vm->globals, name, value);
227 } break;
228 case OP_SET: {
229 Object value = array_pop(vm->stack);
230 Object name = array_pop(vm->stack);
231 Object *prev = ht_lookup(vm->globals, name);
232 if (prev == NULL) {
233 RUNTIME_ERROR(ERR_SYMBOL_NOT_FOUND);
234 return;
235 }
236 ht_insert(vm->globals, name, value);
237 } break;
238 case OP_GET: {
239 Object name = array_pop(vm->stack);
240 Object *value = ht_lookup(vm->globals, name);
241 if (value == NULL) {
242 RUNTIME_ERROR(ERR_SYMBOL_NOT_FOUND);
243 return;
244 }
245 array_push(vm->stack, *value);
246 } break;
247 case OP_SUM: { FIXNUM_ARITHMETIC_OP(+); } break;
248 case OP_SUB: { FIXNUM_ARITHMETIC_OP(-); } break;
249 case OP_MUL: { FIXNUM_ARITHMETIC_OP(*); } break;
250 case OP_DIV: { FIXNUM_ARITHMETIC_OP(/); } break;
251 case OP_MOD: { FIXNUM_ARITHMETIC_OP(%); } break;
252 case OP_NOT: {
253 Object prev = array_pop(vm->stack);
254 Object new = IS_TRUE(prev) ? FALSE_VAL : TRUE_VAL;
255 array_push(vm->stack, new);
256 } break;
257 case OP_AND: { LOGIC_OP(&&); } break;
258 case OP_OR: { LOGIC_OP(||); } break;
259 case OP_EQUAL: { FIXNUM_COMPARE_OP(==); } break;
260 case OP_LESS: { FIXNUM_COMPARE_OP(<); } break;
261 case OP_GREATER: { FIXNUM_COMPARE_OP(>); } break;
262 case OP_LESS_EQUAL: { FIXNUM_COMPARE_OP(<=); } break;
263 case OP_GREATER_EQUAL: { FIXNUM_COMPARE_OP(>=); } break;
264 case OP_JUMP: {
265 u16 a = *vm->pc++;
266 u16 b = *vm->pc++;
267 s16 offset = (a << 8) | b;
268 vm->pc += offset;
269 } break;
270 case OP_JUMP_IF_FALSE: {
271 u16 a = *vm->pc++;
272 u16 b = *vm->pc++;
273 s16 offset = (a << 8) | b;
274 if (IS_FALSE(array_pop(vm->stack))) {
275 vm->pc += offset;
276 }
277 } break;
278 case OP_DISPLAY: {
279 object_display(array_pop(vm->stack));
280 } break;
281 case OP_PRINT: {
282 Object obj = array_pop(vm->stack);
283 if (!IS_STRING(obj)) {
284 RUNTIME_ERROR(ERR_WRONG_ARG_TYPE);
285 }
286 StringView scanner = (StringView) {
287 .start = obj.text,
288 .n = array_size(obj.text),
289 };
290 while (scanner.n != 0) {
291 char c = sv_next(&scanner);
292 if (c == '\\' && sv_peek(&scanner) == 'n') {
293 putchar('\n');
294 sv_next(&scanner);
295 continue;
296 }
297 if (c == '\\' && sv_peek(&scanner) == '"') {
298 putchar('"');
299 sv_next(&scanner);
300 continue;
301 }
302 putchar(c);
303 }
304 } break;
305 case OP_NEWLINE: {
306 printf("\n");
307 } break;
308 case OP_CALL: {
309 ssize_t n_args = AS_FIXNUM(array_pop(vm->stack));
310 Object proc = vm->stack[array_size(vm->stack) - 1 - n_args];
311
312 // Check the number of arguments is correct.
313 // NOTE: This is probably better handled at compilation, but for
314 // now this is simpler to implement.
315 ssize_t n_params = proc.closure->chunk->n_params;
316 ssize_t n_locals = proc.closure->chunk->n_locals - n_params;
317 if (n_args < n_params) {
318 RUNTIME_ERROR(ERR_NOT_ENOUGH_ARGS);
319 } else if (n_args > n_params) {
320 RUNTIME_ERROR(ERR_TOO_MANY_ARGS);
321 }
322
323#ifdef DEBUG
324 disassemble_chunk(proc.closure->chunk);
325 printf("n_locals: %ld\n", n_locals);
326 printf("n_params: %ld\n", n_params);
327#endif
328 // Tail-call optimization.
329 if (proc.closure->chunk != chunk || *vm->pc != OP_RETURN) {
330 // Prepare new call frame.
331 CallFrame new_frame = (CallFrame){
332 .closure = proc.closure,
333 .rp = vm->pc,
334 .stack_offset = array_size(vm->stack) - n_params,
335 };
336 array_push(vm->frames, new_frame);
337 frame = &vm->frames[array_size(vm->frames) - 1];
338 chunk = frame->closure->chunk;
339
340 // Prepare local slots.
341 for (ssize_t i = 0; i < n_locals; i++) {
342 array_push(vm->stack, NIL_VAL);
343 }
344 } else {
345 // Bind tail-call parameters.
346 for (ssize_t i = 0; i < n_params; i++) {
347 size_t offset = n_locals + n_params - 1 - i;
348 Object obj = array_peek(vm->stack, offset);
349 vm->stack[frame->stack_offset + i] = obj;
350 }
351
352 // Reset stack size.
353 size_t offset = frame->stack_offset + n_params + n_locals;
354 array_head(vm->stack)->size = offset;
355 }
356 vm->pc = chunk->code;
357 } break;
358 case OP_RETURN: {
359 if (frame->rp == NULL) {
360 Object ret = array_pop(vm->stack);
361 if (!IS_NIL(ret)) {
362 object_display(ret);
363 printf("\n");
364 }
365 return;
366 }
367 vm->pc = frame->rp;
368 array_head(vm->frames)->size--;
369 Object ret = array_pop(vm->stack);
370 array_head(vm->stack)->size = frame->stack_offset - 1;
371 array_push(vm->stack, ret);
372 frame = &vm->frames[array_size(vm->frames) - 1];
373 chunk = frame->closure->chunk;
374 } break;
375 case OP_DROP: {
376 array_head(vm->stack)->size = 0;
377 } break;
378 default: {
379 RUNTIME_ERROR(ERR_NOT_IMPLEMENTED);
380 } break;
381 }
382 }
383
384 error_push((Error){
385 .type = ERR_TYPE_RUNTIME,
386 .value = ERR_PC_OOB,
387 .line = chunk->lines[0].line,
388 .col = chunk->lines[0].col,
389 });
390}
391
392#undef FIXNUM_ARITHMETIC_OP
393#undef FIXNUM_COMPARE_OP
394#undef LOGIC_OP
395
396#endif // BDL_VM_H