Consolidate files

* Move GC struct into VM for easier use.
* Put all data structures into one file
This commit is contained in:
Calvin Rose 2017-02-09 18:50:47 -05:00
parent 715c239fc1
commit 0557c8b2a6
22 changed files with 800 additions and 897 deletions

2
.gitignore vendored
View File

@ -1,5 +1,5 @@
# Target
./interp
interp
# Created by https://www.gitignore.io/api/c

View File

@ -2,11 +2,12 @@
CFLAGS=-std=c99 -Wall -Wextra -g
TARGET=./interp
TARGET=interp
PREFIX=/usr/local
# C sources
SOURCES=main.c parse.c value.c vm.c dict.c array.c buffer.c gc.c compile.c
HEADERS=vm.h ds.h compile.h parse.h value.h
SOURCES=main.c parse.c value.c vm.c ds.c compile.c
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
all: $(TARGET)
@ -14,7 +15,7 @@ all: $(TARGET)
$(TARGET): $(OBJECTS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJECTS)
%.o : %.c
%.o : %.c $(HEADERS)
$(CC) $(CFLAGS) -o $@ -c $<
install: $(TARGET)
@ -31,6 +32,6 @@ debug: $(TARGET)
gdb $(TARGET)
valgrind: $(TARGET)
valgrind $(TARGET)
valgrind ./$(TARGET)
.PHONY: clean install run debug valgrind

78
array.c
View File

@ -1,78 +0,0 @@
#include <string.h>
#include "array.h"
#include "gc.h"
/* Creates a new array */
Array * ArrayNew(GC * gc, uint32_t capacity) {
Array * array = GCAlloc(gc, sizeof(Array));
Value * data = GCAlloc(gc, capacity * sizeof(Value));
array->data = data;
array->count = 0;
array->capacity = capacity;
return array;
}
/* Ensure the array has enough capacity for capacity elements */
void ArrayEnsure(GC * gc, Array * array, uint32_t capacity) {
Value * newData;
if (capacity <= array->capacity) return;
newData = GCAlloc(gc, capacity * sizeof(Value));
memcpy(newData, array->data, array->count * sizeof(Value));
array->data = newData;
array->capacity = capacity;
}
/* Get a value of an array with bounds checking. */
Value ArrayGet(Array * array, uint32_t index) {
if (index < array->count) {
return array->data[index];
} else {
Value v;
v.type = TYPE_NIL;
v.data.boolean = 0;
return v;
}
}
/* Try to set an index in the array. Return 1 if successful, 0
* on failiure */
int ArraySet(Array * array, uint32_t index, Value x) {
if (index < array->count) {
array->data[index] = x;
return 1;
} else {
return 0;
}
}
/* Add an item to the end of the array */
void ArrayPush(GC * gc, Array * array, Value x) {
if (array->count >= array->capacity) {
ArrayEnsure(gc, array, 2 * array->count);
}
array->data[array->count++] = x;
}
/* Remove the last item from the Array and return it */
Value ArrayPop(Array * array) {
if (array->count) {
return array->data[--array->count];
} else {
Value v;
v.type = TYPE_NIL;
v.data.boolean = 0;
return v;
}
}
/* Look at the last item in the Array */
Value ArrayPeek(Array * array) {
if (array->count) {
return array->data[array->count - 1];
} else {
Value v;
v.type = TYPE_NIL;
v.data.boolean = 0;
return v;
}
}

30
array.h
View File

@ -1,30 +0,0 @@
#ifndef ARRAY_H_BW48JZSK
#define ARRAY_H_BW48JZSK
#include "datatypes.h"
/* Create a new Array */
Array * ArrayNew(GC * gc, uint32_t capacity);
/* Get a value of an array with bounds checking. Returns nil if
* outside bounds. */
Value ArrayGet(Array * array, uint32_t index);
/* Set a value in the array. Does bounds checking but will not grow
* or shrink the array */
int ArraySet(Array * array, uint32_t index, Value x);
/* Ensure that the internal memory hash enough space for capacity items */
void ArrayEnsure(GC * gc, Array * array, uint32_t capacity);
/* Set a value in an array. Will also append to the array if the index is
* greater than the current max index. */
void ArrayPush(GC * gc, Array * array, Value x);
/* Pop the last item in the array, or return NIL if empty */
Value ArrayPop(Array * array);
/* Look at the top most item of an Array */
Value ArrayPeek(Array * array);
#endif /* end of include guard: ARRAY_H_BW48JZSK */

View File

@ -1,56 +0,0 @@
#include <string.h>
#include "buffer.h"
#include "gc.h"
#include "value.h"
#include "vstring.h"
Buffer * BufferNew(GC * gc, uint32_t capacity) {
Buffer * buffer = GCAlloc(gc, sizeof(Buffer));
uint8_t * data = GCAlloc(gc, sizeof(uint8_t) * capacity);
buffer->data = data;
buffer->count = 0;
buffer->capacity = capacity;
return buffer;
}
void BufferEnsure(GC * gc, Buffer * buffer, uint32_t capacity) {
uint8_t * newData;
if (capacity <= buffer->capacity) return;
newData = GCAlloc(gc, capacity * sizeof(uint8_t));
memcpy(newData, buffer->data, buffer->count * sizeof(uint8_t));
buffer->data = newData;
buffer->capacity = capacity;
}
int32_t BufferGet(Buffer * buffer, uint32_t index) {
if (index < buffer->count) {
return buffer->data[index];
} else {
return -1;
}
}
void BufferPush(GC * gc, Buffer * buffer, uint8_t c) {
if (buffer->count >= buffer->capacity) {
BufferEnsure(gc, buffer, 2 * buffer->count);
}
buffer->data[buffer->count++] = c;
}
void BufferAppendData(GC * gc, Buffer * buffer, uint8_t * string, uint32_t length) {
uint32_t newSize = buffer->count + length;
if (newSize > buffer->capacity) {
BufferEnsure(gc, buffer, 2 * newSize);
}
memcpy(buffer->data + buffer->count, string, length);
buffer->count = newSize;
}
uint8_t * BufferToString(GC * gc, Buffer * buffer) {
uint8_t * data = GCAlloc(gc, buffer->count + 2 * sizeof(uint32_t));
data += 2 * sizeof(uint32_t);
VStringSize(data) = buffer->count;
VStringHash(data) = 0;
memcpy(data, buffer->data, buffer->count * sizeof(uint8_t));
return data;
}

View File

@ -1,24 +0,0 @@
#ifndef BUFFER_H_OEA9T4DJ
#define BUFFER_H_OEA9T4DJ
#include "datatypes.h"
Buffer * BufferNew(GC * gc, uint32_t capacity);
void BufferEnsure(GC * gc, Buffer * buffer, uint32_t capacity);
int32_t BufferGet(Buffer * buffer, uint32_t index);
void BufferPush(GC * gc, Buffer * buffer, uint8_t c);
void BufferAppendData(GC * gc, Buffer * buffer, uint8_t * string, uint32_t length);
uint8_t * BufferToString(GC * gc, Buffer * buffer);
#define BufferDefine(name, type) \
static void BufferPush##name (GC * gc, Buffer * buffer, type x) { \
union { type t; uint8_t bytes[sizeof(type)]; } u; \
u.t = x; return BufferAppendData(gc, buffer, u.bytes, sizeof(type)); \
}
#endif /* end of include guard: BUFFER_H_OEA9T4DJ */

220
compile.c
View File

