47 lines
2.3 KiB
Markdown
47 lines
2.3 KiB
Markdown
|
# clox
|
||
|
|
||
|
## Description
|
||
|
My notes for Crafting Interpreters
|
||
|
|
||
|
## Table of Contents
|
||
|
|
||
|
- [Description](#description)
|
||
|
- [Chunks of Bytecode](#chunks-of-bytecode)
|
||
|
|
||
|
|
||
|
## Chunks of Bytecode
|
||
|
|
||
|
### Why Bytecode
|
||
|
Goes over the choice of bytecode vs machine code / assembly. <br>
|
||
|
Compiling to native instruction set the chip supports is what the fastest languages do.<br>
|
||
|
However, this is extremely low-level and time consuming. Portability is just exponential complexity at that level.<br>
|
||
|
We're exchanging performance for portability by writing an emulator in C.<br>
|
||
|
That emulator is a VM or virtual machine to run that bytecode one instruction at a time.
|
||
|
|
||
|
### Dynamic Arrays
|
||
|
Goes over his interface and implementation of dynamic arrays.<br>
|
||
|
The `struct` contains a pointer to items of "type". Ex. `uint8_t *data, void *ptr`, etc. <br>
|
||
|
It also contains the current length and capacity of the array.<br>
|
||
|
The implementation contains a reallocate, initType, freeType, and writeType function.<br>
|
||
|
It exposes three macros `GROW_CAP(cap), GROW_ARRAY(type,ptr,old*type,new*type), and FREE_ARRAY`. <br>
|
||
|
GROW_CAP is how you handle growing your capacity.<br>
|
||
|
FREE_ARRAY and GROW_ARRAY utilize our reallocate implementation.<br>
|
||
|
|
||
|
|
||
|
### Chunks
|
||
|
Now we can build dynamic arrays or "Chunks" of instructions or operations. These will be `uint8_t *ptr`. Which is the instruction number or Enum.<br>
|
||
|
This gives us 255 available operations or instructions we can have.<br>
|
||
|
Once we have instructions, we also want to be able to have constant values. This is another dynamic array inside chunk.<br>
|
||
|
These type "values" are really the C primitive double type. 64 bits.<br>
|
||
|
Instead of writing to it like earlier make a function to add new values and return the index so we can track it. <br>
|
||
|
For line debugging we add an array of integers to act as a LUT with index of the operation. Each operation maps to a line.<br>
|
||
|
To disassemble, we just need to write a disassemble chunk function. Instead of iterating or incrementing the offset, disassembleInstruction returns the <b>next</b> offset.<br>
|
||
|
The offset of the first chunk is it's length. From there disassembleInstruction performs a switch statement on the operation or instruction to return the proper offset.<br>
|
||
|
disassembleChunk prints out the "name" which is going to be used for the __FILE__, the chunk maps to a line, operation, and possible value.<br>
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|