From f7c3b2b8e2e2e206aa9971dad0bdb310ee4a71bc Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Thu, 25 Feb 2021 14:33:06 +0530 Subject: [PATCH] fiber implemented and calling convention fixed. --- TODO.txt | 2 +- include/miniscript.h | 26 +++ src/common.h | 22 +-- src/compiler.c | 28 ++- src/compiler.h | 2 - src/core.c | 33 ++-- src/core.h | 2 +- src/debug.c | 412 +++++++++++++++++++++++-------------------- src/debug.h | 4 +- src/var.c | 30 +++- src/var.h | 23 ++- src/vm.c | 174 ++++++++++-------- src/vm.h | 65 ++++--- 13 files changed, 484 insertions(+), 339 deletions(-) diff --git a/TODO.txt b/TODO.txt index 769ff84..0ced204 100644 --- a/TODO.txt +++ b/TODO.txt @@ -22,5 +22,5 @@ // Bugs. -[ ] `function() "do" end` make 'do' keyword optional here. [ ] Update cache on each recompilation instead of making a new cache. +[*] `function() "do" end` make 'do' keyword optional here. diff --git a/include/miniscript.h b/include/miniscript.h index a177dda..0f22deb 100644 --- a/include/miniscript.h +++ b/include/miniscript.h @@ -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 diff --git a/src/common.h b/src/common.h index 8f5277b..81a7b2a 100644 --- a/src/common.h +++ b/src/common.h @@ -6,6 +6,8 @@ #ifndef MS_COMMON_H #define MS_COMMON_H +#include "miniscript.h" + #include #include #include @@ -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 diff --git a/src/compiler.c b/src/compiler.c index fcd0fe2..b5d305d 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -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; } } diff --git a/src/compiler.h b/src/compiler.h index a856a85..19affa5 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -6,8 +6,6 @@ #ifndef COMPILER_H #define COMPILER_H -#include "miniscript.h" - #include "common.h" #include "var.h" diff --git a/src/core.c b/src/core.c index 5273b80..959cf15 100644 --- a/src/core.c +++ b/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; } \ No newline at end of file diff --git a/src/core.h b/src/core.h index 0e6fd4b..fdbec1b 100644 --- a/src/core.h +++ b/src/core.h @@ -7,7 +7,7 @@ #define CORE_H #include "var.h" -#include "miniscript.h" +#include "common.h" void initializeCore(MSVM* vm); diff --git a/src/debug.c b/src/debug.c index db15c66..8cfa06e 100644 --- a/src/debug.c +++ b/src/debug.c @@ -3,84 +3,91 @@ * Licensed under: MIT License */ +#include + #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. + // + //} + +} diff --git a/src/debug.h b/src/debug.h index bddc83a..5a4cb5d 100644 --- a/src/debug.h +++ b/src/debug.h @@ -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 diff --git a/src/var.c b/src/var.c index 60c2a05..721b393 100644 --- a/src/var.c +++ b/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); diff --git a/src/var.h b/src/var.h index da97fcd..a1efdc6 100644 --- a/src/var.h +++ b/src/var.h @@ -30,8 +30,7 @@ #include #include -#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. diff --git a/src/vm.c b/src/vm.c index bd229b4..b3a4f4e 100644 --- a/src/vm.c +++ b/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 -// 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 + +// 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(); diff --git a/src/vm.h b/src/vm.h index 7513e1e..565c6cc 100644 --- a/src/vm.h +++ b/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.