mirror of
https://github.com/janet-lang/janet
synced 2024-11-14 12:44:49 +00:00
Make some changes and begin work on macros.
This commit is contained in:
parent
47d9aceb0a
commit
fccc7f25b5
212
compile.c
212
compile.c
@ -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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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
161
main.c
@ -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"
|
||||
@ -9,100 +8,98 @@
|
||||
#include "disasm.h"
|
||||
|
||||
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
24
parse.c
@ -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
42
value.c
@ -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
304
vm.c
@ -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;
|
||||
@ -727,14 +728,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 */
|
||||
@ -744,29 +745,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, 20);
|
||||
/* 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, 100);
|
||||
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
11
vm.h
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user