Chapter 19.4 - 19.5

This commit is contained in:
Tom Willemse 2021-09-18 11:10:11 -07:00
parent 8ec9137561
commit aa41f26a26
Signed by: ryuslash
GPG key ID: 7D5C407B435025C1
8 changed files with 207 additions and 8 deletions

View file

@ -16,6 +16,8 @@ add_executable(Lox
scanner.c scanner.c
object.h object.h
object.c object.c
table.h
table.c
main.c) main.c)
install(TARGETS Lox) install(TARGETS Lox)

View file

@ -3,6 +3,7 @@
#include "memory.h" #include "memory.h"
#include "object.h" #include "object.h"
#include "table.h"
#include "value.h" #include "value.h"
#include "vm.h" #include "vm.h"
@ -18,22 +19,45 @@ static Obj *allocateObject(size_t size, ObjType type) {
return object; return object;
} }
static ObjString *allocateString(char *chars, int length) { static ObjString *allocateString(char *chars, int length, uint32_t hash) {
ObjString *string = ALLOCATE_OBJ(ObjString, OBJ_STRING); ObjString *string = ALLOCATE_OBJ(ObjString, OBJ_STRING);
string->length = length; string->length = length;
string->chars = chars; string->chars = chars;
string->hash = hash;
tableSet(&vm.strings, string, NIL_VAL);
return string; return string;
} }
static uint32_t hashString(const char *key, int length) {
uint32_t hash = 2166136261u;
for (int i = 0; i < length; i++) {
hash ^= (uint32_t)key[i];
hash *= 16777619;
}
return hash;
}
ObjString *takeString(char *chars, int length) { ObjString *takeString(char *chars, int length) {
return allocateString(chars, length); uint32_t hash = hashString(chars, length);
ObjString *interned = tableFindString(&vm.strings, chars, length, hash);
if (interned != NULL) {
FREE_ARRAY(char, chars, length + 1);
return interned;
}
return allocateString(chars, length, hash);
} }
ObjString *copyString(const char *chars, int length) { ObjString *copyString(const char *chars, int length) {
uint32_t hash = hashString(chars, length);
ObjString *interned = tableFindString(&vm.strings, chars, length, hash);
if (interned != NULL)
return interned;
char *heapChars = ALLOCATE(char, length + 1); char *heapChars = ALLOCATE(char, length + 1);
memcpy(heapChars, chars, length); memcpy(heapChars, chars, length);
heapChars[length] = '\0'; heapChars[length] = '\0';
return allocateString(heapChars, length); return allocateString(heapChars, length, hash);
} }
void printObject(Value value) { void printObject(Value value) {

View file

@ -24,6 +24,7 @@ struct ObjString {
Obj obj; Obj obj;
int length; int length;
char *chars; char *chars;
uint32_t hash;
}; };
ObjString *takeString(char *chars, int length); ObjString *takeString(char *chars, int length);

142
clox/src/table.c Normal file
View file

@ -0,0 +1,142 @@
#include <stdlib.h>
#include <string.h>
#include "memory.h"
#include "object.h"
#include "table.h"
#include "value.h"
#define TABLE_MAX_LOAD 0.75
void initTable(Table *table) {
table->count = 0;
table->capacity = 0;
table->entries = NULL;
}
void freeTable(Table *table) {
FREE_ARRAY(Entry, table->entries, table->capacity);
initTable(table);
}
static Entry *findEntry(Entry *entries, int capacity, ObjString *key) {
uint32_t index = key->hash % capacity;
Entry *tombstone = NULL;
for (;;) {
Entry *entry = &entries[index];
if (entry->key == NULL) {
if (IS_NIL(entry->value)) {
// Empty entry.
return tombstone != NULL ? tombstone : entry;
} else {
// We found a tombstone.
if (tombstone == NULL)
tombstone = entry;
}
} else if (entry->key == key) {
// We found the entry.
return entry;
}
index = (index + 1) % capacity;
}
}
bool tableGet(Table *table, ObjString *key, Value *value) {
if (table->count == 0)
return false;
Entry *entry = findEntry(table->entries, table->capacity, key);
if (entry->key == NULL)
return false;
*value = entry->value;
return true;
}
static void adjustCapacity(Table *table, int capacity) {
Entry *entries = ALLOCATE(Entry, capacity);
for (int i = 0; i < capacity; i++) {
entries[i].key = NULL;
entries[i].value = NIL_VAL;
}
table->count = 0;
for (int i = 0; i < table->capacity; i++) {
Entry *entry = &table->entries[i];
if (entry->key == NULL)
continue;
Entry *dest = findEntry(entries, capacity, entry->key);
dest->key = entry->key;
dest->value = entry->value;
table->count++;
}
FREE_ARRAY(Entry, table->entries, table->capacity);
table->entries = entries;
table->capacity = capacity;
}
bool tableSet(Table *table, ObjString *key, Value value) {
if (table->count + 1 > table->capacity * TABLE_MAX_LOAD) {
int capacity = GROW_CAPACITY(table->capacity);
adjustCapacity(table, capacity);
}
Entry *entry = findEntry(table->entries, table->capacity, key);
bool isNewKey = entry->key == NULL;
if (isNewKey && IS_NIL(entry->value))
table->count++;
entry->key = key;
entry->value = value;
return isNewKey;
}
bool tableDelete(Table *table, ObjString *key) {
if (table->count == 0)
return false;
/* Find the entry. */
Entry *entry = findEntry(table->entries, table->capacity, key);
if (entry->key == NULL)
return false;
/* Place a tombstone in the entry. */
entry->key = NULL;
entry->value = BOOL_VAL(true);
return true;
}
void tableAddAll(Table *from, Table *to) {
for (int i = 0; i < from->capacity; i++) {
Entry *entry = &from->entries[i];
if (entry->key != NULL) {
tableSet(to, entry->key, entry->value);
}
}
}
ObjString *tableFindString(Table *table, const char *chars, int length,
uint32_t hash) {
if (table->count == 0)
return NULL;
uint32_t index = hash % table->capacity;
for (;;) {
Entry *entry = &table->entries[index];
if (entry->key == NULL) {
/* Stop if we find an empty non-tombstone entry. */
if (IS_NIL(entry->value))
return NULL;
} else if (entry->key->length == length && entry->key->hash == hash &&
memcmp(entry->key->chars, chars, length) == 0) {
/* We found it. */
return entry->key;
}
index = (index + 1) % table->capacity;
}
}

27
clox/src/table.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef TABLE_H
#define TABLE_H
#include "common.h"
#include "value.h"
typedef struct {
ObjString *key;
Value value;
} Entry;
typedef struct {
int count;
int capacity;
Entry *entries;
} Table;
void initTable(Table *table);
void freeTable(Table *table);
bool tableGet(Table *table, ObjString *key, Value *value);
bool tableSet(Table *table, ObjString *key, Value value);
bool tableDelete(Table *table, ObjString *key);
void tableAddAll(Table *from, Table *to);
ObjString *tableFindString(Table *talbe, const char *chars, int length,
uint32_t hash);
#endif

View file

@ -56,10 +56,7 @@ bool valuesEqual(Value a, Value b) {
case VAL_NUMBER: case VAL_NUMBER:
return AS_NUMBER(a) == AS_NUMBER(b); return AS_NUMBER(a) == AS_NUMBER(b);
case VAL_OBJ: { case VAL_OBJ: {
ObjString *aString = AS_STRING(a); return AS_OBJ(a) == AS_OBJ(b);
ObjString *bString = AS_STRING(b);
return aString->length == bString->length &&
memcmp(aString->chars, bString->chars, aString->length) == 0;
} }
default: default:
return false; /* Unreachable */ return false; /* Unreachable */

View file

@ -29,9 +29,13 @@ static void runtimeError(const char *format, ...) {
void initVM() { void initVM() {
resetStack(); resetStack();
vm.objects = NULL; vm.objects = NULL;
initTable(&vm.strings);
} }
void freeVM() { freeObjects(); } void freeVM() {
freeTable(&vm.strings);
freeObjects();
}
void push(Value value) { void push(Value value) {
*vm.stackTop = value; *vm.stackTop = value;

View file

@ -2,6 +2,7 @@
#define clox_vm_h #define clox_vm_h
#include "chunk.h" #include "chunk.h"
#include "table.h"
#include "value.h" #include "value.h"
#define STACK_MAX 256 #define STACK_MAX 256
@ -11,6 +12,7 @@ typedef struct {
uint8_t *ip; uint8_t *ip;
Value stack[STACK_MAX]; Value stack[STACK_MAX];
Value *stackTop; Value *stackTop;
Table strings;
Obj *objects; Obj *objects;
} VM; } VM;