mirror of
https://github.com/janet-lang/janet
synced 2024-11-24 09:17:17 +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
|
# TIL
|
||||||
|
|
||||||
CFLAGS=-std=c99 -Wall -Wextra -g
|
CFLAGS=-std=c99 -Wall -Wextra -Wpedantic -g
|
||||||
|
|
||||||
TARGET=interp
|
TARGET=interp
|
||||||
PREFIX=/usr/local
|
PREFIX=/usr/local
|
||||||
|
77
compile.c
77
compile.c
@ -87,11 +87,11 @@ static FormOptions FormOptionsDefault() {
|
|||||||
/* Create some helpers that allows us to push more than just raw bytes
|
/* 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
|
* to the byte buffer. This helps us create the byte code for the compiled
|
||||||
* functions. */
|
* functions. */
|
||||||
BufferDefine(UInt32, uint32_t);
|
BufferDefine(UInt32, uint32_t)
|
||||||
BufferDefine(Int32, int32_t);
|
BufferDefine(Int32, int32_t)
|
||||||
BufferDefine(Number, Number);
|
BufferDefine(Number, Number)
|
||||||
BufferDefine(UInt16, uint16_t);
|
BufferDefine(UInt16, uint16_t)
|
||||||
BufferDefine(Int16, int16_t);
|
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 */
|
||||||
@ -553,7 +553,7 @@ static Slot CompileArray(Compiler * c, FormOptions opts, Array * array) {
|
|||||||
* called with n arguments, the number of arguments is written
|
* called with n arguments, the number of arguments is written
|
||||||
* after the op code, followed by those arguments.
|
* 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
|
* effects. With this assumptions, if the result of the operator
|
||||||
* is unused, it's calculation can be ignored (the evaluation of
|
* is unused, it's calculation can be ignored (the evaluation of
|
||||||
* its argument is still carried out, but their results can
|
* 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 (form->count < 2) {
|
||||||
if (op0 < 0) {
|
if (op0 < 0) {
|
||||||
if (opn < 0) CError(c, "This operator does not take 0 arguments.");
|
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, opn);
|
||||||
BufferPushUInt16(c->vm, buffer, ret.index);
|
BufferPushUInt16(c->vm, buffer, ret.index);
|
||||||
BufferPushUInt16(c->vm, buffer, 0);
|
BufferPushUInt16(c->vm, buffer, 0);
|
||||||
@ -587,7 +587,7 @@ static Slot CompileOperator(Compiler * c, FormOptions opts, Array * form,
|
|||||||
} else if (form->count == 2) {
|
} else if (form->count == 2) {
|
||||||
if (op1 < 0) {
|
if (op1 < 0) {
|
||||||
if (opn < 0) CError(c, "This operator does not take 1 argument.");
|
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, opn);
|
||||||
BufferPushUInt16(c->vm, buffer, ret.index);
|
BufferPushUInt16(c->vm, buffer, ret.index);
|
||||||
BufferPushUInt16(c->vm, buffer, 1);
|
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) {
|
static Slot CompileNot(Compiler * c, FormOptions opts, Array * form) {
|
||||||
return CompileOperator(c, opts, form, VM_OP_FLS, VM_OP_NOT, -1, -1, 0);
|
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 */
|
/* Compile an assignment operation */
|
||||||
static Slot CompileAssign(Compiler * c, FormOptions opts, Value left, Value right) {
|
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 */
|
/* Assignment special */
|
||||||
static Slot CompileSet(Compiler * c, FormOptions opts, Array * form) {
|
static Slot CompileVar(Compiler * c, FormOptions opts, Array * form) {
|
||||||
if (form->count != 3)
|
if (form->count != 3)
|
||||||
CError(c, "Assignment expects 2 arguments");
|
CError(c, "Assignment expects 2 arguments");
|
||||||
return CompileAssign(c, opts, form->data[1], form->data[2]);
|
return CompileAssign(c, opts, form->data[1], form->data[2]);
|
||||||
@ -968,7 +1001,6 @@ static SpecialFormHelper GetSpecial(Array * form) {
|
|||||||
case '>': return CompileGreaterThan;
|
case '>': return CompileGreaterThan;
|
||||||
case '<': return CompileLessThan;
|
case '<': return CompileLessThan;
|
||||||
case '=': return CompileEquals;
|
case '=': return CompileEquals;
|
||||||
case '\'': return CompileQuote;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -991,6 +1023,14 @@ static SpecialFormHelper GetSpecial(Array * form) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'g':
|
||||||
|
{
|
||||||
|
if (VStringSize(name) == 3 &&
|
||||||
|
name[1] == 'e' &&
|
||||||
|
name[2] == 't') {
|
||||||
|
return CompileGet;
|
||||||
|
}
|
||||||
|
}
|
||||||
case 'd':
|
case 'd':
|
||||||
{
|
{
|
||||||
if (VStringSize(name) == 2 &&
|
if (VStringSize(name) == 2 &&
|
||||||
@ -1053,6 +1093,15 @@ static SpecialFormHelper GetSpecial(Array * form) {
|
|||||||
return CompileWhile;
|
return CompileWhile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
case ':':
|
||||||
|
{
|
||||||
|
if (VStringSize(name) == 2 &&
|
||||||
|
name[1] == '=') {
|
||||||
|
return CompileVar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1152,7 +1201,7 @@ 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);
|
CompilerAddGlobal(c, name, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compile interface. Returns a function that evaluates the
|
/* Compile interface. Returns a function that evaluates the
|
||||||
@ -1175,8 +1224,12 @@ Func * CompilerCompile(Compiler * c, Value form) {
|
|||||||
uint32_t envSize = c->env->count;
|
uint32_t envSize = c->env->count;
|
||||||
FuncEnv * env = VMAlloc(c->vm, sizeof(FuncEnv));
|
FuncEnv * env = VMAlloc(c->vm, sizeof(FuncEnv));
|
||||||
Func * func = VMAlloc(c->vm, sizeof(Func));
|
Func * func = VMAlloc(c->vm, sizeof(Func));
|
||||||
|
if (envSize) {
|
||||||
env->values = VMAlloc(c->vm, sizeof(Value) * envSize);
|
env->values = VMAlloc(c->vm, sizeof(Value) * envSize);
|
||||||
memcpy(env->values, c->env->data, envSize * sizeof(Value));
|
memcpy(env->values, c->env->data, envSize * sizeof(Value));
|
||||||
|
} else {
|
||||||
|
env->values = NULL;
|
||||||
|
}
|
||||||
env->stackOffset = envSize;
|
env->stackOffset = envSize;
|
||||||
env->thread = NULL;
|
env->thread = NULL;
|
||||||
func->parent = NULL;
|
func->parent = NULL;
|
||||||
@ -1201,7 +1254,7 @@ int CompileMacroExpand(VM * vm, Value x, Dictionary * macros, Value * out) {
|
|||||||
VMLoad(vm, macroFn);
|
VMLoad(vm, macroFn);
|
||||||
if (VMStart(vm)) {
|
if (VMStart(vm)) {
|
||||||
/* We encountered an error during parsing */
|
/* We encountered an error during parsing */
|
||||||
return 1;;
|
return 1;
|
||||||
} else {
|
} else {
|
||||||
x = vm->ret;
|
x = vm->ret;
|
||||||
}
|
}
|
||||||
|
16
datatypes.h
16
datatypes.h
@ -43,6 +43,7 @@ typedef struct Parser Parser;
|
|||||||
typedef struct ParseState ParseState;
|
typedef struct ParseState ParseState;
|
||||||
typedef struct Scope Scope;
|
typedef struct Scope Scope;
|
||||||
typedef struct Compiler Compiler;
|
typedef struct Compiler Compiler;
|
||||||
|
typedef struct StackFrame StackFrame;
|
||||||
|
|
||||||
union ValueData {
|
union ValueData {
|
||||||
Boolean boolean;
|
Boolean boolean;
|
||||||
@ -116,6 +117,15 @@ struct DictBucket {
|
|||||||
DictBucket * next;
|
DictBucket * next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct StackFrame {
|
||||||
|
Value callee;
|
||||||
|
uint16_t size;
|
||||||
|
uint16_t prevSize;
|
||||||
|
uint16_t ret;
|
||||||
|
FuncEnv * env;
|
||||||
|
uint16_t * pc;
|
||||||
|
};
|
||||||
|
|
||||||
struct VM {
|
struct VM {
|
||||||
/* Garbage collection */
|
/* Garbage collection */
|
||||||
void * blocks;
|
void * blocks;
|
||||||
@ -127,7 +137,7 @@ struct VM {
|
|||||||
uint16_t * pc;
|
uint16_t * pc;
|
||||||
Array * thread;
|
Array * thread;
|
||||||
Value * base;
|
Value * base;
|
||||||
Value root; /* Global state - prevents GC cleanup */
|
StackFrame * frame;
|
||||||
/* Return state */
|
/* Return state */
|
||||||
const char * error;
|
const char * error;
|
||||||
jmp_buf jump;
|
jmp_buf jump;
|
||||||
@ -206,7 +216,7 @@ enum OpCode {
|
|||||||
VM_OP_DVM, /* 0x001f */
|
VM_OP_DVM, /* 0x001f */
|
||||||
VM_OP_RTN, /* 0x0020 */
|
VM_OP_RTN, /* 0x0020 */
|
||||||
VM_OP_SET, /* 0x0021 */
|
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 *current = byteCode;
|
||||||
uint16_t *end = byteCode + len;
|
uint16_t *end = byteCode + len;
|
||||||
|
|
||||||
fprintf(out, "----- ASM BYTECODE AT %p -----\n", byteCode);
|
fprintf(out, "----- ASM BYTECODE START -----\n");
|
||||||
|
|
||||||
while (current < end) {
|
while (current < end) {
|
||||||
switch (*current) {
|
switch (*current) {
|
||||||
@ -196,6 +196,12 @@ void dasm(FILE * out, uint16_t *byteCode, uint32_t len) {
|
|||||||
case VM_OP_RTN:
|
case VM_OP_RTN:
|
||||||
current += dasmPrintFixedOp(out, current, "returnNil", 0);
|
current += dasmPrintFixedOp(out, current, "returnNil", 0);
|
||||||
break;
|
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");
|
fprintf(out, "\n");
|
||||||
}
|
}
|
||||||
|
2
ds.h
2
ds.h
@ -29,7 +29,7 @@ uint8_t * BufferToString(VM * vm, Buffer * buffer);
|
|||||||
#define BufferDefine(name, type) \
|
#define BufferDefine(name, type) \
|
||||||
static void BufferPush##name (VM * vm, Buffer * buffer, type x) { \
|
static void BufferPush##name (VM * vm, Buffer * buffer, type x) { \
|
||||||
union { type t; uint8_t bytes[sizeof(type)]; } u; \
|
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 (;;) {
|
for (;;) {
|
||||||
|
|
||||||
/* Run garbage collection */
|
/* Run garbage collection */
|
||||||
VMMaybeCollect(&vm);
|
/* VMMaybeCollect(&vm);*/
|
||||||
|
|
||||||
/* Reset state */
|
/* Reset state */
|
||||||
ParserInit(&p, &vm);
|
ParserInit(&p, &vm);
|
||||||
@ -85,6 +85,11 @@ void debugRepl() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Print asm */
|
||||||
|
printf("\n");
|
||||||
|
dasmFunc(stdout, func.data.func);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
/* Execute function */
|
/* Execute function */
|
||||||
VMLoad(&vm, func);
|
VMLoad(&vm, func);
|
||||||
if (VMStart(&vm)) {
|
if (VMStart(&vm)) {
|
||||||
@ -103,4 +108,5 @@ void debugRepl() {
|
|||||||
int main() {
|
int main() {
|
||||||
printf("Super cool interpreter v0.0\n");
|
printf("Super cool interpreter v0.0\n");
|
||||||
debugRepl();
|
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) {
|
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 <= ':') 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;
|
||||||
|
116
value.c
116
value.c
@ -148,26 +148,26 @@ uint8_t * ValueToString(VM * vm, Value x) {
|
|||||||
case TYPE_NUMBER:
|
case TYPE_NUMBER:
|
||||||
return NumberToString(vm, x.data.number);
|
return NumberToString(vm, x.data.number);
|
||||||
case TYPE_ARRAY:
|
case TYPE_ARRAY:
|
||||||
return StringDescription(vm, "array", 5, x.data.array);
|
return StringDescription(vm, "array", 5, x.data.pointer);
|
||||||
case TYPE_FORM:
|
case TYPE_FORM:
|
||||||
return StringDescription(vm, "form", 4, x.data.array);
|
return StringDescription(vm, "form", 4, x.data.pointer);
|
||||||
case TYPE_STRING:
|
case TYPE_STRING:
|
||||||
case TYPE_SYMBOL:
|
case TYPE_SYMBOL:
|
||||||
return x.data.string;
|
return x.data.string;
|
||||||
case TYPE_BYTEBUFFER:
|
case TYPE_BYTEBUFFER:
|
||||||
return StringDescription(vm, "buffer", 6, x.data.buffer);
|
return StringDescription(vm, "buffer", 6, x.data.pointer);
|
||||||
case TYPE_CFUNCTION:
|
case TYPE_CFUNCTION:
|
||||||
return StringDescription(vm, "cfunction", 9, x.data.cfunction);
|
return StringDescription(vm, "cfunction", 9, x.data.pointer);
|
||||||
case TYPE_FUNCTION:
|
case TYPE_FUNCTION:
|
||||||
return StringDescription(vm, "function", 8, x.data.func);
|
return StringDescription(vm, "function", 8, x.data.pointer);
|
||||||
case TYPE_DICTIONARY:
|
case TYPE_DICTIONARY:
|
||||||
return StringDescription(vm, "dictionary", 10, x.data.dict);
|
return StringDescription(vm, "dictionary", 10, x.data.pointer);
|
||||||
case TYPE_FUNCDEF:
|
case TYPE_FUNCDEF:
|
||||||
return StringDescription(vm, "funcdef", 7, x.data.funcdef);
|
return StringDescription(vm, "funcdef", 7, x.data.pointer);
|
||||||
case TYPE_FUNCENV:
|
case TYPE_FUNCENV:
|
||||||
return StringDescription(vm, "funcenv", 7, x.data.funcenv);
|
return StringDescription(vm, "funcenv", 7, x.data.pointer);
|
||||||
case TYPE_THREAD:
|
case TYPE_THREAD:
|
||||||
return StringDescription(vm, "thread", 6, x.data.array);
|
return StringDescription(vm, "thread", 6, x.data.pointer);
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -352,23 +352,115 @@ int ValueCompare(Value x, Value y) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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. */
|
/* Get a value out af an associated data structure. Can throw VM error. */
|
||||||
Value ValueGet(VM * vm, Value ds, Value key) {
|
Value ValueGet(VM * vm, Value ds, Value key) {
|
||||||
|
int32_t index;
|
||||||
|
Value ret;
|
||||||
switch (ds.type) {
|
switch (ds.type) {
|
||||||
case TYPE_ARRAY:
|
case TYPE_ARRAY:
|
||||||
case TYPE_FORM:
|
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:
|
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_SYMBOL:
|
||||||
case TYPE_STRING:
|
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:
|
case TYPE_DICTIONARY:
|
||||||
|
return DictGet(ds.data.dict, key);
|
||||||
case TYPE_FUNCENV:
|
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:
|
default:
|
||||||
VMError(vm, "Cannot get.");
|
VMError(vm, "Cannot get.");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set a value in an associative data structure. Can throw VM error. */
|
/* Set a value in an associative data structure. Can throw VM error. */
|
||||||
int ValueSet(VM * vm, Value ds, Value key, Value value) {
|
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);
|
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);
|
Value ValueLoadCString(VM * vm, const char * string);
|
||||||
|
|
||||||
|
166
vm.c
166
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_ROP[] = "Expected right operand to be number";
|
||||||
static const char VMS_EXPECTED_NUMBER_LOP[] = "Expected left 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. */
|
/* The size of a StackFrame in units of Values. */
|
||||||
#define FRAME_SIZE ((sizeof(StackFrame) + sizeof(Value) - 1) / sizeof(Value))
|
#define FRAME_SIZE ((sizeof(StackFrame) + sizeof(Value) - 1) / sizeof(Value))
|
||||||
|
|
||||||
@ -37,7 +26,7 @@ static StackFrame * ThreadFrame(Array * thread) {
|
|||||||
typedef struct GCMemoryHeader GCMemoryHeader;
|
typedef struct GCMemoryHeader GCMemoryHeader;
|
||||||
struct GCMemoryHeader {
|
struct GCMemoryHeader {
|
||||||
GCMemoryHeader * next;
|
GCMemoryHeader * next;
|
||||||
uint32_t color;
|
uint32_t color : 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Forward declaration */
|
/* Forward declaration */
|
||||||
@ -52,15 +41,41 @@ static void VMMarkFuncEnv(VM * vm, FuncEnv * env) {
|
|||||||
temp.type = TYPE_THREAD;
|
temp.type = TYPE_THREAD;
|
||||||
temp.data.array = env->thread;
|
temp.data.array = env->thread;
|
||||||
VMMark(vm, &temp);
|
VMMark(vm, &temp);
|
||||||
} else {
|
} else if (env->values) {
|
||||||
uint32_t count = env->stackOffset;
|
uint32_t count = env->stackOffset;
|
||||||
uint32_t i;
|
uint32_t i;
|
||||||
GCHeader(env->values)->color = vm->black;
|
GCHeader(env->values)->color = vm->black;
|
||||||
for (i = 0; i < count; ++i) {
|
for (i = 0; i < count; ++i)
|
||||||
VMMark(vm, env->values + 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
|
/* Mark allocated memory associated with a value. This is
|
||||||
@ -97,22 +112,13 @@ static void VMMark(VM * vm, Value * x) {
|
|||||||
|
|
||||||
case TYPE_THREAD:
|
case TYPE_THREAD:
|
||||||
if (GCHeader(x->data.array)->color != vm->black) {
|
if (GCHeader(x->data.array)->color != vm->black) {
|
||||||
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 = ThreadFrame(thread);
|
||||||
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;
|
frame = VMMarkStackFrame(vm, frame);
|
||||||
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;
|
break;
|
||||||
|
|
||||||
@ -121,18 +127,14 @@ static void VMMark(VM * vm, Value * x) {
|
|||||||
Func * f = x->data.func;
|
Func * f = x->data.func;
|
||||||
GCHeader(f)->color = vm->black;
|
GCHeader(f)->color = vm->black;
|
||||||
VMMarkFuncEnv(vm, f->env);
|
VMMarkFuncEnv(vm, f->env);
|
||||||
{
|
VMMarkFuncDef(vm, f->def);
|
||||||
Value temp;
|
|
||||||
temp.type = TYPE_FUNCDEF;
|
|
||||||
temp.data.funcdef = x->data.funcdef;
|
|
||||||
VMMark(vm, &temp);
|
|
||||||
if (f->parent) {
|
if (f->parent) {
|
||||||
|
Value temp;
|
||||||
temp.type = TYPE_FUNCTION;
|
temp.type = TYPE_FUNCTION;
|
||||||
temp.data.func = f->parent;
|
temp.data.func = f->parent;
|
||||||
VMMark(vm, &temp);
|
VMMark(vm, &temp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TYPE_DICTIONARY:
|
case TYPE_DICTIONARY:
|
||||||
@ -151,16 +153,7 @@ static void VMMark(VM * vm, Value * x) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case TYPE_FUNCDEF:
|
case TYPE_FUNCDEF:
|
||||||
if (GCHeader(x->data.funcdef)->color != vm->black) {
|
VMMarkFuncDef(vm, x->data.funcdef);
|
||||||
GCHeader(x->data.funcdef->byteCode)->color = vm->black;
|
|
||||||
uint32_t count, i;
|
|
||||||
count = x->data.funcdef->literalsLen;
|
|
||||||
if (x->data.funcdef->literals) {
|
|
||||||
GCHeader(x->data.funcdef->literals)->color = vm->black;
|
|
||||||
for (i = 0; i < count; ++i)
|
|
||||||
VMMark(vm, x->data.funcdef->literals + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TYPE_FUNCENV:
|
case TYPE_FUNCENV:
|
||||||
@ -223,14 +216,11 @@ void * VMZalloc(VM * vm, uint32_t size) {
|
|||||||
void VMCollect(VM * vm) {
|
void VMCollect(VM * vm) {
|
||||||
if (vm->lock > 0) return;
|
if (vm->lock > 0) return;
|
||||||
/* Thread can be null */
|
/* Thread can be null */
|
||||||
if (vm->thread) {
|
|
||||||
Value thread;
|
Value thread;
|
||||||
thread.type = TYPE_THREAD;
|
thread.type = TYPE_THREAD;
|
||||||
thread.data.array = vm->thread;
|
thread.data.array = vm->thread;
|
||||||
VMMark(vm, &thread);
|
VMMark(vm, &thread);
|
||||||
}
|
|
||||||
VMMark(vm, &vm->ret);
|
VMMark(vm, &vm->ret);
|
||||||
VMMark(vm, &vm->root);
|
|
||||||
VMSweep(vm);
|
VMSweep(vm);
|
||||||
vm->nextCollection = 0;
|
vm->nextCollection = 0;
|
||||||
}
|
}
|
||||||
@ -259,22 +249,23 @@ static void VMThreadPush(VM * vm, Array * thread, Value callee, uint32_t size) {
|
|||||||
* 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);
|
vm->base = thread->data + thread->count;
|
||||||
|
vm->frame = frame = (StackFrame *)(vm->base - FRAME_SIZE);
|
||||||
/* Set up the new stack frame */
|
/* Set up the new stack frame */
|
||||||
frame->prevSize = oldSize;
|
frame->prevSize = oldSize;
|
||||||
frame->size = size;
|
frame->size = size;
|
||||||
frame->env = NULL;
|
frame->env = NULL;
|
||||||
frame->callee = callee;
|
frame->callee = callee;
|
||||||
vm->base = thread->data + thread->count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copy the current function stack to the current closure
|
/* Copy the current function stack to the current closure
|
||||||
environment */
|
environment */
|
||||||
static void VMThreadSplitStack(VM * vm, Array * thread) {
|
static void VMThreadSplitStack(VM * vm) {
|
||||||
StackFrame * frame = ThreadFrame(thread);
|
StackFrame * frame = vm->frame;
|
||||||
FuncEnv * env = frame->env;
|
FuncEnv * env = frame->env;
|
||||||
/* Check for closures */
|
/* Check for closures */
|
||||||
if (env) {
|
if (env) {
|
||||||
|
Array * thread = vm->thread;
|
||||||
uint32_t size = frame->size;
|
uint32_t size = frame->size;
|
||||||
env->thread = NULL;
|
env->thread = NULL;
|
||||||
env->stackOffset = size;
|
env->stackOffset = size;
|
||||||
@ -284,16 +275,18 @@ static void VMThreadSplitStack(VM * vm, Array * thread) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 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) {
|
||||||
StackFrame * frame = ThreadFrame(thread);
|
Array * thread = vm->thread;
|
||||||
|
StackFrame * frame = vm->frame;
|
||||||
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);
|
||||||
} 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;
|
||||||
|
vm->frame = (StackFrame *)(vm->base - FRAME_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get an upvalue */
|
/* Get an upvalue */
|
||||||
@ -329,28 +322,24 @@ static int truthy(Value v) {
|
|||||||
|
|
||||||
/* Return from the vm */
|
/* Return from the vm */
|
||||||
static void VMReturn(VM * vm, Value ret) {
|
static void VMReturn(VM * vm, Value ret) {
|
||||||
Array * thread = vm->thread;
|
VMThreadPop(vm);
|
||||||
StackFrame * frame = ThreadFrame(thread);
|
if (vm->thread->count == 0) {
|
||||||
VMThreadPop(vm, thread);
|
|
||||||
if (thread->count == 0) {
|
|
||||||
VMExit(vm, ret);
|
VMExit(vm, ret);
|
||||||
}
|
}
|
||||||
frame = ThreadFrame(thread);
|
vm->pc = vm->frame->pc;
|
||||||
vm->pc = frame->pc;
|
vm->base[vm->frame->ret] = ret;
|
||||||
vm->base[frame->ret] = ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implementation of the opcode for function calls */
|
/* Implementation of the opcode for function calls */
|
||||||
static void VMCallOp(VM * vm) {
|
static void VMCallOp(VM * vm) {
|
||||||
Array * thread = vm->thread;
|
Array * thread = vm->thread;
|
||||||
StackFrame * frame = ThreadFrame(thread);
|
|
||||||
Value callee = vm->base[vm->pc[1]];
|
Value callee = vm->base[vm->pc[1]];
|
||||||
uint32_t arity = vm->pc[3];
|
uint32_t arity = vm->pc[3];
|
||||||
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;
|
vm->frame->pc = vm->pc + 4 + arity;
|
||||||
frame->ret = vm->pc[2];
|
vm->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;
|
||||||
VMThreadPush(vm, thread, callee, fn->def->locals);
|
VMThreadPush(vm, thread, callee, fn->def->locals);
|
||||||
@ -375,19 +364,17 @@ static void VMCallOp(VM * vm) {
|
|||||||
vm->base[i].type = TYPE_NIL;
|
vm->base[i].type = TYPE_NIL;
|
||||||
vm->pc = f->def->byteCode;
|
vm->pc = f->def->byteCode;
|
||||||
}
|
}
|
||||||
VMMaybeCollect(vm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Implementation of the opcode for tail calls */
|
/* Implementation of the opcode for tail calls */
|
||||||
static void VMTailCallOp(VM * vm) {
|
static void VMTailCallOp(VM * vm) {
|
||||||
Array * thread = vm->thread;
|
Array * thread = vm->thread;
|
||||||
StackFrame * frame = ThreadFrame(thread);
|
|
||||||
Value callee = vm->base[vm->pc[1]];
|
Value callee = vm->base[vm->pc[1]];
|
||||||
uint32_t arity = vm->pc[2];
|
uint32_t arity = vm->pc[2];
|
||||||
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);
|
||||||
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) {
|
||||||
@ -397,9 +384,8 @@ static void VMTailCallOp(VM * vm) {
|
|||||||
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 = vm->frame->size;
|
||||||
ArrayEnsure(vm, thread, thread->count + currentFrameSize + arity);
|
ArrayEnsure(vm, thread, thread->count + currentFrameSize + arity);
|
||||||
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) {
|
||||||
@ -412,9 +398,9 @@ static void VMTailCallOp(VM * vm) {
|
|||||||
vm->base[i].type = TYPE_NIL;
|
vm->base[i].type = TYPE_NIL;
|
||||||
}
|
}
|
||||||
/* Update the stack frame */
|
/* Update the stack frame */
|
||||||
frame->size = newFrameSize;
|
vm->frame->size = newFrameSize;
|
||||||
frame->callee = callee;
|
vm->frame->callee = callee;
|
||||||
frame->env = NULL;
|
vm->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));
|
||||||
@ -423,27 +409,25 @@ static void VMTailCallOp(VM * vm) {
|
|||||||
Func * f = callee.data.func;
|
Func * f = callee.data.func;
|
||||||
vm->pc = f->def->byteCode;
|
vm->pc = f->def->byteCode;
|
||||||
}
|
}
|
||||||
VMMaybeCollect(vm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Instantiate a closure */
|
/* Instantiate a closure */
|
||||||
static Value VMMakeClosure(VM * vm, uint16_t literal) {
|
static Value VMMakeClosure(VM * vm, uint16_t literal) {
|
||||||
Array * thread = vm->thread;
|
Array * thread = vm->thread;
|
||||||
StackFrame * frame = ThreadFrame(thread);
|
if (vm->frame->callee.type != TYPE_FUNCTION) {
|
||||||
if (frame->callee.type != TYPE_FUNCTION) {
|
|
||||||
VMError(vm, EXPECTED_FUNCTION);
|
VMError(vm, EXPECTED_FUNCTION);
|
||||||
} else {
|
} else {
|
||||||
Value constant, ret;
|
Value constant, ret;
|
||||||
Func * fn, * current;
|
Func * fn, * current;
|
||||||
FuncEnv * env = frame->env;
|
FuncEnv * env = vm->frame->env;
|
||||||
if (!env) {
|
if (!env) {
|
||||||
env = VMAlloc(vm, sizeof(FuncEnv));
|
env = VMAlloc(vm, sizeof(FuncEnv));
|
||||||
env->thread = thread;
|
env->thread = thread;
|
||||||
env->stackOffset = thread->count;
|
env->stackOffset = thread->count;
|
||||||
env->values = NULL;
|
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);
|
constant = LoadConstant(vm, current, literal);
|
||||||
if (constant.type != TYPE_FUNCDEF) {
|
if (constant.type != TYPE_FUNCDEF) {
|
||||||
VMError(vm, EXPECTED_FUNCTION);
|
VMError(vm, EXPECTED_FUNCTION);
|
||||||
@ -556,7 +540,7 @@ int VMStart(VM * vm) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case VM_OP_UPV: /* Load Up Value */
|
case VM_OP_UPV: /* Load Up Value */
|
||||||
temp = ThreadFrame(vm->thread)->callee;
|
temp = vm->frame->callee;
|
||||||
VMAssert(vm, temp.type == TYPE_FUNCTION, EXPECTED_FUNCTION);
|
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->base[vm->pc[1]] = *GetUpValue(vm, temp.data.func, vm->pc[2], vm->pc[3]);
|
||||||
vm->pc += 4;
|
vm->pc += 4;
|
||||||
@ -583,14 +567,14 @@ int VMStart(VM * vm) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case VM_OP_SUV: /* Set Up Value */
|
case VM_OP_SUV: /* Set Up Value */
|
||||||
temp = ThreadFrame(vm->thread)->callee;
|
temp = vm->frame->callee;
|
||||||
VMAssert(vm, temp.type == TYPE_FUNCTION, EXPECTED_FUNCTION);
|
VMAssert(vm, temp.type == TYPE_FUNCTION, EXPECTED_FUNCTION);
|
||||||
*GetUpValue(vm, temp.data.func, vm->pc[2], vm->pc[3]) = vm->base[vm->pc[1]];
|
*GetUpValue(vm, temp.data.func, vm->pc[2], vm->pc[3]) = vm->base[vm->pc[1]];
|
||||||
vm->pc += 4;
|
vm->pc += 4;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VM_OP_CST: /* Load constant value */
|
case VM_OP_CST: /* Load constant value */
|
||||||
temp = ThreadFrame(vm->thread)->callee;
|
temp = vm->frame->callee;
|
||||||
VMAssert(vm, temp.type == TYPE_FUNCTION, EXPECTED_FUNCTION);
|
VMAssert(vm, temp.type == TYPE_FUNCTION, EXPECTED_FUNCTION);
|
||||||
vm->base[vm->pc[1]] = LoadConstant(vm, temp.data.func, vm->pc[2]);
|
vm->base[vm->pc[1]] = LoadConstant(vm, temp.data.func, vm->pc[2]);
|
||||||
vm->pc += 3;
|
vm->pc += 3;
|
||||||
@ -653,7 +637,6 @@ int VMStart(VM * vm) {
|
|||||||
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);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -661,7 +644,7 @@ int VMStart(VM * vm) {
|
|||||||
{
|
{
|
||||||
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 + 2);
|
||||||
kvs = kvs + 3;
|
kvs = kvs + 3;
|
||||||
while (i < kvs) {
|
while (i < kvs) {
|
||||||
v1 = vm->base[vm->pc[i++]];
|
v1 = vm->base[vm->pc[i++]];
|
||||||
@ -672,7 +655,6 @@ int VMStart(VM * vm) {
|
|||||||
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);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -718,40 +700,48 @@ int VMStart(VM * vm) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case VM_OP_GET:
|
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;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
VMError(vm, "Unknown opcode");
|
VMError(vm, "Unknown opcode");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
VMMaybeCollect(vm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get an argument from the stack */
|
/* Get an argument from the stack */
|
||||||
Value VMGetArg(VM * vm, uint16_t index) {
|
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");
|
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 = vm->frame->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 */
|
||||||
uint16_t VMCountArgs(VM * vm) {
|
uint16_t VMCountArgs(VM * vm) {
|
||||||
return ThreadFrame(vm->thread)->size;
|
return vm->frame->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize the VM */
|
/* Initialize the VM */
|
||||||
void VMInit(VM * vm) {
|
void VMInit(VM * vm) {
|
||||||
vm->ret.type = TYPE_NIL;
|
vm->ret.type = TYPE_NIL;
|
||||||
vm->root.type = TYPE_NIL;
|
|
||||||
vm->base = NULL;
|
vm->base = NULL;
|
||||||
|
vm->frame = NULL;
|
||||||
vm->pc = NULL;
|
vm->pc = NULL;
|
||||||
vm->error = NULL;
|
vm->error = NULL;
|
||||||
/* Garbage collection */
|
/* Garbage collection */
|
||||||
@ -760,8 +750,8 @@ void VMInit(VM * vm) {
|
|||||||
vm->memoryInterval = 0;
|
vm->memoryInterval = 0;
|
||||||
vm->black = 0;
|
vm->black = 0;
|
||||||
vm->lock = 0;
|
vm->lock = 0;
|
||||||
/* Set to empty thread */
|
/* Add thread */
|
||||||
vm->thread = NULL;
|
vm->thread = ArrayNew(vm, 20);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Load a function into the VM. The function will be called with
|
/* 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)
|
{ if (!(cond)) { VMError((vm), (e)); } } while (0)
|
||||||
|
|
||||||
/* Type assertion */
|
/* Type assertion */
|
||||||
#define VMAssertType(vm, f, type) \
|
#define VMAssertType(vm, f, t) \
|
||||||
VMAssert(vm, (f).type == (type), "Expected type " type)
|
VMAssert((vm), (f).type == (t), "Expected type,")
|
||||||
|
|
||||||
/* Initialize the VM */
|
/* Initialize the VM */
|
||||||
void VMInit(VM * vm);
|
void VMInit(VM * vm);
|
||||||
|
Loading…
Reference in New Issue
Block a user