From 021696daa16fca820e3d525c029946412ddc1c1e Mon Sep 17 00:00:00 2001 From: Randy Jordan Date: Sat, 31 Aug 2024 15:54:46 -0500 Subject: [PATCH] Notes --- NOTES.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ README.md | 3 ++- include/chunk.h | 7 ++++++- include/value.h | 18 ++++++++++++++++++ src/chunk.c | 13 +++++++++++-- src/debug.c | 18 ++++++++++++++++++ src/value.c | 28 ++++++++++++++++++++++++++++ tests/01_main.c | 6 +++++- 8 files changed, 134 insertions(+), 5 deletions(-) create mode 100644 NOTES.md create mode 100644 include/value.h create mode 100644 src/value.c diff --git a/NOTES.md b/NOTES.md new file mode 100644 index 0000000..93b459d --- /dev/null +++ b/NOTES.md @@ -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.
+Compiling to native instruction set the chip supports is what the fastest languages do.
+However, this is extremely low-level and time consuming. Portability is just exponential complexity at that level.
+We're exchanging performance for portability by writing an emulator in C.
+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.
+The `struct` contains a pointer to items of "type". Ex. `uint8_t *data, void *ptr`, etc.
+It also contains the current length and capacity of the array.
+The implementation contains a reallocate, initType, freeType, and writeType function.
+It exposes three macros `GROW_CAP(cap), GROW_ARRAY(type,ptr,old*type,new*type), and FREE_ARRAY`.
+GROW_CAP is how you handle growing your capacity.
+FREE_ARRAY and GROW_ARRAY utilize our reallocate implementation.
+ + +### 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.
+This gives us 255 available operations or instructions we can have.
+Once we have instructions, we also want to be able to have constant values. This is another dynamic array inside chunk.
+These type "values" are really the C primitive double type. 64 bits.
+Instead of writing to it like earlier make a function to add new values and return the index so we can track it.
+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.
+To disassemble, we just need to write a disassemble chunk function. Instead of iterating or incrementing the offset, disassembleInstruction returns the next offset.
+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.
+disassembleChunk prints out the "name" which is going to be used for the __FILE__, the chunk maps to a line, operation, and possible value.
+ + + + + + diff --git a/README.md b/README.md index d66d7e7..84f8e5e 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ ## Description My code/notes for Crafting Interpreters + ## Table of Contents - [Description](#description) @@ -12,7 +13,7 @@ My code/notes for Crafting Interpreters ## Features / TODOS -- [ ] Bytecode +- [ ] Bytecode ## Usage Just copy and paste into your projects. diff --git a/include/chunk.h b/include/chunk.h index d7fa84c..56f40a9 100644 --- a/include/chunk.h +++ b/include/chunk.h @@ -2,8 +2,10 @@ #define CHUNK_INCLUDED #include "common.h" +#include "value.h" typedef enum { + OP_CONSTANT, OP_RETURN, OP_COUNT, }OpCode; @@ -12,10 +14,13 @@ typedef struct { int len; int cap; uint8_t* code; + int *lines; + ValueArray constants; } Chunk; void initChunk(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 diff --git a/include/value.h b/include/value.h new file mode 100644 index 0000000..6257570 --- /dev/null +++ b/include/value.h @@ -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 diff --git a/src/chunk.c b/src/chunk.c index 88ae661..cdf793b 100644 --- a/src/chunk.c +++ b/src/chunk.c @@ -4,18 +4,27 @@ void initChunk(Chunk *chunk){ chunk->len =0; chunk->cap = 0; chunk->code = NULL; + chunk->lines = NULL; + initValueArray(&chunk->constants); } void freeChunk(Chunk *chunk){ FREE_ARRAY(uint8_t, chunk->code, chunk->cap); + FREE_ARRAY(int,chunk->lines,chunk->cap); + freeValueArray(&chunk->constants); 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. int oldCap = chunk->cap; chunk->cap = GROW_CAP(oldCap); 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 chunk->code[chunk->len] = byte; + chunk->lines[chunk->len] = line; chunk->len++; } - +int addConstant(Chunk *chunk, Value value){ + writeValueArray(&chunk->constants,value); + return chunk->constants.len-1; +} diff --git a/src/debug.c b/src/debug.c index 85ad94e..d834772 100644 --- a/src/debug.c +++ b/src/debug.c @@ -1,5 +1,6 @@ #include #include "../include/debug.h" +#include "../include/value.h" void disassembleChunk(Chunk *chunk, const char* name){ printf("== %s ==\n",name); @@ -7,15 +8,32 @@ void disassembleChunk(Chunk *chunk, const char* name){ 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){ printf("%s\n",name); return offset +1; } int disassembleInstruction(Chunk *chunk, int 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]; switch(instruction){ + case OP_CONSTANT: + return constantInstruction("OP_CONSTANT", chunk,offset); + case OP_RETURN: return simpleInstruction("OP_RETURN",offset); default: diff --git a/src/value.c b/src/value.c new file mode 100644 index 0000000..2df31e8 --- /dev/null +++ b/src/value.c @@ -0,0 +1,28 @@ +#include +#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); +} diff --git a/tests/01_main.c b/tests/01_main.c index 144e4d6..c7074bd 100644 --- a/tests/01_main.c +++ b/tests/01_main.c @@ -5,7 +5,11 @@ int main(void){ Chunk 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"); freeChunk(&chunk);