@ -1,10 +1,5 @@
#include "compile.h"
#include "buffer.h"
#include "array.h"
#include "dict.h"
#include "gc.h"
#include "opcodes.h"
#include "vstring.h"
#include "ds.h"
#include "value.h"
#include "vm.h"
#include <string.h>
@ -111,18 +106,14 @@ BufferDefine(Int16, int16_t);
* jump back to start */
#define CError(c, e) ((c)->error = (e), longjmp((c)->onError, 1))
/* Get the garbage collector for the compiler */
#define CompilerGC(c) (&(c)->vm->gc)
/* Push a new scope in the compiler and return
* a pointer to it for configuration. There is
* more configuration that needs to be done if
* the new scope is a function declaration. */
static Scope * CompilerPushScope(Compiler * c, int sameFunction) {
GC * gc = CompilerGC(c);
Scope * scope = GCAlloc(gc, sizeof(Scope));
scope->locals = DictNew(gc, 10);
scope->freeHeap = GCAlloc(gc, 10 * sizeof(uint16_t));
Scope * scope = VMAlloc(c->vm, sizeof(Scope));
scope->locals = DictNew(c->vm, 10);
scope->freeHeap = VMAlloc(c->vm, 10 * sizeof(uint16_t));
scope->heapSize = 0;
scope->heapCapacity = 10;
scope->nextScope = NULL;
@ -138,8 +129,8 @@ static Scope * CompilerPushScope(Compiler * c, int sameFunction) {
scope->literalsArray = c->tail->literalsArray;
} else {
scope->nextLocal = 0;
scope->literals = DictNew(gc, 10);
scope->literalsArray = ArrayNew(gc, 10);
scope->literals = DictNew(c->vm, 10);
scope->literalsArray = ArrayNew(c->vm, 10);
}
c->tail = scope;
if (!c->root)
@ -206,7 +197,6 @@ static uint16_t CompilerGetLocal(Compiler * c, Scope * scope) {
/* Free a slot on the stack for other locals and/or
* intermediate values */
static void CompilerFreeLocal(Compiler * c, Scope * scope, uint16_t slot) {
GC * gc = CompilerGC(c);
if (slot == scope->nextLocal - 1) {
--scope->nextLocal;
return;
@ -217,7 +207,7 @@ static void CompilerFreeLocal(Compiler * c, Scope * scope, uint16_t slot) {
/* Ensure heap has space */
if (scope->heapSize >= scope->heapCapacity) {
uint32_t newCap = 2 * scope->heapSize;
uint16_t * newData = GCAlloc(gc, newCap * sizeof(uint16_t));
uint16_t * newData = VMAlloc(c->vm, newCap * sizeof(uint16_t));
memcpy(newData, scope->freeHeap, scope->heapSize * sizeof(uint16_t));
scope->freeHeap = newData;
scope->heapCapacity = newCap;
@ -245,8 +235,7 @@ static void CompilerFreeLocal(Compiler * c, Scope * scope, uint16_t slot) {
* are used during compilation to free up slots on the stack
* after they are no longer needed. */
static void CompilerTrackerInit(Compiler * c, SlotTracker * tracker) {
GC * gc = CompilerGC(c);
tracker->slots = GCAlloc(gc, 10 * sizeof(Slot));
tracker->slots = VMAlloc(c->vm, 10 * sizeof(Slot));
tracker->count = 0;
tracker->capacity = 10;
}
@ -266,13 +255,12 @@ static void CompilerDropSlot(Compiler * c, Scope * scope, Slot slot) {
* Also optionally write slot locations of all slots to the buffer.
* Useful for dictionary literals, array literals, function calls, etc. */
static void CompilerTrackerFree(Compiler * c, Scope * scope, SlotTracker * tracker, int writeToBuffer) {
GC * gc = CompilerGC(c);
uint32_t i;
if (writeToBuffer) {
Buffer * buffer = c->buffer;
for (i = 0; i < tracker->count; ++i) {
Slot * s = tracker->slots + i;
BufferPushUInt16(gc, buffer, s->index);
BufferPushUInt16(c->vm, buffer, s->index);
}
}
/* Free in reverse order */
@ -284,10 +272,9 @@ static void CompilerTrackerFree(Compiler * c, Scope * scope, SlotTracker * track
/* Add a new Slot to a slot tracker. */
static void CompilerTrackerPush(Compiler * c, SlotTracker * tracker, Slot slot) {
GC * gc = CompilerGC(c);
if (tracker->count >= tracker->capacity) {
uint32_t newCap = 2 * tracker->count;
Slot * newData = GCAlloc(gc, newCap * sizeof(Slot));
Slot * newData = VMAlloc(c->vm, newCap * sizeof(Slot));
memcpy(newData, tracker->slots, tracker->count * sizeof(Slot));
tracker->slots = newData;
tracker->capacity = newCap;
@ -299,7 +286,6 @@ static void CompilerTrackerPush(Compiler * c, SlotTracker * tracker, Slot slot)
* that one instead of creating a new literal. This allows for some reuse
* of things like string constants.*/
static uint16_t CompilerAddLiteral(Compiler * c, Scope * scope, Value x) {
GC * gc = CompilerGC(c);
Value checkDup = DictGet(scope->literals, &x);
uint16_t literalIndex = 0;
if (checkDup.type != TYPE_NIL) {
@ -311,15 +297,14 @@ static uint16_t CompilerAddLiteral(Compiler * c, Scope * scope, Value x) {
valIndex.type = TYPE_NUMBER;
literalIndex = scope->literalsArray->count;
valIndex.data.number = literalIndex;
DictPut(gc, scope->literals, &x, &valIndex);
ArrayPush(gc, scope->literalsArray, x);
DictPut(c->vm, scope->literals, &x, &valIndex);
ArrayPush(c->vm, scope->literalsArray, x);
}
return literalIndex;
}
/* Declare a symbol in a given scope. */
static uint16_t CompilerDeclareSymbol(Compiler * c, Scope * scope, Value sym) {
GC * gc = CompilerGC(c);
if (sym.type != TYPE_SYMBOL) {
CError(c, "Expected symbol");
}
@ -327,7 +312,7 @@ static uint16_t CompilerDeclareSymbol(Compiler * c, Scope * scope, Value sym) {
uint16_t target = CompilerGetLocal(c, scope);
x.type = TYPE_NUMBER;
x.data.number = target;
DictPut(gc, scope->locals, &sym, &x);
DictPut(c->vm, scope->locals, &sym, &x);
return target;
}
@ -366,7 +351,6 @@ static Slot CompileValue(Compiler * c, FormOptions opts, Value x);
static Slot CompileLiteral(Compiler * c, FormOptions opts, Value x) {
Scope * scope = c->tail;
Buffer * buffer = c->buffer;
GC * gc = CompilerGC(c);
Slot ret = SlotDefault();
uint16_t literalIndex;
if (opts.canDrop) {
@ -380,9 +364,9 @@ static Slot CompileLiteral(Compiler * c, FormOptions opts, Value x) {
ret.index = opts.target;
}
literalIndex = CompilerAddLiteral(c, scope, x);
BufferPushUInt16(gc, buffer, VM_OP_CST);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushUInt16(gc, buffer, literalIndex);
BufferPushUInt16(c->vm, buffer, VM_OP_CST);
BufferPushUInt16(c->vm, buffer, ret.index);
BufferPushUInt16(c->vm, buffer, literalIndex);
return ret;
}
@ -390,7 +374,6 @@ static Slot CompileLiteral(Compiler * c, FormOptions opts, Value x) {
static Slot CompileNonReferenceType(Compiler * c, FormOptions opts, Value x) {
Scope * scope = c->tail;
Buffer * buffer = c->buffer;
GC * gc = CompilerGC(c);
Slot ret = SlotDefault();
/* If the value is not used, the compiler can just immediately
* ignore it as there are no side effects. */
@ -405,29 +388,29 @@ static Slot CompileNonReferenceType(Compiler * c, FormOptions opts, Value x) {
ret.index = (uint16_t) opts.target;
}
if (x.type == TYPE_NIL) {
BufferPushUInt16(gc, buffer, VM_OP_NIL);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushUInt16(c->vm, buffer, VM_OP_NIL);
BufferPushUInt16(c->vm, buffer, ret.index);
} else if (x.type == TYPE_BOOLEAN) {
BufferPushUInt16(gc, buffer, x.data.boolean ? VM_OP_TRU : VM_OP_FLS);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushUInt16(c->vm, buffer, x.data.boolean ? VM_OP_TRU : VM_OP_FLS);
BufferPushUInt16(c->vm, buffer, ret.index);
} else if (x.type == TYPE_NUMBER) {
Number number = x.data.number;
int32_t int32Num = (int32_t) number;
if (number == (Number) int32Num) {
if (int32Num <= 32767 && int32Num >= -32768) {
int16_t int16Num = (int16_t) number;
BufferPushUInt16(gc, buffer, VM_OP_I16);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushInt16(gc, buffer, int16Num);
BufferPushUInt16(c->vm, buffer, VM_OP_I16);
BufferPushUInt16(c->vm, buffer, ret.index);
BufferPushInt16(c->vm, buffer, int16Num);
} else {
BufferPushUInt16(gc, buffer, VM_OP_I32);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushInt32(gc, buffer, int32Num);
BufferPushUInt16(c->vm, buffer, VM_OP_I32);
BufferPushUInt16(c->vm, buffer, ret.index);
BufferPushInt32(c->vm, buffer, int32Num);
}
} else {
BufferPushUInt16(gc, buffer, VM_OP_F64);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushNumber(gc, buffer, number);
BufferPushUInt16(c->vm, buffer, VM_OP_F64);
BufferPushUInt16(c->vm, buffer, ret.index);
BufferPushNumber(c->vm, buffer, number);
}
} else {
CError(c, "Expected boolean, nil, or number type.");
@ -438,7 +421,6 @@ static Slot CompileNonReferenceType(Compiler * c, FormOptions opts, Value x) {
/* Compile a symbol. Resolves any kind of symbol. */
static Slot CompileSymbol(Compiler * c, FormOptions opts, Value sym) {
Buffer * buffer = c->buffer;
GC * gc = CompilerGC(c);
Scope * scope = c->tail;
Slot ret = SlotDefault();
uint16_t index = 0;
@ -458,10 +440,10 @@ static Slot CompileSymbol(Compiler * c, FormOptions opts, Value sym) {
} else {
ret.index = opts.target;
}
BufferPushUInt16(gc, buffer, VM_OP_UPV);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushUInt16(gc, buffer, level);
BufferPushUInt16(gc, buffer, index);
BufferPushUInt16(c->vm, buffer, VM_OP_UPV);
BufferPushUInt16(c->vm, buffer, ret.index);
BufferPushUInt16(c->vm, buffer, level);
BufferPushUInt16(c->vm, buffer, index);
} else {
/* Local variable on stack */
if (opts.canChoose) {
@ -470,9 +452,9 @@ static Slot CompileSymbol(Compiler * c, FormOptions opts, Value sym) {
/* We need to move the variable. This
* would occur in a simple assignment like a = b. */
ret.index = opts.target;
BufferPushUInt16(gc, buffer, VM_OP_MOV);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushUInt16(gc, buffer, index);
BufferPushUInt16(c->vm, buffer, VM_OP_MOV);
BufferPushUInt16(c->vm, buffer, ret.index);
BufferPushUInt16(c->vm, buffer, index);
}
}
} else {
@ -487,7 +469,6 @@ static Slot CompileSymbol(Compiler * c, FormOptions opts, Value sym) {
static Slot CompileDict(Compiler * c, FormOptions opts, Dictionary * dict) {
Scope * scope = c->tail;
Buffer * buffer = c->buffer;
GC * gc = CompilerGC(c);
Slot ret = SlotDefault();
FormOptions subOpts = FormOptionsDefault();
DictionaryIterator iter;
@ -516,9 +497,9 @@ static Slot CompileDict(Compiler * c, FormOptions opts, Dictionary * dict) {
} else {
ret.index = opts.target;
}
BufferPushUInt16(gc, buffer, VM_OP_DIC);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushUInt16(gc, buffer, dict->count);
BufferPushUInt16(c->vm, buffer, VM_OP_DIC);
BufferPushUInt16(c->vm, buffer, ret.index);
BufferPushUInt16(c->vm, buffer, dict->count);
} else {
ret.isDud = 1;
}
@ -532,7 +513,6 @@ static Slot CompileDict(Compiler * c, FormOptions opts, Dictionary * dict) {
static Slot CompileArray(Compiler * c, FormOptions opts, Array * array) {
Scope * scope = c->tail;
Buffer * buffer = c->buffer;
GC * gc = CompilerGC(c);
Slot ret = SlotDefault();
FormOptions subOpts = FormOptionsDefault();
SlotTracker tracker;
@ -556,9 +536,9 @@ static Slot CompileArray(Compiler * c, FormOptions opts, Array * array) {
} else {
ret.index = opts.target;
}
BufferPushUInt16(gc, buffer, VM_OP_ARR);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushUInt16(gc, buffer, array->count);
BufferPushUInt16(c->vm, buffer, VM_OP_ARR);
BufferPushUInt16(c->vm, buffer, ret.index);
BufferPushUInt16(c->vm, buffer, array->count);
} else {
ret.isDud = 1;
}
@ -580,7 +560,6 @@ static Slot CompileArray(Compiler * c, FormOptions opts, Array * array) {
* also be ignored). */
static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form,
int16_t op0, int16_t op1, int16_t op2, int16_t opn) {
GC * gc = CompilerGC(c);
Scope * scope = c->tail;
Buffer * buffer = c->buffer;
Slot ret = SlotDefault();
@ -602,17 +581,17 @@ static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form,
/* Write the correct opcode */
if (form->count < 2) {
if (op0 < 0) CError(c, "This operator does not take 0 arguments.");
BufferPushUInt16(gc, buffer, op0);
BufferPushUInt16(c->vm, buffer, op0);
} else if (form->count == 2) {
if (op1 < 0) CError(c, "This operator does not take 1 argument.");
BufferPushUInt16(gc, buffer, op1);
BufferPushUInt16(c->vm, buffer, op1);
} else if (form->count == 3) {
if (op2 < 0) CError(c, "This operator does not take 2 arguments.");
BufferPushUInt16(gc, buffer, op2);
BufferPushUInt16(c->vm, buffer, op2);
} else {
if (opn < 0) CError(c, "This operator does not take n arguments.");
BufferPushUInt16(gc, buffer, opn);
BufferPushUInt16(gc, buffer, form->count - 1);
BufferPushUInt16(c->vm, buffer, opn);
BufferPushUInt16(c->vm, buffer, form->count - 1);
}
if (opts.canChoose) {
ret.isTemp = 1;
@ -620,7 +599,7 @@ static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form,
} else {
ret.isDud = opts.target;
}
BufferPushUInt16(gc, buffer, ret.index);
BufferPushUInt16(c->vm, buffer, ret.index);
}
/* Write the location of all of the arguments */
CompilerTrackerFree(c, scope, &tracker, 1);
@ -643,7 +622,6 @@ static Slot CompileDivision(Compiler * c, FormOptions opts, Array * form) {
/* Compile an assignment operation */
static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value right) {
GC * gc = CompilerGC(c);
Scope * scope = c->tail;
Buffer * buffer = c->buffer;
FormOptions subOpts = FormOptionsDefault();
@ -657,10 +635,10 @@ static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value righ
/* Evaluate the right hand side */
Slot slot = CompileValue(c, subOpts, right);
/* Set the up value */
BufferPushUInt16(gc, buffer, VM_OP_SUV);
BufferPushUInt16(gc, buffer, slot.index);
BufferPushUInt16(gc, buffer, level);
BufferPushUInt16(gc, buffer, target);
BufferPushUInt16(c->vm, buffer, VM_OP_SUV);
BufferPushUInt16(c->vm, buffer, slot.index);
BufferPushUInt16(c->vm, buffer, level);
BufferPushUInt16(c->vm, buffer, target);
/* Drop the possibly temporary slot if it is indeed temporary */
CompilerDropSlot(c, scope, slot);
return ret;
@ -679,21 +657,20 @@ static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value righ
} else {
ret.index = opts.target;
}
BufferPushUInt16(gc, buffer, VM_OP_NIL);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushUInt16(c->vm, buffer, VM_OP_NIL);
BufferPushUInt16(c->vm, buffer, ret.index);
}
return ret;
}
/* Writes bytecode to return a slot */
static void CompilerReturnSlot(Compiler * c, Slot slot) {
GC * gc = CompilerGC(c);
Buffer * buffer = c->buffer;
if (slot.isDud) {
BufferPushUInt16(gc, buffer, VM_OP_RTN);
BufferPushUInt16(c->vm, buffer, VM_OP_RTN);
} else {
BufferPushUInt16(gc, buffer, VM_OP_RET);
BufferPushUInt16(gc, buffer, slot.index);
BufferPushUInt16(c->vm, buffer, VM_OP_RET);
BufferPushUInt16(c->vm, buffer, slot.index);
}
}
@ -725,15 +702,14 @@ static Slot CompileBlock(Compiler * c, FormOptions opts, Array * form, uint32_t
/* Extract the last n bytes from the buffer and use them to construct
* a function definition. */
static FuncDef * CompilerGenFuncDef(Compiler * c, uint32_t lastNBytes, uint32_t arity) {
GC * gc = CompilerGC(c);
Scope * scope = c->tail;
Buffer * buffer = c->buffer;
FuncDef * def = GCAlloc(gc, sizeof(FuncDef));
FuncDef * def = VMAlloc(c->vm, sizeof(FuncDef));
/* Create enough space for the new byteCode */
if (lastNBytes > buffer->count) {
CError(c, "Trying to extract more bytes from buffer than in buffer.");
}
uint8_t * byteCode = GCAlloc(gc, lastNBytes);
uint8_t * byteCode = VMAlloc(c->vm, lastNBytes);
def->byteCode = (uint16_t *) byteCode;
def->byteCodeLen = lastNBytes / 2;
/* Copy the last chunk of bytes in the buffer into the new
@ -746,7 +722,7 @@ static FuncDef * CompilerGenFuncDef(Compiler * c, uint32_t lastNBytes, uint32_t
def->arity = arity;
/* Create the literals used by this function */
if (scope->literalsArray->count) {
def->literals = GCAlloc(gc, scope->literalsArray->count * sizeof(Value));
def->literals = VMAlloc(c->vm, scope->literalsArray->count * sizeof(Value));
memcpy(def->literals, scope->literalsArray->data,
scope->literalsArray->count * sizeof(Value));
} else {
@ -760,7 +736,6 @@ static FuncDef * CompilerGenFuncDef(Compiler * c, uint32_t lastNBytes, uint32_t
/* Compile a function from a function literal */
static Slot CompileFunction(Compiler * c, FormOptions opts, Array * form) {
GC * gc = CompilerGC(c);
Scope * scope = c->tail;
Buffer * buffer = c->buffer;
uint32_t current = 1;
@ -819,16 +794,15 @@ static Slot CompileFunction(Compiler * c, FormOptions opts, Array * form) {
} else {
ret.index = opts.target;
}
BufferPushUInt16(gc, buffer, VM_OP_CLN);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushUInt16(gc, buffer, literalIndex);
BufferPushUInt16(c->vm, buffer, VM_OP_CLN);
BufferPushUInt16(c->vm, buffer, ret.index);
BufferPushUInt16(c->vm, buffer, literalIndex);
}
return ret;
}
/* Branching special */
static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) {
GC * gc = CompilerGC(c);
Scope * scope = c->tail;
Buffer * buffer = c->buffer;
FormOptions condOpts = FormOptionsDefault();
@ -858,14 +832,14 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) {
* the result to nil before doing anything.
* Possible optimization - use the condition. */
if (form->count == 3) {
BufferPushUInt16(gc, buffer, VM_OP_NIL);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushUInt16(c->vm, buffer, VM_OP_NIL);
BufferPushUInt16(c->vm, buffer, ret.index);
}
} else {
ret.index = resOpts.target = opts.target;
if (form->count == 3) {
BufferPushUInt16(gc, buffer, VM_OP_NIL);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushUInt16(c->vm, buffer, VM_OP_NIL);
BufferPushUInt16(c->vm, buffer, ret.index);
}
}
/* Mark where the buffer is now so we can write the jump
@ -874,35 +848,35 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) {
/* For now use a long if bytecode instruction.
* A short if will probably ususually be sufficient. This
* if byte code will be replaced later with the correct index. */
BufferPushUInt16(gc, buffer, VM_OP_JIF);
BufferPushUInt16(gc, buffer, condition.index);
BufferPushUInt32(gc, buffer, 0);
BufferPushUInt16(c->vm, buffer, VM_OP_JIF);
BufferPushUInt16(c->vm, buffer, condition.index);
BufferPushUInt32(c->vm, buffer, 0);
/* Compile true path */
left = CompileValue(c, resOpts, form->data[2]);
if (opts.isTail && !left.isDud) {
BufferPushUInt16(gc, buffer, VM_OP_RET);
BufferPushUInt16(gc, buffer, left.index);
BufferPushUInt16(c->vm, buffer, VM_OP_RET);
BufferPushUInt16(c->vm, buffer, left.index);
}
CompilerDropSlot(c, scope, left);
/* If we need to jump again, do so */
if (!opts.isTail && form->count == 4) {
countAtJump = buffer->count;
BufferPushUInt16(gc, buffer, VM_OP_JMP);
BufferPushUInt32(gc, buffer, 0);
BufferPushUInt16(c->vm, buffer, VM_OP_JMP);
BufferPushUInt32(c->vm, buffer, 0);
}
/* Reinsert jump with correct index */
countAfterFirstBranch = buffer->count;
buffer->count = countAtJump;
BufferPushUInt16(gc, buffer, VM_OP_JIF);
BufferPushUInt16(gc, buffer, condition.index);
BufferPushUInt32(gc, buffer, (countAfterFirstBranch - countAtJump) / 2);
BufferPushUInt16(c->vm, buffer, VM_OP_JIF);
BufferPushUInt16(c->vm, buffer, condition.index);
BufferPushUInt32(c->vm, buffer, (countAfterFirstBranch - countAtJump) / 2);
buffer->count = countAfterFirstBranch;
/* Compile false path */
if (form->count == 4) {
right = CompileValue(c, resOpts, form->data[3]);
if (opts.isTail && !right.isDud) {
BufferPushUInt16(gc, buffer, VM_OP_RET);
BufferPushUInt16(gc, buffer, right.index);
BufferPushUInt16(c->vm, buffer, VM_OP_RET);
BufferPushUInt16(c->vm, buffer, right.index);
}
CompilerDropSlot(c, scope, right);
}
@ -910,8 +884,8 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) {
if (!opts.isTail && form->count == 4) {
countAfterFirstBranch = buffer->count;
buffer->count = countAtJump;
BufferPushUInt16(gc, buffer, VM_OP_JMP);
BufferPushUInt32(gc, buffer, (countAfterFirstBranch - countAtJump) / 2);
BufferPushUInt16(c->vm, buffer, VM_OP_JMP);
BufferPushUInt32(c->vm, buffer, (countAfterFirstBranch - countAtJump) / 2);
buffer->count = countAfterFirstBranch;
}
return ret;
@ -928,7 +902,6 @@ static Slot CompileDo(Compiler * c, FormOptions opts, Array * form) {
/* Quote special - returns its argument without parsing. */
static Slot CompileQuote(Compiler * c, FormOptions opts, Array * form) {
GC * gc = CompilerGC(c);
Scope * scope = c->tail;
Buffer * buffer = c->buffer;
Slot ret = SlotDefault();
@ -953,9 +926,9 @@ static Slot CompileQuote(Compiler * c, FormOptions opts, Array * form) {
ret.index = opts.target;
}
literalIndex = CompilerAddLiteral(c, scope, x);
BufferPushUInt16(gc, buffer, VM_OP_CST);
BufferPushUInt16(gc, buffer, ret.index);
BufferPushUInt16(gc, buffer, literalIndex);
BufferPushUInt16(c->vm, buffer, VM_OP_CST);
BufferPushUInt16(c->vm, buffer, ret.index);
BufferPushUInt16(c->vm, buffer, literalIndex);
return ret;
}
@ -1047,7 +1020,6 @@ static SpecialFormHelper GetSpecial(Array * form) {
/* Compile a form. Checks for special forms and macros. */
static Slot CompileForm(Compiler * c, FormOptions opts, Array * form) {
Scope * scope = c->tail;
GC * gc = CompilerGC(c);
Buffer * buffer = c->buffer;
SpecialFormHelper helper;
/* Empty forms evaluate to nil. */
@ -1074,19 +1046,19 @@ static Slot CompileForm(Compiler * c, FormOptions opts, Array * form) {
/* If this is in tail position do a tail call. */
if (opts.isTail) {
ret.isDud = 1;
BufferPushUInt16(gc, buffer, VM_OP_TCL);
BufferPushUInt16(c->vm, buffer, VM_OP_TCL);
} else {
BufferPushUInt16(gc, buffer, VM_OP_CAL);
BufferPushUInt16(c->vm, buffer, VM_OP_CAL);
if (opts.canDrop) {
ret.isTemp = 1;
ret.index = CompilerGetLocal(c, scope);
} else {
ret.index = opts.target;
}
BufferPushUInt16(gc, buffer, ret.index);
BufferPushUInt16(c->vm, buffer, ret.index);
}
/* Push the number of arguments to the function */
BufferPushUInt16(gc, buffer, form->count - 1);
BufferPushUInt16(c->vm, buffer, form->count - 1);
/* Write the location of all of the arguments */
CompilerTrackerFree(c, scope, &tracker, 1);
return ret;
@ -1116,8 +1088,8 @@ static Slot CompileValue(Compiler * c, FormOptions opts, Value x) {
/* Initialize a Compiler struct */
void CompilerInit(Compiler * c, VM * vm) {
c->vm = vm;
c->buffer = BufferNew(&vm->gc, 128);
c->env = ArrayNew(&vm->gc, 10);
c->buffer = BufferNew(vm, 128);
c->env = ArrayNew(vm, 10);
c->tail = c->root = NULL;
c->error = NULL;
CompilerPushScope(c, 0);
@ -1125,17 +1097,15 @@ void CompilerInit(Compiler * c, VM * vm) {
/* Register a global for the compilation environment. */
void CompilerAddGlobal(Compiler * c, const char * name, Value x) {
GC * gc = CompilerGC(c);
Value sym = ValueLoadCString(CompilerGC(c), name);
Value sym = ValueLoadCString(c->vm, name);
sym.type = TYPE_SYMBOL;
CompilerDeclareSymbol(c, c->root, sym);
ArrayPush(gc, c->env, x);
ArrayPush(c->vm, c->env, x);
}
/* Compile interface. Returns a function that evaluates the
* given AST. Returns NULL if there was an error during compilation. */
Func * CompilerCompile(Compiler * c, Value form) {
GC * gc = CompilerGC(c);
FormOptions opts = FormOptionsDefault();
FuncDef * def;
if (setjmp(c->onError)) {
@ -1151,9 +1121,9 @@ Func * CompilerCompile(Compiler * c, Value form) {
def = CompilerGenFuncDef(c, c->buffer->count, 0);
{
uint32_t envSize = c->env->count;
FuncEnv * env = GCAlloc(gc, sizeof(FuncEnv));
Func * func = GCAlloc(gc, sizeof(Func));
env->values = GCAlloc(gc, sizeof(Value) * envSize);
FuncEnv * env = VMAlloc(c->vm, sizeof(FuncEnv));
Func * func = VMAlloc(c->vm, sizeof(Func));
env->values = VMAlloc(c->vm, sizeof(Value) * envSize);
memcpy(env->values, c->env->data, envSize * sizeof(Value));
env->stackOffset = envSize;
env->thread = NULL;

View File

@ -39,7 +39,6 @@ typedef struct Array Array;
typedef struct Buffer Buffer;
typedef struct Dictionary Dictionary;
typedef struct DictionaryIterator DictionaryIterator;
typedef struct GC GC;
typedef struct Parser Parser;
typedef struct ParseState ParseState;
typedef struct Scope Scope;
@ -134,21 +133,18 @@ struct DictBucket {
DictBucket * next;
};
struct GC {
struct VM {
/* Garbage collection */
void * blocks;
void * user;
void (*handleOutOfMemory)(GC * gc);
uint32_t memoryInterval;
uint32_t nextCollection;
uint32_t black : 1;
};
struct VM {
GC gc;
const char * error;
/* Thread */
uint16_t * pc;
Array * thread;
Value * base;
/* Return state */
const char * error;
jmp_buf jump;
Value tempRoot; /* Temporary GC root */
};
@ -182,4 +178,48 @@ struct Compiler {
Buffer * buffer;
};
/* String utils */
#define VStringRaw(s) ((uint32_t *)(s) - 2)
#define VStringSize(v) (VStringRaw(v)[0])
#define VStringHash(v) (VStringRaw(v)[1])
/* Bytecode */
enum OpCode {
VM_OP_ADD = 0, /* 0x0000 */
VM_OP_SUB, /* 0x0001 */
VM_OP_MUL, /* 0x0002 */
VM_OP_DIV, /* 0x0003 */
VM_OP_NOT, /* 0x0004 */
VM_OP_LD0, /* 0x0005 */
VM_OP_LD1, /* 0x0006 */
VM_OP_FLS, /* 0x0007 */
VM_OP_TRU, /* 0x0008 */
VM_OP_NIL, /* 0x0009 */
VM_OP_I16, /* 0x000a */
VM_OP_UPV, /* 0x000b */
VM_OP_JIF, /* 0x000c */
VM_OP_JMP, /* 0x000d */
VM_OP_CAL, /* 0x000e */
VM_OP_RET, /* 0x000f */
VM_OP_SUV, /* 0x0010 */
VM_OP_CST, /* 0x0011 */
VM_OP_I32, /* 0x0012 */
VM_OP_F64, /* 0x0013 */
VM_OP_MOV, /* 0x0014 */
VM_OP_CLN, /* 0x0015 */
VM_OP_EQL, /* 0x0016 */
VM_OP_LTN, /* 0x0017 */
VM_OP_LTE, /* 0x0018 */
VM_OP_ARR, /* 0x0019 */
VM_OP_DIC, /* 0x001a */
VM_OP_TCL, /* 0x001b */
VM_OP_ADM, /* 0x001c */
VM_OP_SBM, /* 0x001d */
VM_OP_MUM, /* 0x001e */
VM_OP_DVM, /* 0x001f */
VM_OP_RTN /* 0x0020 */
};
#endif /* end of include guard: DATATYPES_H_PJJ035NT */

152
dict.c
View File

@ -1,152 +0,0 @@
#include "dict.h"
#include "value.h"
#include "gc.h"
/* Create a new dictionary */
Dictionary * DictNew(GC * gc, uint32_t capacity) {
Dictionary * dict = GCAlloc(gc, sizeof(Dictionary));
DictBucket ** buckets = GCZalloc(gc, capacity * sizeof(DictBucket *));
dict->buckets = buckets;
dict->capacity = capacity;
dict->count = 0;
return dict;
}
/* Resize the dictionary table. */
static void DictReHash(GC * gc, Dictionary * dict, uint32_t size) {
DictBucket ** newBuckets = GCZalloc(gc, size * sizeof(DictBucket *));
uint32_t i, count;
for (i = 0, count = dict->capacity; i < count; ++i) {
DictBucket * bucket = dict->buckets[i];
while (bucket) {
uint32_t index;
DictBucket * next = bucket->next;
index = ValueHash(&bucket->key) % size;
bucket->next = newBuckets[index];
newBuckets[index] = bucket;
bucket = next;
}
}
dict->buckets = newBuckets;
dict->capacity = size;
}
/* Find the bucket that contains the given key */
static DictBucket * DictFind(Dictionary * dict, Value * key) {
uint32_t index = ValueHash(key) % dict->capacity;
DictBucket * bucket = dict->buckets[index];
while (bucket) {
if (ValueEqual(&bucket->key, key))
return bucket;
bucket = bucket->next;
}
return (DictBucket *)0;
}
/* Get a value out of the dictionary */
Value DictGet(Dictionary * dict, Value * key) {
DictBucket * bucket = DictFind(dict, key);
if (bucket) {
return bucket->value;
} else {
Value nil;
nil.type = TYPE_NIL;
return nil;
}
}
/* Remove an entry from the dictionary */
Value DictRemove(GC * gc, Dictionary * dict, Value * key) {
DictBucket * bucket, * previous;
uint32_t index = ValueHash(key) % dict->capacity;
bucket = dict->buckets[index];
previous = (DictBucket *)0;
while (bucket) {
if (ValueEqual(&bucket->key, key)) {
if (previous) {
previous->next = bucket->next;
} else {
dict->buckets[index] = bucket->next;
}
if (dict->count < dict->capacity / 4) {
DictReHash(gc, dict, dict->capacity / 2);
}
--dict->count;
return bucket->value;
}
previous = bucket;
bucket = bucket->next;
}
/* Return nil if we found nothing */
{
Value nil;
nil.type = TYPE_NIL;
return nil;
}
}
/* Put a value into the dictionary. Returns 1 if successful, 0 if out of memory.
* The VM pointer is needed for memory allocation. */
void DictPut(GC * gc, Dictionary * dict, Value * key, Value * value) {
DictBucket * bucket, * previous;
uint32_t index = ValueHash(key) % dict->capacity;
if (key->type == TYPE_NIL) return;
/* Do a removal if value is nil */
if (value->type == TYPE_NIL) {
bucket = dict->buckets[index];
previous = (DictBucket *)0;
while (bucket) {
if (ValueEqual(&bucket->key, key)) {
if (previous) {
previous->next = bucket->next;
} else {
dict->buckets[index] = bucket->next;
}
if (dict->count < dict->capacity / 4) {
DictReHash(gc, dict, dict->capacity / 2);
}
--dict->count;
return;
}
previous = bucket;
bucket = bucket->next;
}
} else {
bucket = DictFind(dict, key);
if (bucket) {
bucket->value = *value;
} else {
if (dict->count >= 2 * dict->capacity) {
DictReHash(gc, dict, 2 * dict->capacity);
}
bucket = GCAlloc(gc, sizeof(DictBucket));
bucket->next = dict->buckets[index];
bucket->value = *value;
bucket->key = *key;
dict->buckets[index] = bucket;
++dict->count;
}
}
}
/* Begin iteration through a dictionary */
void DictIterate(Dictionary * dict, DictionaryIterator * iterator) {
iterator->index = 0;
iterator->dict = dict;
iterator->bucket = dict->buckets[0];
}
/* Provides a mechanism for iterating through a table. */
int DictIterateNext(DictionaryIterator * iterator, DictBucket ** bucket) {
Dictionary * dict = iterator->dict;
for (;;) {
if (iterator->bucket) {
*bucket = iterator->bucket;
iterator->bucket = iterator->bucket->next;
return 1;
}
if (++iterator->index >= dict->capacity) break;
iterator->bucket = dict->buckets[iterator->index];
}
return 0;
}

26
dict.h
View File

@ -1,26 +0,0 @@
#ifndef DICT_H_YN2BKHUQ
#define DICT_H_YN2BKHUQ
#include "datatypes.h"
/* Create a new dictionary */
Dictionary * DictNew(GC * gc, uint32_t capacity);
/* Get a value out of the dictionary */
Value DictGet(Dictionary * dict, Value * key);
/* Get a Value from the dictionary, but remove it at the same
* time. */
Value DictRemove(GC * gc, Dictionary * dict, Value * key);
/* Put a value into the dictionary. Returns 1 if successful, 0 if out of memory.
* The VM pointer is needed for memory allocation. */
void DictPut(GC * gc, Dictionary * dict, Value * key, Value * value);
/* Begin iteration through a dictionary */
void DictIterate(Dictionary * dict, DictionaryIterator * iterator);
/* Provides a mechanism for iterating through a table. */
int DictIterateNext(DictionaryIterator * iterator, DictBucket ** bucket);
#endif /* end of include guard: DICT_H_YN2BKHUQ */

297
ds.c Normal file
View File

@ -0,0 +1,297 @@
#include "ds.h"
#include "value.h"
#include "vm.h"
#include <string.h>
/****/
/* Buffer functions */
/****/
/* Create a new Buffer */
Buffer * BufferNew(VM * vm, uint32_t capacity) {
Buffer * buffer = VMAlloc(vm, sizeof(Buffer));
uint8_t * data = VMAlloc(vm, sizeof(uint8_t) * capacity);
buffer->data = data;
buffer->count = 0;
buffer->capacity = capacity;
return buffer;
}
/* Ensure that the buffer has enough internal capacity */
void BufferEnsure(VM * vm, Buffer * buffer, uint32_t capacity) {
uint8_t * newData;
if (capacity <= buffer->capacity) return;
newData = VMAlloc(vm, capacity * sizeof(uint8_t));
memcpy(newData, buffer->data, buffer->count * sizeof(uint8_t));
buffer->data = newData;
buffer->capacity = capacity;
}
/* Get a byte from an index in the buffer */
int32_t BufferGet(Buffer * buffer, uint32_t index) {
if (index < buffer->count) {
return buffer->data[index];
} else {
return -1;
}
}
/* Push a byte into the buffer */
void BufferPush(VM * vm, Buffer * buffer, uint8_t c) {
if (buffer->count >= buffer->capacity) {
BufferEnsure(vm, buffer, 2 * buffer->count);
}
buffer->data[buffer->count++] = c;
}
/* Push multiple bytes into the buffer */
void BufferAppendData(VM * vm, Buffer * buffer, uint8_t * string, uint32_t length) {
uint32_t newSize = buffer->count + length;
if (newSize > buffer->capacity) {
BufferEnsure(vm, buffer, 2 * newSize);
}
memcpy(buffer->data + buffer->count, string, length);
buffer->count = newSize;
}
/* Convert the buffer to a string */
uint8_t * BufferToString(VM * vm, Buffer * buffer) {
uint8_t * data = VMAlloc(vm, buffer->count + 2 * sizeof(uint32_t));
data += 2 * sizeof(uint32_t);
VStringSize(data) = buffer->count;
VStringHash(data) = 0;
memcpy(data, buffer->data, buffer->count * sizeof(uint8_t));
return data;
}
/****/
/* Array functions */
/****/
/* Creates a new array */
Array * ArrayNew(VM * vm, uint32_t capacity) {
Array * array = VMAlloc(vm, sizeof(Array));
Value * data = VMAlloc(vm, capacity * sizeof(Value));
array->data = data;
array->count = 0;
array->capacity = capacity;
return array;
}
/* Ensure the array has enough capacity for capacity elements */
void ArrayEnsure(VM * vm, Array * array, uint32_t capacity) {
Value * newData;
if (capacity <= array->capacity) return;
newData = VMAlloc(vm, capacity * sizeof(Value));
memcpy(newData, array->data, array->count * sizeof(Value));
array->data = newData;
array->capacity = capacity;
}
/* Get a value of an array with bounds checking. */
Value ArrayGet(Array * array, uint32_t index) {
if (index < array->count) {
return array->data[index];
} else {
Value v;
v.type = TYPE_NIL;
v.data.boolean = 0;
return v;
}
}
/* Try to set an index in the array. Return 1 if successful, 0
* on failiure */
int ArraySet(Array * array, uint32_t index, Value x) {
if (index < array->count) {
array->data[index] = x;
return 1;
} else {
return 0;
}
}
/* Add an item to the end of the array */
void ArrayPush(VM * vm, Array * array, Value x) {
if (array->count >= array->capacity) {
ArrayEnsure(vm, array, 2 * array->count);
}
array->data[array->count++] = x;
}
/* Remove the last item from the Array and return it */
Value ArrayPop(Array * array) {
if (array->count) {
return array->data[--array->count];
} else {
Value v;
v.type = TYPE_NIL;
v.data.boolean = 0;
return v;
}
}
/* Look at the last item in the Array */
Value ArrayPeek(Array * array) {
if (array->count) {
return array->data[array->count - 1];
} else {
Value v;
v.type = TYPE_NIL;
v.data.boolean = 0;
return v;
}
}
/****/
/* Dictionary functions */
/****/
/* Create a new dictionary */
Dictionary * DictNew(VM * vm, uint32_t capacity) {
Dictionary * dict = VMAlloc(vm, sizeof(Dictionary));
DictBucket ** buckets = VMZalloc(vm, capacity * sizeof(DictBucket *));
dict->buckets = buckets;
dict->capacity = capacity;
dict->count = 0;
return dict;
}
/* Resize the dictionary table. */
static void DictReHash(VM * vm, Dictionary * dict, uint32_t size) {
DictBucket ** newBuckets = VMZalloc(vm, size * sizeof(DictBucket *));
uint32_t i, count;
for (i = 0, count = dict->capacity; i < count; ++i) {
DictBucket * bucket = dict->buckets[i];
while (bucket) {
uint32_t index;
DictBucket * next = bucket->next;
index = ValueHash(&bucket->key) % size;
bucket->next = newBuckets[index];
newBuckets[index] = bucket;
bucket = next;
}
}
dict->buckets = newBuckets;
dict->capacity = size;
}
/* Find the bucket that contains the given key */
static DictBucket * DictFind(Dictionary * dict, Value * key) {
uint32_t index = ValueHash(key) % dict->capacity;
DictBucket * bucket = dict->buckets[index];
while (bucket) {
if (ValueEqual(&bucket->key, key))
return bucket;
bucket = bucket->next;
}
return (DictBucket *)0;
}
/* Get a value out of the dictionary */
Value DictGet(Dictionary * dict, Value * key) {
DictBucket * bucket = DictFind(dict, key);
if (bucket) {
return bucket->value;
} else {
Value nil;
nil.type = TYPE_NIL;
return nil;
}
}
/* Remove an entry from the dictionary */
Value DictRemove(VM * vm, Dictionary * dict, Value * key) {
DictBucket * bucket, * previous;
uint32_t index = ValueHash(key) % dict->capacity;
bucket = dict->buckets[index];
previous = (DictBucket *)0;
while (bucket) {
if (ValueEqual(&bucket->key, key)) {
if (previous) {
previous->next = bucket->next;
} else {
dict->buckets[index] = bucket->next;
}
if (dict->count < dict->capacity / 4) {
DictReHash(vm, dict, dict->capacity / 2);
}
--dict->count;
return bucket->value;
}
previous = bucket;
bucket = bucket->next;
}
/* Return nil if we found nothing */
{
Value nil;
nil.type = TYPE_NIL;
return nil;
}
}
/* Put a value into the dictionary. Returns 1 if successful, 0 if out of memory.
* The VM pointer is needed for memory allocation. */
void DictPut(VM * vm, Dictionary * dict, Value * key, Value * value) {
DictBucket * bucket, * previous;
uint32_t index = ValueHash(key) % dict->capacity;
if (key->type == TYPE_NIL) return;
/* Do a removal if value is nil */
if (value->type == TYPE_NIL) {
bucket = dict->buckets[index];
previous = (DictBucket *)0;
while (bucket) {
if (ValueEqual(&bucket->key, key)) {
if (previous) {
previous->next = bucket->next;
} else {
dict->buckets[index] = bucket->next;
}
if (dict->count < dict->capacity / 4) {
DictReHash(vm, dict, dict->capacity / 2);
}
--dict->count;
return;
}
previous = bucket;
bucket = bucket->next;
}
} else {
bucket = DictFind(dict, key);
if (bucket) {
bucket->value = *value;
} else {
if (dict->count >= 2 * dict->capacity) {
DictReHash(vm, dict, 2 * dict->capacity);
}
bucket = VMAlloc(vm, sizeof(DictBucket));
bucket->next = dict->buckets[index];
bucket->value = *value;
bucket->key = *key;
dict->buckets[index] = bucket;
++dict->count;
}
}
}
/* Begin iteration through a dictionary */
void DictIterate(Dictionary * dict, DictionaryIterator * iterator) {
iterator->index = 0;
iterator->dict = dict;
iterator->bucket = dict->buckets[0];
}
/* Provides a mechanism for iterating through a table. */
int DictIterateNext(DictionaryIterator * iterator, DictBucket ** bucket) {
Dictionary * dict = iterator->dict;
for (;;) {
if (iterator->bucket) {
*bucket = iterator->bucket;
iterator->bucket = iterator->bucket->next;
return 1;
}
if (++iterator->index >= dict->capacity) break;
iterator->bucket = dict->buckets[iterator->index];
}
return 0;
}

87
ds.h Normal file
View File

@ -0,0 +1,87 @@
#ifndef ds_h_INCLUDED
#define ds_h_INCLUDED
#include "datatypes.h"
/****/
/* Buffer functions */
/****/
/* Create a new buffer */
Buffer * BufferNew(VM * vm, uint32_t capacity);
/* Ensure the buffer has enough capacity */
void BufferEnsure(VM * vm, Buffer * buffer, uint32_t capacity);
/* Get a value from the buffer */
int32_t BufferGet(Buffer * buffer, uint32_t index);
/* Push a value to the buffer */
void BufferPush(VM * vm, Buffer * buffer, uint8_t c);
/* Append a piece of memory to the buffer */
void BufferAppendData(VM * vm, Buffer * buffer, uint8_t * string, uint32_t length);
/* Convert the buffer to a string */
uint8_t * BufferToString(VM * vm, Buffer * buffer);
/* Define a push function for pushing a certain type to the buffer */
#define BufferDefine(name, type) \
static void BufferPush##name (VM * vm, Buffer * buffer, type x) { \
union { type t; uint8_t bytes[sizeof(type)]; } u; \
u.t = x; return BufferAppendData(vm, buffer, u.bytes, sizeof(type)); \
}
/****/
/* Array functions */
/****/
/* Create a new Array */
Array * ArrayNew(VM * vm, uint32_t capacity);
/* Get a value of an array with bounds checking. Returns nil if
* outside bounds. */
Value ArrayGet(Array * array, uint32_t index);
/* Set a value in the array. Does bounds checking but will not grow
* or shrink the array */
int ArraySet(Array * array, uint32_t index, Value x);
/* Ensure that the internal memory hash enough space for capacity items */
void ArrayEnsure(VM * vm, Array * array, uint32_t capacity);
/* Set a value in an array. Will also append to the array if the index is
* greater than the current max index. */
void ArrayPush(VM * vm, Array * array, Value x);
/* Pop the last item in the array, or return NIL if empty */
Value ArrayPop(Array * array);
/* Look at the top most item of an Array */
Value ArrayPeek(Array * array);
/****/
/* Dictionary functions */
/****/
/* Create a new dictionary */
Dictionary * DictNew(VM * vm, uint32_t capacity);
/* Get a value out of the dictionary */
Value DictGet(Dictionary * dict, Value * key);
/* Get a Value from the dictionary, but remove it at the same
* time. */
Value DictRemove(VM * vm, Dictionary * dict, Value * key);
/* Put a value into the dictionary. Returns 1 if successful, 0 if out of memory.
* The VM pointer is needed for memory allocation. */
void DictPut(VM * vm, Dictionary * dict, Value * key, Value * value);
/* Begin iteration through a dictionary */
void DictIterate(Dictionary * dict, DictionaryIterator * iterator);
/* Provides a mechanism for iterating through a table. */
int DictIterateNext(DictionaryIterator * iterator, DictBucket ** bucket);
#endif // ds_h_INCLUDED

209
gc.c
View File

@ -1,209 +0,0 @@
#include <stdlib.h>
#include "gc.h"
#include "dict.h"
#include "vstring.h"
#include "parse.h"
#define GCHeader(mem) ((GCMemoryHeader *)(mem) - 1)
typedef struct GCMemoryHeader GCMemoryHeader;
struct GCMemoryHeader {
GCMemoryHeader * next;
uint32_t color;
};
/* Initialize a garbage collector */
void GCInit(GC * gc, uint32_t memoryInterval) {
gc->black = 0;
gc->blocks = NULL;
gc->nextCollection = 0;
gc->memoryInterval = memoryInterval;
gc->handleOutOfMemory = NULL;
}
/* Mark some raw memory */
void GCMarkMemory(GC * gc, void * memory) {
GCHeader(memory)->color = gc->black;
}
/* Helper to mark function environments */
static void GCMarkFuncEnv(GC * gc, FuncEnv * env) {
if (GCHeader(env)->color != gc->black) {
Value temp;
GCHeader(env)->color = gc->black;
if (env->thread) {
temp.type = TYPE_THREAD;
temp.data.array = env->thread;
GCMark(gc, &temp);
} else {
uint32_t count = env->stackOffset;
uint32_t i;
GCHeader(env->values)->color = gc->black;
for (i = 0; i < count; ++i) {
GCMark(gc, env->values + i);
}
}
}
}
/* Mark allocated memory associated with a value. This is
* the main function for doing garbage collection. */
void GCMark(GC * gc, Value * x) {
switch (x->type) {
case TYPE_NIL:
case TYPE_BOOLEAN:
case TYPE_NUMBER:
case TYPE_CFUNCTION:
break;
case TYPE_STRING:
case TYPE_SYMBOL:
GCHeader(VStringRaw(x->data.string))->color = gc->black;
break;
case TYPE_BYTEBUFFER:
GCHeader(x->data.buffer)->color = gc->black;
GCHeader(x->data.buffer->data)->color = gc->black;
break;
case TYPE_ARRAY:
case TYPE_FORM:
if (GCHeader(x->data.array)->color != gc->black) {
uint32_t i, count;
count = x->data.array->count;
GCHeader(x->data.array)->color = gc->black;
GCHeader(x->data.array->data)->color = gc->black;
for (i = 0; i < count; ++i)
GCMark(gc, x->data.array->data + i);
}
break;
case TYPE_THREAD:
if (GCHeader(x->data.array)->color != gc->black) {
uint32_t i, count;
count = x->data.array->count;
GCHeader(x->data.array)->color = gc->black;
GCHeader(x->data.array->data)->color = gc->black;
if (count) {
count += FrameSize(x->data.array);
for (i = 0; i < count; ++i)
GCMark(gc, x->data.array->data + i);
}
}
break;
case TYPE_FUNCTION:
if (GCHeader(x->data.func)->color != gc->black) {
Func * f = x->data.func;
GCHeader(f)->color = gc->black;
GCMarkFuncEnv(gc, f->env);
{
Value temp;
temp.type = TYPE_FUNCDEF;
temp.data.funcdef = x->data.funcdef;
GCMark(gc, &temp);
if (f->parent) {
temp.type = TYPE_FUNCTION;
temp.data.func = f->parent;
GCMark(gc, &temp);
}
}
}
break;
case TYPE_DICTIONARY:
if (GCHeader(x->data.dict)->color != gc->black) {
DictionaryIterator iter;
DictBucket * bucket;
GCHeader(x->data.dict)->color = gc->black;
GCHeader(x->data.dict->buckets)->color = gc->black;
DictIterate(x->data.dict, &iter);
while (DictIterateNext(&iter, &bucket)) {
GCHeader(bucket)->color = gc->black;
GCMark(gc, &bucket->key);
GCMark(gc, &bucket->value);
}
}
break;
case TYPE_FUNCDEF:
if (GCHeader(x->data.funcdef)->color != gc->black) {
GCHeader(x->data.funcdef->byteCode)->color = gc->black;
uint32_t count, i;
count = x->data.funcdef->literalsLen;
if (x->data.funcdef->literals) {
GCHeader(x->data.funcdef->literals)->color = gc->black;
for (i = 0; i < count; ++i)
GCMark(gc, x->data.funcdef->literals + i);
}
}
break;
case TYPE_FUNCENV:
GCMarkFuncEnv(gc, x->data.funcenv);
break;
}
}
/* Iterate over all allocated memory, and free memory that is not
* marked as reachable. Flip the gc color flag for next sweep. */
void GCSweep(GC * gc) {
GCMemoryHeader * previous = NULL;
GCMemoryHeader * current = gc->blocks;
while (current) {
if (current->color != gc->black) {
if (previous) {
previous->next = current->next;
} else {
gc->blocks = current->next;
}
free(current);
} else {
previous = current;
}
current = current->next;
}
/* Rotate flag */
gc->black = !gc->black;
}
/* Clean up all memory */
void GCClear(GC * gc) {
GCMemoryHeader * current = gc->blocks;
while (current) {
GCMemoryHeader * next = current->next;
free(current);
current = next;
}
gc->blocks = NULL;
}
/* Prepare a memory block */
static void * GCPrepare(GC * gc, char * rawBlock, uint32_t size) {
GCMemoryHeader * mdata;
if (rawBlock == NULL) {
if (gc->handleOutOfMemory != NULL)
gc->handleOutOfMemory(gc);
return NULL;
}
gc->nextCollection += size;
mdata = (GCMemoryHeader *) rawBlock;
mdata->next = gc->blocks;
gc->blocks = mdata;
mdata->color = !gc->black;
return rawBlock + sizeof(GCMemoryHeader);
}
/* Allocate some memory that is tracked for garbage collection */
void * GCAlloc(GC * gc, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
return GCPrepare(gc, malloc(totalSize), totalSize);
}
/* Allocate some zeroed memory that is tracked for garbage collection */
void * GCZalloc(GC * gc, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
return GCPrepare(gc, calloc(1, totalSize), totalSize);
}

41
gc.h
View File

@ -1,41 +0,0 @@
#ifndef GC_H_N8L3U4KK
#define GC_H_N8L3U4KK
#include "datatypes.h"
/* Initialize a GC */
void GCInit(GC * gc, uint32_t memoryInterval);
/* Iterate over all allocated memory, and frees memory that is not
* marked as reachable */
void GCSweep(GC * gc);
/* Do a depth first search of the variables and mark all reachable memory.
* Root variables are just everyting in the stack. */
void GCMark(GC * gc, Value * x);
/* Mark some raw memory as reachable */
void GCMarkMemory(GC * gc, void * memory);
/* Clean up all memory, including locked memory */
void GCClear(GC * gc);
/* Allocate some memory that is tracked for garbage collection */
void * GCAlloc(GC * gc, uint32_t size);
/* Allocate zeroed memory */
void * GCZalloc(GC * gc, uint32_t size);
/* Run a collection */
#define GCCollect(gc, root) \
(GCMark((gc), (root)), GCSweep(gc), (gc)->nextCollection = 0)
/* Check if a collection needs to be run */
#define GCNeedsCollect(gc) \
((gc)->nextCollection >= (gc)->memoryInterval)
/* Run a collection if enough memory has been allocated since last collection */
#define GCMaybeCollect(gc, root) \
(GCNeedsCollect(gc) ? GCCollect((gc), (root)) : 0)
#endif /* end of include guard: GC_H_N8L3U4KK */

BIN
interp

Binary file not shown.

View File

@ -1,40 +0,0 @@
#ifndef OPCODES_H_EFPEYNZ0
#define OPCODES_H_EFPEYNZ0
enum OpCode {
VM_OP_ADD = 0, /* 0x0000 */
VM_OP_SUB, /* 0x0001 */
VM_OP_MUL, /* 0x0002 */
VM_OP_DIV, /* 0x0003 */
VM_OP_NOT, /* 0x0004 */
VM_OP_LD0, /* 0x0005 */
VM_OP_LD1, /* 0x0006 */
VM_OP_FLS, /* 0x0007 */
VM_OP_TRU, /* 0x0008 */
VM_OP_NIL, /* 0x0009 */
VM_OP_I16, /* 0x000a */
VM_OP_UPV, /* 0x000b */
VM_OP_JIF, /* 0x000c */
VM_OP_JMP, /* 0x000d */
VM_OP_CAL, /* 0x000e */
VM_OP_RET, /* 0x000f */
VM_OP_SUV, /* 0x0010 */
VM_OP_CST, /* 0x0011 */
VM_OP_I32, /* 0x0012 */
VM_OP_F64, /* 0x0013 */
VM_OP_MOV, /* 0x0014 */
VM_OP_CLN, /* 0x0015 */
VM_OP_EQL, /* 0x0016 */
VM_OP_LTN, /* 0x0017 */
VM_OP_LTE, /* 0x0018 */
VM_OP_ARR, /* 0x0019 */
VM_OP_DIC, /* 0x001a */
VM_OP_TCL, /* 0x001b */
VM_OP_ADM, /* 0x001c */
VM_OP_SBM, /* 0x001d */
VM_OP_MUM, /* 0x001e */
VM_OP_DVM, /* 0x001f */
VM_OP_RTN /* 0x0020 */
};
#endif /* end of include guard: OPCODES_H_EFPEYNZ0 */

32
parse.c
View File

@ -3,13 +3,9 @@
#include <string.h>
#include <setjmp.h>
#include "datatypes.h"
#include "array.h"
#include "dict.h"
#include "gc.h"
#include "buffer.h"
#include "ds.h"
#include "parse.h"
#include "vm.h"
#include "vstring.h"
static const char UNEXPECTED_CLOSING_DELIM[] = "Unexpected closing delimiter";
@ -68,11 +64,10 @@ static ParseState * ParserPop(Parser * p) {
/* Add a new, empty ParseState to the ParseStack. */
static void ParserPush(Parser *p, ParseType type) {
GC * gc = &p->vm->gc;
ParseState * top;
if (p->count >= p->cap) {
uint32_t newCap = 2 * p->count;
ParseState * data = GCAlloc(gc, newCap);
ParseState * data = VMAlloc(p->vm, newCap);
p->data = data;
p->cap = newCap;
}
@ -86,14 +81,14 @@ static void ParserPush(Parser *p, ParseType type) {
case PTYPE_STRING:
top->buf.string.state = STRING_STATE_BASE;
case PTYPE_TOKEN:
top->buf.string.buffer = BufferNew(gc, 10);
top->buf.string.buffer = BufferNew(p->vm, 10);
break;
case PTYPE_ARRAY:
case PTYPE_FORM:
top->buf.array = ArrayNew(gc, 10);
top->buf.array = ArrayNew(p->vm, 10);
break;
case PTYPE_DICTIONARY:
top->buf.dictState.dict = DictNew(gc, 10);
top->buf.dictState.dict = DictNew(p->vm, 10);
top->buf.dictState.keyFound = 0;
break;
}
@ -101,7 +96,6 @@ static void ParserPush(Parser *p, ParseType type) {
/* Append a value to the top-most state in the Parser's stack. */
static void ParserTopAppend(Parser * p, Value x) {
GC * gc = &p->vm->gc;
ParseState * top = ParserPeek(p);
if (!top) return;
switch (top->type) {
@ -111,11 +105,11 @@ static void ParserTopAppend(Parser * p, Value x) {
break;
case PTYPE_ARRAY:
case PTYPE_FORM:
ArrayPush(gc, top->buf.array, x);
ArrayPush(p->vm, top->buf.array, x);
break;
case PTYPE_DICTIONARY:
if (top->buf.dictState.keyFound) {
DictPut(gc, top->buf.dictState.dict, &top->buf.dictState.key, &x);
DictPut(p->vm, top->buf.dictState.dict, &top->buf.dictState.key, &x);
} else {
top->buf.dictState.key = x;
}
@ -261,7 +255,7 @@ static Value ParserBuildTokenBuffer(Parser * p, Buffer * buf) {
x.type = TYPE_NIL;
} else {
x.type = TYPE_SYMBOL;
x.data.string = BufferToString(&p->vm->gc, buf);
x.data.string = BufferToString(p->vm, buf);
}
}
return x;
@ -276,7 +270,7 @@ static int ParserTokenState(Parser * p, uint8_t c) {
ParserTopAppend(p, ParserBuildTokenBuffer(p, buf));
return !(c == ')' || c == ']' || c == '}');
} else if (isSymbolChar(c)) {
BufferPush(&p->vm->gc, buf, c);
BufferPush(p->vm, buf, c);
return 1;
} else {
PError(p, "Expected symbol character.");
@ -294,11 +288,11 @@ static int ParserStringState(Parser * p, uint8_t c) {
} else if (c == '"') {
Value x;
x.type = TYPE_STRING;
x.data.string = BufferToString(&p->vm->gc, top->buf.string.buffer);
x.data.string = BufferToString(p->vm, top->buf.string.buffer);
ParserPop(p);
ParserTopAppend(p, x);
} else {
BufferPush(&p->vm->gc, top->buf.string.buffer, c);
BufferPush(p->vm, top->buf.string.buffer, c);
}
break;
case STRING_STATE_ESCAPE:
@ -317,7 +311,7 @@ static int ParserStringState(Parser * p, uint8_t c) {
PError(p, "Unknown string escape sequence.");
return 1;
}
BufferPush(&p->vm->gc, top->buf.string.buffer, next);
BufferPush(p->vm, top->buf.string.buffer, next);
top->buf.string.state = STRING_STATE_BASE;
}
break;
@ -444,7 +438,7 @@ int ParserParseCString(Parser * p, const char * string) {
/* Parser initialization (memory allocation) */
void ParserInit(Parser * p, VM * vm) {
p->vm = vm;
ParseState * data = GCAlloc(&vm->gc, sizeof(ParseState) * 10);
ParseState * data = VMAlloc(vm, sizeof(ParseState) * 10);
p->data = data;
p->count = 0;
p->cap = 10;

53
value.c
View File

@ -1,10 +1,9 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "gc.h"
#include "vstring.h"
#include "value.h"
#include "buffer.h"
#include "ds.h"
#include "vm.h"
/* Print the bytecode for a FuncDef */
static void FuncDefBytecodePrint(FuncDef * def) {
@ -79,8 +78,8 @@ void ValuePrint(Value * x, uint32_t indent) {
}
}
static uint8_t * LoadCString(GC * gc, const char * string, uint32_t len) {
uint8_t * data = GCAlloc(gc, len + 2 * sizeof(uint32_t));
static uint8_t * LoadCString(VM * vm, const char * string, uint32_t len) {
uint8_t * data = VMAlloc(vm, len + 2 * sizeof(uint32_t));
data += 2 * sizeof(uint32_t);
VStringHash(data) = 0;
VStringSize(data) = len;
@ -88,16 +87,16 @@ static uint8_t * LoadCString(GC * gc, const char * string, uint32_t len) {
return data;
}
Value ValueLoadCString(GC * gc, const char * string) {
Value ValueLoadCString(VM * vm, const char * string) {
Value ret;
ret.type = TYPE_STRING;
ret.data.string = LoadCString(gc, string, strlen(string));
ret.data.string = LoadCString(vm, string, strlen(string));
return ret;
}
static uint8_t * NumberToString(GC * gc, Number x) {
static uint8_t * NumberToString(VM * vm, Number x) {
static const uint32_t SIZE = 20;
uint8_t * data = GCAlloc(gc, SIZE + 2 * sizeof(uint32_t));
uint8_t * data = VMAlloc(vm, SIZE + 2 * sizeof(uint32_t));
data += 2 * sizeof(uint32_t);
snprintf((char *) data, SIZE, "%.17g", x);
VStringHash(data) = 0;
@ -109,10 +108,10 @@ static const char * HEX_CHARACTERS = "0123456789ABCDEF";
#define HEX(i) (((uint8_t *) HEX_CHARACTERS)[(i)])
/* Returns a string description for a pointer */
static uint8_t * StringDescription(GC * gc, const char * title, uint32_t titlelen, void * pointer) {
static uint8_t * StringDescription(VM * vm, const char * title, uint32_t titlelen, void * pointer) {
uint32_t len = 3 + titlelen + sizeof(pointer) * 2;
uint32_t i;
uint8_t * data = GCAlloc(gc, len + 2 * sizeof(uint32_t));
uint8_t * data = VMAlloc(vm, len + 2 * sizeof(uint32_t));
uint8_t * c;
union {
uint8_t bytes[sizeof(void *)];
@ -136,39 +135,39 @@ static uint8_t * StringDescription(GC * gc, const char * title, uint32_t titlele
}
/* Returns a string pointer or NULL if could not allocate memory. */
uint8_t * ValueToString(GC * gc, Value * x) {
uint8_t * ValueToString(VM * vm, Value * x) {
switch (x->type) {
case TYPE_NIL:
return LoadCString(gc, "nil", 3);
return LoadCString(vm, "nil", 3);
case TYPE_BOOLEAN:
if (x->data.boolean) {
return LoadCString(gc, "true", 4);
return LoadCString(vm, "true", 4);
} else {
return LoadCString(gc, "false", 5);
return LoadCString(vm, "false", 5);
}
case TYPE_NUMBER:
return NumberToString(gc, x->data.number);
return NumberToString(vm, x->data.number);
case TYPE_ARRAY:
return StringDescription(gc, "array", 5, x->data.array);
return StringDescription(vm, "array", 5, x->data.array);
case TYPE_FORM:
return StringDescription(gc, "form", 4, x->data.array);
return StringDescription(vm, "form", 4, x->data.array);
case TYPE_STRING:
case TYPE_SYMBOL:
return x->data.string;
case TYPE_BYTEBUFFER:
return StringDescription(gc, "buffer", 6, x->data.buffer);
return StringDescription(vm, "buffer", 6, x->data.buffer);
case TYPE_CFUNCTION:
return StringDescription(gc, "cfunction", 9, x->data.cfunction);
return StringDescription(vm, "cfunction", 9, x->data.cfunction);
case TYPE_FUNCTION:
return StringDescription(gc, "function", 8, x->data.func);
return StringDescription(vm, "function", 8, x->data.func);
case TYPE_DICTIONARY:
return StringDescription(gc, "dictionary", 10, x->data.dict);
return StringDescription(vm, "dictionary", 10, x->data.dict);
case TYPE_FUNCDEF:
return StringDescription(gc, "funcdef", 7, x->data.funcdef);
return StringDescription(vm, "funcdef", 7, x->data.funcdef);
case TYPE_FUNCENV:
return StringDescription(gc, "funcenv", 7, x->data.funcenv);
return StringDescription(vm, "funcenv", 7, x->data.funcenv);
case TYPE_THREAD:
return StringDescription(gc, "thread", 6, x->data.array);
return StringDescription(vm, "thread", 6, x->data.array);
}
return NULL;
}
@ -184,7 +183,7 @@ uint32_t djb2(const uint8_t * str) {
/* Check if two values are equal. This is strict equality with no conversion. */
int ValueEqual(Value * x, Value * y) {
int result;
int result = 0;
if (x->type != y->type) {
result = 0;
} else {
@ -245,7 +244,7 @@ int ValueEqual(Value * x, Value * y) {
/* Computes a hash value for a function */
uint32_t ValueHash(Value * x) {
uint32_t hash;
uint32_t hash = 0;
switch (x->type) {
case TYPE_NIL:
hash = 0;

View File

@ -9,9 +9,9 @@ int ValueCompare(Value * x, Value * y);
int ValueEqual(Value * x, Value * y);
Value ValueLoadCString(GC * gc, const char * string);
Value ValueLoadCString(VM * vm, const char * string);
uint8_t * ValueToString(GC * gc, Value * x);
uint8_t * ValueToString(VM * vm, Value * x);
uint32_t ValueHash(Value * x);

257
vm.c
View File

@ -3,12 +3,7 @@
#include <stdio.h>
#include "vm.h"
#include "value.h"
#include "array.h"
#include "vstring.h"
#include "dict.h"
#include "gc.h"
#include "buffer.h"
#include "opcodes.h"
#include "ds.h"
#define VMArg(i) (vm->base + (i))
#define VMOpArg(i) (VMArg(vm->pc[(i)]))
@ -19,34 +14,206 @@ static const char EXPECTED_FUNCTION[] = "Expected function";
static const char VMS_EXPECTED_NUMBER_ROP[] = "Expected right operand to be number";
static const char VMS_EXPECTED_NUMBER_LOP[] = "Expected left operand to be number";
/* Mark memory reachable by VM */
void VMMark(VM * vm) {
Value thread;
thread.type = TYPE_THREAD;
thread.data.array = vm->thread;
GCMark(&vm->gc, &thread);
GCMark(&vm->gc, &vm->tempRoot);
/* The metadata header associated with an allocated block of memory */
#define GCHeader(mem) ((GCMemoryHeader *)(mem) - 1)
/* Memory header struct. Node of a linked list of memory blocks. */
typedef struct GCMemoryHeader GCMemoryHeader;
struct GCMemoryHeader {
GCMemoryHeader * next;
uint32_t color;
};
/* Forward declaration */
static void VMMark(VM * vm, Value * x);
/* Helper to mark function environments */
static void VMMarkFuncEnv(VM * vm, FuncEnv * env) {
if (GCHeader(env)->color != vm->black) {
Value temp;
GCHeader(env)->color = vm->black;
if (env->thread) {
temp.type = TYPE_THREAD;
temp.data.array = env->thread;
VMMark(vm, &temp);
} else {
uint32_t count = env->stackOffset;
uint32_t i;
GCHeader(env->values)->color = vm->black;
for (i = 0; i < count; ++i) {
VMMark(vm, env->values + i);
}
}
}
}
/* Mark allocated memory associated with a value. This is
* the main function for doing garbage collection. */
static void VMMark(VM * vm, Value * x) {
switch (x->type) {
case TYPE_NIL:
case TYPE_BOOLEAN:
case TYPE_NUMBER:
case TYPE_CFUNCTION:
break;
case TYPE_STRING:
case TYPE_SYMBOL:
GCHeader(VStringRaw(x->data.string))->color = vm->black;
break;
case TYPE_BYTEBUFFER:
GCHeader(x->data.buffer)->color = vm->black;
GCHeader(x->data.buffer->data)->color = vm->black;
break;
case TYPE_ARRAY:
case TYPE_FORM:
if (GCHeader(x->data.array)->color != vm->black) {
uint32_t i, count;
count = x->data.array->count;
GCHeader(x->data.array)->color = vm->black;
GCHeader(x->data.array->data)->color = vm->black;
for (i = 0; i < count; ++i)
VMMark(vm, x->data.array->data + i);
}
break;
case TYPE_THREAD:
if (GCHeader(x->data.array)->color != vm->black) {
uint32_t i, count;
count = x->data.array->count;
GCHeader(x->data.array)->color = vm->black;
GCHeader(x->data.array->data)->color = vm->black;
if (count) {
count += FrameSize(x->data.array);
for (i = 0; i < count; ++i)
VMMark(vm, x->data.array->data + i);
}
}
break;
case TYPE_FUNCTION:
if (GCHeader(x->data.func)->color != vm->black) {
Func * f = x->data.func;
GCHeader(f)->color = vm->black;
VMMarkFuncEnv(vm, f->env);
{
Value temp;
temp.type = TYPE_FUNCDEF;
temp.data.funcdef = x->data.funcdef;
VMMark(vm, &temp);
if (f->parent) {
temp.type = TYPE_FUNCTION;
temp.data.func = f->parent;
VMMark(vm, &temp);
}
}
}
break;
case TYPE_DICTIONARY:
if (GCHeader(x->data.dict)->color != vm->black) {
DictionaryIterator iter;
DictBucket * bucket;
GCHeader(x->data.dict)->color = vm->black;
GCHeader(x->data.dict->buckets)->color = vm->black;
DictIterate(x->data.dict, &iter);
while (DictIterateNext(&iter, &bucket)) {
GCHeader(bucket)->color = vm->black;
VMMark(vm, &bucket->key);
VMMark(vm, &bucket->value);
}
}
break;
case TYPE_FUNCDEF:
if (GCHeader(x->data.funcdef)->color != vm->black) {
GCHeader(x->data.funcdef->byteCode)->color = vm->black;
uint32_t count, i;
count = x->data.funcdef->literalsLen;
if (x->data.funcdef->literals) {
GCHeader(x->data.funcdef->literals)->color = vm->black;
for (i = 0; i < count; ++i)
VMMark(vm, x->data.funcdef->literals + i);
}
}
break;
case TYPE_FUNCENV:
VMMarkFuncEnv(vm, x->data.funcenv);
break;
}
}
/* Iterate over all allocated memory, and free memory that is not
* marked as reachable. Flip the gc color flag for next sweep. */
static void VMSweep(VM * vm) {
GCMemoryHeader * previous = NULL;
GCMemoryHeader * current = vm->blocks;
while (current) {
if (current->color != vm->black) {
if (previous) {
previous->next = current->next;
} else {
vm->blocks = current->next;
}
free(current);
} else {
previous = current;
}
current = current->next;
}
/* Rotate flag */
vm->black = !vm->black;
}
/* Prepare a memory block */
static void * VMAllocPrepare(VM * vm, char * rawBlock, uint32_t size) {
GCMemoryHeader * mdata;
if (rawBlock == NULL) {
VMCrash(vm, OOM);
}
vm->nextCollection += size;
mdata = (GCMemoryHeader *) rawBlock;
mdata->next = vm->blocks;
vm->blocks = mdata;
mdata->color = !vm->black;
return rawBlock + sizeof(GCMemoryHeader);
}
/* Allocate some memory that is tracked for garbage collection */
void * VMAlloc(VM * vm, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
return VMAllocPrepare(vm, malloc(totalSize), totalSize);
}
/* Allocate some zeroed memory that is tracked for garbage collection */
void * VMZalloc(VM * vm, uint32_t size) {
uint32_t totalSize = size + sizeof(GCMemoryHeader);
return VMAllocPrepare(vm, calloc(1, totalSize), totalSize);
}
/* Run garbage collection */
void VMCollect(VM * vm) {
VMMark(vm);
GCSweep(&vm->gc);
Value thread;
thread.type = TYPE_THREAD;
thread.data.array = vm->thread;
VMMark(vm, &thread);
VMMark(vm, &vm->tempRoot);
VMSweep(vm);
vm->nextCollection = 0;
}
/* Run garbage collection if needed */
void VMMaybeCollect(VM * vm) {
if (GCNeedsCollect(&vm->gc)) {
if (vm->nextCollection >= vm->memoryInterval) {
VMCollect(vm);
}
}
/* OOM handler for the vm's gc */
static void VMHandleOutOfMemory(GC * gc) {
VM * vm = (VM *) gc->user;
VMError(vm, OOM);
}
/* Push a stack frame onto a thread */
static void VMThreadPush(VM * vm, Array * thread, Value callee, uint32_t size) {
uint16_t oldSize;
@ -58,7 +225,7 @@ static void VMThreadPush(VM * vm, Array * thread, Value callee, uint32_t size) {
oldSize = 0;
nextCount = FRAME_SIZE;
}
ArrayEnsure(&vm->gc, thread, nextCount + size);
ArrayEnsure(vm, thread, nextCount + size);
/* Ensure values start out as nil so as to not confuse
* the garabage collector */
for (i = nextCount; i < nextCount + size; ++i) {
@ -84,7 +251,7 @@ static void VMThreadSplitStack(VM * vm, Array * thread) {
uint32_t size = FrameSize(thread);
env->thread = NULL;
env->stackOffset = size;
env->values = GCAlloc(&vm->gc, sizeof(Value) * size);
env->values = VMAlloc(vm, sizeof(Value) * size);
memcpy(env->values, ThreadStack(thread), size * sizeof(Value));
}
}
@ -191,7 +358,6 @@ static void VMCallOp(VM * vm) {
/* Implementation of the opcode for tail calls */
static void VMTailCallOp(VM * vm) {
GC * gc = &vm->gc;
uint32_t arity = vm->pc[1];
Value callee = *VMOpArg(2);
Value * extra, * argWriter;
@ -202,7 +368,7 @@ static void VMTailCallOp(VM * vm) {
if (FrameEnvValue(thread).type == TYPE_FUNCENV) {
FuncEnv * env = FrameEnv(thread);
uint16_t frameSize = FrameSize(thread);
Value * envValues = GCAlloc(gc, FrameSize(thread) * sizeof(Value));
Value * envValues = VMAlloc(vm, FrameSize(thread) * sizeof(Value));
env->values = envValues;
memcpy(envValues, vm->base, frameSize * sizeof(Value));
env->stackOffset = frameSize;
@ -217,7 +383,7 @@ static void VMTailCallOp(VM * vm) {
VMError(vm, EXPECTED_FUNCTION);
}
/* Ensure that stack is zeroed in this spot */
ArrayEnsure(&vm->gc, thread, thread->count + newFrameSize + arity);
ArrayEnsure(vm, thread, thread->count + newFrameSize + arity);
vm->base = ThreadStack(thread);
extra = argWriter = vm->base + FrameSize(thread) + FRAME_SIZE;
for (i = 0; i < arity; ++i) {
@ -252,7 +418,7 @@ static void VMMakeClosure(VM * vm, uint16_t ret, uint16_t literal) {
Array * thread = vm->thread;
FuncEnv * env = FrameEnv(vm->thread);
if (!env) {
env = GCAlloc(&vm->gc, sizeof(FuncEnv));
env = VMAlloc(vm, sizeof(FuncEnv));
env->thread = thread;
env->stackOffset = thread->count;
env->values = NULL;
@ -264,7 +430,7 @@ static void VMMakeClosure(VM * vm, uint16_t ret, uint16_t literal) {
if (constant->type != TYPE_FUNCDEF) {
VMError(vm, EXPECTED_FUNCTION);
}
fn = GCAlloc(&vm->gc, sizeof(Func));
fn = VMAlloc(vm, sizeof(Func));
fn->def = constant->data.funcdef;
fn->parent = current;
fn->env = env;
@ -285,7 +451,7 @@ int VMStart(VM * vm) {
if (n == 1) {
return 0;
} else {
/* Error */
/* Error or crash. Handling TODO. */
return n;
}
}
@ -491,7 +657,7 @@ int VMStart(VM * vm) {
{
uint32_t i;
uint32_t arrayLen = vm->pc[2];
Array * array = ArrayNew(&vm->gc, arrayLen);
Array * array = ArrayNew(vm, arrayLen);
array->count = arrayLen;
for (i = 0; i < arrayLen; ++i)
array->data[i] = *VMOpArg(3 + i);
@ -507,12 +673,12 @@ int VMStart(VM * vm) {
{
uint32_t i = 3;
uint32_t kvs = vm->pc[2];
Dictionary * dict = DictNew(&vm->gc, kvs);
Dictionary * dict = DictNew(vm, kvs);
kvs = kvs * 2 + 3;
while (i < kvs) {
v1 = VMOpArg(i++);
v2 = VMOpArg(i++);
DictPut(&vm->gc, dict, v1, v2);
DictPut(vm, dict, v1, v2);
}
vRet->type = TYPE_DICTIONARY;
vRet->data.dict = dict;
@ -574,15 +740,21 @@ int VMStart(VM * vm) {
}
}
#undef VMOpArg
#undef VMArg
/* Initialize the VM */
void VMInit(VM * vm) {
GCInit(&vm->gc, 100000000);
vm->gc.handleOutOfMemory = VMHandleOutOfMemory;
vm->tempRoot.type = TYPE_NIL;
vm->base = NULL;
vm->pc = NULL;
vm->error = NULL;
vm->thread = ArrayNew(&vm->gc, 20);
vm->thread = ArrayNew(vm, 20);
/* Garbage collection */
vm->blocks = NULL;
vm->nextCollection = 0;
vm->memoryInterval = 1024 * 256;
vm->black = 0;
}
/* Load a function into the VM. The function will be called with
@ -591,15 +763,18 @@ void VMLoad(VM * vm, Func * func) {
Value callee;
callee.type = TYPE_FUNCTION;
callee.data.func = func;
vm->thread = ArrayNew(&vm->gc, 20);
vm->thread = ArrayNew(vm, 20);
VMThreadPush(vm, vm->thread, callee, func->def->locals);
vm->pc = func->def->byteCode;
}
/* Clear all memory associated with the VM */
void VMDeinit(VM * vm) {
GCClear(&vm->gc);
GCMemoryHeader * current = vm->blocks;
while (current) {
GCMemoryHeader * next = current->next;
free(current);
current = next;
}
vm->blocks = NULL;
}
#undef VMOpArg
#undef VMArg

12
vm.h
View File

@ -9,6 +9,9 @@
/* Bail from the VM with an error. */
#define VMError(vm, e) ((vm)->error = (e), longjmp((vm)->jump, 2))
/* Crash. Not catchable, unlike error. */
#define VMCrash(vm, e) ((vm)->error = (e), longjmp((vm)->jump, 3))
/* Error if the condition is false */
#define VMAssert(vm, cond, e) do \
{ if (!(cond)) { VMError((vm), (e)); } } while (0)
@ -28,9 +31,6 @@ int VMStart(VM * vm);
/* Get the result after VMStart returns */
#define VMResult(vm) ((vm)->tempRoot)
/* Mark memory reachable by VM */
void VMMark(VM * vm);
/* Run garbage collection */
void VMCollect(VM * vm);
@ -38,4 +38,10 @@ void VMCollect(VM * vm);
* the previous collection */
void VMMaybeCollect(VM * vm);
/* Allocate memory */
void * VMAlloc(VM * vm, uint32_t amount);
/* Allocate zeroed memory */
void * VMZalloc(VM * vm, uint32_t amount);
#endif /* end of include guard: VM_H_C4OZU8CQ */

View File

@ -1,10 +0,0 @@
#ifndef VSTRING_H_TFCSVBEE
#define VSTRING_H_TFCSVBEE
#include "datatypes.h"
#define VStringRaw(s) ((uint32_t *)(s) - 2)
#define VStringSize(v) (VStringRaw(v)[0])
#define VStringHash(v) (VStringRaw(v)[1])
#endif /* end of include guard: VSTRING_H_TFCSVBEE */