Skip to content

Commit

Permalink
@@@
Browse files Browse the repository at this point in the history
  • Loading branch information
vegerot committed Oct 29, 2024
1 parent ad55fa1 commit 8ff83a9
Show file tree
Hide file tree
Showing 10 changed files with 191 additions and 51 deletions.
6 changes: 3 additions & 3 deletions bytecode-vm-compiler/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
CC ?= cc
CFLAGS ?= -std=gnu2x -g -Wall -Wextra -Wpedantic -Wimplicit-fallthrough -Werror -Wno-gnu-statement-expression -Wno-strict-prototypes
CFLAGS ?= -std=gnu2x -g -Wall -Wextra -Wpedantic -Wimplicit-fallthrough -Werror -Wno-gnu-statement-expression -Wno-strict-prototypes -Wno-unused-parameter
SRC_FILES= src/chunk.c src/compiler.c src/debug.c src/memory.c src/object.c src/scanner.c src/stack.c src/value.c src/vm.c

lox: $(SRC_FILES) src/*.h src/main.c
Expand All @@ -11,7 +11,7 @@ test/test-vm: test/test-vm.c src/vm.h src/vm.c src/chunk.h src/chunk.c src/value
$(CC) -fsanitize=address $(CFLAGS) -Wno-error test/test-vm.c $(SRC_FILES) -o test/test-vm
test/test-vm || (mv test/test-vm test/test-vm.failed && false)
test/test-stack: test/test-stack.c src/stack.h src/stack.c
$(CC) -fsanitize=address $(CFLAGS) test/test-stack.c $(SRC_FILES) -o test/test-stack
$(CC) -fsanitize=address $(CFLAGS) -Wno-error test/test-stack.c $(SRC_FILES) -o test/test-stack
test/test-stack
test/test-scanner: test/test-scanner.c src/scanner.h src/scanner.c src/lox_assert.h
$(CC) -fsanitize=address $(CFLAGS) -Wno-error test/test-scanner.c $(SRC_FILES) -o test/test-scanner
Expand All @@ -20,7 +20,7 @@ test/test-compiler: test/test-compiler.c src/compiler.h src/compiler.c src/scann
$(CC) -DDEBUG_TRACE_EXECUTION -fsanitize=address $(CFLAGS) -Wno-error test/test-compiler.c $(SRC_FILES) -o test/test-compiler
test/test-compiler || mv -v test/test-compiler test/test-compiler.failed
test/test-table: test/test-table.c test/test-vm
$(CC) -fsanitize=address $(CFLAGS) -Wno-error test/test-table.c $(SRC_FILES) -o test/test-table
$(CC) $(CFLAGS) -fsanitize=address $(EXTRA_CFLAGS) -Wno-error test/test-table.c $(SRC_FILES) -o test/test-table
test/test-table || mv test/test-table test/test-table.failed


Expand Down
4 changes: 3 additions & 1 deletion bytecode-vm-compiler/src/compiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ LoxString* fromCString(char const* cString, size_t length) {
str->chars[length] = '\0';

str->length = length;
str->hash = computeHashOfCString(cString, length);
return str;
}

Expand Down Expand Up @@ -273,12 +274,13 @@ static void String() {
LOX_ASSERT(parser.previous.type == TOKEN_STRING);

// TODO: support string escape sequences like '\n'
char const* strStart = parser.previous.start + 1;
int strLength = parser.previous.length - 2; // subtract quotes

// FIXME: This sucks. We need to embed the string into the bytecode
// directly instead of allocating on the heap because now the bytecode
// isn't portable.
LoxString* str = fromCString(parser.previous.start + 1, strLength);
LoxString* str = fromCString(strStart, strLength);

Value value = {.type = VAL_OBJ, .as.obj = (LoxObj*)str};

Expand Down
3 changes: 3 additions & 0 deletions bytecode-vm-compiler/src/memory.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ void freeObjects(VM* vm) {
LoxObj* next = NULL;
while (curr) {
next = curr->next;
#ifdef DEBUG_TRACE_EXECUTION
printObject(OBJ_VAL(curr));
#endif
freeLoxObject(curr);
curr = next;
}
Expand Down
13 changes: 12 additions & 1 deletion bytecode-vm-compiler/src/object.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ static LoxObj* allocateObj(VM* vm, size_t size, ObjType type) {
}

// TODO: refactor to use `allocateEmptyString`
LoxString* allocateString(VM* vm, char const* cString, size_t length) {
LoxString* allocateString(VM* vm, char const* cString, size_t length,
uint32_t hash) {
LoxString* str = (LoxString*)allocateObj(
vm, sizeof(LoxString) + (length + 1) * sizeof(char), OBJ_STRING);

Expand All @@ -39,6 +40,7 @@ LoxString* allocateString(VM* vm, char const* cString, size_t length) {
str->chars[length] = '\0';

str->length = length;
str->hash = hash;
return str;
}

Expand All @@ -53,3 +55,12 @@ LoxString* allocateEmptyString(VM* vm, size_t length) {
str->length = length;
return str;
}

uint32_t computeHashOfCString(char const* chars, size_t length) {
uint32_t hash = 2166136261u;
for (size_t i = 0; i < length; ++i) {
hash ^= (uint8_t)chars[i];
hash *= 16777619;
}
return hash;
}
2 changes: 2 additions & 0 deletions bytecode-vm-compiler/src/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ typedef struct {
LoxObj obj;
/** same as return value of strlen */
size_t length;
uint32_t hash;
char chars[];
} LoxString;

