mirror of
https://github.com/janet-lang/janet
synced 2024-12-25 07:50:27 +00:00
Merge branch 'master' of git+ssh://eng-grid.bu.edu/home/calsrose/code/interp
This commit is contained in:
commit
f2d6b979f0
212
compile.c
212
compile.c
@ -96,8 +96,8 @@ BufferDefine(Int16, int16_t);
|
|||||||
/* If there is an error during compilation,
|
/* If there is an error during compilation,
|
||||||
* jump back to start */
|
* jump back to start */
|
||||||
static void CError(Compiler * c, const char * e) {
|
static void CError(Compiler * c, const char * e) {
|
||||||
c->error = e;
|
c->error = e;
|
||||||
longjmp(c->onError, 1);
|
longjmp(c->onError, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push a new scope in the compiler and return
|
/* Push a new scope in the compiler and return
|
||||||
@ -117,11 +117,11 @@ static Scope * CompilerPushScope(Compiler * c, int sameFunction) {
|
|||||||
c->tail->nextScope = scope;
|
c->tail->nextScope = scope;
|
||||||
scope->level = c->tail->level + (sameFunction ? 0 : 1);
|
scope->level = c->tail->level + (sameFunction ? 0 : 1);
|
||||||
} else {
|
} else {
|
||||||
scope->level = 0;
|
scope->level = 0;
|
||||||
}
|
}
|
||||||
if (sameFunction) {
|
if (sameFunction) {
|
||||||
if (!c->tail) {
|
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->nextLocal = c->tail->nextLocal;
|
||||||
scope->literals = c->tail->literals;
|
scope->literals = c->tail->literals;
|
||||||
@ -144,17 +144,17 @@ static void CompilerPopScope(Compiler * c) {
|
|||||||
CError(c, "No scope to pop.");
|
CError(c, "No scope to pop.");
|
||||||
} else {
|
} else {
|
||||||
if (last->nextLocal > last->frameSize) {
|
if (last->nextLocal > last->frameSize) {
|
||||||
last->frameSize = last->nextLocal;
|
last->frameSize = last->nextLocal;
|
||||||
}
|
}
|
||||||
c->tail = last->previousScope;
|
c->tail = last->previousScope;
|
||||||
if (c->tail) {
|
if (c->tail) {
|
||||||
if (last->frameSize > c->tail->frameSize) {
|
if (last->frameSize > c->tail->frameSize) {
|
||||||
c->tail->frameSize = last->frameSize;
|
c->tail->frameSize = last->frameSize;
|
||||||
}
|
}
|
||||||
c->tail->nextScope = NULL;
|
c->tail->nextScope = NULL;
|
||||||
} else {
|
} else {
|
||||||
/* We deleted the last scope */
|
/* 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. */
|
/* Gets a temporary slot for the bottom-most scope. */
|
||||||
static Slot CompilerGetTemp(Compiler * c) {
|
static Slot CompilerGetTemp(Compiler * c) {
|
||||||
Scope * scope = c->tail;
|
Scope * scope = c->tail;
|
||||||
Slot ret;
|
Slot ret;
|
||||||
ret.isTemp = 1;
|
ret.isTemp = 1;
|
||||||
ret.isNil = 0;
|
ret.isNil = 0;
|
||||||
ret.hasReturned = 0;
|
ret.hasReturned = 0;
|
||||||
ret.index = CompilerGetLocal(c, scope);
|
ret.index = CompilerGetLocal(c, scope);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return a slot that is the target Slot given some FormOptions. Will
|
/* 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. */
|
* Create a temporary slot if needed, so be sure to drop the slot after use. */
|
||||||
static Slot CompilerGetTarget(Compiler * c, FormOptions opts) {
|
static Slot CompilerGetTarget(Compiler * c, FormOptions opts) {
|
||||||
if (opts.canChoose) {
|
if (opts.canChoose) {
|
||||||
return CompilerGetTemp(c);
|
return CompilerGetTemp(c);
|
||||||
} else {
|
} else {
|
||||||
Slot ret;
|
Slot ret;
|
||||||
ret.isTemp = 0;
|
ret.isTemp = 0;
|
||||||
ret.isNil = 0;
|
ret.isNil = 0;
|
||||||
ret.hasReturned = 0;
|
ret.hasReturned = 0;
|
||||||
ret.index = opts.target;
|
ret.index = opts.target;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If a slot is a nil slot, create a slot that has
|
/* If a slot is a nil slot, create a slot that has
|
||||||
* an actual location on the stack. */
|
* an actual location on the stack. */
|
||||||
static Slot CompilerRealizeSlot(Compiler * c, Slot slot) {
|
static Slot CompilerRealizeSlot(Compiler * c, Slot slot) {
|
||||||
if (slot.isNil) {
|
if (slot.isNil) {
|
||||||
slot = CompilerGetTemp(c);
|
slot = CompilerGetTemp(c);
|
||||||
BufferPushUInt16(c->vm, c->buffer, VM_OP_NIL);
|
BufferPushUInt16(c->vm, c->buffer, VM_OP_NIL);
|
||||||
BufferPushUInt16(c->vm, c->buffer, slot.index);
|
BufferPushUInt16(c->vm, c->buffer, slot.index);
|
||||||
}
|
}
|
||||||
return slot;
|
return slot;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Helper to get a nil 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
|
/* Compile a structure that evaluates to a literal value. Useful
|
||||||
* for objects like strings, or anything else that cannot be instatiated else {
|
* 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) {
|
static Slot CompileLiteral(Compiler * c, FormOptions opts, Value x) {
|
||||||
Scope * scope = c->tail;
|
Scope * scope = c->tail;
|
||||||
Buffer * buffer = c->buffer;
|
Buffer * buffer = c->buffer;
|
||||||
@ -569,7 +569,7 @@ static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form,
|
|||||||
/* Free up space */
|
/* Free up space */
|
||||||
CompilerTrackerFree(c, scope, &tracker);
|
CompilerTrackerFree(c, scope, &tracker);
|
||||||
if (opts.resultUnused) {
|
if (opts.resultUnused) {
|
||||||
ret = NilSlot();
|
ret = NilSlot();
|
||||||
} else {
|
} else {
|
||||||
ret = CompilerGetTarget(c, opts);
|
ret = CompilerGetTarget(c, opts);
|
||||||
/* Write the correct opcode */
|
/* Write the correct opcode */
|
||||||
@ -669,7 +669,7 @@ static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value righ
|
|||||||
/* Local variable */
|
/* Local variable */
|
||||||
subOpts.canChoose = 0;
|
subOpts.canChoose = 0;
|
||||||
subOpts.target = target;
|
subOpts.target = target;
|
||||||
slot = CompileValue(c, subOpts, right);
|
slot = CompileValue(c, subOpts, right);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* We need to declare a new symbol */
|
/* 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]));
|
CompilerDropSlot(c, scope, CompileValue(c, subOpts, form->data[current]));
|
||||||
++current;
|
++current;
|
||||||
}
|
}
|
||||||
/* Compile the last expression in the body */
|
/* Compile the last expression in the body */
|
||||||
return CompileValue(c, opts, form->data[form->count - 1]);
|
return CompileValue(c, opts, form->data[form->count - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extract the last n bytes from the buffer and use them to construct
|
/* 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));
|
FuncDef * def = VMAlloc(c->vm, sizeof(FuncDef));
|
||||||
/* Create enough space for the new byteCode */
|
/* Create enough space for the new byteCode */
|
||||||
if (lastNBytes > buffer->count)
|
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);
|
uint8_t * byteCode = VMAlloc(c->vm, lastNBytes);
|
||||||
def->byteCode = (uint16_t *) byteCode;
|
def->byteCode = (uint16_t *) byteCode;
|
||||||
def->byteCodeLen = lastNBytes / 2;
|
def->byteCodeLen = lastNBytes / 2;
|
||||||
@ -726,9 +726,9 @@ static FuncDef * CompilerGenFuncDef(Compiler * c, uint32_t lastNBytes, uint32_t
|
|||||||
if (scope->literalsArray->count) {
|
if (scope->literalsArray->count) {
|
||||||
def->literals = VMAlloc(c->vm, scope->literalsArray->count * sizeof(Value));
|
def->literals = VMAlloc(c->vm, scope->literalsArray->count * sizeof(Value));
|
||||||
memcpy(def->literals, scope->literalsArray->data,
|
memcpy(def->literals, scope->literalsArray->data,
|
||||||
scope->literalsArray->count * sizeof(Value));
|
scope->literalsArray->count * sizeof(Value));
|
||||||
} else {
|
} else {
|
||||||
def->literals = NULL;
|
def->literals = NULL;
|
||||||
}
|
}
|
||||||
def->literalsLen = scope->literalsArray->count;
|
def->literalsLen = scope->literalsArray->count;
|
||||||
/* Delete the sub scope */
|
/* Delete the sub scope */
|
||||||
@ -736,7 +736,7 @@ static FuncDef * CompilerGenFuncDef(Compiler * c, uint32_t lastNBytes, uint32_t
|
|||||||
/* Initialize the new FuncDef */
|
/* Initialize the new FuncDef */
|
||||||
def->locals = scope->frameSize;
|
def->locals = scope->frameSize;
|
||||||
def->arity = arity;
|
def->arity = arity;
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compile a function from a function literal */
|
/* 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 the condition is nil, just compile false path */
|
||||||
if (condition.isNil) {
|
if (condition.isNil) {
|
||||||
if (form->count == 4) {
|
if (form->count == 4) {
|
||||||
return CompileValue(c, opts, form->data[3]);
|
return CompileValue(c, opts, form->data[3]);
|
||||||
}
|
}
|
||||||
return condition;
|
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, VM_OP_JIF);
|
||||||
BufferPushUInt16(c->vm, buffer, condition.index);
|
BufferPushUInt16(c->vm, buffer, condition.index);
|
||||||
BufferPushUInt32(c->vm, buffer, 0);
|
BufferPushUInt32(c->vm, buffer, 0);
|
||||||
/* Configure branch form options */
|
/* Configure branch form options */
|
||||||
branchOpts.canChoose = 0;
|
branchOpts.canChoose = 0;
|
||||||
branchOpts.target = condition.index;
|
branchOpts.target = condition.index;
|
||||||
/* Compile true path */
|
/* Compile true path */
|
||||||
left = CompileValue(c, branchOpts, form->data[2]);
|
left = CompileValue(c, branchOpts, form->data[2]);
|
||||||
if (opts.isTail) {
|
if (opts.isTail) {
|
||||||
@ -870,21 +870,21 @@ static Slot CompileIf(Compiler * c, FormOptions opts, Array * form) {
|
|||||||
/* While special */
|
/* While special */
|
||||||
static Slot CompileWhile(Compiler * c, FormOptions opts, Array * form) {
|
static Slot CompileWhile(Compiler * c, FormOptions opts, Array * form) {
|
||||||
Slot cond;
|
Slot cond;
|
||||||
uint32_t countAtStart = c->buffer->count;
|
uint32_t countAtStart = c->buffer->count;
|
||||||
uint32_t countAtJumpDelta;
|
uint32_t countAtJumpDelta;
|
||||||
uint32_t countAtFinish;
|
uint32_t countAtFinish;
|
||||||
FormOptions defaultOpts = FormOptionsDefault();
|
FormOptions defaultOpts = FormOptionsDefault();
|
||||||
CompilerPushScope(c, 1);
|
CompilerPushScope(c, 1);
|
||||||
/* Compile condition */
|
/* Compile condition */
|
||||||
cond = CompileValue(c, defaultOpts, form->data[1]);
|
cond = CompileValue(c, defaultOpts, form->data[1]);
|
||||||
/* Assert that cond is a real value - otherwise do nothing (nil is false,
|
/* Assert that cond is a real value - otherwise do nothing (nil is false,
|
||||||
* so loop never runs.) */
|
* so loop never runs.) */
|
||||||
if (cond.isNil) return cond;
|
if (cond.isNil) return cond;
|
||||||
/* Leave space for jump later */
|
/* Leave space for jump later */
|
||||||
countAtJumpDelta = c->buffer->count;
|
countAtJumpDelta = c->buffer->count;
|
||||||
c->buffer->count += sizeof(uint16_t) * 2 + sizeof(int32_t);
|
c->buffer->count += sizeof(uint16_t) * 2 + sizeof(int32_t);
|
||||||
/* Compile loop body */
|
/* Compile loop body */
|
||||||
defaultOpts.resultUnused = 1;
|
defaultOpts.resultUnused = 1;
|
||||||
CompilerDropSlot(c, c->tail, CompileBlock(c, defaultOpts, form, 2));
|
CompilerDropSlot(c, c->tail, CompileBlock(c, defaultOpts, form, 2));
|
||||||
/* Jump back to the loop start */
|
/* Jump back to the loop start */
|
||||||
countAtFinish = c->buffer->count;
|
countAtFinish = c->buffer->count;
|
||||||
@ -893,16 +893,16 @@ static Slot CompileWhile(Compiler * c, FormOptions opts, Array * form) {
|
|||||||
countAtFinish = c->buffer->count;
|
countAtFinish = c->buffer->count;
|
||||||
/* Set the jump to the correct length */
|
/* Set the jump to the correct length */
|
||||||
c->buffer->count = countAtJumpDelta;
|
c->buffer->count = countAtJumpDelta;
|
||||||
BufferPushUInt16(c->vm, c->buffer, VM_OP_JIF);
|
BufferPushUInt16(c->vm, c->buffer, VM_OP_JIF);
|
||||||
BufferPushUInt16(c->vm, c->buffer, cond.index);
|
BufferPushUInt16(c->vm, c->buffer, cond.index);
|
||||||
BufferPushInt32(c->vm, c->buffer, (int32_t)(countAtFinish - countAtJumpDelta) / 2);
|
BufferPushInt32(c->vm, c->buffer, (int32_t)(countAtFinish - countAtJumpDelta) / 2);
|
||||||
/* Pop scope */
|
/* Pop scope */
|
||||||
c->buffer->count = countAtFinish;
|
c->buffer->count = countAtFinish;
|
||||||
CompilerPopScope(c);
|
CompilerPopScope(c);
|
||||||
/* Return nil */
|
/* Return nil */
|
||||||
if (opts.resultUnused)
|
if (opts.resultUnused)
|
||||||
return NilSlot();
|
return NilSlot();
|
||||||
else
|
else
|
||||||
return cond;
|
return cond;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -967,30 +967,30 @@ static SpecialFormHelper GetSpecial(Array * form) {
|
|||||||
case '/': return CompileDivision;
|
case '/': return CompileDivision;
|
||||||
case '>': return CompileGreaterThan;
|
case '>': return CompileGreaterThan;
|
||||||
case '<': return CompileLessThan;
|
case '<': return CompileLessThan;
|
||||||
case '=': return CompileEquals;
|
case '=': return CompileEquals;
|
||||||
case '\'': return CompileQuote;
|
case '\'': return CompileQuote;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* Multi character specials. Mostly control flow. */
|
/* Multi character specials. Mostly control flow. */
|
||||||
switch (name[0]) {
|
switch (name[0]) {
|
||||||
case '>':
|
case '>':
|
||||||
{
|
{
|
||||||
if (VStringSize(name) == 2 &&
|
if (VStringSize(name) == 2 &&
|
||||||
name[1] == '=') {
|
name[1] == '=') {
|
||||||
return CompileGreaterThanOrEqual;
|
return CompileGreaterThanOrEqual;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '<':
|
case '<':
|
||||||
{
|
{
|
||||||
if (VStringSize(name) == 2 &&
|
if (VStringSize(name) == 2 &&
|
||||||
name[1] == '=') {
|
name[1] == '=') {
|
||||||
return CompileLessThanOrEqual;
|
return CompileLessThanOrEqual;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
{
|
{
|
||||||
if (VStringSize(name) == 2 &&
|
if (VStringSize(name) == 2 &&
|
||||||
@ -1015,14 +1015,14 @@ static SpecialFormHelper GetSpecial(Array * form) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
{
|
{
|
||||||
if (VStringSize(name) == 3 &&
|
if (VStringSize(name) == 3 &&
|
||||||
name[1] == 'o' &&
|
name[1] == 'o' &&
|
||||||
name[2] == 't') {
|
name[2] == 't') {
|
||||||
return CompileNot;
|
return CompileNot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case 'q':
|
case 'q':
|
||||||
{
|
{
|
||||||
if (VStringSize(name) == 5 &&
|
if (VStringSize(name) == 5 &&
|
||||||
@ -1030,7 +1030,7 @@ static SpecialFormHelper GetSpecial(Array * form) {
|
|||||||
name[2] == 'o' &&
|
name[2] == 'o' &&
|
||||||
name[3] == 't' &&
|
name[3] == 't' &&
|
||||||
name[4] == 'e') {
|
name[4] == 'e') {
|
||||||
return CompileQuote;
|
return CompileQuote;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1044,15 +1044,15 @@ static SpecialFormHelper GetSpecial(Array * form) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'w':
|
case 'w':
|
||||||
{
|
{
|
||||||
if (VStringSize(name) == 5 &&
|
if (VStringSize(name) == 5 &&
|
||||||
name[1] == 'h' &&
|
name[1] == 'h' &&
|
||||||
name[2] == 'i' &&
|
name[2] == 'i' &&
|
||||||
name[3] == 'l' &&
|
name[3] == 'l' &&
|
||||||
name[4] == 'e') {
|
name[4] == 'e') {
|
||||||
return CompileWhile;
|
return CompileWhile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1149,10 +1149,10 @@ void CompilerAddGlobal(Compiler * c, const char * name, Value x) {
|
|||||||
|
|
||||||
/* Register a global c function for the compilation environment. */
|
/* Register a global c function for the compilation environment. */
|
||||||
void CompilerAddGlobalCFunc(Compiler * c, const char * name, CFunction f) {
|
void CompilerAddGlobalCFunc(Compiler * c, const char * name, CFunction f) {
|
||||||
Value func;
|
Value func;
|
||||||
func.type = TYPE_CFUNCTION;
|
func.type = TYPE_CFUNCTION;
|
||||||
func.data.cfunction = f;
|
func.data.cfunction = f;
|
||||||
return CompilerAddGlobal(c, name, func);
|
return CompilerAddGlobal(c, name, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compile interface. Returns a function that evaluates the
|
/* Compile interface. Returns a function that evaluates the
|
||||||
@ -1185,3 +1185,27 @@ Func * CompilerCompile(Compiler * c, Value form) {
|
|||||||
return func;
|
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;
|
||||||
|
}
|
||||||
|
@ -15,4 +15,10 @@ void CompilerAddGlobalCFunc(Compiler * c, const char * name, CFunction f);
|
|||||||
/* Compile a function that evaluates the given form. */
|
/* Compile a function that evaluates the given form. */
|
||||||
Func * CompilerCompile(Compiler * c, Value 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 */
|
#endif /* end of include guard: COMPILE_H_9VXF71HY */
|
||||||
|
@ -127,10 +127,11 @@ struct VM {
|
|||||||
uint16_t * pc;
|
uint16_t * pc;
|
||||||
Array * thread;
|
Array * thread;
|
||||||
Value * base;
|
Value * base;
|
||||||
|
Value root; /* Global state - prevents GC cleanup */
|
||||||
/* Return state */
|
/* Return state */
|
||||||
const char * error;
|
const char * error;
|
||||||
jmp_buf jump;
|
jmp_buf jump;
|
||||||
Value tempRoot; /* Temporary GC root */
|
Value ret; /* Returned value from VMStart */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Parsing */
|
/* Parsing */
|
||||||
|
161
main.c
161
main.c
@ -1,7 +1,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "datatypes.h"
|
#include "datatypes.h"
|
||||||
#include "gc.h"
|
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
#include "parse.h"
|
#include "parse.h"
|
||||||
#include "compile.h"
|
#include "compile.h"
|
||||||
@ -10,100 +9,98 @@
|
|||||||
|
|
||||||
/* Test c function */
|
/* Test c function */
|
||||||
Value print(VM * vm) {
|
Value print(VM * vm) {
|
||||||
uint32_t i, j, count;
|
uint32_t i, j, count;
|
||||||
Value nil;
|
Value nil;
|
||||||
count = VMCountArgs(vm);
|
count = VMCountArgs(vm);
|
||||||
for (j = 0; j < count; ++j) {
|
for (j = 0; j < count; ++j) {
|
||||||
uint8_t * string = ValueToString(vm, VMGetArg(vm, j));
|
uint8_t * string = ValueToString(vm, VMGetArg(vm, j));
|
||||||
uint32_t len = VStringSize(string);
|
uint32_t len = VStringSize(string);
|
||||||
for (i = 0; i < len; ++i)
|
for (i = 0; i < len; ++i)
|
||||||
fputc(string[i], stdout);
|
fputc(string[i], stdout);
|
||||||
fputc('\n', stdout);
|
fputc('\n', stdout);
|
||||||
}
|
}
|
||||||
nil.type = TYPE_NIL;
|
nil.type = TYPE_NIL;
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* A simple repl for debugging */
|
/* A simple repl for debugging */
|
||||||
void debugRepl() {
|
void debugRepl() {
|
||||||
char buffer[128] = {0};
|
char buffer[128] = {0};
|
||||||
const char * reader = buffer;
|
const char * reader = buffer;
|
||||||
Func * func;
|
Value func;
|
||||||
VM vm;
|
VM vm;
|
||||||
Parser p;
|
Parser p;
|
||||||
Compiler c;
|
Compiler c;
|
||||||
|
|
||||||
VMInit(&vm);
|
VMInit(&vm);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
/* Run garbage collection */
|
/* Run garbage collection */
|
||||||
VMMaybeCollect(&vm);
|
VMMaybeCollect(&vm);
|
||||||
|
|
||||||
/* Reset state */
|
/* Reset state */
|
||||||
ParserInit(&p, &vm);
|
ParserInit(&p, &vm);
|
||||||
|
|
||||||
/* Get and parse input until we have a full form */
|
/* Get and parse input until we have a full form */
|
||||||
while (p.status == PARSER_PENDING) {
|
while (p.status == PARSER_PENDING) {
|
||||||
/* Get some input if we are done */
|
/* Get some input if we are done */
|
||||||
if (*reader == '\0') {
|
if (*reader == '\0') {
|
||||||
printf("> ");
|
printf("> ");
|
||||||
if (!fgets(buffer, sizeof(buffer), stdin)) {
|
if (!fgets(buffer, sizeof(buffer), stdin)) {
|
||||||
return;
|
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");
|
|
||||||
}
|
}
|
||||||
|
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() {
|
int main() {
|
||||||
printf("Super cool interpreter v0.0\n");
|
printf("Super cool interpreter v0.0\n");
|
||||||
debugRepl();
|
debugRepl();
|
||||||
}
|
}
|
||||||
|
24
parse.c
24
parse.c
@ -128,16 +128,16 @@ static int isWhitespace(uint8_t c) {
|
|||||||
|
|
||||||
/* Check if a character is a valid symbol character */
|
/* Check if a character is a valid symbol character */
|
||||||
static int isSymbolChar(uint8_t c) {
|
static int isSymbolChar(uint8_t c) {
|
||||||
if (c >= 'a' && c <= 'z') return 1;
|
if (c >= 'a' && c <= 'z') return 1;
|
||||||
if (c >= 'A' && c <= 'Z') return 1;
|
if (c >= 'A' && c <= 'Z') return 1;
|
||||||
if (c >= '0' && c <= '9') 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 >= '*' && 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;
|
if (c == '^') return 1;
|
||||||
if (c == '!') return 1;
|
if (c == '!') return 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get an integer power of 10 */
|
/* Get an integer power of 10 */
|
||||||
@ -308,8 +308,8 @@ static int ParserStringState(Parser * p, uint8_t c) {
|
|||||||
case '\'': next = '\''; break;
|
case '\'': next = '\''; break;
|
||||||
case 'z': next = '\0'; break;
|
case 'z': next = '\0'; break;
|
||||||
default:
|
default:
|
||||||
PError(p, "Unknown string escape sequence.");
|
PError(p, "Unknown string escape sequence.");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
BufferPush(p->vm, top->buf.string.buffer, next);
|
BufferPush(p->vm, top->buf.string.buffer, next);
|
||||||
top->buf.string.state = STRING_STATE_BASE;
|
top->buf.string.state = STRING_STATE_BASE;
|
||||||
|
42
value.c
42
value.c
@ -9,14 +9,14 @@
|
|||||||
static void FuncDefBytecodePrint(FuncDef * def) {
|
static void FuncDefBytecodePrint(FuncDef * def) {
|
||||||
uint32_t count, i;
|
uint32_t count, i;
|
||||||
count = def->byteCodeLen;
|
count = def->byteCodeLen;
|
||||||
printf("(bytecode)[");
|
printf("(bytecode)[");
|
||||||
if (count) {
|
if (count) {
|
||||||
for (i = 0; i < count - 1; ++i) {
|
for (i = 0; i < count - 1; ++i) {
|
||||||
printf("%04x ", def->byteCode[i]);
|
printf("%04x ", def->byteCode[i]);
|
||||||
}
|
}
|
||||||
printf("%04x", def->byteCode[i]);
|
printf("%04x", def->byteCode[i]);
|
||||||
}
|
}
|
||||||
printf("]");
|
printf("]");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Print a value recursively. Used for debugging */
|
/* Print a value recursively. Used for debugging */
|
||||||
@ -55,8 +55,8 @@ void ValuePrint(Value x, uint32_t indent) {
|
|||||||
break;
|
break;
|
||||||
case TYPE_FUNCTION:
|
case TYPE_FUNCTION:
|
||||||
printf("<function ");
|
printf("<function ");
|
||||||
FuncDefBytecodePrint(x.data.func->def);
|
FuncDefBytecodePrint(x.data.func->def);
|
||||||
printf(">");
|
printf(">");
|
||||||
break;
|
break;
|
||||||
case TYPE_DICTIONARY:
|
case TYPE_DICTIONARY:
|
||||||
printf("<dictionary>");
|
printf("<dictionary>");
|
||||||
@ -66,8 +66,8 @@ void ValuePrint(Value x, uint32_t indent) {
|
|||||||
break;
|
break;
|
||||||
case TYPE_FUNCDEF:
|
case TYPE_FUNCDEF:
|
||||||
printf("<funcdef ");
|
printf("<funcdef ");
|
||||||
FuncDefBytecodePrint(x.data.funcdef);
|
FuncDefBytecodePrint(x.data.funcdef);
|
||||||
printf(">");
|
printf(">");
|
||||||
break;
|
break;
|
||||||
case TYPE_FUNCENV:
|
case TYPE_FUNCENV:
|
||||||
printf("<funcenv>");
|
printf("<funcenv>");
|
||||||
@ -197,10 +197,10 @@ int ValueEqual(Value x, Value y) {
|
|||||||
case TYPE_NUMBER:
|
case TYPE_NUMBER:
|
||||||
result = (x.data.number == y.data.number);
|
result = (x.data.number == y.data.number);
|
||||||
break;
|
break;
|
||||||
/* Assume that when strings are created, equal strings
|
/* Assume that when strings are created, equal strings
|
||||||
* are set to the same string */
|
* are set to the same string */
|
||||||
case TYPE_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:
|
case TYPE_SYMBOL:
|
||||||
if (x.data.string == y.data.string) {
|
if (x.data.string == y.data.string) {
|
||||||
result = 1;
|
result = 1;
|
||||||
@ -274,12 +274,12 @@ uint32_t ValueHash(Value x) {
|
|||||||
case TYPE_THREAD:
|
case TYPE_THREAD:
|
||||||
/* Cast the pointer */
|
/* Cast the pointer */
|
||||||
{
|
{
|
||||||
union {
|
union {
|
||||||
void * pointer;
|
void * pointer;
|
||||||
uint32_t hash;
|
uint32_t hash;
|
||||||
} u;
|
} u;
|
||||||
u.pointer = x.data.pointer;
|
u.pointer = x.data.pointer;
|
||||||
hash = u.hash;
|
hash = u.hash;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
304
vm.c
304
vm.c
@ -15,21 +15,19 @@ static const char VMS_EXPECTED_NUMBER_LOP[] = "Expected left operand to be numbe
|
|||||||
typedef struct StackFrame StackFrame;
|
typedef struct StackFrame StackFrame;
|
||||||
struct StackFrame {
|
struct StackFrame {
|
||||||
Value callee;
|
Value callee;
|
||||||
uint16_t size;
|
uint16_t size;
|
||||||
uint16_t prevSize;
|
uint16_t prevSize;
|
||||||
uint16_t ret;
|
uint16_t ret;
|
||||||
FuncEnv * env;
|
FuncEnv * env;
|
||||||
uint16_t * pc;
|
uint16_t * pc;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* The size of a StackFrame in units of Values. */
|
/* The size of a StackFrame in units of Values. */
|
||||||
static size_t FRAME_SIZE() {
|
#define FRAME_SIZE ((sizeof(StackFrame) + sizeof(Value) - 1) / sizeof(Value))
|
||||||
return ((sizeof(StackFrame) + sizeof(Value) - 1) / sizeof(Value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the stack frame pointer for a thread */
|
/* Get the stack frame pointer for a thread */
|
||||||
static StackFrame * ThreadFrame(Array * 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 */
|
/* 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)->color = vm->black;
|
||||||
GCHeader(x->data.array->data)->color = vm->black;
|
GCHeader(x->data.array->data)->color = vm->black;
|
||||||
for (i = 0; i < count; ++i)
|
for (i = 0; i < count; ++i)
|
||||||
VMMark(vm, x->data.array->data + i);
|
VMMark(vm, x->data.array->data + i);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -102,19 +100,19 @@ static void VMMark(VM * vm, Value * x) {
|
|||||||
uint32_t i;
|
uint32_t i;
|
||||||
Array * thread = x->data.array;
|
Array * thread = x->data.array;
|
||||||
StackFrame * frame = (StackFrame *)thread->data;
|
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)->color = vm->black;
|
||||||
GCHeader(thread->data)->color = vm->black;
|
GCHeader(thread->data)->color = vm->black;
|
||||||
while (frame <= end) {
|
while (frame <= end) {
|
||||||
Value * stack = (Value *)frame + FRAME_SIZE();
|
Value * stack = (Value *)frame + FRAME_SIZE;
|
||||||
VMMark(vm, &frame->callee);
|
VMMark(vm, &frame->callee);
|
||||||
if (frame->env)
|
if (frame->env)
|
||||||
VMMarkFuncEnv(vm, frame->env);
|
VMMarkFuncEnv(vm, frame->env);
|
||||||
for (i = 0; i < frame->size; ++i) {
|
for (i = 0; i < frame->size; ++i) {
|
||||||
VMMark(vm, stack + i);
|
VMMark(vm, stack + i);
|
||||||
}
|
}
|
||||||
frame = (StackFrame *)(stack + frame->size);
|
frame = (StackFrame *)(stack + frame->size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -165,9 +163,9 @@ static void VMMark(VM * vm, Value * x) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TYPE_FUNCENV:
|
case TYPE_FUNCENV:
|
||||||
VMMarkFuncEnv(vm, x->data.funcenv);
|
VMMarkFuncEnv(vm, x->data.funcenv);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,20 +222,23 @@ void * VMZalloc(VM * vm, uint32_t size) {
|
|||||||
/* Run garbage collection */
|
/* Run garbage collection */
|
||||||
void VMCollect(VM * vm) {
|
void VMCollect(VM * vm) {
|
||||||
if (vm->lock > 0) return;
|
if (vm->lock > 0) return;
|
||||||
Value thread;
|
/* Thread can be null */
|
||||||
thread.type = TYPE_THREAD;
|
if (vm->thread) {
|
||||||
thread.data.array = vm->thread;
|
Value thread;
|
||||||
VMMark(vm, &thread);
|
thread.type = TYPE_THREAD;
|
||||||
VMMark(vm, &vm->tempRoot);
|
thread.data.array = vm->thread;
|
||||||
|
VMMark(vm, &thread);
|
||||||
|
}
|
||||||
|
VMMark(vm, &vm->ret);
|
||||||
|
VMMark(vm, &vm->root);
|
||||||
VMSweep(vm);
|
VMSweep(vm);
|
||||||
vm->nextCollection = 0;
|
vm->nextCollection = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Run garbage collection if needed */
|
/* Run garbage collection if needed */
|
||||||
void VMMaybeCollect(VM * vm) {
|
void VMMaybeCollect(VM * vm) {
|
||||||
if (vm->nextCollection >= vm->memoryInterval) {
|
if (vm->nextCollection >= vm->memoryInterval)
|
||||||
VMCollect(vm);
|
VMCollect(vm);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push a stack frame onto a thread */
|
/* 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;
|
uint16_t oldSize;
|
||||||
uint32_t nextCount, i;
|
uint32_t nextCount, i;
|
||||||
StackFrame * frame;
|
StackFrame * frame;
|
||||||
if (thread->count) {
|
if (thread->count) {
|
||||||
frame = ThreadFrame(thread);
|
frame = ThreadFrame(thread);
|
||||||
oldSize = frame->size;
|
oldSize = frame->size;
|
||||||
} else {
|
} else {
|
||||||
oldSize = 0;
|
oldSize = 0;
|
||||||
}
|
}
|
||||||
nextCount = thread->count + oldSize + FRAME_SIZE();
|
nextCount = thread->count + oldSize + FRAME_SIZE;
|
||||||
ArrayEnsure(vm, thread, nextCount + size);
|
ArrayEnsure(vm, thread, nextCount + size);
|
||||||
thread->count = nextCount;
|
thread->count = nextCount;
|
||||||
/* Ensure values start out as nil so as to not confuse
|
/* Ensure values start out as nil so as to not confuse
|
||||||
* the garabage collector */
|
* the garabage collector */
|
||||||
for (i = nextCount; i < nextCount + size; ++i)
|
for (i = nextCount; i < nextCount + size; ++i)
|
||||||
thread->data[i].type = TYPE_NIL;
|
thread->data[i].type = TYPE_NIL;
|
||||||
frame = ThreadFrame(thread);
|
frame = ThreadFrame(thread);
|
||||||
/* Set up the new stack frame */
|
/* Set up the new stack frame */
|
||||||
frame->prevSize = oldSize;
|
frame->prevSize = oldSize;
|
||||||
@ -271,28 +272,28 @@ static void VMThreadPush(VM * vm, Array * thread, Value callee, uint32_t size) {
|
|||||||
environment */
|
environment */
|
||||||
static void VMThreadSplitStack(VM * vm, Array * thread) {
|
static void VMThreadSplitStack(VM * vm, Array * thread) {
|
||||||
StackFrame * frame = ThreadFrame(thread);
|
StackFrame * frame = ThreadFrame(thread);
|
||||||
FuncEnv * env = frame->env;
|
FuncEnv * env = frame->env;
|
||||||
/* Check for closures */
|
/* Check for closures */
|
||||||
if (env) {
|
if (env) {
|
||||||
uint32_t size = frame->size;
|
uint32_t size = frame->size;
|
||||||
env->thread = NULL;
|
env->thread = NULL;
|
||||||
env->stackOffset = size;
|
env->stackOffset = size;
|
||||||
env->values = VMAlloc(vm, sizeof(Value) * size);
|
env->values = VMAlloc(vm, sizeof(Value) * size);
|
||||||
memcpy(env->values, thread->data + thread->count, size * sizeof(Value));
|
memcpy(env->values, thread->data + thread->count, size * sizeof(Value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pop the top-most stack frame from stack */
|
/* Pop the top-most stack frame from stack */
|
||||||
static void VMThreadPop(VM * vm, Array * thread) {
|
static void VMThreadPop(VM * vm, Array * thread) {
|
||||||
StackFrame * frame = ThreadFrame(thread);
|
StackFrame * frame = ThreadFrame(thread);
|
||||||
uint32_t delta = FRAME_SIZE() + frame->prevSize;
|
uint32_t delta = FRAME_SIZE + frame->prevSize;
|
||||||
if (thread->count) {
|
if (thread->count) {
|
||||||
VMThreadSplitStack(vm, thread);
|
VMThreadSplitStack(vm, thread);
|
||||||
} else {
|
} else {
|
||||||
VMError(vm, "Nothing to pop from stack.");
|
VMError(vm, "Nothing to pop from stack.");
|
||||||
}
|
}
|
||||||
thread->count -= delta;
|
thread->count -= delta;
|
||||||
vm->base -= delta;
|
vm->base -= delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get an upvalue */
|
/* Get an upvalue */
|
||||||
@ -300,7 +301,7 @@ static Value * GetUpValue(VM * vm, Func * fn, uint16_t level, uint16_t index) {
|
|||||||
FuncEnv * env;
|
FuncEnv * env;
|
||||||
Value * stack;
|
Value * stack;
|
||||||
if (!level) {
|
if (!level) {
|
||||||
return vm->base + index;
|
return vm->base + index;
|
||||||
}
|
}
|
||||||
while (fn && --level)
|
while (fn && --level)
|
||||||
fn = fn->parent;
|
fn = fn->parent;
|
||||||
@ -330,7 +331,7 @@ static int truthy(Value v) {
|
|||||||
static void VMReturn(VM * vm, Value ret) {
|
static void VMReturn(VM * vm, Value ret) {
|
||||||
Array * thread = vm->thread;
|
Array * thread = vm->thread;
|
||||||
StackFrame * frame = ThreadFrame(thread);
|
StackFrame * frame = ThreadFrame(thread);
|
||||||
VMThreadPop(vm, thread);
|
VMThreadPop(vm, thread);
|
||||||
if (thread->count == 0) {
|
if (thread->count == 0) {
|
||||||
VMExit(vm, ret);
|
VMExit(vm, ret);
|
||||||
}
|
}
|
||||||
@ -348,7 +349,7 @@ static void VMCallOp(VM * vm) {
|
|||||||
uint32_t oldCount = thread->count;
|
uint32_t oldCount = thread->count;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
Value * oldBase;
|
Value * oldBase;
|
||||||
frame->pc = vm->pc + 4 + arity;
|
frame->pc = vm->pc + 4 + arity;
|
||||||
frame->ret = vm->pc[2];
|
frame->ret = vm->pc[2];
|
||||||
if (callee.type == TYPE_FUNCTION) {
|
if (callee.type == TYPE_FUNCTION) {
|
||||||
Func * fn = callee.data.func;
|
Func * fn = callee.data.func;
|
||||||
@ -365,15 +366,15 @@ static void VMCallOp(VM * vm) {
|
|||||||
++vm->lock;
|
++vm->lock;
|
||||||
VMReturn(vm, callee.data.cfunction(vm));
|
VMReturn(vm, callee.data.cfunction(vm));
|
||||||
--vm->lock;
|
--vm->lock;
|
||||||
} else {
|
} else {
|
||||||
Func * f = callee.data.func;
|
Func * f = callee.data.func;
|
||||||
uint32_t locals = f->def->locals;
|
uint32_t locals = f->def->locals;
|
||||||
for (i = 0; i < arity; ++i)
|
for (i = 0; i < arity; ++i)
|
||||||
vm->base[i] = oldBase[vm->pc[4 + i]];
|
vm->base[i] = oldBase[vm->pc[4 + i]];
|
||||||
for (; i < locals; ++i)
|
for (; i < locals; ++i)
|
||||||
vm->base[i].type = TYPE_NIL;
|
vm->base[i].type = TYPE_NIL;
|
||||||
vm->pc = f->def->byteCode;
|
vm->pc = f->def->byteCode;
|
||||||
}
|
}
|
||||||
VMMaybeCollect(vm);
|
VMMaybeCollect(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,42 +387,42 @@ static void VMTailCallOp(VM * vm) {
|
|||||||
uint16_t newFrameSize, currentFrameSize;
|
uint16_t newFrameSize, currentFrameSize;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
/* Check for closures */
|
/* Check for closures */
|
||||||
VMThreadSplitStack(vm, thread);
|
VMThreadSplitStack(vm, thread);
|
||||||
if (callee.type == TYPE_CFUNCTION) {
|
if (callee.type == TYPE_CFUNCTION) {
|
||||||
newFrameSize = arity;
|
newFrameSize = arity;
|
||||||
} else if (callee.type == TYPE_FUNCTION) {
|
} else if (callee.type == TYPE_FUNCTION) {
|
||||||
Func * f = callee.data.func;
|
Func * f = callee.data.func;
|
||||||
newFrameSize = f->def->locals;
|
newFrameSize = f->def->locals;
|
||||||
} else {
|
} else {
|
||||||
VMError(vm, EXPECTED_FUNCTION);
|
VMError(vm, EXPECTED_FUNCTION);
|
||||||
}
|
}
|
||||||
/* Ensure stack has enough space for copies of arguments */
|
/* Ensure stack has enough space for copies of arguments */
|
||||||
currentFrameSize = frame->size;
|
currentFrameSize = frame->size;
|
||||||
ArrayEnsure(vm, thread, thread->count + currentFrameSize + arity);
|
ArrayEnsure(vm, thread, thread->count + currentFrameSize + arity);
|
||||||
frame = ThreadFrame(thread);
|
frame = ThreadFrame(thread);
|
||||||
vm->base = thread->data + thread->count;
|
vm->base = thread->data + thread->count;
|
||||||
/* Copy the arguments into the extra space */
|
/* Copy the arguments into the extra space */
|
||||||
for (i = 0; i < arity; ++i) {
|
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 */
|
/* Copy the end of the stack to the parameter position */
|
||||||
memcpy(vm->base, vm->base + currentFrameSize, arity * sizeof(Value));
|
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) {
|
for (i = arity; i < newFrameSize; ++i) {
|
||||||
vm->base[i].type = TYPE_NIL;
|
vm->base[i].type = TYPE_NIL;
|
||||||
}
|
}
|
||||||
/* Update the stack frame */
|
/* Update the stack frame */
|
||||||
frame->size = newFrameSize;
|
frame->size = newFrameSize;
|
||||||
frame->callee = callee;
|
frame->callee = callee;
|
||||||
frame->env = NULL;
|
frame->env = NULL;
|
||||||
if (callee.type == TYPE_CFUNCTION) {
|
if (callee.type == TYPE_CFUNCTION) {
|
||||||
++vm->lock;
|
++vm->lock;
|
||||||
VMReturn(vm, callee.data.cfunction(vm));
|
VMReturn(vm, callee.data.cfunction(vm));
|
||||||
--vm->lock;
|
--vm->lock;
|
||||||
} else {
|
} else {
|
||||||
Func * f = callee.data.func;
|
Func * f = callee.data.func;
|
||||||
vm->pc = f->def->byteCode;
|
vm->pc = f->def->byteCode;
|
||||||
}
|
}
|
||||||
VMMaybeCollect(vm);
|
VMMaybeCollect(vm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -440,7 +441,7 @@ static Value VMMakeClosure(VM * vm, uint16_t literal) {
|
|||||||
env->thread = thread;
|
env->thread = thread;
|
||||||
env->stackOffset = thread->count;
|
env->stackOffset = thread->count;
|
||||||
env->values = NULL;
|
env->values = NULL;
|
||||||
frame->env = env;
|
frame->env = env;
|
||||||
}
|
}
|
||||||
current = frame->callee.data.func;
|
current = frame->callee.data.func;
|
||||||
constant = LoadConstant(vm, current, literal);
|
constant = LoadConstant(vm, current, literal);
|
||||||
@ -482,29 +483,29 @@ int VMStart(VM * vm) {
|
|||||||
Value temp, v1, v2;
|
Value temp, v1, v2;
|
||||||
|
|
||||||
#define DO_BINARY_MATH(op) \
|
#define DO_BINARY_MATH(op) \
|
||||||
v1 = vm->base[vm->pc[2]]; \
|
v1 = vm->base[vm->pc[2]]; \
|
||||||
v2 = vm->base[vm->pc[3]]; \
|
v2 = vm->base[vm->pc[3]]; \
|
||||||
VMAssert(vm, v1.type == TYPE_NUMBER, VMS_EXPECTED_NUMBER_LOP); \
|
VMAssert(vm, v1.type == TYPE_NUMBER, VMS_EXPECTED_NUMBER_LOP); \
|
||||||
VMAssert(vm, v2.type == TYPE_NUMBER, VMS_EXPECTED_NUMBER_ROP); \
|
VMAssert(vm, v2.type == TYPE_NUMBER, VMS_EXPECTED_NUMBER_ROP); \
|
||||||
temp.type = TYPE_NUMBER; \
|
temp.type = TYPE_NUMBER; \
|
||||||
temp.data.number = v1.data.number op v2.data.number; \
|
temp.data.number = v1.data.number op v2.data.number; \
|
||||||
vm->base[vm->pc[1]] = temp; \
|
vm->base[vm->pc[1]] = temp; \
|
||||||
vm->pc += 4; \
|
vm->pc += 4; \
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VM_OP_ADD: /* Addition */
|
case VM_OP_ADD: /* Addition */
|
||||||
DO_BINARY_MATH(+)
|
DO_BINARY_MATH(+)
|
||||||
|
|
||||||
case VM_OP_SUB: /* Subtraction */
|
case VM_OP_SUB: /* Subtraction */
|
||||||
DO_BINARY_MATH(-)
|
DO_BINARY_MATH(-)
|
||||||
|
|
||||||
case VM_OP_MUL: /* Multiplication */
|
case VM_OP_MUL: /* Multiplication */
|
||||||
DO_BINARY_MATH(*)
|
DO_BINARY_MATH(*)
|
||||||
|
|
||||||
case VM_OP_DIV: /* Division */
|
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) */
|
case VM_OP_NOT: /* Boolean unary (Boolean not) */
|
||||||
temp.type = TYPE_BOOLEAN;
|
temp.type = TYPE_BOOLEAN;
|
||||||
@ -528,22 +529,22 @@ int VMStart(VM * vm) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case VM_OP_FLS: /* Load False */
|
case VM_OP_FLS: /* Load False */
|
||||||
temp.type = TYPE_BOOLEAN;
|
temp.type = TYPE_BOOLEAN;
|
||||||
temp.data.boolean = 0;
|
temp.data.boolean = 0;
|
||||||
vm->base[vm->pc[1]] = temp;
|
vm->base[vm->pc[1]] = temp;
|
||||||
vm->pc += 2;
|
vm->pc += 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VM_OP_TRU: /* Load True */
|
case VM_OP_TRU: /* Load True */
|
||||||
temp.type = TYPE_BOOLEAN;
|
temp.type = TYPE_BOOLEAN;
|
||||||
temp.data.boolean = 1;
|
temp.data.boolean = 1;
|
||||||
vm->base[vm->pc[1]] = temp;
|
vm->base[vm->pc[1]] = temp;
|
||||||
vm->pc += 2;
|
vm->pc += 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VM_OP_NIL: /* Load Nil */
|
case VM_OP_NIL: /* Load Nil */
|
||||||
temp.type = TYPE_NIL;
|
temp.type = TYPE_NIL;
|
||||||
vm->base[vm->pc[1]] = temp;
|
vm->base[vm->pc[1]] = temp;
|
||||||
vm->pc += 2;
|
vm->pc += 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -578,7 +579,7 @@ int VMStart(VM * vm) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case VM_OP_RET: /* Return */
|
case VM_OP_RET: /* Return */
|
||||||
VMReturn(vm, vm->base[vm->pc[1]]);
|
VMReturn(vm, vm->base[vm->pc[1]]);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VM_OP_SUV: /* Set Up Value */
|
case VM_OP_SUV: /* Set Up Value */
|
||||||
@ -641,46 +642,46 @@ int VMStart(VM * vm) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case VM_OP_ARR: /* Array literal */
|
case VM_OP_ARR: /* Array literal */
|
||||||
{
|
{
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
uint32_t arrayLen = vm->pc[2];
|
uint32_t arrayLen = vm->pc[2];
|
||||||
Array * array = ArrayNew(vm, arrayLen);
|
Array * array = ArrayNew(vm, arrayLen);
|
||||||
array->count = arrayLen;
|
array->count = arrayLen;
|
||||||
for (i = 0; i < arrayLen; ++i)
|
for (i = 0; i < arrayLen; ++i)
|
||||||
array->data[i] = vm->base[vm->pc[3 + i]];
|
array->data[i] = vm->base[vm->pc[3 + i]];
|
||||||
temp.type = TYPE_ARRAY;
|
temp.type = TYPE_ARRAY;
|
||||||
temp.data.array = array;
|
temp.data.array = array;
|
||||||
vm->base[vm->pc[1]] = temp;
|
vm->base[vm->pc[1]] = temp;
|
||||||
vm->pc += 3 + arrayLen;
|
vm->pc += 3 + arrayLen;
|
||||||
VMMaybeCollect(vm);
|
VMMaybeCollect(vm);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VM_OP_DIC: /* Dictionary literal */
|
case VM_OP_DIC: /* Dictionary literal */
|
||||||
{
|
{
|
||||||
uint32_t i = 3;
|
uint32_t i = 3;
|
||||||
uint32_t kvs = vm->pc[2];
|
uint32_t kvs = vm->pc[2];
|
||||||
Dictionary * dict = DictNew(vm, kvs);
|
Dictionary * dict = DictNew(vm, kvs);
|
||||||
kvs = kvs + 3;
|
kvs = kvs + 3;
|
||||||
while (i < kvs) {
|
while (i < kvs) {
|
||||||
v1 = vm->base[vm->pc[i++]];
|
v1 = vm->base[vm->pc[i++]];
|
||||||
v2 = 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.type = TYPE_DICTIONARY;
|
||||||
temp.data.dict = dict;
|
temp.data.dict = dict;
|
||||||
vm->base[vm->pc[1]] = temp;
|
vm->base[vm->pc[1]] = temp;
|
||||||
vm->pc += kvs;
|
vm->pc += kvs;
|
||||||
VMMaybeCollect(vm);
|
VMMaybeCollect(vm);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VM_OP_TCL: /* Tail call */
|
case VM_OP_TCL: /* Tail call */
|
||||||
VMTailCallOp(vm);
|
VMTailCallOp(vm);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* Macro for generating some math operators */
|
/* 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 count = vm->pc[2]; \
|
||||||
uint16_t i; \
|
uint16_t i; \
|
||||||
Number accum = start; \
|
Number accum = start; \
|
||||||
@ -709,7 +710,7 @@ int VMStart(VM * vm) {
|
|||||||
case VM_OP_DVM:
|
case VM_OP_DVM:
|
||||||
DO_MULTI_MATH(/, 1)
|
DO_MULTI_MATH(/, 1)
|
||||||
|
|
||||||
#undef DO_MULTI_MATH
|
#undef DO_MULTI_MATH
|
||||||
|
|
||||||
case VM_OP_RTN: /* Return nil */
|
case VM_OP_RTN: /* Return nil */
|
||||||
temp.type = TYPE_NIL;
|
temp.type = TYPE_NIL;
|
||||||
@ -731,14 +732,14 @@ int VMStart(VM * vm) {
|
|||||||
Value VMGetArg(VM * vm, uint16_t index) {
|
Value VMGetArg(VM * vm, uint16_t index) {
|
||||||
uint16_t frameSize = ThreadFrame(vm->thread)->size;
|
uint16_t frameSize = ThreadFrame(vm->thread)->size;
|
||||||
VMAssert(vm, frameSize > index, "Cannot get arg out of stack bounds");
|
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 */
|
/* Put a value on the stack */
|
||||||
void VMSetArg(VM * vm, uint16_t index, Value x) {
|
void VMSetArg(VM * vm, uint16_t index, Value x) {
|
||||||
uint16_t frameSize = ThreadFrame(vm->thread)->size;
|
uint16_t frameSize = ThreadFrame(vm->thread)->size;
|
||||||
VMAssert(vm, frameSize > index, "Cannot set arg out of stack bounds");
|
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 */
|
/* Get the size of the VMStack */
|
||||||
@ -748,29 +749,36 @@ uint16_t VMCountArgs(VM * vm) {
|
|||||||
|
|
||||||
/* Initialize the VM */
|
/* Initialize the VM */
|
||||||
void VMInit(VM * vm) {
|
void VMInit(VM * vm) {
|
||||||
vm->tempRoot.type = TYPE_NIL;
|
vm->ret.type = TYPE_NIL;
|
||||||
|
vm->root.type = TYPE_NIL;
|
||||||
vm->base = NULL;
|
vm->base = NULL;
|
||||||
vm->pc = NULL;
|
vm->pc = NULL;
|
||||||
vm->error = NULL;
|
vm->error = NULL;
|
||||||
/* Garbage collection */
|
/* Garbage collection */
|
||||||
vm->blocks = NULL;
|
vm->blocks = NULL;
|
||||||
vm->nextCollection = 0;
|
vm->nextCollection = 0;
|
||||||
vm->memoryInterval = 1024 * 256;
|
vm->memoryInterval = 0;
|
||||||
vm->black = 0;
|
vm->black = 0;
|
||||||
vm->lock = 0;
|
vm->lock = 0;
|
||||||
/* Create new thread */
|
/* Set to empty thread */
|
||||||
vm->thread = ArrayNew(vm, 32);
|
vm->thread = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load a function into the VM. The function will be called with
|
/* Load a function into the VM. The function will be called with
|
||||||
* no arguments when run */
|
* no arguments when run */
|
||||||
void VMLoad(VM * vm, Func * func) {
|
void VMLoad(VM * vm, Value func) {
|
||||||
Value callee;
|
Array * thread = ArrayNew(vm, 100);
|
||||||
callee.type = TYPE_FUNCTION;
|
vm->thread = thread;
|
||||||
callee.data.func = func;
|
if (func.type == TYPE_FUNCTION) {
|
||||||
vm->thread = ArrayNew(vm, 32);
|
Func * fn = func.data.func;
|
||||||
VMThreadPush(vm, vm->thread, callee, func->def->locals);
|
VMThreadPush(vm, thread, func, fn->def->locals);
|
||||||
vm->pc = func->def->byteCode;
|
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 */
|
/* Clear all memory associated with the VM */
|
||||||
|
11
vm.h
11
vm.h
@ -4,7 +4,7 @@
|
|||||||
#include "datatypes.h"
|
#include "datatypes.h"
|
||||||
|
|
||||||
/* Exit from the VM normally */
|
/* 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. */
|
/* Bail from the VM with an error. */
|
||||||
#define VMError(vm, e) ((vm)->error = (e), longjmp((vm)->jump, 2))
|
#define VMError(vm, e) ((vm)->error = (e), longjmp((vm)->jump, 2))
|
||||||
@ -16,6 +16,10 @@
|
|||||||
#define VMAssert(vm, cond, e) do \
|
#define VMAssert(vm, cond, e) do \
|
||||||
{ if (!(cond)) { VMError((vm), (e)); } } while (0)
|
{ 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 */
|
/* Initialize the VM */
|
||||||
void VMInit(VM * vm);
|
void VMInit(VM * vm);
|
||||||
|
|
||||||
@ -23,14 +27,11 @@ void VMInit(VM * vm);
|
|||||||
void VMDeinit(VM * vm);
|
void VMDeinit(VM * vm);
|
||||||
|
|
||||||
/* Load a function to be run on the 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 */
|
/* Start running the VM */
|
||||||
int VMStart(VM * vm);
|
int VMStart(VM * vm);
|
||||||
|
|
||||||
/* Get the result after VMStart returns */
|
|
||||||
#define VMResult(vm) ((vm)->tempRoot)
|
|
||||||
|
|
||||||
/* Run garbage collection */
|
/* Run garbage collection */
|
||||||
void VMCollect(VM * vm);
|
void VMCollect(VM * vm);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user