From 0557c8b2a670d1de17c88bff8620bbd7eec826c6 Mon Sep 17 00:00:00 2001 From: Calvin Rose Date: Thu, 9 Feb 2017 18:50:47 -0500 Subject: [PATCH] Consolidate files * Move GC struct into VM for easier use. * Put all data structures into one file --- .gitignore | 2 +- Makefile | 9 +- array.c | 78 -------------- array.h | 30 ------ buffer.c | 56 ---------- buffer.h | 24 ----- compile.c | 220 +++++++++++++++++--------------------- datatypes.h | 58 ++++++++-- dict.c | 152 --------------------------- dict.h | 26 ----- ds.c | 297 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ds.h | 87 +++++++++++++++ gc.c | 209 ------------------------------------ gc.h | 41 -------- interp | Bin 109720 -> 0 bytes opcodes.h | 40 ------- parse.c | 32 +++--- value.c | 53 +++++----- value.h | 4 +- vm.c | 257 +++++++++++++++++++++++++++++++++++++-------- vm.h | 12 ++- vstring.h | 10 -- 22 files changed, 800 insertions(+), 897 deletions(-) delete mode 100644 array.c delete mode 100644 array.h delete mode 100644 buffer.c delete mode 100644 buffer.h delete mode 100644 dict.c delete mode 100644 dict.h create mode 100644 ds.c create mode 100644 ds.h delete mode 100644 gc.c delete mode 100644 gc.h delete mode 100755 interp delete mode 100644 opcodes.h delete mode 100644 vstring.h diff --git a/.gitignore b/.gitignore index eac1e87f..638ba6f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Target -./interp +interp # Created by https://www.gitignore.io/api/c diff --git a/Makefile b/Makefile index 6c0974a7..d4878e4d 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/array.c b/array.c deleted file mode 100644 index e22c0954..00000000 --- a/array.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -#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; - } -} diff --git a/array.h b/array.h deleted file mode 100644 index 20573793..00000000 --- a/array.h +++ /dev/null @@ -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 */ diff --git a/buffer.c b/buffer.c deleted file mode 100644 index dac07a66..00000000 --- a/buffer.c +++ /dev/null @@ -1,56 +0,0 @@ -#include -#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; -} diff --git a/buffer.h b/buffer.h deleted file mode 100644 index cbf27912..00000000 --- a/buffer.h +++ /dev/null @@ -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 */ diff --git a/compile.c b/compile.c index d1c7f5d7..f279eb0b 100644 --- a/compile.c +++ b/compile.c @@ -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 @@ -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; diff --git a/datatypes.h b/datatypes.h index e7ff09eb..a6a65547 100644 --- a/datatypes.h +++ b/datatypes.h @@ -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 */ diff --git a/dict.c b/dict.c deleted file mode 100644 index 29d3a698..00000000 --- a/dict.c +++ /dev/null @@ -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; -} diff --git a/dict.h b/dict.h deleted file mode 100644 index 59073209..00000000 --- a/dict.h +++ /dev/null @@ -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 */ diff --git a/ds.c b/ds.c new file mode 100644 index 00000000..68d94aa9 --- /dev/null +++ b/ds.c @@ -0,0 +1,297 @@ +#include "ds.h" +#include "value.h" +#include "vm.h" +#include + +/****/ +/* 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; +} diff --git a/ds.h b/ds.h new file mode 100644 index 00000000..ab313aa1 --- /dev/null +++ b/ds.h @@ -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 diff --git a/gc.c b/gc.c deleted file mode 100644 index 063de728..00000000 --- a/gc.c +++ /dev/null @@ -1,209 +0,0 @@ -#include -#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); -} diff --git a/gc.h b/gc.h deleted file mode 100644 index 99875c51..00000000 --- a/gc.h +++ /dev/null @@ -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 */ diff --git a/interp b/interp deleted file mode 100755 index cc8b537b17558434bb10a51e7e8f379456e17582..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 109720 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 diff --git a/opcodes.h b/opcodes.h deleted file mode 100644 index 5a064efd..00000000 --- a/opcodes.h +++ /dev/null @@ -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 */ diff --git a/parse.c b/parse.c index 9fde27f6..0302aca9 100644 --- a/parse.c +++ b/parse.c @@ -3,13 +3,9 @@ #include #include #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; diff --git a/value.c b/value.c index e4e2ad92..76fa57f5 100644 --- a/value.c +++ b/value.c @@ -1,10 +1,9 @@ #include #include #include -#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; diff --git a/value.h b/value.h index ed1824e3..ff6d9737 100644 --- a/value.h +++ b/value.h @@ -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); diff --git a/vm.c b/vm.c index 4b92462a..e07c4b49 100644 --- a/vm.c +++ b/vm.c @@ -3,12 +3,7 @@ #include #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 diff --git a/vm.h b/vm.h index 0774aabe..cec3e412 100644 --- a/vm.h +++ b/vm.h @@ -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 */ diff --git a/vstring.h b/vstring.h deleted file mode 100644 index 415bfcdc..00000000 --- a/vstring.h +++ /dev/null @@ -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 */