Expand All @@ -32,3 +33,4 @@ void printObject(Value value);
static inline bool isObjType(Value value, ObjType type) {
return IS_OBJ(value) && AS_OBJ(value)->type == type;
}
uint32_t computeHashOfCString(char const* chars, size_t length);
1 change: 1 addition & 0 deletions bytecode-vm-compiler/src/value.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ void printValue(Value value) {
}

static bool compareLoxStrings(LoxString* a, LoxString* b) {
// TODO(perf): compare string.hash instead
if (a == b)
return true;
if (a->length != b->length)
Expand Down
5 changes: 4 additions & 1 deletion bytecode-vm-compiler/src/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void freeVM(void) {
* q: does this copy the VM? Looking through the properties of the VM, the only
* property that might actually be copied is `vm.stack.cap`
*/
VM getVM_(void) { return vm; }
VM* getVM_(void) { return &vm; }

InterpretResult run(void) {
#define READ_BYTE() (*vm.ip++)
Expand Down Expand Up @@ -185,6 +185,9 @@ InterpretResult run(void) {
// This line does NOTHING.
// See NOTE[why-LoxString-null-terminates]
addedLoxStr->chars[finalStringLength] = '\0';
uint32_t hash = computeHashOfCString(addedLoxStr->chars,
addedLoxStr->length);
addedLoxStr->hash = hash;
stack_push(&vm.stack, OBJ_VAL(addedLoxStr));
} else {
runtimeError(
Expand Down
2 changes: 1 addition & 1 deletion bytecode-vm-compiler/src/vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ typedef enum {
void initVM(void);
void freeVM(void);

VM getVM_(void);
VM* getVM_(void);

InterpretResult interpret_bytecode_(Chunk* chunk);
InterpretResult interpret(char const* source);
Expand Down
130 changes: 124 additions & 6 deletions bytecode-vm-compiler/test/test-table.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
/**
* idea: make this its own library independent of Lox, with keys of type
* `{uint32_t hash}` and values of type `void*`.
* Then create a wrapper in Lox that takes keys with type `LoxString` and values
* of type `Value*`
*/
#include "../src/lox_assert.h"
#include "../src/vm.h"
#include <string.h>
Expand Down Expand Up @@ -31,24 +37,136 @@ void tableFree(LoxTable* table) {
}
FREE_ARRAY(Entry, table->entries, table->capacity);
}

static TableEntry* findEntry(LoxTable const* table, LoxString const* key) {
uint32_t index = key->hash % table->capacity;
while (true) {
// printf("index: %u\n", index);
TableEntry* curr = &table->entries[index];
if (curr->key == NULL || curr->key->hash == key->hash)
return curr;
index = (index + 1) % (table->capacity);
}
}

static void adjustCapacity(LoxTable* table, size_t newCapacity) {
TableEntry* newEntries = ALLOCATE(TableEntry, newCapacity);

// init each entry
for (size_t i = 0; i < newCapacity; ++i) {
newEntries[i].key = NULL;
newEntries[i].value = NIL_VAL;
}

table->capacity = newCapacity;
table->entries = newEntries;
}

bool tableSet(LoxTable* table, LoxString* key, Value value);
bool tableSet(LoxTable* table, LoxString* key, Value value) {}
#define TABLE_MAX_LOAD_RATIO 0.69
bool tableSet(LoxTable* table, LoxString* key, Value value) {
// FIXME: this conversion looks weird. Probably should multiply and divide
// whole numbers instead
if (table->count + 1 >
(size_t)((int)table->capacity * TABLE_MAX_LOAD_RATIO)) {
size_t newCapacity = GROW_CAPACITY(table->capacity);
adjustCapacity(table, newCapacity);
}
TableEntry* entry = findEntry(table, key);
++table->count;

entry->key = key;
entry->value = value;
return true;
}
Value* tableGet(LoxTable* table, LoxString* key);
Value* tableGet(LoxTable* table, LoxString* key) {}
Value* tableGet(LoxTable* table, LoxString* key) {
return &findEntry(table, key)->value;
}

int main(void) {
// basic
static void basic(void) {
initVM();
VM vm = getVM_();
VM* vm = getVM_();
LoxTable table;
tableInit(&table);
char const* k = "key";
LoxString* key = allocateString(&vm, "key", strlen(k));
LoxString* key = allocateString(vm, "key", strlen(k));
Value want = NUMBER_VAL(42.0);
tableSet(&table, key, want);

Value* res = tableGet(&table, key);

LOX_ASSERT_VALUE_EQUALS(*res, want);
tableFree(&table);
freeVM();
}

static void overwrite(void) {
initVM();
VM* vm = getVM_();
LoxTable table;
tableInit(&table);
char const* k = "key";
LoxString* key = allocateString(vm, "key", strlen(k));
Value one = NUMBER_VAL(1);
tableSet(&table, key, one);

Value* res = tableGet(&table, key);

LOX_ASSERT_VALUE_EQUALS(*res, one);
Value want = NUMBER_VAL(2);
tableSet(&table, key, want);

res = tableGet(&table, key);

LOX_ASSERT_VALUE_EQUALS(*res, want);
tableFree(&table);
freeVM();
}

/**
* This test no longer does what it was designed for.
* When I wrote this test, the hash function was hardcoded to always return 69,
* and this test passed. Now we have a better hash function.
*/
static void collision(void) {
initVM();
VM* vm = getVM_();
LoxTable table;
tableInit(&table);
char const* k1 = "costarring";
LoxString* key1 = allocateString(vm, k1, strlen(k1));
Value one = NUMBER_VAL(1);
tableSet(&table, key1, one);

char const* k2 = "liquid";
LoxString* key2 = allocateString(vm, k2, strlen(k2));
Value two = NUMBER_VAL(2);
tableSet(&table, key2, two);

Value* res1 = tableGet(&table, key1);
Value* res2 = tableGet(&table, key2);

LOX_ASSERT_VALUE_EQUALS(*res1, one);
LOX_ASSERT_VALUE_EQUALS(*res2, two);
tableFree(&table);
freeVM();
}

int main(void) {
basic();
overwrite();
// delete();
// grow();

// FIXME: how do I test collisions?
// ideas:
// 1. take the hash function as an argument
// 2. make a small hash table, and add hundreds of entries to it,
// statistically there'd be a collision somewhere in here...
// I prefer 2.
//
// Currently, when I want to test this function, I change the
// `computeHashOfCString` function to always return 69. That works too...
collision();
}
Loading

0 comments on commit 8ff83a9

Please sign in to comment.