Notes
This commit is contained in:
parent
a6153e87ec
commit
021696daa1
46
NOTES.md
Normal file
46
NOTES.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# 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>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
## Description
|
## Description
|
||||||
My code/notes for Crafting Interpreters
|
My code/notes for Crafting Interpreters
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [Description](#description)
|
- [Description](#description)
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
#define CHUNK_INCLUDED
|
#define CHUNK_INCLUDED
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "value.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
OP_CONSTANT,
|
||||||
OP_RETURN,
|
OP_RETURN,
|
||||||
OP_COUNT,
|
OP_COUNT,
|
||||||
}OpCode;
|
}OpCode;
|
||||||
@ -12,10 +14,13 @@ typedef struct {
|
|||||||
int len;
|
int len;
|
||||||
int cap;
|
int cap;
|
||||||
uint8_t* code;
|
uint8_t* code;
|
||||||
|
int *lines;
|
||||||
|
ValueArray constants;
|
||||||
} Chunk;
|
} Chunk;
|
||||||
|
|
||||||
void initChunk(Chunk *chunk);
|
void initChunk(Chunk *chunk);
|
||||||
void freeChunk(Chunk *chunk);
|
void freeChunk(Chunk *chunk);
|
||||||
void writeChunk(Chunk *chunk, uint8_t byte);
|
void writeChunk(Chunk *chunk, uint8_t byte,int line);
|
||||||
|
int addConstant(Chunk *chunk, Value value);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
18
include/value.h
Normal file
18
include/value.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef VALUE_INCLUDED
|
||||||
|
#define VALUE_INCLUDED
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
typedef double Value;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int len;
|
||||||
|
int cap;
|
||||||
|
Value *values;
|
||||||
|
} ValueArray;
|
||||||
|
|
||||||
|
void initValueArray(ValueArray *array);
|
||||||
|
void writeValueArray(ValueArray *array, Value value);
|
||||||
|
void freeValueArray(ValueArray *array);
|
||||||
|
void printValue(Value value);
|
||||||
|
#endif
|
13
src/chunk.c
13
src/chunk.c
@ -4,18 +4,27 @@ void initChunk(Chunk *chunk){
|
|||||||
chunk->len =0;
|
chunk->len =0;
|
||||||
chunk->cap = 0;
|
chunk->cap = 0;
|
||||||
chunk->code = NULL;
|
chunk->code = NULL;
|
||||||
|
chunk->lines = NULL;
|
||||||
|
initValueArray(&chunk->constants);
|
||||||
}
|
}
|
||||||
void freeChunk(Chunk *chunk){
|
void freeChunk(Chunk *chunk){
|
||||||
FREE_ARRAY(uint8_t, chunk->code, chunk->cap);
|
FREE_ARRAY(uint8_t, chunk->code, chunk->cap);
|
||||||
|
FREE_ARRAY(int,chunk->lines,chunk->cap);
|
||||||
|
freeValueArray(&chunk->constants);
|
||||||
initChunk(chunk);
|
initChunk(chunk);
|
||||||
}
|
}
|
||||||
void writeChunk(Chunk *chunk, uint8_t byte){
|
void writeChunk(Chunk *chunk, uint8_t byte, int line){
|
||||||
if(chunk->cap < chunk->len+1){// If we don't have room grow it.
|
if(chunk->cap < chunk->len+1){// If we don't have room grow it.
|
||||||
int oldCap = chunk->cap;
|
int oldCap = chunk->cap;
|
||||||
chunk->cap = GROW_CAP(oldCap);
|
chunk->cap = GROW_CAP(oldCap);
|
||||||
chunk->code = GROW_ARRAY(uint8_t, chunk->code, oldCap, chunk->cap);
|
chunk->code = GROW_ARRAY(uint8_t, chunk->code, oldCap, chunk->cap);
|
||||||
|
chunk->lines =GROW_ARRAY(int,chunk->lines,oldCap,chunk->cap);
|
||||||
} // Else write byte to chunk
|
} // Else write byte to chunk
|
||||||
chunk->code[chunk->len] = byte;
|
chunk->code[chunk->len] = byte;
|
||||||
|
chunk->lines[chunk->len] = line;
|
||||||
chunk->len++;
|
chunk->len++;
|
||||||
}
|
}
|
||||||
|
int addConstant(Chunk *chunk, Value value){
|
||||||
|
writeValueArray(&chunk->constants,value);
|
||||||
|
return chunk->constants.len-1;
|
||||||
|
}
|
||||||
|
18
src/debug.c
18
src/debug.c
@ -1,5 +1,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "../include/debug.h"
|
#include "../include/debug.h"
|
||||||
|
#include "../include/value.h"
|
||||||
|
|
||||||
void disassembleChunk(Chunk *chunk, const char* name){
|
void disassembleChunk(Chunk *chunk, const char* name){
|
||||||
printf("== %s ==\n",name);
|
printf("== %s ==\n",name);
|
||||||
@ -7,15 +8,32 @@ void disassembleChunk(Chunk *chunk, const char* name){
|
|||||||
offset = disassembleInstruction(chunk,offset);
|
offset = disassembleInstruction(chunk,offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static int constantInstruction(const char *name, Chunk *chunk, int offset){
|
||||||
|
uint8_t constant = chunk->code[offset+1];
|
||||||
|
printf("%-16s %4d '",name, constant);
|
||||||
|
printValue(chunk->constants.values[constant]);
|
||||||
|
printf("\n");
|
||||||
|
return offset +2;
|
||||||
|
}
|
||||||
static int simpleInstruction(const char* name, int offset){
|
static int simpleInstruction(const char* name, int offset){
|
||||||
printf("%s\n",name);
|
printf("%s\n",name);
|
||||||
return offset +1;
|
return offset +1;
|
||||||
}
|
}
|
||||||
int disassembleInstruction(Chunk *chunk, int offset){
|
int disassembleInstruction(Chunk *chunk, int offset){
|
||||||
printf("%04d\t",offset);
|
printf("%04d\t",offset);
|
||||||
|
|
||||||
|
if(offset > 0 && chunk->lines[offset] == chunk->lines[offset -1]){
|
||||||
|
printf(" | ");
|
||||||
|
} else {
|
||||||
|
printf("%4d ", chunk->lines[offset]);
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t instruction = chunk->code[offset];
|
uint8_t instruction = chunk->code[offset];
|
||||||
|
|
||||||
switch(instruction){
|
switch(instruction){
|
||||||
|
case OP_CONSTANT:
|
||||||
|
return constantInstruction("OP_CONSTANT", chunk,offset);
|
||||||
|
|
||||||
case OP_RETURN:
|
case OP_RETURN:
|
||||||
return simpleInstruction("OP_RETURN",offset);
|
return simpleInstruction("OP_RETURN",offset);
|
||||||
default:
|
default:
|
||||||
|
28
src/value.c
Normal file
28
src/value.c
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include "../include/memory.h"
|
||||||
|
#include "../include/value.h"
|
||||||
|
|
||||||
|
void initValueArray(ValueArray *array){
|
||||||
|
array->values = NULL;
|
||||||
|
array->cap = 0;
|
||||||
|
array->len = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeValueArray(ValueArray *array, Value value){
|
||||||
|
if(array->cap < array->len+1){
|
||||||
|
int oldCap = array->cap;
|
||||||
|
array->cap = GROW_CAP(oldCap);
|
||||||
|
array->values = GROW_ARRAY(Value,array->values,oldCap, array->cap);
|
||||||
|
}
|
||||||
|
array->values[array->len] = value;
|
||||||
|
array->len ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void freeValueArray(ValueArray *array){
|
||||||
|
FREE_ARRAY(Value, array->values,array->cap);
|
||||||
|
initValueArray(array);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printValue(Value value){
|
||||||
|
printf("%g",value);
|
||||||
|
}
|
@ -5,7 +5,11 @@
|
|||||||
int main(void){
|
int main(void){
|
||||||
Chunk chunk;
|
Chunk chunk;
|
||||||
initChunk(&chunk);
|
initChunk(&chunk);
|
||||||
writeChunk(&chunk, OP_RETURN);
|
|
||||||
|
int constant = addConstant(&chunk,1.2);
|
||||||
|
writeChunk(&chunk, OP_CONSTANT,10);
|
||||||
|
writeChunk(&chunk,constant,10);
|
||||||
|
writeChunk(&chunk, OP_RETURN,10);
|
||||||
|
|
||||||
disassembleChunk(&chunk,"Test Chunk");
|
disassembleChunk(&chunk,"Test Chunk");
|
||||||
freeChunk(&chunk);
|
freeChunk(&chunk);
|
||||||
|
Loading…
Reference in New Issue
Block a user