mirror of
https://github.com/janet-lang/janet
synced 2024-11-10 18:59:54 +00:00
Consolidate files
* Move GC struct into VM for easier use. * Put all data structures into one file
This commit is contained in:
parent
715c239fc1
commit
0557c8b2a6
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
||||
# Target
|
||||
./interp
|
||||
interp
|
||||
|
||||
# Created by https://www.gitignore.io/api/c
|
||||
|
||||
|
9
Makefile
9
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
|
||||
|
78
array.c
78
array.c
@ -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
30
array.h
@ -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 */
|
56
buffer.c
56
buffer.c
@ -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;
|
||||
}
|
24
buffer.h
24
buffer.h
@ -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
220
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 <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;
|
||||
|
58
datatypes.h
58
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 */
|
||||
|
152
dict.c
152
dict.c
@ -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
26
dict.h
@ -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
297
ds.c
Normal 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
87
ds.h
Normal 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
209
gc.c
@ -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
41
gc.h
@ -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 */
|
40
opcodes.h
40
opcodes.h
@ -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
32
parse.c
@ -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
53
value.c
@ -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;
|
||||
|
4
value.h
4
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);
|
||||
|
||||
|
257
vm.c
257
vm.c
@ -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
12
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 */
|
||||
|
Loading…
Reference in New Issue
Block a user