mirror of
https://github.com/janet-lang/janet
synced 2024-11-24 01:17:16 +00:00
Add get and set instructions. GC is still buggy and currently
crashes everything all the time. :(
This commit is contained in:
parent
f2d6b979f0
commit
439650f26a
2
Makefile
2
Makefile
@ -1,6 +1,6 @@
|
||||
# TIL
|
||||
|
||||
CFLAGS=-std=c99 -Wall -Wextra -g
|
||||
CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -g
|
||||
|
||||
TARGET=interp
|
||||
PREFIX=/usr/local
|
||||
|
81
compile.c
81
compile.c
@ -87,11 +87,11 @@ static FormOptions FormOptionsDefault() {
|
||||
/* Create some helpers that allows us to push more than just raw bytes
|
||||
* to the byte buffer. This helps us create the byte code for the compiled
|
||||
* functions. */
|
||||
BufferDefine(UInt32, uint32_t);
|
||||
BufferDefine(Int32, int32_t);
|
||||
BufferDefine(Number, Number);
|
||||
BufferDefine(UInt16, uint16_t);
|
||||
BufferDefine(Int16, int16_t);
|
||||
BufferDefine(UInt32, uint32_t)
|
||||
BufferDefine(Int32, int32_t)
|
||||
BufferDefine(Number, Number)
|
||||
BufferDefine(UInt16, uint16_t)
|
||||
BufferDefine(Int16, int16_t)
|
||||
|
||||
/* If there is an error during compilation,
|
||||
* jump back to start */
|
||||
@ -553,7 +553,7 @@ static Slot CompileArray(Compiler * c, FormOptions opts, Array * array) {
|
||||
* called with n arguments, the number of arguments is written
|
||||
* after the op code, followed by those arguments.
|
||||
*
|
||||
* This makes a few assumptions about the opertors. One, no side
|
||||
* This makes a few assumptions about the operators. One, no side
|
||||
* effects. With this assumptions, if the result of the operator
|
||||
* is unused, it's calculation can be ignored (the evaluation of
|
||||
* its argument is still carried out, but their results can
|
||||
@ -576,7 +576,7 @@ static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form,
|
||||
if (form->count < 2) {
|
||||
if (op0 < 0) {
|
||||
if (opn < 0) CError(c, "This operator does not take 0 arguments.");
|
||||
/* Use multiple form */
|
||||
/* Use multiple form of op */
|
||||
BufferPushUInt16(c->vm, buffer, opn);
|
||||
BufferPushUInt16(c->vm, buffer, ret.index);
|
||||
BufferPushUInt16(c->vm, buffer, 0);
|
||||
@ -587,7 +587,7 @@ static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form,
|
||||
} else if (form->count == 2) {
|
||||
if (op1 < 0) {
|
||||
if (opn < 0) CError(c, "This operator does not take 1 argument.");
|
||||
/* Use multiple form */
|
||||
/* Use multiple form of op */
|
||||
BufferPushUInt16(c->vm, buffer, opn);
|
||||
BufferPushUInt16(c->vm, buffer, ret.index);
|
||||
BufferPushUInt16(c->vm, buffer, 1);
|
||||
@ -642,6 +642,39 @@ static Slot CompileGreaterThanOrEqual(Compiler * c, FormOptions opts, Array * fo
|
||||
static Slot CompileNot(Compiler * c, FormOptions opts, Array * form) {
|
||||
return CompileOperator(c, opts, form, VM_OP_FLS, VM_OP_NOT, -1, -1, 0);
|
||||
}
|
||||
static Slot CompileGet(Compiler * c, FormOptions opts, Array * form) {
|
||||
return CompileOperator(c, opts, form, -1, -1, VM_OP_GET, -1, 0);
|
||||
}
|
||||
|
||||
/* Associative set */
|
||||
static Slot CompileSet(Compiler * c, FormOptions opts, Array * form) {
|
||||
Buffer * buffer = c->buffer;
|
||||
FormOptions subOpts = FormOptionsDefault();
|
||||
Slot ds, key, val;
|
||||
if (form->count != 4) CError(c, "Set expects 4 arguments");
|
||||
if (opts.resultUnused) {
|
||||
ds = CompilerRealizeSlot(c, CompileValue(c, subOpts, form->data[1]));
|
||||
} else {
|
||||
subOpts = opts;
|
||||
subOpts.isTail = 0;
|
||||
ds = CompilerRealizeSlot(c, CompileValue(c, subOpts, form->data[1]));
|
||||
subOpts = FormOptionsDefault();
|
||||
}
|
||||
key = CompilerRealizeSlot(c, CompileValue(c, subOpts, form->data[2]));
|
||||
val = CompilerRealizeSlot(c, CompileValue(c, subOpts, form->data[3]));
|
||||
BufferPushUInt16(c->vm, buffer, VM_OP_SET);
|
||||
BufferPushUInt16(c->vm, buffer, ds.index);
|
||||
BufferPushUInt16(c->vm, buffer, key.index);
|
||||
BufferPushUInt16(c->vm, buffer, val.index);
|
||||
CompilerDropSlot(c, c->tail, key);
|
||||
CompilerDropSlot(c, c->tail, val);
|
||||
if (opts.resultUnused) {
|
||||
CompilerDropSlot(c, c->tail, ds);
|
||||
return NilSlot();
|
||||
} else {
|
||||
return ds;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compile an assignment operation */
|
||||
static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value right) {
|
||||
@ -939,7 +972,7 @@ static Slot CompileQuote(Compiler * c, FormOptions opts, Array * form) {
|
||||
}
|
||||
|
||||
/* Assignment special */
|
||||
static Slot CompileSet(Compiler * c, FormOptions opts, Array * form) {
|
||||
static Slot CompileVar(Compiler * c, FormOptions opts, Array * form) {
|
||||
if (form->count != 3)
|
||||
CError(c, "Assignment expects 2 arguments");
|
||||
return CompileAssign(c, opts, form->data[1], form->data[2]);
|
||||
@ -968,7 +1001,6 @@ static SpecialFormHelper GetSpecial(Array * form) {
|
||||
case '>': return CompileGreaterThan;
|
||||
case '<': return CompileLessThan;
|
||||
case '=': return CompileEquals;
|
||||
case '\'': return CompileQuote;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -991,6 +1023,14 @@ static SpecialFormHelper GetSpecial(Array * form) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'g':
|
||||
{
|
||||
if (VStringSize(name) == 3 &&
|
||||
name[1] == 'e' &&
|
||||
name[2] == 't') {
|
||||
return CompileGet;
|
||||
}
|
||||
}
|
||||
case 'd':
|
||||
{
|
||||
if (VStringSize(name) == 2 &&
|
||||
@ -1053,6 +1093,15 @@ static SpecialFormHelper GetSpecial(Array * form) {
|
||||
return CompileWhile;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
{
|
||||
if (VStringSize(name) == 2 &&
|
||||
name[1] == '=') {
|
||||
return CompileVar;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1152,7 +1201,7 @@ void CompilerAddGlobalCFunc(Compiler * c, const char * name, CFunction f) {
|
||||
Value func;
|
||||
func.type = TYPE_CFUNCTION;
|
||||
func.data.cfunction = f;
|
||||
return CompilerAddGlobal(c, name, func);
|
||||
CompilerAddGlobal(c, name, func);
|
||||
}
|
||||
|
||||
/* Compile interface. Returns a function that evaluates the
|
||||
@ -1175,8 +1224,12 @@ Func * CompilerCompile(Compiler * c, Value form) {
|
||||
uint32_t envSize = c->env->count;
|
||||
FuncEnv * env = VMAlloc(c->vm, sizeof(FuncEnv));
|
||||
Func * func = VMAlloc(c->vm, sizeof(Func));
|
||||
env->values = VMAlloc(c->vm, sizeof(Value) * envSize);
|
||||
memcpy(env->values, c->env->data, envSize * sizeof(Value));
|
||||
if (envSize) {
|
||||
env->values = VMAlloc(c->vm, sizeof(Value) * envSize);
|
||||
memcpy(env->values, c->env->data, envSize * sizeof(Value));
|
||||
} else {
|
||||
env->values = NULL;
|
||||
}
|
||||
env->stackOffset = envSize;
|
||||
env->thread = NULL;
|
||||
func->parent = NULL;
|
||||
@ -1201,7 +1254,7 @@ int CompileMacroExpand(VM * vm, Value x, Dictionary * macros, Value * out) {
|
||||
VMLoad(vm, macroFn);
|
||||
if (VMStart(vm)) {
|
||||
/* We encountered an error during parsing */
|
||||
return 1;;
|
||||
return 1;
|
||||
} else {
|
||||
x = vm->ret;
|
||||
}
|
||||
|
16
datatypes.h
16
datatypes.h
@ -43,6 +43,7 @@ typedef struct Parser Parser;
|
||||
typedef struct ParseState ParseState;
|
||||
typedef struct Scope Scope;
|
||||
typedef struct Compiler Compiler;
|
||||
typedef struct StackFrame StackFrame;
|
||||
|
||||
union ValueData {
|
||||
Boolean boolean;
|
||||
@ -116,6 +117,15 @@ struct DictBucket {
|
||||
DictBucket * next;
|
||||
};
|
||||
|
||||
struct StackFrame {
|
||||
Value callee;
|
||||
uint16_t size;
|
||||
uint16_t prevSize;
|
||||
uint16_t ret;
|
||||
FuncEnv * env;
|
||||
uint16_t * pc;
|
||||
};
|
||||
|
||||
struct VM {
|
||||
/* Garbage collection */
|
||||
void * blocks;
|
||||
@ -127,7 +137,7 @@ struct VM {
|
||||
uint16_t * pc;
|
||||
Array * thread;
|
||||
Value * base;
|
||||
Value root; /* Global state - prevents GC cleanup */
|
||||
StackFrame * frame;
|
||||
/* Return state */
|
||||
const char * error;
|
||||
jmp_buf jump;
|
||||
@ -206,7 +216,7 @@ enum OpCode {
|
||||
VM_OP_DVM, /* 0x001f */
|
||||
VM_OP_RTN, /* 0x0020 */
|
||||
VM_OP_SET, /* 0x0021 */
|
||||
VM_OP_GET, /* 0x0022 */
|
||||
VM_OP_GET /* 0x0022 */
|
||||
};
|
||||
|
||||
#endif /* end of include guard: DATATYPES_H_PJJ035NT */
|
||||
#endif
|
||||
|
8
disasm.c
8
disasm.c
@ -67,7 +67,7 @@ void dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
|
||||
uint16_t *current = byteCode;
|
||||
uint16_t *end = byteCode + len;
|
||||
|
||||
fprintf(out, "----- ASM BYTECODE AT %p -----\n", byteCode);
|
||||
fprintf(out, "----- ASM BYTECODE START -----\n");
|
||||
|
||||
while (current < end) {
|
||||
switch (*current) {
|
||||
@ -196,6 +196,12 @@ void dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
|
||||
case VM_OP_RTN:
|
||||
current += dasmPrintFixedOp(out, current, "returnNil", 0);
|
||||
break;
|
||||
case VM_OP_GET:
|
||||
current += dasmPrintFixedOp(out, current, "get", 3);
|
||||
break;
|
||||
case VM_OP_SET:
|
||||
current += dasmPrintFixedOp(out, current, "set", 3);
|
||||
break;
|
||||
}
|
||||
fprintf(out, "\n");
|
||||
}
|
||||
|
2
ds.h
2
ds.h
@ -29,7 +29,7 @@ uint8_t * BufferToString(VM * vm, Buffer * buffer);
|
||||
#define BufferDefine(name, type) \
|
||||
static void BufferPush##name (VM * vm, Buffer * buffer, type x) { \
|
||||
union { type t; uint8_t bytes[sizeof(type)]; } u; \
|
||||
u.t = x; return BufferAppendData(vm, buffer, u.bytes, sizeof(type)); \
|
||||
u.t = x; BufferAppendData(vm, buffer, u.bytes, sizeof(type)); \
|
||||
}
|
||||
|
||||
/****/
|
||||
|
8
main.c
8
main.c
@ -37,7 +37,7 @@ void debugRepl() {
|
||||
for (;;) {
|
||||
|
||||
/* Run garbage collection */
|
||||
VMMaybeCollect(&vm);
|
||||
/* VMMaybeCollect(&vm);*/
|
||||
|
||||
/* Reset state */
|
||||
ParserInit(&p, &vm);
|
||||
@ -85,6 +85,11 @@ void debugRepl() {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Print asm */
|
||||
printf("\n");
|
||||
dasmFunc(stdout, func.data.func);
|
||||
printf("\n");
|
||||
|
||||
/* Execute function */
|
||||
VMLoad(&vm, func);
|
||||
if (VMStart(&vm)) {
|
||||
@ -103,4 +108,5 @@ void debugRepl() {
|
||||
int main() {
|
||||
printf("Super cool interpreter v0.0\n");
|
||||
debugRepl();
|
||||
return 0;
|
||||
}
|
||||
|
2
parse.c
2
parse.c
@ -130,7 +130,7 @@ static int isWhitespace(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 >= '0' && c <= '9') return 1;
|
||||
if (c >= '0' && c <= ':') return 1;
|
||||
if (c >= '<' && c <= '@') return 1;
|
||||
if (c >= '*' && c <= '/') return 1;
|
||||
if (c >= '#' && c <= '&') return 1;
|
||||
|
142
value.c
142
value.c
@ -148,26 +148,26 @@ uint8_t * ValueToString(VM * vm, Value x) {
|
||||
case TYPE_NUMBER:
|
||||
return NumberToString(vm, x.data.number);
|
||||
case TYPE_ARRAY:
|
||||
return StringDescription(vm, "array", 5, x.data.array);
|
||||
return StringDescription(vm, "array", 5, x.data.pointer);
|
||||
case TYPE_FORM:
|
||||
return StringDescription(vm, "form", 4, x.data.array);
|
||||
return StringDescription(vm, "form", 4, x.data.pointer);
|
||||
case TYPE_STRING:
|
||||
case TYPE_SYMBOL:
|
||||
return x.data.string;
|
||||
case TYPE_BYTEBUFFER:
|
||||
return StringDescription(vm, "buffer", 6, x.data.buffer);
|
||||
return StringDescription(vm, "buffer", 6, x.data.pointer);
|
||||
case TYPE_CFUNCTION:
|
||||
return StringDescription(vm, "cfunction", 9, x.data.cfunction);
|
||||
return StringDescription(vm, "cfunction", 9, x.data.pointer);
|
||||
case TYPE_FUNCTION:
|
||||
return StringDescription(vm, "function", 8, x.data.func);
|
||||
return StringDescription(vm, "function", 8, x.data.pointer);
|
||||
case TYPE_DICTIONARY:
|
||||
return StringDescription(vm, "dictionary", 10, x.data.dict);
|
||||
return StringDescription(vm, "dictionary", 10, x.data.pointer);
|
||||
case TYPE_FUNCDEF:
|
||||
return StringDescription(vm, "funcdef", 7, x.data.funcdef);
|
||||
return StringDescription(vm, "funcdef", 7, x.data.pointer);
|
||||
case TYPE_FUNCENV:
|
||||
return StringDescription(vm, "funcenv", 7, x.data.funcenv);
|
||||
return StringDescription(vm, "funcenv", 7, x.data.pointer);
|
||||
case TYPE_THREAD:
|
||||
return StringDescription(vm, "thread", 6, x.data.array);
|
||||
return StringDescription(vm, "thread", 6, x.data.pointer);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
@ -352,23 +352,115 @@ int ValueCompare(Value x, Value y) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Get a value out af an associated data structure. Can throw VM error. */
|
||||
Value ValueGet(VM * vm, Value ds, Value key) {
|
||||
switch (ds.type) {
|
||||
case TYPE_ARRAY:
|
||||
case TYPE_FORM:
|
||||
case TYPE_BYTEBUFFER:
|
||||
case TYPE_SYMBOL:
|
||||
case TYPE_STRING:
|
||||
case TYPE_DICTIONARY:
|
||||
case TYPE_FUNCENV:
|
||||
default:
|
||||
VMError(vm, "Cannot get.");
|
||||
break;
|
||||
/* Allow negative indexing to get from end of array like structure */
|
||||
/* This probably isn't very fast - look at Lua conversion function.
|
||||
* I would like to keep this standard C for as long as possible, though. */
|
||||
static int32_t ToIndex(Number raw, int64_t len) {
|
||||
int32_t toInt = raw;
|
||||
if ((Number) toInt == raw) {
|
||||
/* We were able to convert */
|
||||
if (toInt < 0) {
|
||||
/* Index from end */
|
||||
if (toInt < -len) return -1;
|
||||
return len + toInt;
|
||||
} else {
|
||||
/* Normal indexing */
|
||||
if (toInt >= len) return -1;
|
||||
return toInt;
|
||||
}
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set a value in an associative data structure. Can throw VM error. */
|
||||
int ValueSet(VM * vm, Value ds, Value key, Value value) {
|
||||
|
||||
/* Convert a number into a byte. */
|
||||
static uint8_t NumberToByte(Number raw) {
|
||||
if (raw > 255) return 255;
|
||||
if (raw < 0) return 0;
|
||||
return (uint8_t) raw;
|
||||
}
|
||||
|
||||
/* Get a value out af an associated data structure. Can throw VM error. */
|
||||
Value ValueGet(VM * vm, Value ds, Value key) {
|
||||
int32_t index;
|
||||
Value ret;
|
||||
switch (ds.type) {
|
||||
case TYPE_ARRAY:
|
||||
case TYPE_FORM:
|
||||
VMAssertType(vm, key, TYPE_NUMBER);
|
||||
index = ToIndex(key.data.number, ds.data.array->count);
|
||||
if (index == -1) VMError(vm, "Invalid array access");
|
||||
return ds.data.array->data[index];
|
||||
case TYPE_BYTEBUFFER:
|
||||
VMAssertType(vm, key, TYPE_NUMBER);
|
||||
index = ToIndex(key.data.number, ds.data.buffer->count);
|
||||
if (index == -1) VMError(vm, "Invalid buffer access");
|
||||
ret.type = TYPE_NUMBER;
|
||||
ret.data.number = ds.data.buffer->data[index];
|
||||
break;
|
||||
case TYPE_SYMBOL:
|
||||
case TYPE_STRING:
|
||||
VMAssertType(vm, key, TYPE_NUMBER);
|
||||
index = ToIndex(key.data.number, VStringSize(ds.data.string));
|
||||
if (index == -1) VMError(vm, "Invalid string access");
|
||||
ret.type = TYPE_NUMBER;
|
||||
ret.data.number = ds.data.string[index];
|
||||
break;
|
||||
case TYPE_DICTIONARY:
|
||||
return DictGet(ds.data.dict, key);
|
||||
case TYPE_FUNCENV:
|
||||
VMAssertType(vm, key, TYPE_NUMBER);
|
||||
if (ds.data.funcenv->thread) {
|
||||
Array * thread = ds.data.funcenv->thread;
|
||||
index = ToIndex(key.data.number, vm->frame->size);
|
||||
if (index == -1) VMError(vm, "Invalid funcenv access");
|
||||
return thread->data[thread->count + index];
|
||||
} else {
|
||||
index = ToIndex(key.data.number, ds.data.funcenv->stackOffset);
|
||||
if (index == -1) VMError(vm, "Invalid funcenv access");
|
||||
return ds.data.funcenv->values[index];
|
||||
}
|
||||
default:
|
||||
VMError(vm, "Cannot get.");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Set a value in an associative data structure. Can throw VM error. */
|
||||
void ValueSet(VM * vm, Value ds, Value key, Value value) {
|
||||
int32_t index;
|
||||
switch (ds.type) {
|
||||
case TYPE_ARRAY:
|
||||
case TYPE_FORM:
|
||||
VMAssertType(vm, key, TYPE_NUMBER);
|
||||
index = ToIndex(key.data.number, ds.data.array->count);
|
||||
if (index == -1) VMError(vm, "Invalid array access");
|
||||
ds.data.array->data[index] = value;
|
||||
break;
|
||||
case TYPE_BYTEBUFFER:
|
||||
VMAssertType(vm, key, TYPE_NUMBER);
|
||||
VMAssertType(vm, value, TYPE_NUMBER);
|
||||
index = ToIndex(key.data.number, ds.data.buffer->count);
|
||||
if (index == -1) VMError(vm, "Invalid buffer access");
|
||||
ds.data.buffer->data[index] = NumberToByte(value.data.number);
|
||||
break;
|
||||
case TYPE_DICTIONARY:
|
||||
DictPut(vm, ds.data.dict, key, value);
|
||||
break;
|
||||
case TYPE_FUNCENV:
|
||||
VMAssertType(vm, key, TYPE_NUMBER);
|
||||
if (ds.data.funcenv->thread) {
|
||||
Array * thread = ds.data.funcenv->thread;
|
||||
index = ToIndex(key.data.number, vm->frame->size);
|
||||
if (index == -1) VMError(vm, "Invalid funcenv access");
|
||||
thread->data[thread->count + index] = value;
|
||||
} else {
|
||||
index = ToIndex(key.data.number, ds.data.funcenv->stackOffset);
|
||||
if (index == -1) VMError(vm, "Invalid funcenv access");
|
||||
ds.data.funcenv->values[index] = value;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
VMError(vm, "Cannot set.");
|
||||
}
|
||||
}
|
||||
|
2
value.h
2
value.h
@ -11,7 +11,7 @@ int ValueEqual(Value x, Value y);
|
||||
|
||||
Value ValueGet(VM * vm, Value ds, Value key);
|
||||
|
||||
int ValueSet(VM * vm, Value ds, Value key, Value value);
|
||||
void ValueSet(VM * vm, Value ds, Value key, Value value);
|
||||
|
||||
Value ValueLoadCString(VM * vm, const char * string);
|
||||
|
||||
|
184
vm.c
184
vm.c
@ -11,17 +11,6 @@ static const char EXPECTED_FUNCTION[] = "Expected function";
|
||||
static const char VMS_EXPECTED_NUMBER_ROP[] = "Expected right operand to be number";
|
||||
static const char VMS_EXPECTED_NUMBER_LOP[] = "Expected left operand to be number";
|
||||
|
||||
/* The stack frame data */
|
||||
typedef struct StackFrame StackFrame;
|
||||
struct StackFrame {
|
||||
Value callee;
|
||||
uint16_t size;
|
||||
uint16_t prevSize;
|
||||
uint16_t ret;
|
||||
FuncEnv * env;
|
||||
uint16_t * pc;
|
||||
};
|
||||
|
||||
/* The size of a StackFrame in units of Values. */
|
||||
#define FRAME_SIZE ((sizeof(StackFrame) + sizeof(Value) - 1) / sizeof(Value))
|
||||
|
||||
@ -37,7 +26,7 @@ static StackFrame * ThreadFrame(Array * thread) {
|
||||
typedef struct GCMemoryHeader GCMemoryHeader;
|
||||
struct GCMemoryHeader {
|
||||
GCMemoryHeader * next;
|
||||
uint32_t color;
|
||||
uint32_t color : 1;
|
||||
};
|
||||
|
||||
/* Forward declaration */
|
||||
@ -52,17 +41,43 @@ static void VMMarkFuncEnv(VM * vm, FuncEnv * env) {
|
||||
temp.type = TYPE_THREAD;
|
||||
temp.data.array = env->thread;
|
||||
VMMark(vm, &temp);
|
||||
} else {
|
||||
} else if (env->values) {
|
||||
uint32_t count = env->stackOffset;
|
||||
uint32_t i;
|
||||
GCHeader(env->values)->color = vm->black;
|
||||
for (i = 0; i < count; ++i) {
|
||||
for (i = 0; i < count; ++i)
|
||||
VMMark(vm, env->values + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* GC helper to mark a FuncDef */
|
||||
static void VMMarkFuncDef(VM * vm, FuncDef * def) {
|
||||
if (GCHeader(def)->color != vm->black) {
|
||||
GCHeader(def)->color = vm->black;
|
||||
GCHeader(def->byteCode)->color = vm->black;
|
||||
uint32_t count, i;
|
||||
if (def->literals) {
|
||||
count = def->literalsLen;
|
||||
GCHeader(def->literals)->color = vm->black;
|
||||
for (i = 0; i < count; ++i)
|
||||
VMMark(vm, def->literals + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper to mark a stack frame. Returns the next frame. */
|
||||
static StackFrame * VMMarkStackFrame(VM * vm, StackFrame * frame) {
|
||||
uint32_t i;
|
||||
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);
|
||||
return (StackFrame *)(stack + frame->size);
|
||||
}
|
||||
|
||||
/* Mark allocated memory associated with a value. This is
|
||||
* the main function for doing garbage collection. */
|
||||
static void VMMark(VM * vm, Value * x) {
|
||||
@ -97,22 +112,13 @@ static void VMMark(VM * vm, Value * x) {
|
||||
|
||||
case TYPE_THREAD:
|
||||
if (GCHeader(x->data.array)->color != vm->black) {
|
||||
uint32_t i;
|
||||
Array * thread = x->data.array;
|
||||
StackFrame * frame = (StackFrame *)thread->data;
|
||||
StackFrame * end = (StackFrame *)(thread->data + thread->count - FRAME_SIZE);
|
||||
StackFrame * end = ThreadFrame(thread);
|
||||
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)
|
||||
frame = VMMarkStackFrame(vm, frame);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -121,16 +127,12 @@ static void VMMark(VM * vm, Value * x) {
|
||||
Func * f = x->data.func;
|
||||
GCHeader(f)->color = vm->black;
|
||||
VMMarkFuncEnv(vm, f->env);
|
||||
{
|
||||
VMMarkFuncDef(vm, f->def);
|
||||
if (f->parent) {
|
||||
Value temp;
|
||||
temp.type = TYPE_FUNCDEF;
|
||||
temp.data.funcdef = x->data.funcdef;
|
||||
temp.type = TYPE_FUNCTION;
|
||||
temp.data.func = f->parent;
|
||||
VMMark(vm, &temp);
|
||||
if (f->parent) {
|
||||
temp.type = TYPE_FUNCTION;
|
||||
temp.data.func = f->parent;
|
||||
VMMark(vm, &temp);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -151,16 +153,7 @@ static void VMMark(VM * vm, Value * x) {
|
||||
break;
|
||||
|
||||
case TYPE_FUNCDEF:
|
||||
if (GCHeader(x->data.funcdef)->color != vm->black) {
|
||||
GCHeader(x->data.funcdef->byteCode)->color = vm->black;
|
||||
uint32_t count, i;
|
||||
count = x->data.funcdef->literalsLen;
|
||||
if (x->data.funcdef->literals) {
|
||||
GCHeader(x->data.funcdef->literals)->color = vm->black;
|
||||
for (i = 0; i < count; ++i)
|
||||
VMMark(vm, x->data.funcdef->literals + i);
|
||||
}
|
||||
}
|
||||
VMMarkFuncDef(vm, x->data.funcdef);
|
||||
break;
|
||||
|
||||
case TYPE_FUNCENV:
|
||||
@ -223,14 +216,11 @@ void * VMZalloc(VM * vm, uint32_t size) {
|
||||
void VMCollect(VM * vm) {
|
||||
if (vm->lock > 0) return;
|
||||
/* Thread can be null */
|
||||
if (vm->thread) {
|
||||
Value thread;
|
||||
thread.type = TYPE_THREAD;
|
||||
thread.data.array = vm->thread;
|
||||
VMMark(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;
|
||||
}
|
||||
@ -259,22 +249,23 @@ static void VMThreadPush(VM * vm, Array * thread, Value callee, uint32_t size) {
|
||||
* the garabage collector */
|
||||
for (i = nextCount; i < nextCount + size; ++i)
|
||||
thread->data[i].type = TYPE_NIL;
|
||||
frame = ThreadFrame(thread);
|
||||
vm->base = thread->data + thread->count;
|
||||
vm->frame = frame = (StackFrame *)(vm->base - FRAME_SIZE);
|
||||
/* Set up the new stack frame */
|
||||
frame->prevSize = oldSize;
|
||||
frame->size = size;
|
||||
frame->env = NULL;
|
||||
frame->callee = callee;
|
||||
vm->base = thread->data + thread->count;
|
||||
}
|
||||
|
||||
/* Copy the current function stack to the current closure
|
||||
environment */
|
||||
static void VMThreadSplitStack(VM * vm, Array * thread) {
|
||||
StackFrame * frame = ThreadFrame(thread);
|
||||
static void VMThreadSplitStack(VM * vm) {
|
||||
StackFrame * frame = vm->frame;
|
||||
FuncEnv * env = frame->env;
|
||||
/* Check for closures */
|
||||
if (env) {
|
||||
Array * thread = vm->thread;
|
||||
uint32_t size = frame->size;
|
||||
env->thread = NULL;
|
||||
env->stackOffset = size;
|
||||
@ -284,16 +275,18 @@ static void VMThreadSplitStack(VM * vm, Array * thread) {
|
||||
}
|
||||
|
||||
/* Pop the top-most stack frame from stack */
|
||||
static void VMThreadPop(VM * vm, Array * thread) {
|
||||
StackFrame * frame = ThreadFrame(thread);
|
||||
static void VMThreadPop(VM * vm) {
|
||||
Array * thread = vm->thread;
|
||||
StackFrame * frame = vm->frame;
|
||||
uint32_t delta = FRAME_SIZE + frame->prevSize;
|
||||
if (thread->count) {
|
||||
VMThreadSplitStack(vm, thread);
|
||||
VMThreadSplitStack(vm);
|
||||
} else {
|
||||
VMError(vm, "Nothing to pop from stack.");
|
||||
}
|
||||
thread->count -= delta;
|
||||
vm->base -= delta;
|
||||
vm->frame = (StackFrame *)(vm->base - FRAME_SIZE);
|
||||
}
|
||||
|
||||
/* Get an upvalue */
|
||||
@ -329,28 +322,24 @@ static int truthy(Value v) {
|
||||
|
||||
/* Return from the vm */
|
||||
static void VMReturn(VM * vm, Value ret) {
|
||||
Array * thread = vm->thread;
|
||||
StackFrame * frame = ThreadFrame(thread);
|
||||
VMThreadPop(vm, thread);
|
||||
if (thread->count == 0) {
|
||||
VMThreadPop(vm);
|
||||
if (vm->thread->count == 0) {
|
||||
VMExit(vm, ret);
|
||||
}
|
||||
frame = ThreadFrame(thread);
|
||||
vm->pc = frame->pc;
|
||||
vm->base[frame->ret] = ret;
|
||||
vm->pc = vm->frame->pc;
|
||||
vm->base[vm->frame->ret] = ret;
|
||||
}
|
||||
|
||||
/* Implementation of the opcode for function calls */
|
||||
static void VMCallOp(VM * vm) {
|
||||
Array * thread = vm->thread;
|
||||
StackFrame * frame = ThreadFrame(thread);
|
||||
Value callee = vm->base[vm->pc[1]];
|
||||
uint32_t arity = vm->pc[3];
|
||||
uint32_t oldCount = thread->count;
|
||||
uint32_t i;
|
||||
Value * oldBase;
|
||||
frame->pc = vm->pc + 4 + arity;
|
||||
frame->ret = vm->pc[2];
|
||||
vm->frame->pc = vm->pc + 4 + arity;
|
||||
vm->frame->ret = vm->pc[2];
|
||||
if (callee.type == TYPE_FUNCTION) {
|
||||
Func * fn = callee.data.func;
|
||||
VMThreadPush(vm, thread, callee, fn->def->locals);
|
||||
@ -375,19 +364,17 @@ static void VMCallOp(VM * vm) {
|
||||
vm->base[i].type = TYPE_NIL;
|
||||
vm->pc = f->def->byteCode;
|
||||
}
|
||||
VMMaybeCollect(vm);
|
||||
}
|
||||
|
||||
/* Implementation of the opcode for tail calls */
|
||||
static void VMTailCallOp(VM * vm) {
|
||||
Array * thread = vm->thread;
|
||||
StackFrame * frame = ThreadFrame(thread);
|
||||
Value callee = vm->base[vm->pc[1]];
|
||||
uint32_t arity = vm->pc[2];
|
||||
uint16_t newFrameSize, currentFrameSize;
|
||||
uint32_t i;
|
||||
/* Check for closures */
|
||||
VMThreadSplitStack(vm, thread);
|
||||
VMThreadSplitStack(vm);
|
||||
if (callee.type == TYPE_CFUNCTION) {
|
||||
newFrameSize = arity;
|
||||
} else if (callee.type == TYPE_FUNCTION) {
|
||||
@ -397,9 +384,8 @@ static void VMTailCallOp(VM * vm) {
|
||||
VMError(vm, EXPECTED_FUNCTION);
|
||||
}
|
||||
/* Ensure stack has enough space for copies of arguments */
|
||||
currentFrameSize = frame->size;
|
||||
currentFrameSize = vm->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) {
|
||||
@ -412,9 +398,9 @@ static void VMTailCallOp(VM * vm) {
|
||||
vm->base[i].type = TYPE_NIL;
|
||||
}
|
||||
/* Update the stack frame */
|
||||
frame->size = newFrameSize;
|
||||
frame->callee = callee;
|
||||
frame->env = NULL;
|
||||
vm->frame->size = newFrameSize;
|
||||
vm->frame->callee = callee;
|
||||
vm->frame->env = NULL;
|
||||
if (callee.type == TYPE_CFUNCTION) {
|
||||
++vm->lock;
|
||||
VMReturn(vm, callee.data.cfunction(vm));
|
||||
@ -423,27 +409,25 @@ static void VMTailCallOp(VM * vm) {
|
||||
Func * f = callee.data.func;
|
||||
vm->pc = f->def->byteCode;
|
||||
}
|
||||
VMMaybeCollect(vm);
|
||||
}
|
||||
|
||||
/* Instantiate a closure */
|
||||
static Value VMMakeClosure(VM * vm, uint16_t literal) {
|
||||
Array * thread = vm->thread;
|
||||
StackFrame * frame = ThreadFrame(thread);
|
||||
if (frame->callee.type != TYPE_FUNCTION) {
|
||||
if (vm->frame->callee.type != TYPE_FUNCTION) {
|
||||
VMError(vm, EXPECTED_FUNCTION);
|
||||
} else {
|
||||
Value constant, ret;
|
||||
Func * fn, * current;
|
||||
FuncEnv * env = frame->env;
|
||||
FuncEnv * env = vm->frame->env;
|
||||
if (!env) {
|
||||
env = VMAlloc(vm, sizeof(FuncEnv));
|
||||
env->thread = thread;
|
||||
env->stackOffset = thread->count;
|
||||
env->values = NULL;
|
||||
frame->env = env;
|
||||
vm->frame->env = env;
|
||||
}
|
||||
current = frame->callee.data.func;
|
||||
current = vm->frame->callee.data.func;
|
||||
constant = LoadConstant(vm, current, literal);
|
||||
if (constant.type != TYPE_FUNCDEF) {
|
||||
VMError(vm, EXPECTED_FUNCTION);
|
||||
@ -500,10 +484,10 @@ int VMStart(VM * vm) {
|
||||
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
|
||||
|
||||
@ -556,7 +540,7 @@ int VMStart(VM * vm) {
|
||||
break;
|
||||
|
||||
case VM_OP_UPV: /* Load Up Value */
|
||||
temp = ThreadFrame(vm->thread)->callee;
|
||||
temp = vm->frame->callee;
|
||||
VMAssert(vm, temp.type == TYPE_FUNCTION, EXPECTED_FUNCTION);
|
||||
vm->base[vm->pc[1]] = *GetUpValue(vm, temp.data.func, vm->pc[2], vm->pc[3]);
|
||||
vm->pc += 4;
|
||||
@ -583,14 +567,14 @@ int VMStart(VM * vm) {
|
||||
break;
|
||||
|
||||
case VM_OP_SUV: /* Set Up Value */
|
||||
temp = ThreadFrame(vm->thread)->callee;
|
||||
temp = vm->frame->callee;
|
||||
VMAssert(vm, temp.type == TYPE_FUNCTION, EXPECTED_FUNCTION);
|
||||
*GetUpValue(vm, temp.data.func, vm->pc[2], vm->pc[3]) = vm->base[vm->pc[1]];
|
||||
vm->pc += 4;
|
||||
break;
|
||||
|
||||
case VM_OP_CST: /* Load constant value */
|
||||
temp = ThreadFrame(vm->thread)->callee;
|
||||
temp = vm->frame->callee;
|
||||
VMAssert(vm, temp.type == TYPE_FUNCTION, EXPECTED_FUNCTION);
|
||||
vm->base[vm->pc[1]] = LoadConstant(vm, temp.data.func, vm->pc[2]);
|
||||
vm->pc += 3;
|
||||
@ -653,7 +637,6 @@ int VMStart(VM * vm) {
|
||||
temp.data.array = array;
|
||||
vm->base[vm->pc[1]] = temp;
|
||||
vm->pc += 3 + arrayLen;
|
||||
VMMaybeCollect(vm);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -661,7 +644,7 @@ int VMStart(VM * vm) {
|
||||
{
|
||||
uint32_t i = 3;
|
||||
uint32_t kvs = vm->pc[2];
|
||||
Dictionary * dict = DictNew(vm, kvs);
|
||||
Dictionary * dict = DictNew(vm, kvs + 2);
|
||||
kvs = kvs + 3;
|
||||
while (i < kvs) {
|
||||
v1 = vm->base[vm->pc[i++]];
|
||||
@ -672,7 +655,6 @@ int VMStart(VM * vm) {
|
||||
temp.data.dict = dict;
|
||||
vm->base[vm->pc[1]] = temp;
|
||||
vm->pc += kvs;
|
||||
VMMaybeCollect(vm);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -718,40 +700,48 @@ int VMStart(VM * vm) {
|
||||
break;
|
||||
|
||||
case VM_OP_GET:
|
||||
temp = ValueGet(vm, vm->base[vm->pc[2]], vm->base[vm->pc[3]]);
|
||||
vm->base[vm->pc[1]] = temp;
|
||||
vm->pc += 4;
|
||||
break;
|
||||
|
||||
case VM_OP_SET:
|
||||
ValueSet(vm, vm->base[vm->pc[1]], vm->base[vm->pc[2]], vm->base[vm->pc[3]]);
|
||||
vm->pc += 4;
|
||||
break;
|
||||
|
||||
default:
|
||||
VMError(vm, "Unknown opcode");
|
||||
break;
|
||||
}
|
||||
VMMaybeCollect(vm);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get an argument from the stack */
|
||||
Value VMGetArg(VM * vm, uint16_t index) {
|
||||
uint16_t frameSize = ThreadFrame(vm->thread)->size;
|
||||
uint16_t frameSize = vm->frame->size;
|
||||
VMAssert(vm, frameSize > index, "Cannot get arg out of stack bounds");
|
||||
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;
|
||||
uint16_t frameSize = vm->frame->size;
|
||||
VMAssert(vm, frameSize > index, "Cannot set arg out of stack bounds");
|
||||
vm->base[index] = x;
|
||||
}
|
||||
|
||||
/* Get the size of the VMStack */
|
||||
uint16_t VMCountArgs(VM * vm) {
|
||||
return ThreadFrame(vm->thread)->size;
|
||||
return vm->frame->size;
|
||||
}
|
||||
|
||||
/* Initialize the VM */
|
||||
void VMInit(VM * vm) {
|
||||
vm->ret.type = TYPE_NIL;
|
||||
vm->root.type = TYPE_NIL;
|
||||
vm->base = NULL;
|
||||
vm->frame = NULL;
|
||||
vm->pc = NULL;
|
||||
vm->error = NULL;
|
||||
/* Garbage collection */
|
||||
@ -760,8 +750,8 @@ void VMInit(VM * vm) {
|
||||
vm->memoryInterval = 0;
|
||||
vm->black = 0;
|
||||
vm->lock = 0;
|
||||
/* Set to empty thread */
|
||||
vm->thread = NULL;
|
||||
/* Add thread */
|
||||
vm->thread = ArrayNew(vm, 20);
|
||||
}
|
||||
|
||||
/* Load a function into the VM. The function will be called with
|
||||
|
4
vm.h
4
vm.h
@ -17,8 +17,8 @@
|
||||
{ if (!(cond)) { VMError((vm), (e)); } } while (0)
|
||||
|
||||
/* Type assertion */
|
||||
#define VMAssertType(vm, f, type) \
|
||||
VMAssert(vm, (f).type == (type), "Expected type " type)
|
||||
#define VMAssertType(vm, f, t) \
|
||||
VMAssert((vm), (f).type == (t), "Expected type,")
|
||||
|
||||
/* Initialize the VM */
|
||||
void VMInit(VM * vm);
|
||||
|
Loading…
Reference in New Issue
Block a user