Merge branch 'master' of git+ssh://eng-grid.bu.edu/home/calsrose/code/interp

This commit is contained in:
Calvin Rose 2017-02-12 15:55:45 -05:00
commit f2d6b979f0
8 changed files with 400 additions and 363 deletions

212
compile.c
View File

@ -96,8 +96,8 @@ BufferDefine(Int16, int16_t);
/* If there is an error during compilation,
* jump back to start */
static void CError(Compiler * c, const char * e) {
c->error = e;
longjmp(c->onError, 1);
c->error = e;
longjmp(c->onError, 1);
}
/* Push a new scope in the compiler and return
@ -117,11 +117,11 @@ static Scope * CompilerPushScope(Compiler * c, int sameFunction) {
c->tail->nextScope = scope;
scope->level = c->tail->level + (sameFunction ? 0 : 1);
} else {
scope->level = 0;
scope->level = 0;
}
if (sameFunction) {
if (!c->tail) {
CError(c, "Cannot inherit scope when root scope");
CError(c, "Cannot inherit scope when root scope");
}
scope->nextLocal = c->tail->nextLocal;
scope->literals = c->tail->literals;
@ -144,17 +144,17 @@ static void CompilerPopScope(Compiler * c) {
CError(c, "No scope to pop.");
} else {
if (last->nextLocal > last->frameSize) {
last->frameSize = last->nextLocal;
last->frameSize = last->nextLocal;
}
c->tail = last->previousScope;
if (c->tail) {
if (last->frameSize > c->tail->frameSize) {
c->tail->frameSize = last->frameSize;
c->tail->frameSize = last->frameSize;
}
c->tail->nextScope = NULL;
} else {
/* We deleted the last scope */
c->root = NULL;
c->root = NULL;
}
}
}
@ -227,39 +227,39 @@ static Slot CompilerReturn(Compiler * c, Slot slot) {
/* Gets a temporary slot for the bottom-most scope. */
static Slot CompilerGetTemp(Compiler * c) {
Scope * scope = c->tail;
Slot ret;
ret.isTemp = 1;
ret.isNil = 0;
ret.hasReturned = 0;
ret.index = CompilerGetLocal(c, scope);
return ret;
Scope * scope = c->tail;
Slot ret;
ret.isTemp = 1;
ret.isNil = 0;
ret.hasReturned = 0;
ret.index = CompilerGetLocal(c, scope);
return ret;
}
/* Return a slot that is the target Slot given some FormOptions. Will
* Create a temporary slot if needed, so be sure to drop the slot after use. */
static Slot CompilerGetTarget(Compiler * c, FormOptions opts) {
if (opts.canChoose) {
return CompilerGetTemp(c);
} else {
if (opts.canChoose) {
return CompilerGetTemp(c);
} else {
Slot ret;
ret.isTemp = 0;
ret.isNil = 0;
ret.hasReturned = 0;
ret.index = opts.target;
return ret;
}
}
}
/* If a slot is a nil slot, create a slot that has
* an actual location on the stack. */
static Slot CompilerRealizeSlot(Compiler * c, Slot slot) {
if (slot.isNil) {
slot = CompilerGetTemp(c);
if (slot.isNil) {
slot = CompilerGetTemp(c);
BufferPushUInt16(c->vm, c->buffer, VM_OP_NIL);
BufferPushUInt16(c->vm, c->buffer, slot.index);
}
return slot;
}
return slot;
}
/* Helper to get a nil slot */
@ -368,9 +368,9 @@ static Slot CompileValue(Compiler * c, FormOptions opts, Value x);
/* Compile a structure that evaluates to a literal value. Useful
* for objects like strings, or anything else that cannot be instatiated else {
break;
break;
}
* from bytecode and doesn't do anything in the AST. */
* from bytecode and doesn't do anything in the AST. */
static Slot CompileLiteral(Compiler * c, FormOptions opts, Value x) {
Scope * scope = c->tail;
Buffer * buffer = c->buffer;
@ -569,7 +569,7 @@ static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form,
/* Free up space */
CompilerTrackerFree(c, scope, &tracker);
if (opts.resultUnused) {
ret = NilSlot();
ret = NilSlot();
} else {
ret = CompilerGetTarget(c, opts);
/* Write the correct opcode */
@ -669,7 +669,7 @@ static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value righ
/* Local variable */
subOpts.canChoose = 0;
subOpts.target = target;
slot = CompileValue(c, subOpts, right);
slot = CompileValue(c, subOpts, right);
}
} else {
/* We need to declare a new symbol */
@ -701,8 +701,8 @@ static Slot CompileBlock(Compiler * c, FormOptions opts, Array * form, uint32_t
CompilerDropSlot(c, scope, CompileValue(c, subOpts, form->data[current]));
++current;
}
/* Compile the last expression in the body */
return CompileValue(c, opts, form->data[form->count - 1]);
/* Compile the last expression in the body */
return CompileValue(c, opts, form->data[form->count - 1]);
}
/* Extract the last n bytes from the buffer and use them to construct
@ -713,7 +713,7 @@ static FuncDef * CompilerGenFuncDef(Compiler * c, uint32_t lastNBytes, uint32_t
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.");
CError(c, "Trying to extract more bytes from buffer than in buffer.");
uint8_t * byteCode = VMAlloc(c->vm, lastNBytes);
def->byteCode = (uint16_t *) byteCode;
def->byteCodeLen = lastNBytes / 2;
@ -726,9 +726,9 @@ static FuncDef * CompilerGenFuncDef(Compiler * c, uint32_t lastNBytes, uint32_t
if (scope->literalsArray->count) {
def->literals = VMAlloc(c->vm, scope->literalsArray->count * sizeof(Value));
memcpy(def->literals, scope->literalsArray->data,
scope->literalsArray->count * sizeof(Value));
scope->literalsArray->count * sizeof(Value));
} else {
def->literals = NULL;
def->literals = NULL;
}
def->literalsLen = scope->literalsArray->count;
/* Delete the sub scope */
@ -736,7 +736,7 @@ static FuncDef * CompilerGenFuncDef(Compiler * c, uint32_t lastNBytes, uint32_t
/* Initialize the new FuncDef */
def->locals = scope->frameSize;
def->arity = arity;
return def;
return def;
}
/* Compile a function from a function literal */
@ -812,7 +812,7 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) {
/* If the condition is nil, just compile false path */
if (condition.isNil) {
if (form->count == 4) {
return CompileValue(c, opts, form->data[3]);
return CompileValue(c, opts, form->data[3]);
}
return condition;
}
@ -823,9 +823,9 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) {
BufferPushUInt16(c->vm, buffer, VM_OP_JIF);
BufferPushUInt16(c->vm, buffer, condition.index);
BufferPushUInt32(c->vm, buffer, 0);
/* Configure branch form options */
branchOpts.canChoose = 0;
branchOpts.target = condition.index;
/* Configure branch form options */
branchOpts.canChoose = 0;
branchOpts.target = condition.index;
/* Compile true path */
left = CompileValue(c, branchOpts, form->data[2]);
if (opts.isTail) {
@ -870,21 +870,21 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) {
/* While special */
static Slot CompileWhile(Compiler * c, FormOptions opts, Array * form) {
Slot cond;
uint32_t countAtStart = c->buffer->count;
uint32_t countAtJumpDelta;
uint32_t countAtFinish;
FormOptions defaultOpts = FormOptionsDefault();
uint32_t countAtStart = c->buffer->count;
uint32_t countAtJumpDelta;
uint32_t countAtFinish;
FormOptions defaultOpts = FormOptionsDefault();
CompilerPushScope(c, 1);
/* Compile condition */
cond = CompileValue(c, defaultOpts, form->data[1]);
/* Assert that cond is a real value - otherwise do nothing (nil is false,
cond = CompileValue(c, defaultOpts, form->data[1]);
/* Assert that cond is a real value - otherwise do nothing (nil is false,
* so loop never runs.) */
if (cond.isNil) return cond;
/* Leave space for jump later */
countAtJumpDelta = c->buffer->count;
c->buffer->count += sizeof(uint16_t) * 2 + sizeof(int32_t);
/* Compile loop body */
defaultOpts.resultUnused = 1;
if (cond.isNil) return cond;
/* Leave space for jump later */
countAtJumpDelta = c->buffer->count;
c->buffer->count += sizeof(uint16_t) * 2 + sizeof(int32_t);
/* Compile loop body */
defaultOpts.resultUnused = 1;
CompilerDropSlot(c, c->tail, CompileBlock(c, defaultOpts, form, 2));
/* Jump back to the loop start */
countAtFinish = c->buffer->count;
@ -893,16 +893,16 @@ static Slot CompileWhile(Compiler * c, FormOptions opts, Array * form) {
countAtFinish = c->buffer->count;
/* Set the jump to the correct length */
c->buffer->count = countAtJumpDelta;
BufferPushUInt16(c->vm, c->buffer, VM_OP_JIF);
BufferPushUInt16(c->vm, c->buffer, cond.index);
BufferPushUInt16(c->vm, c->buffer, VM_OP_JIF);
BufferPushUInt16(c->vm, c->buffer, cond.index);
BufferPushInt32(c->vm, c->buffer, (int32_t)(countAtFinish - countAtJumpDelta) / 2);
/* Pop scope */
c->buffer->count = countAtFinish;
/* Pop scope */
c->buffer->count = countAtFinish;
CompilerPopScope(c);
/* Return nil */
if (opts.resultUnused)
return NilSlot();
else
/* Return nil */
if (opts.resultUnused)
return NilSlot();
else
return cond;
}
@ -967,30 +967,30 @@ static SpecialFormHelper GetSpecial(Array * form) {
case '/': return CompileDivision;
case '>': return CompileGreaterThan;
case '<': return CompileLessThan;
case '=': return CompileEquals;
case '=': return CompileEquals;
case '\'': return CompileQuote;
default:
break;
break;
}
}
/* Multi character specials. Mostly control flow. */
switch (name[0]) {
case '>':
{
if (VStringSize(name) == 2 &&
name[1] == '=') {
return CompileGreaterThanOrEqual;
}
}
break;
{
if (VStringSize(name) == 2 &&
name[1] == '=') {
return CompileGreaterThanOrEqual;
}
}
break;
case '<':
{
if (VStringSize(name) == 2 &&
name[1] == '=') {
return CompileLessThanOrEqual;
}
}
break;
{
if (VStringSize(name) == 2 &&
name[1] == '=') {
return CompileLessThanOrEqual;
}
}
break;
case 'd':
{
if (VStringSize(name) == 2 &&
@ -1015,14 +1015,14 @@ static SpecialFormHelper GetSpecial(Array * form) {
}
}
break;
case 'n':
{
if (VStringSize(name) == 3 &&
name[1] == 'o' &&
name[2] == 't') {
return CompileNot;
}
}
case 'n':
{
if (VStringSize(name) == 3 &&
name[1] == 'o' &&
name[2] == 't') {
return CompileNot;
}
}
case 'q':
{
if (VStringSize(name) == 5 &&
@ -1030,7 +1030,7 @@ static SpecialFormHelper GetSpecial(Array * form) {
name[2] == 'o' &&
name[3] == 't' &&
name[4] == 'e') {
return CompileQuote;
return CompileQuote;
}
}
break;
@ -1044,15 +1044,15 @@ static SpecialFormHelper GetSpecial(Array * form) {
}
break;
case 'w':
{
if (VStringSize(name) == 5 &&
name[1] == 'h' &&
name[2] == 'i' &&
name[3] == 'l' &&
name[4] == 'e') {
return CompileWhile;
}
}
{
if (VStringSize(name) == 5 &&
name[1] == 'h' &&
name[2] == 'i' &&
name[3] == 'l' &&
name[4] == 'e') {
return CompileWhile;
}
}
default:
break;
}
@ -1149,10 +1149,10 @@ void CompilerAddGlobal(Compiler * c, const char * name, Value x) {
/* Register a global c function for the compilation environment. */
void CompilerAddGlobalCFunc(Compiler * c, const char * name, CFunction f) {
Value func;
func.type = TYPE_CFUNCTION;
func.data.cfunction = f;
return CompilerAddGlobal(c, name, func);
Value func;
func.type = TYPE_CFUNCTION;
func.data.cfunction = f;
return CompilerAddGlobal(c, name, func);
}
/* Compile interface. Returns a function that evaluates the
@ -1185,3 +1185,27 @@ Func * CompilerCompile(Compiler * c, Value form) {
return func;
}
}
/* Macro expansion. Macro expansion happens prior to the compilation process
* and is completely separate. This allows the compilation to not have to worry
* about garbage collection and other issues that would complicate both the
* runtime and the compilation. */
int CompileMacroExpand(VM * vm, Value x, Dictionary * macros, Value * out) {
while (x.type == TYPE_FORM) {
Array * form = x.data.array;
Value sym, macroFn;
if (form->count == 0) break;
sym = form->data[0];
macroFn = DictGet(macros, sym);
if (macroFn.type != TYPE_FUNCTION && macroFn.type != TYPE_CFUNCTION) break;
VMLoad(vm, macroFn);
if (VMStart(vm)) {
/* We encountered an error during parsing */
return 1;;
} else {
x = vm->ret;
}
}
*out = x;
return 0;
}

View File

@ -15,4 +15,10 @@ void CompilerAddGlobalCFunc(Compiler * c, const char * name, CFunction f);
/* Compile a function that evaluates the given form. */
Func * CompilerCompile(Compiler * c, Value form);
/* Macro expansion. Macro expansion happens prior to the compilation process
* and is completely separate. This allows the compilation to not have to worry
* about garbage collection and other issues that would complicate both the
* runtime and the compilation. */
int CompileMacroExpand(VM * vm, Value x, Dictionary * macros, Value * out);
#endif /* end of include guard: COMPILE_H_9VXF71HY */

View File

@ -127,10 +127,11 @@ struct VM {
uint16_t * pc;
Array * thread;
Value * base;
Value root; /* Global state - prevents GC cleanup */
/* Return state */
const char * error;
jmp_buf jump;
Value tempRoot; /* Temporary GC root */
Value ret; /* Returned value from VMStart */
};
/* Parsing */

161
main.c
View File

@ -1,7 +1,6 @@
#include <stdlib.h>
#include <stdio.h>
#include "datatypes.h"
#include "gc.h"
#include "vm.h"
#include "parse.h"
#include "compile.h"
@ -10,100 +9,98 @@
/* Test c function */
Value print(VM * vm) {
uint32_t i, j, count;
Value nil;
count = VMCountArgs(vm);
for (j = 0; j < count; ++j) {
uint8_t * string = ValueToString(vm, VMGetArg(vm, j));
uint32_t len = VStringSize(string);
for (i = 0; i < len; ++i)
fputc(string[i], stdout);
fputc('\n', stdout);
}
nil.type = TYPE_NIL;
return nil;
uint32_t i, j, count;
Value nil;
count = VMCountArgs(vm);
for (j = 0; j < count; ++j) {
uint8_t * string = ValueToString(vm, VMGetArg(vm, j));
uint32_t len = VStringSize(string);
for (i = 0; i < len; ++i)
fputc(string[i], stdout);
fputc('\n', stdout);
}
nil.type = TYPE_NIL;
return nil;
}
/* A simple repl for debugging */
void debugRepl() {
char buffer[128] = {0};
const char * reader = buffer;
Func * func;
VM vm;
Parser p;
Compiler c;
char buffer[128] = {0};
const char * reader = buffer;
Value func;
VM vm;
Parser p;
Compiler c;
VMInit(&vm);
VMInit(&vm);
for (;;) {
for (;;) {
/* Run garbage collection */
VMMaybeCollect(&vm);
/* Run garbage collection */
VMMaybeCollect(&vm);
/* Reset state */
ParserInit(&p, &vm);
/* Reset state */
ParserInit(&p, &vm);
/* Get and parse input until we have a full form */
while (p.status == PARSER_PENDING) {
/* Get some input if we are done */
if (*reader == '\0') {
printf("> ");
if (!fgets(buffer, sizeof(buffer), stdin)) {
return;
}
p.index = 0;
reader = buffer;
}
reader += ParserParseCString(&p, reader);
}
/* Check for parsing errors */
if (p.error) {
unsigned i;
printf("\n");
printf("%s\n", buffer);
for (i = 0; i < p.index; ++i) {
printf(" ");
}
printf("^\n");
printf("\nParse error: %s\n", p.error);
reader = buffer; /* Flush the input buffer */
buffer[0] = '\0';
continue;
}
/* Try to compile generated AST */
CompilerInit(&c, &vm);
CompilerAddGlobalCFunc(&c, "print", print);
func = CompilerCompile(&c, p.value);
/* Check for compilation errors */
if (c.error) {
printf("Compiler error: %s\n", c.error);
reader = buffer;
buffer[0] = 0;
continue;
}
/* Print the function that will be executed */
//dasmFunc(stdout, func);
/* Execute function */
VMLoad(&vm, func);
if (VMStart(&vm)) {
printf("VM error: %s\n", vm.error);
reader = buffer;
buffer[0] = 0;
continue;
} else {
ValuePrint(vm.tempRoot, 0);
printf("\n");
/* Get and parse input until we have a full form */
while (p.status == PARSER_PENDING) {
/* Get some input if we are done */
if (*reader == '\0') {
printf("> ");
if (!fgets(buffer, sizeof(buffer), stdin)) {
return;
}
p.index = 0;
reader = buffer;
}
reader += ParserParseCString(&p, reader);
}
/* Check for parsing errors */
if (p.error) {
unsigned i;
printf("\n");
printf("%s\n", buffer);
for (i = 0; i < p.index; ++i) {
printf(" ");
}
printf("^\n");
printf("\nParse error: %s\n", p.error);
reader = buffer; /* Flush the input buffer */
buffer[0] = '\0';
continue;
}
/* Try to compile generated AST */
CompilerInit(&c, &vm);
CompilerAddGlobalCFunc(&c, "print", print);
func.type = TYPE_FUNCTION;
func.data.func = CompilerCompile(&c, p.value);
/* Check for compilation errors */
if (c.error) {
printf("Compiler error: %s\n", c.error);
reader = buffer;
buffer[0] = 0;
continue;
}
/* Execute function */
VMLoad(&vm, func);
if (VMStart(&vm)) {
printf("VM error: %s\n", vm.error);
reader = buffer;
buffer[0] = 0;
continue;
} else {
ValuePrint(vm.ret, 0);
printf("\n");
}
}
}
int main() {
printf("Super cool interpreter v0.0\n");
debugRepl();
printf("Super cool interpreter v0.0\n");
debugRepl();
}

24
parse.c
View File

@ -128,16 +128,16 @@ static int isWhitespace(uint8_t c) {
/* Check if a character is a valid symbol character */
static int isSymbolChar(uint8_t c) {
if (c >= 'a' && c <= 'z') return 1;
if (c >= 'A' && c <= 'Z') return 1;
if (c >= '0' && c <= '9') return 1;
if (c >= '<' && c <= '@') return 1;
if (c >= '*' && c <= '/') return 1;
if (c >= '#' && c <= '&') return 1;
if (c == '_') return 1;
if (c == '^') return 1;
if (c == '!') return 1;
return 0;
if (c >= 'a' && c <= 'z') return 1;
if (c >= 'A' && c <= 'Z') return 1;
if (c >= '0' && c <= '9') return 1;
if (c >= '<' && c <= '@') return 1;
if (c >= '*' && c <= '/') return 1;
if (c >= '#' && c <= '&') return 1;
if (c == '_') return 1;
if (c == '^') return 1;
if (c == '!') return 1;
return 0;
}
/* Get an integer power of 10 */
@ -308,8 +308,8 @@ static int ParserStringState(Parser * p, uint8_t c) {
case '\'': next = '\''; break;
case 'z': next = '\0'; break;
default:
PError(p, "Unknown string escape sequence.");
return 1;
PError(p, "Unknown string escape sequence.");
return 1;
}
BufferPush(p->vm, top->buf.string.buffer, next);
top->buf.string.state = STRING_STATE_BASE;

42
value.c
View File

@ -9,14 +9,14 @@
static void FuncDefBytecodePrint(FuncDef * def) {
uint32_t count, i;
count = def->byteCodeLen;
printf("(bytecode)[");
if (count) {
for (i = 0; i < count - 1; ++i) {
printf("%04x ", def->byteCode[i]);
}
printf("%04x", def->byteCode[i]);
}
printf("]");
printf("(bytecode)[");
if (count) {
for (i = 0; i < count - 1; ++i) {
printf("%04x ", def->byteCode[i]);
}
printf("%04x", def->byteCode[i]);
}
printf("]");
}
/* Print a value recursively. Used for debugging */
@ -55,8 +55,8 @@ void ValuePrint(Value x, uint32_t indent) {
break;
case TYPE_FUNCTION:
printf("<function ");
FuncDefBytecodePrint(x.data.func->def);
printf(">");
FuncDefBytecodePrint(x.data.func->def);
printf(">");
break;
case TYPE_DICTIONARY:
printf("<dictionary>");
@ -66,8 +66,8 @@ void ValuePrint(Value x, uint32_t indent) {
break;
case TYPE_FUNCDEF:
printf("<funcdef ");
FuncDefBytecodePrint(x.data.funcdef);
printf(">");
FuncDefBytecodePrint(x.data.funcdef);
printf(">");
break;
case TYPE_FUNCENV:
printf("<funcenv>");
@ -197,10 +197,10 @@ int ValueEqual(Value x, Value y) {
case TYPE_NUMBER:
result = (x.data.number == y.data.number);
break;
/* Assume that when strings are created, equal strings
* are set to the same string */
/* Assume that when strings are created, equal strings
* are set to the same string */
case TYPE_STRING:
/* TODO: keep cache to for symbols to get quick equality. */
/* TODO: keep cache to for symbols to get quick equality. */
case TYPE_SYMBOL:
if (x.data.string == y.data.string) {
result = 1;
@ -274,12 +274,12 @@ uint32_t ValueHash(Value x) {
case TYPE_THREAD:
/* Cast the pointer */
{
union {
void * pointer;
uint32_t hash;
} u;
u.pointer = x.data.pointer;
hash = u.hash;
union {
void * pointer;
uint32_t hash;
} u;
u.pointer = x.data.pointer;
hash = u.hash;
}
break;
}

304
vm.c
View File

@ -15,21 +15,19 @@ static const char VMS_EXPECTED_NUMBER_LOP[] = "Expected left operand to be numbe
typedef struct StackFrame StackFrame;
struct StackFrame {
Value callee;
uint16_t size;
uint16_t prevSize;
uint16_t ret;
FuncEnv * env;
uint16_t * pc;
uint16_t size;
uint16_t prevSize;
uint16_t ret;
FuncEnv * env;
uint16_t * pc;
};
/* The size of a StackFrame in units of Values. */
static size_t FRAME_SIZE() {
return ((sizeof(StackFrame) + sizeof(Value) - 1) / sizeof(Value));
}
#define FRAME_SIZE ((sizeof(StackFrame) + sizeof(Value) - 1) / sizeof(Value))
/* Get the stack frame pointer for a thread */
static StackFrame * ThreadFrame(Array * thread) {
return (StackFrame *)(thread->data + thread->count - FRAME_SIZE());
return (StackFrame *)(thread->data + thread->count - FRAME_SIZE);
}
/* The metadata header associated with an allocated block of memory */
@ -93,7 +91,7 @@ static void VMMark(VM * vm, Value * x) {
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);
VMMark(vm, x->data.array->data + i);
}
break;
@ -102,19 +100,19 @@ static void VMMark(VM * vm, Value * x) {
uint32_t i;
Array * thread = x->data.array;
StackFrame * frame = (StackFrame *)thread->data;
StackFrame * end = (StackFrame *)(thread->data + thread->count - FRAME_SIZE());
StackFrame * end = (StackFrame *)(thread->data + thread->count - FRAME_SIZE);
GCHeader(thread)->color = vm->black;
GCHeader(thread->data)->color = vm->black;
while (frame <= end) {
Value * stack = (Value *)frame + FRAME_SIZE();
VMMark(vm, &frame->callee);
if (frame->env)
VMMarkFuncEnv(vm, frame->env);
for (i = 0; i < frame->size; ++i) {
VMMark(vm, stack + i);
}
frame = (StackFrame *)(stack + frame->size);
}
while (frame <= end) {
Value * stack = (Value *)frame + FRAME_SIZE;
VMMark(vm, &frame->callee);
if (frame->env)
VMMarkFuncEnv(vm, frame->env);
for (i = 0; i < frame->size; ++i) {
VMMark(vm, stack + i);
}
frame = (StackFrame *)(stack + frame->size);
}
}
break;
@ -165,9 +163,9 @@ static void VMMark(VM * vm, Value * x) {
}
break;
case TYPE_FUNCENV:
VMMarkFuncEnv(vm, x->data.funcenv);
break;
case TYPE_FUNCENV:
VMMarkFuncEnv(vm, x->data.funcenv);
break;
}
@ -224,20 +222,23 @@ void * VMZalloc(VM * vm, uint32_t size) {
/* Run garbage collection */
void VMCollect(VM * vm) {
if (vm->lock > 0) return;
Value thread;
thread.type = TYPE_THREAD;
thread.data.array = vm->thread;
VMMark(vm, &thread);
VMMark(vm, &vm->tempRoot);
/* Thread can be null */
if (vm->thread) {
Value thread;
thread.type = TYPE_THREAD;
thread.data.array = vm->thread;
VMMark(vm, &thread);
}
VMMark(vm, &vm->ret);
VMMark(vm, &vm->root);
VMSweep(vm);
vm->nextCollection = 0;
}
/* Run garbage collection if needed */
void VMMaybeCollect(VM * vm) {
if (vm->nextCollection >= vm->memoryInterval) {
if (vm->nextCollection >= vm->memoryInterval)
VMCollect(vm);
}
}
/* Push a stack frame onto a thread */
@ -245,19 +246,19 @@ static void VMThreadPush(VM * vm, Array * thread, Value callee, uint32_t size) {
uint16_t oldSize;
uint32_t nextCount, i;
StackFrame * frame;
if (thread->count) {
frame = ThreadFrame(thread);
oldSize = frame->size;
if (thread->count) {
frame = ThreadFrame(thread);
oldSize = frame->size;
} else {
oldSize = 0;
oldSize = 0;
}
nextCount = thread->count + oldSize + FRAME_SIZE();
ArrayEnsure(vm, thread, nextCount + size);
nextCount = thread->count + oldSize + FRAME_SIZE;
ArrayEnsure(vm, thread, nextCount + size);
thread->count = nextCount;
/* Ensure values start out as nil so as to not confuse
* the garabage collector */
for (i = nextCount; i < nextCount + size; ++i)
thread->data[i].type = TYPE_NIL;
/* Ensure values start out as nil so as to not confuse
* the garabage collector */
for (i = nextCount; i < nextCount + size; ++i)
thread->data[i].type = TYPE_NIL;
frame = ThreadFrame(thread);
/* Set up the new stack frame */
frame->prevSize = oldSize;
@ -271,28 +272,28 @@ static void VMThreadPush(VM * vm, Array * thread, Value callee, uint32_t size) {
environment */
static void VMThreadSplitStack(VM * vm, Array * thread) {
StackFrame * frame = ThreadFrame(thread);
FuncEnv * env = frame->env;
/* Check for closures */
if (env) {
uint32_t size = frame->size;
env->thread = NULL;
env->stackOffset = size;
env->values = VMAlloc(vm, sizeof(Value) * size);
memcpy(env->values, thread->data + thread->count, size * sizeof(Value));
}
FuncEnv * env = frame->env;
/* Check for closures */
if (env) {
uint32_t size = frame->size;
env->thread = NULL;
env->stackOffset = size;
env->values = VMAlloc(vm, sizeof(Value) * size);
memcpy(env->values, thread->data + thread->count, size * sizeof(Value));
}
}
/* Pop the top-most stack frame from stack */
static void VMThreadPop(VM * vm, Array * thread) {
StackFrame * frame = ThreadFrame(thread);
uint32_t delta = FRAME_SIZE() + frame->prevSize;
if (thread->count) {
VMThreadSplitStack(vm, thread);
} else {
VMError(vm, "Nothing to pop from stack.");
}
thread->count -= delta;
vm->base -= delta;
uint32_t delta = FRAME_SIZE + frame->prevSize;
if (thread->count) {
VMThreadSplitStack(vm, thread);
} else {
VMError(vm, "Nothing to pop from stack.");
}
thread->count -= delta;
vm->base -= delta;
}
/* Get an upvalue */
@ -300,7 +301,7 @@ static Value * GetUpValue(VM * vm, Func * fn, uint16_t level, uint16_t index) {
FuncEnv * env;
Value * stack;
if (!level) {
return vm->base + index;
return vm->base + index;
}
while (fn && --level)
fn = fn->parent;
@ -330,7 +331,7 @@ static int truthy(Value v) {
static void VMReturn(VM * vm, Value ret) {
Array * thread = vm->thread;
StackFrame * frame = ThreadFrame(thread);
VMThreadPop(vm, thread);
VMThreadPop(vm, thread);
if (thread->count == 0) {
VMExit(vm, ret);
}
@ -348,7 +349,7 @@ static void VMCallOp(VM * vm) {
uint32_t oldCount = thread->count;
uint32_t i;
Value * oldBase;
frame->pc = vm->pc + 4 + arity;
frame->pc = vm->pc + 4 + arity;
frame->ret = vm->pc[2];
if (callee.type == TYPE_FUNCTION) {
Func * fn = callee.data.func;
@ -365,15 +366,15 @@ static void VMCallOp(VM * vm) {
++vm->lock;
VMReturn(vm, callee.data.cfunction(vm));
--vm->lock;
} else {
Func * f = callee.data.func;
uint32_t locals = f->def->locals;
for (i = 0; i < arity; ++i)
vm->base[i] = oldBase[vm->pc[4 + i]];
for (; i < locals; ++i)
vm->base[i].type = TYPE_NIL;
} else {
Func * f = callee.data.func;
uint32_t locals = f->def->locals;
for (i = 0; i < arity; ++i)
vm->base[i] = oldBase[vm->pc[4 + i]];
for (; i < locals; ++i)
vm->base[i].type = TYPE_NIL;
vm->pc = f->def->byteCode;
}
}
VMMaybeCollect(vm);
}
@ -386,42 +387,42 @@ static void VMTailCallOp(VM * vm) {
uint16_t newFrameSize, currentFrameSize;
uint32_t i;
/* Check for closures */
VMThreadSplitStack(vm, thread);
VMThreadSplitStack(vm, thread);
if (callee.type == TYPE_CFUNCTION) {
newFrameSize = arity;
} else if (callee.type == TYPE_FUNCTION) {
Func * f = callee.data.func;
newFrameSize = f->def->locals;
} else {
VMError(vm, EXPECTED_FUNCTION);
}
/* Ensure stack has enough space for copies of arguments */
currentFrameSize = frame->size;
ArrayEnsure(vm, thread, thread->count + currentFrameSize + arity);
frame = ThreadFrame(thread);
newFrameSize = arity;
} else if (callee.type == TYPE_FUNCTION) {
Func * f = callee.data.func;
newFrameSize = f->def->locals;
} else {
VMError(vm, EXPECTED_FUNCTION);
}
/* Ensure stack has enough space for copies of arguments */
currentFrameSize = frame->size;
ArrayEnsure(vm, thread, thread->count + currentFrameSize + arity);
frame = ThreadFrame(thread);
vm->base = thread->data + thread->count;
/* Copy the arguments into the extra space */
for (i = 0; i < arity; ++i) {
vm->base[currentFrameSize + i] = vm->base[vm->pc[3 + i]];
vm->base[currentFrameSize + i] = vm->base[vm->pc[3 + i]];
}
/* Copy the end of the stack to the parameter position */
memcpy(vm->base, vm->base + currentFrameSize, arity * sizeof(Value));
/* nil the non argument part of the stack for gc */
/* nil the non argument part of the stack for gc */
for (i = arity; i < newFrameSize; ++i) {
vm->base[i].type = TYPE_NIL;
vm->base[i].type = TYPE_NIL;
}
/* Update the stack frame */
frame->size = newFrameSize;
frame->size = newFrameSize;
frame->callee = callee;
frame->env = NULL;
if (callee.type == TYPE_CFUNCTION) {
++vm->lock;
VMReturn(vm, callee.data.cfunction(vm));
--vm->lock;
} else {
Func * f = callee.data.func;
} else {
Func * f = callee.data.func;
vm->pc = f->def->byteCode;
}
}
VMMaybeCollect(vm);
}
@ -440,7 +441,7 @@ static Value VMMakeClosure(VM * vm, uint16_t literal) {
env->thread = thread;
env->stackOffset = thread->count;
env->values = NULL;
frame->env = env;
frame->env = env;
}
current = frame->callee.data.func;
constant = LoadConstant(vm, current, literal);
@ -482,29 +483,29 @@ int VMStart(VM * vm) {
Value temp, v1, v2;
#define DO_BINARY_MATH(op) \
v1 = vm->base[vm->pc[2]]; \
v2 = vm->base[vm->pc[3]]; \
v1 = vm->base[vm->pc[2]]; \
v2 = vm->base[vm->pc[3]]; \
VMAssert(vm, v1.type == TYPE_NUMBER, VMS_EXPECTED_NUMBER_LOP); \
VMAssert(vm, v2.type == TYPE_NUMBER, VMS_EXPECTED_NUMBER_ROP); \
temp.type = TYPE_NUMBER; \
temp.data.number = v1.data.number op v2.data.number; \
vm->base[vm->pc[1]] = temp; \
vm->pc += 4; \
break;
break;
case VM_OP_ADD: /* Addition */
DO_BINARY_MATH(+)
case VM_OP_ADD: /* Addition */
DO_BINARY_MATH(+)
case VM_OP_SUB: /* Subtraction */
DO_BINARY_MATH(-)
DO_BINARY_MATH(-)
case VM_OP_MUL: /* Multiplication */
DO_BINARY_MATH(*)
DO_BINARY_MATH(*)
case VM_OP_DIV: /* Division */
DO_BINARY_MATH(/)
DO_BINARY_MATH(/)
#undef DO_BINARY_MATH
#undef DO_BINARY_MATH
case VM_OP_NOT: /* Boolean unary (Boolean not) */
temp.type = TYPE_BOOLEAN;
@ -528,22 +529,22 @@ int VMStart(VM * vm) {
break;
case VM_OP_FLS: /* Load False */
temp.type = TYPE_BOOLEAN;
temp.data.boolean = 0;
vm->base[vm->pc[1]] = temp;
temp.type = TYPE_BOOLEAN;
temp.data.boolean = 0;
vm->base[vm->pc[1]] = temp;
vm->pc += 2;
break;
case VM_OP_TRU: /* Load True */
temp.type = TYPE_BOOLEAN;
temp.data.boolean = 1;
vm->base[vm->pc[1]] = temp;
temp.type = TYPE_BOOLEAN;
temp.data.boolean = 1;
vm->base[vm->pc[1]] = temp;
vm->pc += 2;
break;
case VM_OP_NIL: /* Load Nil */
temp.type = TYPE_NIL;
vm->base[vm->pc[1]] = temp;
temp.type = TYPE_NIL;
vm->base[vm->pc[1]] = temp;
vm->pc += 2;
break;
@ -578,7 +579,7 @@ int VMStart(VM * vm) {
break;
case VM_OP_RET: /* Return */
VMReturn(vm, vm->base[vm->pc[1]]);
VMReturn(vm, vm->base[vm->pc[1]]);
break;
case VM_OP_SUV: /* Set Up Value */
@ -641,46 +642,46 @@ int VMStart(VM * vm) {
break;
case VM_OP_ARR: /* Array literal */
{
uint32_t i;
uint32_t arrayLen = vm->pc[2];
Array * array = ArrayNew(vm, arrayLen);
array->count = arrayLen;
for (i = 0; i < arrayLen; ++i)
array->data[i] = vm->base[vm->pc[3 + i]];
temp.type = TYPE_ARRAY;
temp.data.array = array;
vm->base[vm->pc[1]] = temp;
{
uint32_t i;
uint32_t arrayLen = vm->pc[2];
Array * array = ArrayNew(vm, arrayLen);
array->count = arrayLen;
for (i = 0; i < arrayLen; ++i)
array->data[i] = vm->base[vm->pc[3 + i]];
temp.type = TYPE_ARRAY;
temp.data.array = array;
vm->base[vm->pc[1]] = temp;
vm->pc += 3 + arrayLen;
VMMaybeCollect(vm);
}
}
break;
case VM_OP_DIC: /* Dictionary literal */
{
uint32_t i = 3;
uint32_t kvs = vm->pc[2];
Dictionary * dict = DictNew(vm, kvs);
kvs = kvs + 3;
while (i < kvs) {
{
uint32_t i = 3;
uint32_t kvs = vm->pc[2];
Dictionary * dict = DictNew(vm, kvs);
kvs = kvs + 3;
while (i < kvs) {
v1 = vm->base[vm->pc[i++]];
v2 = vm->base[vm->pc[i++]];
DictPut(vm, dict, v1, v2);
DictPut(vm, dict, v1, v2);
}
temp.type = TYPE_DICTIONARY;
temp.data.dict = dict;
vm->base[vm->pc[1]] = temp;
vm->pc += kvs;
VMMaybeCollect(vm);
}
temp.type = TYPE_DICTIONARY;
temp.data.dict = dict;
vm->base[vm->pc[1]] = temp;
vm->pc += kvs;
VMMaybeCollect(vm);
}
break;
case VM_OP_TCL: /* Tail call */
case VM_OP_TCL: /* Tail call */
VMTailCallOp(vm);
break;
/* Macro for generating some math operators */
#define DO_MULTI_MATH(op, start) { \
#define DO_MULTI_MATH(op, start) { \
uint16_t count = vm->pc[2]; \
uint16_t i; \
Number accum = start; \
@ -709,7 +710,7 @@ int VMStart(VM * vm) {
case VM_OP_DVM:
DO_MULTI_MATH(/, 1)
#undef DO_MULTI_MATH
#undef DO_MULTI_MATH
case VM_OP_RTN: /* Return nil */
temp.type = TYPE_NIL;
@ -731,14 +732,14 @@ int VMStart(VM * vm) {
Value VMGetArg(VM * vm, uint16_t index) {
uint16_t frameSize = ThreadFrame(vm->thread)->size;
VMAssert(vm, frameSize > index, "Cannot get arg out of stack bounds");
return vm->base[index];
return vm->base[index];
}
/* Put a value on the stack */
void VMSetArg(VM * vm, uint16_t index, Value x) {
uint16_t frameSize = ThreadFrame(vm->thread)->size;
VMAssert(vm, frameSize > index, "Cannot set arg out of stack bounds");
vm->base[index] = x;
vm->base[index] = x;
}
/* Get the size of the VMStack */
@ -748,29 +749,36 @@ uint16_t VMCountArgs(VM * vm) {
/* Initialize the VM */
void VMInit(VM * vm) {
vm->tempRoot.type = TYPE_NIL;
vm->ret.type = TYPE_NIL;
vm->root.type = TYPE_NIL;
vm->base = NULL;
vm->pc = NULL;
vm->error = NULL;
/* Garbage collection */
/* Garbage collection */
vm->blocks = NULL;
vm->nextCollection = 0;
vm->memoryInterval = 1024 * 256;
vm->memoryInterval = 0;
vm->black = 0;
vm->lock = 0;
/* Create new thread */
vm->thread = ArrayNew(vm, 32);
/* Set to empty thread */
vm->thread = NULL;
}
/* Load a function into the VM. The function will be called with
* no arguments when run */
void VMLoad(VM * vm, Func * func) {
Value callee;
callee.type = TYPE_FUNCTION;
callee.data.func = func;
vm->thread = ArrayNew(vm, 32);
VMThreadPush(vm, vm->thread, callee, func->def->locals);
vm->pc = func->def->byteCode;
void VMLoad(VM * vm, Value func) {
Array * thread = ArrayNew(vm, 100);
vm->thread = thread;
if (func.type == TYPE_FUNCTION) {
Func * fn = func.data.func;
VMThreadPush(vm, thread, func, fn->def->locals);
vm->pc = fn->def->byteCode;
} else if (func.type == TYPE_CFUNCTION) {
VMThreadPush(vm, thread, func, 0);
vm->pc = NULL;
} else {
return;
}
}
/* Clear all memory associated with the VM */

11
vm.h
View File

@ -4,7 +4,7 @@
#include "datatypes.h"
/* Exit from the VM normally */
#define VMExit(vm, r) ((vm)->tempRoot = (r), longjmp((vm)->jump, 1))
#define VMExit(vm, r) ((vm)->ret = (r), longjmp((vm)->jump, 1))
/* Bail from the VM with an error. */
#define VMError(vm, e) ((vm)->error = (e), longjmp((vm)->jump, 2))
@ -16,6 +16,10 @@
#define VMAssert(vm, cond, e) do \
{ if (!(cond)) { VMError((vm), (e)); } } while (0)
/* Type assertion */
#define VMAssertType(vm, f, type) \
VMAssert(vm, (f).type == (type), "Expected type " type)
/* Initialize the VM */
void VMInit(VM * vm);
@ -23,14 +27,11 @@ void VMInit(VM * vm);
void VMDeinit(VM * vm);
/* Load a function to be run on the VM */
void VMLoad(VM * vm, Func * func);
void VMLoad(VM * vm, Value func);
/* Start running the VM */
int VMStart(VM * vm);
/* Get the result after VMStart returns */
#define VMResult(vm) ((vm)->tempRoot)
/* Run garbage collection */
void VMCollect(VM * vm);