Halfway through Chapter 2 of Crafting Interpreters. We are implementing a VM to "execute" the instructions we had managed to store in chunks in the last post.
So far, we've added an interpret
method, which initializes the Instruction Pointer (a pointer which always points to the next instruction to execute) and calls a run
method, which just keeps iterating over the vm.chunk
which consists of opcodes and their operands, and runs corresponding C code. It looks like this:
InterpretResult run() {
#define READ_BYTE() (*vm.ip++)
#define READ_CONSTANT() (vm.chunk->constants.values[READ_BYTE()])
for (;;) {
uint8_t instruction = READ_BYTE();
#ifdef DEBUG_TRACE_EXECUTION
disassembleInstruction(vm.chunk, (int)(vm.ip - vm.chunk->code));
#endif
switch (instruction) {
case OP_RETURN: {
return INTERPRET_OK;
}
case OP_CONSTANT: {
Value constant = READ_CONSTANT();
// For now, let's just print the value
printValue(constant);
printf("\n");
break;
}
}
}
#undef READ_CONSTANT
#undef READ_BYTE
}
Not a whole lot useful, you'd say but hey, we don't have any supporting infrastructure right now. Bob tells me that this is the heart of the interpreter we are building and it will spend some 90% of its time here.
For the same "program" we embedded in our last post, this outputs:
== Test Chunk ==
0000 122 OP_CONSANT 0 '1.2'
0002 123 OP_CONSANT 1 '456'
0004 | OP_RETURN
1.2
456
Here on, I see that we're going to build a stack -- for holding values of local variables? perhaps. Let's find out!
Housekeeping announcement: I'm not really enjoying writing daily posts. Way too less time to cram the interesting, juicy stuff. The entire point of this was to learn CS every day, I added daily posts to keep myself accountable. I'll still be pushing a bullet-point-style log every day, and push interesting long form posts only when I have something interesting to write about.
Top comments (0)