mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 07:00:58 +08:00
Merge pull request #3 from ThakeeNathees/fiber-implementation
Fiber implemented and calling convention fixed.
This commit is contained in:
commit
c601e5cf90
2
TODO.txt
2
TODO.txt
@ -22,5 +22,5 @@
|
||||
|
||||
// Bugs.
|
||||
|
||||
[ ] `function() "do" <body> end` make 'do' keyword optional here.
|
||||
[ ] Update cache on each recompilation instead of making a new cache.
|
||||
[*] `function() "do" <body> end` make 'do' keyword optional here.
|
||||
|
@ -26,6 +26,20 @@ extern "C" {
|
||||
// allocations.
|
||||
typedef struct MSVM MSVM;
|
||||
|
||||
// Nan-Tagging could be disable for debugging/portability purposes only when
|
||||
// compiling the compiler. Do not change this if using the miniscript library
|
||||
// for embedding. To disable when compiling the compiler, define
|
||||
// `VAR_NAN_TAGGING 0`, otherwise it defaults to Nan-Tagging.
|
||||
#ifndef VAR_NAN_TAGGING
|
||||
#define VAR_NAN_TAGGING 1
|
||||
#endif
|
||||
|
||||
#if VAR_NAN_TAGGING
|
||||
typedef uint64_t Var;
|
||||
#else
|
||||
typedef struct Var Var;
|
||||
#endif
|
||||
|
||||
// C function pointer which is callable from MiniScript.
|
||||
typedef void (*MiniScriptNativeFn)(MSVM* vm);
|
||||
|
||||
@ -102,6 +116,18 @@ void* msGetUserData(MSVM* vm);
|
||||
// Update the user data of the vm.
|
||||
void msSetUserData(MSVM* vm, void* user_data);
|
||||
|
||||
// Encode types to var.
|
||||
// TODO: user need to use vmPushTempRoot() for strings.
|
||||
Var msVarBool(MSVM* vm, bool value);
|
||||
Var msVarNumber(MSVM* vm, double value);
|
||||
Var msVarString(MSVM* vm, const char* value);
|
||||
|
||||
// Decode var types.
|
||||
// TODO: const char* should be copied otherwise it'll become dangling pointer.
|
||||
bool msAsBool(MSVM* vm, Var value);
|
||||
double msAsNumber(MSVM* vm, Var value);
|
||||
const char* msAsString(MSVM* vm, Var value);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
22
src/common.h
22
src/common.h
@ -6,6 +6,8 @@
|
||||
#ifndef MS_COMMON_H
|
||||
#define MS_COMMON_H
|
||||
|
||||
#include "miniscript.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
@ -38,7 +40,7 @@
|
||||
#endif
|
||||
|
||||
// Set this to dump compiled opcodes of each functions.
|
||||
#define DEBUG_DUMP_COMPILED_CODE 1
|
||||
#define DEBUG_DUMP_COMPILED_CODE 0
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
@ -73,8 +75,8 @@
|
||||
#else
|
||||
|
||||
#define DEBUG_BREAK()
|
||||
|
||||
#define ASSERT(condition, message) do { } while (false)
|
||||
#define ASSERT_INDEX(index, size) do {} while (false)
|
||||
|
||||
// Reference : https://github.com/wren-lang/
|
||||
#if defined( _MSC_VER )
|
||||
@ -116,19 +118,6 @@
|
||||
#define DEALLOCATE(vm, pointer) \
|
||||
vmRealloc(vm, pointer, 0, 0)
|
||||
|
||||
|
||||
// Nan-Tagging could be disable for debugging/portability purposes.
|
||||
// To disable define `VAR_NAN_TAGGING 0`, otherwise it defaults to Nan-Tagging.
|
||||
#ifndef VAR_NAN_TAGGING
|
||||
#define VAR_NAN_TAGGING 1
|
||||
#endif
|
||||
|
||||
#if VAR_NAN_TAGGING
|
||||
typedef uint64_t Var;
|
||||
#else
|
||||
typedef struct Var Var;
|
||||
#endif
|
||||
|
||||
typedef struct Object Object;
|
||||
typedef struct String String;
|
||||
typedef struct List List;
|
||||
@ -140,4 +129,7 @@ typedef struct Function Function;
|
||||
// Unique number to identify for various cases.
|
||||
typedef uint32_t ID;
|
||||
|
||||
// VM's fiber type.
|
||||
typedef struct Fiber Fiber;
|
||||
|
||||
#endif //MS_COMMON_H
|
||||
|
@ -306,6 +306,12 @@ struct Compiler {
|
||||
Script* script; //< Current script.
|
||||
Loop* loop; //< Current loop.
|
||||
Func* func; //< Current function.
|
||||
|
||||
// True if the last statement is a new local variable assignment. Because
|
||||
// the assignment is different than reqular assignment and use this boolean
|
||||
// to tell the compiler that dont pop it's assigned value because the value
|
||||
// itself is the local.
|
||||
bool new_local;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
@ -1009,7 +1015,13 @@ static void exprName(Compiler* compiler, bool can_assign) {
|
||||
int index = compilerAddVariable(compiler, name_start, name_len,
|
||||
name_line);
|
||||
compileExpression(compiler);
|
||||
emitStoreVariable(compiler, index, compiler->scope_depth == DEPTH_GLOBAL);
|
||||
if (compiler->scope_depth == DEPTH_GLOBAL) {
|
||||
emitStoreVariable(compiler, index, true);
|
||||
} else {
|
||||
// This will prevent the assignment from poped out from the stack
|
||||
// since the assigned value itself is the local and not a temp.
|
||||
compiler->new_local = true;
|
||||
}
|
||||
} else {
|
||||
parseError(parser, "Name \"%.*s\" is not defined.", name_len, name_start);
|
||||
}
|
||||
@ -1277,8 +1289,10 @@ static void compilerInit(Compiler* compiler, MSVM* vm, const char* source,
|
||||
compiler->var_count = 0;
|
||||
compiler->global_count = 0;
|
||||
compiler->stack_size = 0;
|
||||
Loop* loop = NULL;
|
||||
Function* fn = NULL;
|
||||
compiler->loop = NULL;
|
||||
compiler->func = NULL;
|
||||
compiler->script = NULL;
|
||||
compiler->new_local = false;
|
||||
}
|
||||
|
||||
// Add a variable and return it's index to the context. Assumes that the
|
||||
@ -1499,7 +1513,7 @@ static void compileBlockBody(Compiler* compiler, BlockType type) {
|
||||
|
||||
compilerEnterBlock(compiler);
|
||||
|
||||
if (type != BLOCK_ELIF) {
|
||||
if (type != BLOCK_ELSE && type != BLOCK_ELIF) {
|
||||
consumeStartBlock(&compiler->parser);
|
||||
skipNewLines(&compiler->parser);
|
||||
}
|
||||
@ -1707,9 +1721,13 @@ static void compileStatement(Compiler* compiler) {
|
||||
compileForStatement(compiler);
|
||||
|
||||
} else {
|
||||
compiler->new_local = false;
|
||||
compileExpression(compiler);
|
||||
consumeEndStatement(parser);
|
||||
emitOpcode(compiler, OP_POP);
|
||||
if (!compiler->new_local) {
|
||||
emitOpcode(compiler, OP_POP);
|
||||
}
|
||||
compiler->new_local = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,6 @@
|
||||
#ifndef COMPILER_H
|
||||
#define COMPILER_H
|
||||
|
||||
#include "miniscript.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "var.h"
|
||||
|
||||
|
33
src/core.c
33
src/core.c
@ -105,16 +105,16 @@ static inline bool validateIndex(MSVM* vm, int32_t index, int32_t size,
|
||||
/*****************************************************************************/
|
||||
|
||||
// Argument getter (1 based).
|
||||
#define ARG(n) vm->rbp[n]
|
||||
#define ARG(n) vm->fiber->ret[n]
|
||||
|
||||
// Argument count used in variadic functions.
|
||||
#define ARGC ((int)(vm->sp - vm->rbp) - 1)
|
||||
#define ARGC ((int)(vm->fiber->sp - vm->fiber->ret) - 1)
|
||||
|
||||
// Set return value.
|
||||
#define RET(value) \
|
||||
do { \
|
||||
vm->rbp[0] = value; \
|
||||
return; \
|
||||
#define RET(value) \
|
||||
do { \
|
||||
*(vm->fiber->ret) = value; \
|
||||
return; \
|
||||
} while (false)
|
||||
|
||||
Function* getBuiltinFunction(int index) {
|
||||
@ -178,7 +178,7 @@ void corePrint(MSVM* vm) {
|
||||
}
|
||||
|
||||
void coreImport(MSVM* vm) {
|
||||
Var arg1 = vm->rbp[1];
|
||||
Var arg1 = vm->fiber->ret[1];
|
||||
if (!IS_OBJ(arg1) || AS_OBJ(arg1)->type != OBJ_STRING) {
|
||||
msSetRuntimeError(vm, "Expected a String argument.");
|
||||
}
|
||||
@ -252,7 +252,6 @@ void initializeCore(MSVM* vm) {
|
||||
do { \
|
||||
std = newScript(vm); \
|
||||
std->name = _name; \
|
||||
std->name_length = (int)strlen(_name); \
|
||||
vmPushTempRef(vm, &std->_super); \
|
||||
vmAddStdScript(vm, std); \
|
||||
vmPopTempRef(vm); \
|
||||
@ -417,12 +416,14 @@ Var varGetAttrib(MSVM* vm, Var on, String* attrib) {
|
||||
}
|
||||
|
||||
case OBJ_FUNC:
|
||||
case OBJ_FIBER:
|
||||
case OBJ_USER:
|
||||
TODO;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
CHECK_MISSING_OBJ_TYPE(7);
|
||||
|
||||
UNREACHABLE();
|
||||
return VAR_NULL;
|
||||
@ -486,6 +487,10 @@ do { \
|
||||
ERR_NO_ATTRIB();
|
||||
return;
|
||||
|
||||
case OBJ_FIBER:
|
||||
ERR_NO_ATTRIB();
|
||||
return;
|
||||
|
||||
case OBJ_USER:
|
||||
ERR_NO_ATTRIB();
|
||||
return;
|
||||
@ -493,7 +498,7 @@ do { \
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
CHECK_MISSING_OBJ_TYPE(7);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@ -536,12 +541,14 @@ Var varGetSubscript(MSVM* vm, Var on, Var key) {
|
||||
case OBJ_RANGE:
|
||||
case OBJ_SCRIPT:
|
||||
case OBJ_FUNC:
|
||||
case OBJ_FIBER:
|
||||
case OBJ_USER:
|
||||
TODO;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
CHECK_MISSING_OBJ_TYPE(7);
|
||||
UNREACHABLE();
|
||||
return VAR_NULL;
|
||||
}
|
||||
@ -570,12 +577,13 @@ void varsetSubscript(MSVM* vm, Var on, Var key, Var value) {
|
||||
case OBJ_RANGE:
|
||||
case OBJ_SCRIPT:
|
||||
case OBJ_FUNC:
|
||||
case OBJ_FIBER:
|
||||
case OBJ_USER:
|
||||
TODO;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
CHECK_MISSING_OBJ_TYPE(7);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
@ -615,7 +623,7 @@ bool varIterate(MSVM* vm, Var seq, Var* iterator, Var* value) {
|
||||
case OBJ_STRING: {
|
||||
// TODO: // Need to consider utf8.
|
||||
String* str = ((String*)obj);
|
||||
if (iter < 0 || iter >= str->length) {
|
||||
if (iter < 0 || iter >= (int)str->length) {
|
||||
return false; //< Stop iteration.
|
||||
}
|
||||
// TODO: Or I could add char as a type for efficiency.
|
||||
@ -656,13 +664,14 @@ bool varIterate(MSVM* vm, Var seq, Var* iterator, Var* value) {
|
||||
|
||||
case OBJ_SCRIPT:
|
||||
case OBJ_FUNC:
|
||||
case OBJ_FIBER:
|
||||
case OBJ_USER:
|
||||
TODO;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
CHECK_MISSING_OBJ_TYPE(7);
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
@ -7,7 +7,7 @@
|
||||
#define CORE_H
|
||||
|
||||
#include "var.h"
|
||||
#include "miniscript.h"
|
||||
#include "common.h"
|
||||
|
||||
void initializeCore(MSVM* vm);
|
||||
|
||||
|
412
src/debug.c
412
src/debug.c
@ -3,84 +3,91 @@
|
||||
* Licensed under: MIT License
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "debug.h"
|
||||
#include "vm.h"
|
||||
|
||||
static const char* op_name[] = {
|
||||
#define OPCODE(name, params, stack) #name,
|
||||
#include "opcodes.h"
|
||||
#undef OPCODE
|
||||
NULL,
|
||||
#define OPCODE(name, params, stack) #name,
|
||||
#include "opcodes.h"
|
||||
#undef OPCODE
|
||||
NULL,
|
||||
};
|
||||
|
||||
|
||||
static void _dumpValue(MSVM* vm, Var value, bool recursive) {
|
||||
if (IS_NULL(value)) {
|
||||
printf("null");
|
||||
return;
|
||||
}
|
||||
if (IS_BOOL(value)) {
|
||||
printf((AS_BOOL(value)) ? "true" : "false");
|
||||
return;
|
||||
}
|
||||
if (IS_NUM(value)) {
|
||||
printf("%.14g", AS_NUM(value));
|
||||
return;
|
||||
}
|
||||
ASSERT(IS_OBJ(value), OOPS);
|
||||
Object* obj = AS_OBJ(value);
|
||||
switch (obj->type) {
|
||||
case OBJ_STRING:
|
||||
printf("\"%s\"", ((String*)obj)->data);
|
||||
return;
|
||||
case OBJ_LIST:
|
||||
{
|
||||
List* list = ((List*)obj);
|
||||
if (recursive) {
|
||||
printf("[...]");
|
||||
} else {
|
||||
printf("[");
|
||||
for (int i = 0; i < list->elements.count; i++) {
|
||||
if (i != 0) printf(", ");
|
||||
_dumpValue(vm, list->elements.data[i], true);
|
||||
}
|
||||
printf("]");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (IS_NULL(value)) {
|
||||
printf("null");
|
||||
return;
|
||||
}
|
||||
if (IS_BOOL(value)) {
|
||||
printf((AS_BOOL(value)) ? "true" : "false");
|
||||
return;
|
||||
}
|
||||
if (IS_NUM(value)) {
|
||||
printf("%.14g", AS_NUM(value));
|
||||
return;
|
||||
}
|
||||
ASSERT(IS_OBJ(value), OOPS);
|
||||
Object* obj = AS_OBJ(value);
|
||||
switch (obj->type) {
|
||||
case OBJ_STRING:
|
||||
printf("\"%s\"", ((String*)obj)->data);
|
||||
return;
|
||||
case OBJ_LIST:
|
||||
{
|
||||
List* list = ((List*)obj);
|
||||
if (recursive) {
|
||||
printf("[...]");
|
||||
} else {
|
||||
printf("[");
|
||||
for (int i = 0; i < list->elements.count; i++) {
|
||||
if (i != 0) printf(", ");
|
||||
_dumpValue(vm, list->elements.data[i], true);
|
||||
}
|
||||
printf("]");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
case OBJ_MAP:
|
||||
TODO;
|
||||
return;
|
||||
case OBJ_MAP:
|
||||
TODO;
|
||||
return;
|
||||
|
||||
case OBJ_RANGE:
|
||||
{
|
||||
Range* range = ((Range*)obj);
|
||||
printf("%.2g..%.2g", range->from, range->to);
|
||||
}
|
||||
case OBJ_RANGE:
|
||||
{
|
||||
Range* range = ((Range*)obj);
|
||||
printf("%.2g..%.2g", range->from, range->to);
|
||||
return;
|
||||
}
|
||||
|
||||
case OBJ_SCRIPT:
|
||||
printf("[Script:%p]", obj);
|
||||
case OBJ_FUNC:
|
||||
printf("[Fn:%p]", obj);
|
||||
case OBJ_USER:
|
||||
printf("[UserObj:%p]", obj);
|
||||
}
|
||||
case OBJ_SCRIPT:
|
||||
printf("[Script:%p]", obj);
|
||||
return;
|
||||
case OBJ_FUNC:
|
||||
printf("[Fn:%p]", obj);
|
||||
return;
|
||||
case OBJ_USER:
|
||||
printf("[UserObj:%p]", obj);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void dumpValue(MSVM* vm, Var value) {
|
||||
_dumpValue(vm, value, false);
|
||||
_dumpValue(vm, value, false);
|
||||
}
|
||||
|
||||
void dumpInstructions(MSVM* vm, Function* func) {
|
||||
void dumpInstructions(MSVM* vm, Function* func) {
|
||||
|
||||
|
||||
int i = 0;
|
||||
uint8_t* opcodes = func->fn->opcodes.data;
|
||||
int* lines = func->fn->oplines.data;
|
||||
int line = 1, last_line = 0;
|
||||
int i = 0;
|
||||
uint8_t* opcodes = func->fn->opcodes.data;
|
||||
int* lines = func->fn->oplines.data;
|
||||
int line = 1, last_line = 0;
|
||||
|
||||
printf("Instruction Dump of function '%s'\n", func->name);
|
||||
printf("Instruction Dump of function '%s'\n", func->name);
|
||||
#define READ_BYTE() (opcodes[i++])
|
||||
#define READ_SHORT() (i += 2, opcodes[i - 2] << 8 | opcodes[i-1])
|
||||
|
||||
@ -88,163 +95,178 @@ void dumpInstructions(MSVM* vm, Function* func) {
|
||||
#define SHORT_ARG() printf("%5d\n", READ_SHORT())
|
||||
#define INDENTATION " "
|
||||
|
||||
while (i < func->fn->opcodes.count) {
|
||||
ASSERT_INDEX(i, func->fn->opcodes.count);
|
||||
while (i < func->fn->opcodes.count) {
|
||||
ASSERT_INDEX(i, func->fn->opcodes.count);
|
||||
|
||||
// Print the line number.
|
||||
line = lines[i];
|
||||
if (line != last_line) {
|
||||
printf(INDENTATION "%4d:", line);
|
||||
last_line = line;
|
||||
} else {
|
||||
printf(INDENTATION " ");
|
||||
}
|
||||
// Print the line number.
|
||||
line = lines[i];
|
||||
if (line != last_line) {
|
||||
printf(INDENTATION "%4d:", line);
|
||||
last_line = line;
|
||||
} else {
|
||||
printf(INDENTATION " ");
|
||||
}
|
||||
|
||||
printf(INDENTATION "%4d %-16s", i, op_name[opcodes[i]]);
|
||||
printf(INDENTATION "%4d %-16s", i, op_name[opcodes[i]]);
|
||||
|
||||
Opcode op = (Opcode)func->fn->opcodes.data[i++];
|
||||
switch (op) {
|
||||
case OP_CONSTANT:
|
||||
{
|
||||
int index = READ_SHORT();
|
||||
printf("%5d ", index);
|
||||
ASSERT_INDEX(index, func->owner->literals.count);
|
||||
Var value = func->owner->literals.data[index];
|
||||
dumpValue(vm, value);
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
Opcode op = (Opcode)func->fn->opcodes.data[i++];
|
||||
switch (op) {
|
||||
case OP_CONSTANT:
|
||||
{
|
||||
int index = READ_SHORT();
|
||||
printf("%5d ", index);
|
||||
ASSERT_INDEX(index, func->owner->literals.count);
|
||||
Var value = func->owner->literals.data[index];
|
||||
dumpValue(vm, value);
|
||||
printf("\n");
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_PUSH_NULL:
|
||||
case OP_PUSH_SELF:
|
||||
case OP_PUSH_TRUE:
|
||||
case OP_PUSH_FALSE:
|
||||
NO_ARGS();
|
||||
break;
|
||||
case OP_PUSH_NULL:
|
||||
case OP_PUSH_SELF:
|
||||
case OP_PUSH_TRUE:
|
||||
case OP_PUSH_FALSE:
|
||||
NO_ARGS();
|
||||
break;
|
||||
|
||||
case OP_PUSH_LIST:
|
||||
SHORT_ARG();
|
||||
break;
|
||||
case OP_PUSH_LIST:
|
||||
SHORT_ARG();
|
||||
break;
|
||||
|
||||
case OP_LIST_APPEND: NO_ARGS(); break;
|
||||
case OP_LIST_APPEND: NO_ARGS(); break;
|
||||
|
||||
case OP_PUSH_LOCAL_0:
|
||||
case OP_PUSH_LOCAL_1:
|
||||
case OP_PUSH_LOCAL_2:
|
||||
case OP_PUSH_LOCAL_3:
|
||||
case OP_PUSH_LOCAL_4:
|
||||
case OP_PUSH_LOCAL_5:
|
||||
case OP_PUSH_LOCAL_6:
|
||||
case OP_PUSH_LOCAL_7:
|
||||
case OP_PUSH_LOCAL_8:
|
||||
NO_ARGS();
|
||||
break;
|
||||
case OP_PUSH_LOCAL_0:
|
||||
case OP_PUSH_LOCAL_1:
|
||||
case OP_PUSH_LOCAL_2:
|
||||
case OP_PUSH_LOCAL_3:
|
||||
case OP_PUSH_LOCAL_4:
|
||||
case OP_PUSH_LOCAL_5:
|
||||
case OP_PUSH_LOCAL_6:
|
||||
case OP_PUSH_LOCAL_7:
|
||||
case OP_PUSH_LOCAL_8:
|
||||
NO_ARGS();
|
||||
break;
|
||||
|
||||
case OP_PUSH_LOCAL_N:
|
||||
SHORT_ARG();
|
||||
break;
|
||||
case OP_PUSH_LOCAL_N:
|
||||
SHORT_ARG();
|
||||
break;
|
||||
|
||||
case OP_STORE_LOCAL_0:
|
||||
case OP_STORE_LOCAL_1:
|
||||
case OP_STORE_LOCAL_2:
|
||||
case OP_STORE_LOCAL_3:
|
||||
case OP_STORE_LOCAL_4:
|
||||
case OP_STORE_LOCAL_5:
|
||||
case OP_STORE_LOCAL_6:
|
||||
case OP_STORE_LOCAL_7:
|
||||
case OP_STORE_LOCAL_8:
|
||||
NO_ARGS();
|
||||
break;
|
||||
case OP_STORE_LOCAL_0:
|
||||
case OP_STORE_LOCAL_1:
|
||||
case OP_STORE_LOCAL_2:
|
||||
case OP_STORE_LOCAL_3:
|
||||
case OP_STORE_LOCAL_4:
|
||||
case OP_STORE_LOCAL_5:
|
||||
case OP_STORE_LOCAL_6:
|
||||
case OP_STORE_LOCAL_7:
|
||||
case OP_STORE_LOCAL_8:
|
||||
NO_ARGS();
|
||||
break;
|
||||
|
||||
case OP_STORE_LOCAL_N:
|
||||
SHORT_ARG();
|
||||
break;
|
||||
case OP_STORE_LOCAL_N:
|
||||
SHORT_ARG();
|
||||
break;
|
||||
|
||||
case OP_PUSH_GLOBAL:
|
||||
case OP_STORE_GLOBAL:
|
||||
case OP_PUSH_FN:
|
||||
SHORT_ARG();
|
||||
break;
|
||||
case OP_PUSH_GLOBAL:
|
||||
case OP_STORE_GLOBAL:
|
||||
case OP_PUSH_FN:
|
||||
SHORT_ARG();
|
||||
break;
|
||||
|
||||
case OP_PUSH_BUILTIN_FN:
|
||||
{
|
||||
int index = READ_SHORT();
|
||||
printf("%5d [Fn:%s]\n", index, getBuiltinFunctionName(index));
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_PUSH_BUILTIN_FN:
|
||||
{
|
||||
int index = READ_SHORT();
|
||||
printf("%5d [Fn:%s]\n", index, getBuiltinFunctionName(index));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case OP_POP:
|
||||
NO_ARGS();
|
||||
break;
|
||||
case OP_POP:
|
||||
NO_ARGS();
|
||||
break;
|
||||
|
||||
case OP_CALL:
|
||||
printf("%5d (argc)\n", READ_SHORT());
|
||||
break;
|
||||
case OP_CALL:
|
||||
printf("%5d (argc)\n", READ_SHORT());
|
||||
break;
|
||||
|
||||
case OP_ITER:
|
||||
case OP_JUMP:
|
||||
case OP_JUMP_IF:
|
||||
case OP_JUMP_IF_NOT:
|
||||
{
|
||||
int offset = READ_SHORT();
|
||||
printf("%5d (ip:%d)\n", offset, i + offset);
|
||||
break;
|
||||
}
|
||||
case OP_ITER:
|
||||
case OP_JUMP:
|
||||
case OP_JUMP_IF:
|
||||
case OP_JUMP_IF_NOT:
|
||||
{
|
||||
int offset = READ_SHORT();
|
||||
printf("%5d (ip:%d)\n", offset, i + offset);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_LOOP:
|
||||
{
|
||||
int offset = READ_SHORT();
|
||||
printf("%5d (ip:%d)\n", -offset, i - offset);
|
||||
break;
|
||||
}
|
||||
case OP_LOOP:
|
||||
{
|
||||
int offset = READ_SHORT();
|
||||
printf("%5d (ip:%d)\n", -offset, i - offset);
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_RETURN: NO_ARGS(); break;
|
||||
case OP_RETURN: NO_ARGS(); break;
|
||||
|
||||
case OP_GET_ATTRIB:
|
||||
case OP_GET_ATTRIB_AOP:
|
||||
case OP_SET_ATTRIB:
|
||||
SHORT_ARG();
|
||||
break;
|
||||
case OP_GET_ATTRIB:
|
||||
case OP_GET_ATTRIB_AOP:
|
||||
case OP_SET_ATTRIB:
|
||||
SHORT_ARG();
|
||||
break;
|
||||
|
||||
case OP_GET_SUBSCRIPT:
|
||||
case OP_GET_SUBSCRIPT_AOP:
|
||||
case OP_SET_SUBSCRIPT:
|
||||
NO_ARGS();
|
||||
break;
|
||||
case OP_GET_SUBSCRIPT:
|
||||
case OP_GET_SUBSCRIPT_AOP:
|
||||
case OP_SET_SUBSCRIPT:
|
||||
NO_ARGS();
|
||||
break;
|
||||
|
||||
case OP_NEGATIVE:
|
||||
case OP_NOT:
|
||||
case OP_BIT_NOT:
|
||||
case OP_ADD:
|
||||
case OP_SUBTRACT:
|
||||
case OP_MULTIPLY:
|
||||
case OP_DIVIDE:
|
||||
case OP_MOD:
|
||||
case OP_BIT_AND:
|
||||
case OP_BIT_OR:
|
||||
case OP_BIT_XOR:
|
||||
case OP_BIT_LSHIFT:
|
||||
case OP_BIT_RSHIFT:
|
||||
case OP_AND:
|
||||
case OP_OR:
|
||||
case OP_EQEQ:
|
||||
case OP_NOTEQ:
|
||||
case OP_LT:
|
||||
case OP_LTEQ:
|
||||
case OP_GT:
|
||||
case OP_GTEQ:
|
||||
case OP_RANGE:
|
||||
case OP_IN:
|
||||
case OP_END:
|
||||
NO_ARGS();
|
||||
break;
|
||||
case OP_NEGATIVE:
|
||||
case OP_NOT:
|
||||
case OP_BIT_NOT:
|
||||
case OP_ADD:
|
||||
case OP_SUBTRACT:
|
||||
case OP_MULTIPLY:
|
||||
case OP_DIVIDE:
|
||||
case OP_MOD:
|
||||
case OP_BIT_AND:
|
||||
case OP_BIT_OR:
|
||||
case OP_BIT_XOR:
|
||||
case OP_BIT_LSHIFT:
|
||||
case OP_BIT_RSHIFT:
|
||||
case OP_AND:
|
||||
case OP_OR:
|
||||
case OP_EQEQ:
|
||||
case OP_NOTEQ:
|
||||
case OP_LT:
|
||||
case OP_LTEQ:
|
||||
case OP_GT:
|
||||
case OP_GTEQ:
|
||||
case OP_RANGE:
|
||||
case OP_IN:
|
||||
case OP_END:
|
||||
NO_ARGS();
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reportStackTrace(MSVM* vm) {
|
||||
Fiber* fiber = vm->fiber;
|
||||
Script* script = fiber->func->owner;
|
||||
|
||||
//vm->config.error_fn(vm, MS_ERROR_RUNTIME, NULL, -1, fiber->error )
|
||||
//
|
||||
//// TODO: I'm not confident about this approach.
|
||||
//if (script->path != NULL) { // User script.
|
||||
//
|
||||
//
|
||||
//} else { // "std" script.
|
||||
//
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
#define DEBUG_H
|
||||
|
||||
#include "common.h"
|
||||
#include "miniscript.h"
|
||||
|
||||
// Dump the value of the [value] without a new line at the end.
|
||||
void dumpValue(MSVM* vm, Var value);
|
||||
@ -15,4 +14,7 @@ void dumpValue(MSVM* vm, Var value);
|
||||
// Dump opcodes of the given function.
|
||||
void dumpInstructions(MSVM* vm, Function* func);
|
||||
|
||||
// Print stack track.
|
||||
void reportStackTrace(MSVM* vm);
|
||||
|
||||
#endif // DEBUG_H
|
||||
|
30
src/var.c
30
src/var.c
@ -8,7 +8,34 @@
|
||||
#include "var.h"
|
||||
#include "vm.h"
|
||||
|
||||
// Number of maximum digits for to_string buffer.
|
||||
// Public Api /////////////////////////////////////////////////////////////////
|
||||
Var msVarBool(MSVM* vm, bool value) {
|
||||
return VAR_BOOL(value);
|
||||
}
|
||||
|
||||
Var msVarNumber(MSVM* vm, double value) {
|
||||
return VAR_NUM(value);
|
||||
}
|
||||
|
||||
Var msVarString(MSVM* vm, const char* value) {
|
||||
return VAR_OBJ(newString(vm, value, (uint32_t)strlen(value)));
|
||||
}
|
||||
|
||||
bool msAsBool(MSVM* vm, Var value) {
|
||||
return AS_BOOL(value);
|
||||
}
|
||||
|
||||
double msAsNumber(MSVM* vm, Var value) {
|
||||
return AS_NUM(value);
|
||||
}
|
||||
|
||||
const char* msAsString(MSVM* vm, Var value) {
|
||||
return AS_STRING(value)->data;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Number of maximum digits for to_string buffer.
|
||||
#define TO_STRING_BUFF_SIZE 128
|
||||
|
||||
void varInitObject(Object* self, MSVM* vm, ObjectType type) {
|
||||
@ -84,7 +111,6 @@ Script* newScript(MSVM* vm) {
|
||||
varInitObject(&script->_super, vm, OBJ_SCRIPT);
|
||||
|
||||
script->name = NULL;
|
||||
script->name_length = 0;
|
||||
script->path = NULL;
|
||||
|
||||
varBufferInit(&script->globals);
|
||||
|
23
src/var.h
23
src/var.h
@ -30,8 +30,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "miniscript.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "types/gen/byte_buffer.h"
|
||||
#include "types/gen/function_buffer.h"
|
||||
#include "types/gen/int_buffer.h"
|
||||
@ -157,8 +156,6 @@
|
||||
#define AS_MAP(value) ((Map*)AS_OBJ(value))
|
||||
#define AS_RANGE(value) ((Range*)AS_OBJ(value))
|
||||
|
||||
typedef uint64_t Var;
|
||||
|
||||
#else
|
||||
|
||||
// TODO: Union tagging implementation of all the above macros ignore macros
|
||||
@ -196,9 +193,20 @@ typedef enum /* ObjectType */ {
|
||||
OBJ_SCRIPT,
|
||||
OBJ_FUNC,
|
||||
|
||||
OBJ_FIBER,
|
||||
|
||||
OBJ_USER,
|
||||
} ObjectType;
|
||||
|
||||
// This will terminate compiler (because of 1/0 evaluvated) if ObjectType max
|
||||
// is not [count]. Use this to ensure every time switching ObjectType will
|
||||
// cover all object types.
|
||||
#if DEBUG
|
||||
#define CHECK_MISSING_OBJ_TYPE(count) (1/ ((int)(!(count ^ OBJ_USER))) )
|
||||
#else
|
||||
#define CHECK_MISSING_OBJ_TYPE(count) do {} while (false)
|
||||
#endif
|
||||
|
||||
// Base struct for all heap allocated objects.
|
||||
struct Object {
|
||||
ObjectType type; //< Type of the object in \ref var_Object_Type.
|
||||
@ -233,8 +241,9 @@ struct Range {
|
||||
struct Script {
|
||||
Object _super;
|
||||
|
||||
// One of the below is null and other one is not. Since "std" script names
|
||||
// are hardcoded and user script names are constructed.
|
||||
const char* name; //< Std script's name. Null for user script.
|
||||
int name_length; //< Length of the name.
|
||||
String* path; //< Absolute path of the script. Null for std scripts.
|
||||
|
||||
ID imports[MAX_IMPORT_SCRIPTS]; //< Imported script IDs.
|
||||
@ -271,7 +280,7 @@ struct Function {
|
||||
};
|
||||
};
|
||||
|
||||
// Methods.
|
||||
// Methods ////////////////////////////////////////////////////////////////////
|
||||
|
||||
void varInitObject(Object* self, MSVM* vm, ObjectType type);
|
||||
|
||||
@ -305,7 +314,7 @@ const char* varTypeName(Var v);
|
||||
bool isVauesSame(Var v1, Var v2);
|
||||
|
||||
// Returns the string version of the value. Note: pass false as [_recursive]
|
||||
// It's an internal use (or may be I could make a wrapper around).
|
||||
// It's for internal use (or may be I could make a wrapper around).
|
||||
String* toString(MSVM* vm, Var v, bool _recursive);
|
||||
|
||||
// Returns the truthy value of the var.
|
||||
|
174
src/vm.c
174
src/vm.c
@ -9,7 +9,7 @@
|
||||
#include "debug.h"
|
||||
#include "utils.h"
|
||||
|
||||
#define HAS_ERROR() (vm->error != NULL)
|
||||
#define HAS_ERROR() (vm->fiber->error != NULL)
|
||||
|
||||
// Initially allocated call frame capacity. Will grow dynamically.
|
||||
#define INITIAL_CALL_FRAMES 4
|
||||
@ -17,6 +17,13 @@
|
||||
// Minimum size of the stack.
|
||||
#define MIN_STACK_SIZE 128
|
||||
|
||||
Fiber* newFiber(MSVM* vm) {
|
||||
Fiber* fiber = ALLOCATE(vm, Fiber);
|
||||
memset(fiber, 0, sizeof(Fiber));
|
||||
varInitObject(&fiber->_super, vm, OBJ_FIBER);
|
||||
return fiber;
|
||||
}
|
||||
|
||||
void* vmRealloc(MSVM* self, void* memory, size_t old_size, size_t new_size) {
|
||||
|
||||
// Track the total allocated memory of the VM to trigger the GC.
|
||||
@ -82,19 +89,8 @@ void msSetUserData(MSVM* vm, void* user_data) {
|
||||
* RUNTIME *
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
// TODO: A function for quick debug. REMOVE.
|
||||
void _printStackTop(MSVM* vm) {
|
||||
if (vm->sp != vm->stack) {
|
||||
Var v = *(vm->sp - 1);
|
||||
printf("%s\n", toString(vm, v, false)->data);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ensureStackSize(MSVM* vm, int size) {
|
||||
if (vm->stack_size > size) return;
|
||||
if (vm->fiber->stack_size > size) return;
|
||||
TODO;
|
||||
}
|
||||
|
||||
@ -102,27 +98,27 @@ static inline void pushCallFrame(MSVM* vm, Function* fn) {
|
||||
ASSERT(!fn->is_native, "Native function shouldn't use call frames.");
|
||||
|
||||
// Grow the stack frame if needed.
|
||||
if (vm->frame_count + 1 > vm->frame_capacity) {
|
||||
int new_capacity = vm->frame_capacity * 2;
|
||||
vm->frames = (CallFrame*)vmRealloc(vm, vm->frames,
|
||||
sizeof(CallFrame) * vm->frame_capacity,
|
||||
if (vm->fiber->frame_count + 1 > vm->fiber->frame_capacity) {
|
||||
int new_capacity = vm->fiber->frame_capacity * 2;
|
||||
vm->fiber->frames = (CallFrame*)vmRealloc(vm, vm->fiber->frames,
|
||||
sizeof(CallFrame) * vm->fiber->frame_capacity,
|
||||
sizeof(CallFrame) * new_capacity);
|
||||
vm->frame_capacity = new_capacity;
|
||||
vm->fiber->frame_capacity = new_capacity;
|
||||
}
|
||||
|
||||
// Grow the stack if needed.
|
||||
int stack_size = (int)(vm->sp - vm->stack);
|
||||
int stack_size = (int)(vm->fiber->sp - vm->fiber->stack);
|
||||
int needed = stack_size + fn->fn->stack_size;
|
||||
ensureStackSize(vm, needed);
|
||||
|
||||
CallFrame* frame = &vm->frames[vm->frame_count++];
|
||||
frame->rbp = vm->rbp + 1; // vm->rbp is the return value.
|
||||
CallFrame* frame = &vm->fiber->frames[vm->fiber->frame_count++];
|
||||
frame->rbp = vm->fiber->ret;
|
||||
frame->fn = fn;
|
||||
frame->ip = fn->fn->opcodes.data;
|
||||
}
|
||||
|
||||
void msSetRuntimeError(MSVM* vm, const char* format, ...) {
|
||||
vm->error = newString(vm, "TODO:", 5);
|
||||
vm->fiber->error = newString(vm, "TODO:", 5);
|
||||
TODO;
|
||||
}
|
||||
|
||||
@ -146,6 +142,34 @@ MSInterpretResult msInterpret(MSVM* vm, const char* file) {
|
||||
return vmRunScript(vm, script);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
|
||||
// FIXME: for temp debugging. (implement dump stack frames).
|
||||
void _debugRuntime(MSVM* vm) {
|
||||
return;
|
||||
system("cls");
|
||||
Fiber* fiber = vm->fiber;
|
||||
|
||||
for (int i = fiber->frame_count - 1; i >= 0; i--) {
|
||||
CallFrame frame = fiber->frames[i];
|
||||
|
||||
Var* top = fiber->sp - 1;
|
||||
if (i != fiber->frame_count - 1) {
|
||||
top = fiber->frames[i + 1].rbp - 1;
|
||||
}
|
||||
|
||||
for (; top >= frame.rbp; top--) {
|
||||
printf("[*]: ");
|
||||
dumpValue(vm, *top); printf("\n");
|
||||
}
|
||||
|
||||
printf("----------------\n");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
|
||||
register uint8_t* ip; //< Current instruction pointer.
|
||||
@ -153,11 +177,32 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
register CallFrame* frame; //< Current call frame.
|
||||
register Script* script; //< Currently executing script.
|
||||
|
||||
#define PUSH(value) (*vm->sp++ = (value))
|
||||
#define POP() (*(--vm->sp))
|
||||
#define DROP() (--vm->sp)
|
||||
#define PEEK() (*(vm->sp - 1))
|
||||
#define READ_BYTE() (*ip++)
|
||||
vm->fiber = newFiber(vm);
|
||||
vm->fiber->func = _script->body;
|
||||
|
||||
// Allocate stack.
|
||||
int stack_size = utilPowerOf2Ceil(vm->fiber->func->fn->stack_size + 1);
|
||||
if (stack_size < MIN_STACK_SIZE) stack_size = MIN_STACK_SIZE;
|
||||
vm->fiber->stack_size = stack_size;
|
||||
vm->fiber->stack = ALLOCATE_ARRAY(vm, Var, vm->fiber->stack_size);
|
||||
vm->fiber->sp = vm->fiber->stack;
|
||||
vm->fiber->ret = vm->fiber->stack;
|
||||
|
||||
// Allocate call frames.
|
||||
vm->fiber->frame_capacity = INITIAL_CALL_FRAMES;
|
||||
vm->fiber->frames = ALLOCATE_ARRAY(vm, CallFrame, vm->fiber->frame_capacity);
|
||||
vm->fiber->frame_count = 1;
|
||||
|
||||
// Initialize VM's first frame.
|
||||
vm->fiber->frames[0].ip = _script->body->fn->opcodes.data;
|
||||
vm->fiber->frames[0].fn = _script->body;
|
||||
vm->fiber->frames[0].rbp = vm->fiber->stack;
|
||||
|
||||
#define PUSH(value) (*vm->fiber->sp++ = (value))
|
||||
#define POP() (*(--vm->fiber->sp))
|
||||
#define DROP() (--vm->fiber->sp)
|
||||
#define PEEK() (*(vm->fiber->sp - 1))
|
||||
#define READ_BYTE() (*ip++)
|
||||
#define READ_SHORT() (ip+=2, (uint16_t)((ip[-2] << 8) | ip[-1]))
|
||||
|
||||
// Check if any runtime error exists and if so returns RESULT_RUNTIME_ERROR.
|
||||
@ -184,19 +229,24 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
|
||||
// Update the call frame and ip once vm's call frame pushed or popped.
|
||||
// fuction call, return or done running imported script.
|
||||
#define LOAD_FRAME() \
|
||||
do { \
|
||||
frame = &vm->frames[vm->frame_count-1]; \
|
||||
ip = frame->ip; \
|
||||
rbp = frame->rbp; \
|
||||
script = frame->fn->owner; \
|
||||
#define LOAD_FRAME() \
|
||||
do { \
|
||||
frame = &vm->fiber->frames[vm->fiber->frame_count-1]; \
|
||||
ip = frame->ip; \
|
||||
rbp = frame->rbp; \
|
||||
script = frame->fn->owner; \
|
||||
} while (false)
|
||||
|
||||
#ifdef OPCODE
|
||||
#error "OPCODE" should not be deifined here.
|
||||
#endif
|
||||
|
||||
#define DEBUG_INSTRUCTION() //_printStackTop(vm)
|
||||
#if DEBUG
|
||||
#define DEBUG_INSTRUCTION() _debugRuntime(vm)
|
||||
#else
|
||||
#define DEBUG_INSTRUCTION() do { } while (false)
|
||||
#endif
|
||||
|
||||
|
||||
#define SWITCH(code) \
|
||||
L_vm_main_loop: \
|
||||
@ -205,25 +255,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
#define OPCODE(code) case OP_##code
|
||||
#define DISPATCH() goto L_vm_main_loop
|
||||
|
||||
// Allocate stack.
|
||||
int stack_size = utilPowerOf2Ceil(_script->body->fn->stack_size + 1);
|
||||
if (stack_size < MIN_STACK_SIZE) stack_size = MIN_STACK_SIZE;
|
||||
vm->stack_size = stack_size;
|
||||
vm->stack = ALLOCATE_ARRAY(vm, Var, vm->stack_size);
|
||||
vm->sp = vm->stack;
|
||||
vm->rbp = vm->stack;
|
||||
PUSH(VAR_NULL); // Return value of the script body.
|
||||
|
||||
// Allocate call frames.
|
||||
vm->frame_capacity = INITIAL_CALL_FRAMES;
|
||||
vm->frames = ALLOCATE_ARRAY(vm, CallFrame, vm->frame_capacity);
|
||||
vm->frame_count = 1;
|
||||
|
||||
// Initialize VM's first frame.
|
||||
vm->frames[0].ip = _script->body->fn->opcodes.data;
|
||||
vm->frames[0].fn = _script->body;
|
||||
vm->frames[0].rbp = vm->rbp + 1; // +1 to skip script's null return value.
|
||||
|
||||
LOAD_FRAME();
|
||||
|
||||
Opcode instruction;
|
||||
@ -259,7 +291,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
OPCODE(LIST_APPEND):
|
||||
{
|
||||
Var elem = POP();
|
||||
Var list = *(vm->sp - 1);
|
||||
Var list = *(vm->fiber->sp - 1);
|
||||
ASSERT(IS_OBJ(list) && AS_OBJ(list)->type == OBJ_LIST, OOPS);
|
||||
varBufferWrite(&((List*)AS_OBJ(list))->elements, vm, elem);
|
||||
DISPATCH();
|
||||
@ -276,13 +308,13 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
OPCODE(PUSH_LOCAL_8):
|
||||
{
|
||||
int index = (int)(instruction - OP_PUSH_LOCAL_0);
|
||||
PUSH(rbp[index]);
|
||||
PUSH(rbp[index + 1]); // +1: rbp[0] is return value.
|
||||
DISPATCH();
|
||||
}
|
||||
OPCODE(PUSH_LOCAL_N):
|
||||
{
|
||||
int index = READ_SHORT();
|
||||
PUSH(rbp[index]);
|
||||
PUSH(rbp[index + 1]); // +1: rbp[0] is return value.
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -297,13 +329,13 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
OPCODE(STORE_LOCAL_8):
|
||||
{
|
||||
int index = (int)(instruction - OP_STORE_LOCAL_0);
|
||||
rbp[index] = PEEK();
|
||||
rbp[index + 1] = PEEK(); // +1: rbp[0] is return value.
|
||||
DISPATCH();
|
||||
}
|
||||
OPCODE(STORE_LOCAL_N):
|
||||
{
|
||||
int index = READ_SHORT();
|
||||
rbp[index] = PEEK();
|
||||
rbp[index + 1] = PEEK(); // +1: rbp[0] is return value.
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
@ -346,7 +378,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
OPCODE(CALL):
|
||||
{
|
||||
int argc = READ_SHORT();
|
||||
Var* callable = vm->sp - argc - 1;
|
||||
Var* callable = vm->fiber->sp - argc - 1;
|
||||
|
||||
if (IS_OBJ(*callable) && AS_OBJ(*callable)->type == OBJ_FUNC) {
|
||||
|
||||
@ -362,12 +394,12 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
*callable = VAR_NULL;
|
||||
|
||||
// Next call frame starts here. (including return value).
|
||||
vm->rbp = callable;
|
||||
vm->fiber->ret = callable;
|
||||
|
||||
if (fn->is_native) {
|
||||
fn->native(vm);
|
||||
// Pop function arguments except for the return value.
|
||||
vm->sp = vm->rbp + 1;
|
||||
vm->fiber->sp = vm->fiber->ret + 1;
|
||||
CHECK_ERROR();
|
||||
|
||||
} else {
|
||||
@ -384,9 +416,9 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
|
||||
OPCODE(ITER) :
|
||||
{
|
||||
Var* iter_value = (vm->sp - 1);
|
||||
Var* iterator = (vm->sp - 2);
|
||||
Var* container = (vm->sp - 3);
|
||||
Var* iter_value = (vm->fiber->sp - 1);
|
||||
Var* iterator = (vm->fiber->sp - 2);
|
||||
Var* container = (vm->fiber->sp - 3);
|
||||
int jump_offset = READ_SHORT();
|
||||
if (!varIterate(vm, *container, iterator, iter_value)) {
|
||||
DROP(); //< Iter value.
|
||||
@ -433,20 +465,20 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
OPCODE(RETURN):
|
||||
{
|
||||
Var ret = POP();
|
||||
vm->frame_count--;
|
||||
vm->fiber->frame_count--;
|
||||
|
||||
// If no more call frames. We're done.
|
||||
if (vm->frame_count == 0) {
|
||||
vm->sp = vm->stack;
|
||||
if (vm->fiber->frame_count == 0) {
|
||||
vm->fiber->sp = vm->fiber->stack;
|
||||
PUSH(ret);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// Set the return value.
|
||||
*(frame->rbp - 1) = ret;
|
||||
*(frame->rbp) = ret;
|
||||
|
||||
// Pop the locals and update stack pointer.
|
||||
vm->sp = frame->rbp;
|
||||
vm->fiber->sp = frame->rbp + 1; // +1: rbp is returned value.
|
||||
|
||||
LOAD_FRAME();
|
||||
DISPATCH();
|
||||
@ -462,7 +494,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
|
||||
OPCODE(GET_ATTRIB_AOP):
|
||||
{
|
||||
Var on = *(vm->sp - 1);
|
||||
Var on = *(vm->fiber->sp - 1);
|
||||
String* name = script->names.data[READ_SHORT()];
|
||||
PUSH(varGetAttrib(vm, on, name));
|
||||
DISPATCH();
|
||||
@ -482,8 +514,8 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
||||
|
||||
OPCODE(GET_SUBSCRIPT_AOP):
|
||||
{
|
||||
Var key = *(vm->sp - 1);
|
||||
Var on = *(vm->sp - 2);
|
||||
Var key = *(vm->fiber->sp - 1);
|
||||
Var on = *(vm->fiber->sp - 2);
|
||||
PUSH(varGetSubscript(vm, on, key));
|
||||
CHECK_ERROR();
|
||||
DISPATCH();
|
||||
|
65
src/vm.h
65
src/vm.h
@ -6,8 +6,6 @@
|
||||
#ifndef VM_H
|
||||
#define VM_H
|
||||
|
||||
#include "miniscript.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "compiler.h"
|
||||
#include "var.h"
|
||||
@ -31,6 +29,40 @@ typedef struct {
|
||||
Var* rbp; //< Stack base pointer. (%rbp)
|
||||
} CallFrame;
|
||||
|
||||
struct Fiber {
|
||||
Object _super;
|
||||
|
||||
// The root function of the fiber. (For script it'll be the script's implicit
|
||||
// body function).
|
||||
Function* func;
|
||||
|
||||
// The stack of the execution holding locals and temps. A heap allocated
|
||||
// Will and grow as needed.
|
||||
Var* stack;
|
||||
|
||||
// The stack pointer (%rsp) pointing to the stack top.
|
||||
Var* sp;
|
||||
|
||||
// The stack base pointer of the current frame. It'll be updated before
|
||||
// calling a native function.
|
||||
Var* ret;
|
||||
|
||||
// Size of the allocated stack.
|
||||
int stack_size;
|
||||
|
||||
// Heap allocated array of call frames will grow as needed.
|
||||
CallFrame* frames;
|
||||
|
||||
// Capacity of the frames array.
|
||||
int frame_capacity;
|
||||
|
||||
// Number of frame entry in frames.
|
||||
int frame_count;
|
||||
|
||||
// Runtime error initially NULL, heap allocated.
|
||||
String* error;
|
||||
};
|
||||
|
||||
struct MSVM {
|
||||
|
||||
// The first object in the link list of all heap allocated objects.
|
||||
@ -63,33 +95,12 @@ struct MSVM {
|
||||
// Number of script cache.
|
||||
int script_count;
|
||||
|
||||
// The stack of the execution holding locals and temps. A heap allocated
|
||||
// Will and grow as needed.
|
||||
Var* stack;
|
||||
|
||||
// The stack pointer (%rsp) pointing to the stack top.
|
||||
Var* sp;
|
||||
|
||||
// The stack base pointer of the current frame. It'll be updated before
|
||||
// calling a native function.
|
||||
Var* rbp;
|
||||
|
||||
// Size of the allocated stack.
|
||||
int stack_size;
|
||||
|
||||
// Heap allocated array of call frames will grow as needed.
|
||||
CallFrame* frames;
|
||||
|
||||
// Capacity of the frames array.
|
||||
int frame_capacity;
|
||||
|
||||
// Number of frame entry in frames.
|
||||
int frame_count;
|
||||
|
||||
// Runtime error initially NULL, heap allocated.
|
||||
String* error;
|
||||
// Current fiber.
|
||||
Fiber* fiber;
|
||||
};
|
||||
|
||||
Fiber* newFiber(MSVM* vm);
|
||||
|
||||
// A realloc wrapper which handles memory allocations of the VM.
|
||||
// - To allocate new memory pass NULL to parameter [memory] and 0 to
|
||||
// parameter [old_size] on failure it'll return NULL.
|
||||
|
Loading…
Reference in New Issue
Block a user