Chapter 19.4 - 19.5
This commit is contained in:
parent
8ec9137561
commit
aa41f26a26
8 changed files with 207 additions and 8 deletions
|
@ -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)
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
142
clox/src/table.c
Normal 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
27
clox/src/table.h
Normal 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
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue