From e613203cca087f590a5ed6dc230af45cf7739031 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Thu, 6 May 2021 19:49:30 +0530 Subject: [PATCH] import statement refactor --- include/miniscript.h | 61 ++++++++++++--------- src/compiler.c | 30 +++++++--- src/core.c | 128 ++++++++++++++++++++++--------------------- src/debug.c | 5 +- src/opcodes.h | 5 ++ src/var.c | 28 +++++----- src/var.h | 7 ++- src/vm.c | 128 +++++++++++++++++++++++++++++-------------- src/vm.h | 23 ++------ test/main.c | 30 ++++++---- 10 files changed, 258 insertions(+), 187 deletions(-) diff --git a/include/miniscript.h b/include/miniscript.h index fc3e171..0cfabb3 100644 --- a/include/miniscript.h +++ b/include/miniscript.h @@ -50,10 +50,10 @@ typedef struct MSVM MSVM; // // - To free an allocated memory pass [memory] and 0 to [new_size]. The // function will return NULL. -typedef void* (*MiniScriptReallocFn)(void* memory, size_t new_size, void* user_data); +typedef void* (*msReallocFn)(void* memory, size_t new_size, void* user_data); // C function pointer which is callable from MiniScript. -typedef void (*MiniScriptNativeFn)(MSVM* vm); +typedef void (*msNativeFn)(MSVM* vm); typedef enum { @@ -69,51 +69,60 @@ typedef enum { // Error callback function pointer. for runtime error it'll call first with // MS_ERROR_RUNTIME followed by multiple callbacks with MS_ERROR_STACKTRACE. -typedef void (*MiniScriptErrorFn) (MSVM* vm, MSErrorType type, - const char* file, int line, - const char* message); +typedef void (*msErrorFn) (MSVM* vm, MSErrorType type, + const char* file, int line, + const char* message); // A function callback used by `print()` statement. -typedef void (*MiniScriptWriteFn) (MSVM* vm, const char* text); +typedef void (*msWriteFn) (MSVM* vm, const char* text); + +typedef struct msStringResult msStringResult; + +// A function callback symbol for clean/free the msStringResult. +typedef void (*msResultDoneFn) (MSVM* vm, msStringResult result); // Result of the MiniScriptLoadScriptFn function. -typedef struct { - bool is_failed; - const char* source; - void* user_data; -} MSLoadScriptResult; +struct msStringResult { + bool success; //< State of the result. + const char* string; //< The string result. + void* user_data; //< User related data. + msResultDoneFn on_done; //< Called once vm done with the string. +}; + +// A function callback to resolve the import script name from the [from] path +// to an absolute (or relative to the cwd). This is required to solve same +// script imported with different relative path. +typedef msStringResult (*msResolvePathFn) (MSVM* vm, const char* from, + const char* name); // Load and return the script. Called by the compiler to fetch initial source // code and source for import statements. -typedef MSLoadScriptResult (*MiniScriptLoadScriptFn)(MSVM* vm, - const char* path); +typedef msStringResult (*msLoadScriptFn) (MSVM* vm, const char* path); -// This function will be called once it done with the loaded script. -// [user_data] would be be the one returned in MiniScriptLoadScriptFn -// which is useful to free the source memory if needed. -typedef void (*MiniScriptLoadScriptDoneFn) (MSVM* vm, const char* path, - void* user_data); +// This function will be called once it done with the loaded script only if +// it's corresponding MSLoadScriptResult is succeeded (ie. is_failed = false). +//typedef void (*msLoadDoneFn) (MSVM* vm, msStringResult result); typedef struct { // The callback used to allocate, reallocate, and free. If the function // pointer is NULL it defaults to the VM's realloc(), free() wrappers. - MiniScriptReallocFn realloc_fn; + msReallocFn realloc_fn; - MiniScriptErrorFn error_fn; - MiniScriptWriteFn write_fn; + msErrorFn error_fn; + msWriteFn write_fn; - MiniScriptLoadScriptFn load_script_fn; - MiniScriptLoadScriptDoneFn load_script_done_fn; + msResolvePathFn resolve_path_fn; + msLoadScriptFn load_script_fn; // User defined data associated with VM. void* user_data; -} MSConfiguration; +} msConfiguration; // Initialize the configuration and set ALL of it's values to the defaults. // Call this before setting any particular field of it. -void msInitConfiguration(MSConfiguration* config); +void msInitConfiguration(msConfiguration* config); typedef enum { RESULT_SUCCESS = 0, @@ -122,7 +131,7 @@ typedef enum { } MSInterpretResult; // Allocate initialize and returns a new VM -MSVM* msNewVM(MSConfiguration* config); +MSVM* msNewVM(msConfiguration* config); // Clean the VM and dispose all the resources allocated by the VM. void msFreeVM(MSVM* vm); diff --git a/src/compiler.c b/src/compiler.c index 1ea3df0..dcebc8e 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -91,6 +91,7 @@ typedef enum { //TK_XOREQ, // ^= // Keywords. + TK_IMPORT, // import TK_DEF, // def TK_NATIVE, // native (C function declaration) TK_FUNCTION, // function (literal function) @@ -150,6 +151,7 @@ typedef struct { // List of keywords mapped into their identifiers. static _Keyword _keywords[] = { + { "import", 6, TK_IMPORT }, { "def", 3, TK_DEF }, { "native", 6, TK_NATIVE }, { "function", 8, TK_FUNCTION }, @@ -409,7 +411,7 @@ static void eatString(Parser* parser, bool single_quote) { } // '\0' will be added by varNewSring(); - Var string = VAR_OBJ(&newString(parser->vm, (const char*)buff.data, + Var string = VAR_OBJ(&newStringLength(parser->vm, (const char*)buff.data, (uint32_t)buff.count)->_super); byteBufferClear(&buff, parser->vm); @@ -863,6 +865,7 @@ static void parsePrecedence(Compiler* compiler, Precedence precedence); static void compileExpression(Compiler* compiler); static void exprLiteral(Compiler* compiler, bool can_assign); +static void exprImport(Compiler* compiler, bool can_assign); static void exprFunc(Compiler* compiler, bool can_assign); static void exprName(Compiler* compiler, bool can_assign); @@ -922,9 +925,10 @@ GrammarRule rules[] = { // Prefix Infix Infix Precedence /* TK_DIVEQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT /* TK_SRIGHT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT }, /* TK_SLEFT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT }, + /* TK_IMPORT */ { exprImport, NULL, NO_INFIX }, /* TK_DEF */ NO_RULE, /* TK_EXTERN */ NO_RULE, - /* TK_FUNCTION */ { exprFunc, NULL, NO_INFIX }, + /* TK_FUNCTION */ { exprFunc, NULL, NO_INFIX }, /* TK_END */ NO_RULE, /* TK_NULL */ { exprValue, NULL, NO_INFIX }, /* TK_SELF */ { exprValue, NULL, NO_INFIX }, @@ -990,6 +994,17 @@ static void exprLiteral(Compiler* compiler, bool can_assign) { emitShort(compiler, index); } +static void exprImport(Compiler* compiler, bool can_assign) { + + consume(&compiler->parser, TK_LPARAN, "Expected '(' after import."); + skipNewLines(&compiler->parser); + compileExpression(compiler); + skipNewLines(&compiler->parser); + consume(&compiler->parser, TK_RPARAN, "Expected ')' after parameter list."); + + emitOpcode(compiler, OP_IMPORT); +} + static void exprFunc(Compiler* compiler, bool can_assign) { int fn_index = compileFunction(compiler, FN_LITERAL); emitOpcode(compiler, OP_PUSH_FN); @@ -1200,7 +1215,7 @@ static void exprAttrib(Compiler* compiler, bool can_assign) { int length = parser->previous.length; // Store the name in script's names. - String* string = newString(compiler->vm, name, length); + String* string = newStringLength(compiler->vm, name, length); vmPushTempRef(compiler->vm, &string->_super); stringBufferWrite(&compiler->script->names, compiler->vm, string); vmPopTempRef(compiler->vm); @@ -1756,11 +1771,11 @@ static void compileStatement(Compiler* compiler) { Script* compileSource(MSVM* vm, const char* path) { - MSLoadScriptResult res = vm->config.load_script_fn(vm, path); - if (res.is_failed) // FIXME: + msStringResult res = vm->config.load_script_fn(vm, path); + if (!res.success) // FIXME: vm->config.error_fn(vm, MS_ERROR_COMPILE, NULL, -1, "file load source failed."); - const char* source = res.source; + const char* source = res.string; // Skip utf8 BOM if there is any. if (strncmp(source, "\xEF\xBB\xBF", 3) == 0) source += 3; @@ -1805,8 +1820,7 @@ Script* compileSource(MSVM* vm, const char* path) { emitOpcode(&compiler, OP_END); // Source done callback. - if (vm->config.load_script_done_fn != NULL) - vm->config.load_script_done_fn(vm, path, res.user_data); + if (res.on_done != NULL) res.on_done(vm, res); // Create script globals. for (int i = 0; i < compiler.var_count; i++) { diff --git a/src/core.c b/src/core.c index 2af68e6..4732409 100644 --- a/src/core.c +++ b/src/core.c @@ -28,7 +28,7 @@ static _BuiltinFn builtins[BUILTIN_COUNT]; static int builtins_count = 0; static void initializeBuiltinFN(MSVM* vm, _BuiltinFn* bfn, const char* name, - int length, int arity, MiniScriptNativeFn ptr) { + int length, int arity, msNativeFn ptr) { bfn->name = name; bfn->length = length; @@ -68,7 +68,7 @@ static inline bool isNumeric(Var var, double* value) { static inline bool validateNumeric(MSVM* vm, Var var, double* value, const char* name) { if (isNumeric(var, value)) return true; - msSetRuntimeError(vm, "%s must be a numeric value.", name); + vm->fiber->error = stringFormat(vm, "$ must be a numeric value.", name); return false; } @@ -84,14 +84,14 @@ static inline bool validateIngeger(MSVM* vm, Var var, int32_t* value, } } - msSetRuntimeError(vm, "%s must be an integer.", name); + vm->fiber->error = stringFormat(vm, "$ must be an integer.", name); return false; } static inline bool validateIndex(MSVM* vm, int32_t index, int32_t size, const char* container) { if (index < 0 || size <= index) { - msSetRuntimeError(vm, "%s index out of range.", container); + vm->fiber->error = stringFormat(vm, "$ index out of range.", container); return false; } return true; @@ -174,21 +174,21 @@ void corePrint(MSVM* vm) { vm->config.write_fn(vm, "\n"); } -void coreImport(MSVM* vm) { - Var arg1 = vm->fiber->ret[1]; - if (!IS_OBJ(arg1) || AS_OBJ(arg1)->type != OBJ_STRING) { - msSetRuntimeError(vm, "Expected a String argument."); - } - - String* path = (String*)AS_OBJ(arg1); - if (path->length > 4 && strncmp(path->data, "std:", 4) == 0) { - Script* scr = vmGetStdScript(vm, path->data + 4); - ASSERT(scr != NULL, OOPS); - RET(VAR_OBJ(scr)); - } - - TODO; -} +//void coreImport(MSVM* vm) { +// Var arg1 = vm->fiber->ret[1]; +// if (!IS_OBJ(arg1) || AS_OBJ(arg1)->type != OBJ_STRING) { +// msSetRuntimeError(vm, "Expected a String argument."); +// } +// +// String* path = (String*)AS_OBJ(arg1); +// if (path->length > 4 && strncmp(path->data, "std:", 4) == 0) { +// Script* scr = vmGetStdScript(vm, path->data + 4); +// ASSERT(scr != NULL, OOPS); +// RET(VAR_OBJ(scr)); +// } +// +// TODO; +//} /*****************************************************************************/ /* STD METHODS */ @@ -198,7 +198,7 @@ void coreImport(MSVM* vm) { void stdListSort(MSVM* vm) { Var list = ARG(1); if (!IS_OBJ(list) || AS_OBJ(list)->type != OBJ_LIST) { - msSetRuntimeError(vm, "Expected a list at argument 1."); + vm->fiber->error = newString(vm, "Expected a list at argument 1."); } // TODO: sort. @@ -237,15 +237,14 @@ void initializeCore(MSVM* vm) { INITALIZE_BUILTIN_FN("to_string", coreToString, 1); INITALIZE_BUILTIN_FN("print", corePrint, -1); - INITALIZE_BUILTIN_FN("import", coreImport, 1); + //INITALIZE_BUILTIN_FN("import", coreImport, 1); // Sentinal to mark the end of the array. //initializeBuiltinFN(vm, &builtins[i], NULL, 0, 0, NULL); // Make STD scripts. - Script* std; // A temporary pointer to the current std script. - Function* fn; // A temporary pointer to the allocated function function. - + //Script* std; // A temporary pointer to the current std script. + //Function* fn; // A temporary pointer to the allocated function function. #define STD_NEW_SCRIPT(_name) \ do { \ std = newScript(vm); \ @@ -261,14 +260,13 @@ void initializeCore(MSVM* vm) { fn->native = fptr; \ fn->arity = _arity; \ } while (false) - - // std:list script. - STD_NEW_SCRIPT("std:list"); - STD_ADD_FUNCTION("sort", stdListSort, 1); - - // std:os script. - STD_NEW_SCRIPT("std:os"); - STD_ADD_FUNCTION("clock", stdOsClock, 0); + // TODO: add std scripts to vm. +// STD_NEW_SCRIPT("std:list"); +// STD_ADD_FUNCTION("sort", stdListSort, 1); +// +// // std:os script. +// STD_NEW_SCRIPT("std:os"); +// STD_ADD_FUNCTION("clock", stdOsClock, 0); } void markCoreObjects(MSVM* vm) { @@ -314,8 +312,8 @@ Var varAdd(MSVM* vm, Var v1, Var v2) { } - msSetRuntimeError(vm, "Unsupported operand types for operator '-' " - "%s and %s", varTypeName(v1), varTypeName(v2)); + vm->fiber->error = stringFormat(vm, "Unsupported operand types for operator '-' " + "$ and $", varTypeName(v1), varTypeName(v2)); return VAR_NULL; } @@ -331,8 +329,8 @@ Var varSubtract(MSVM* vm, Var v1, Var v2) { TODO; // for user objects call vm.config.sub_userobj_sub(handles). - msSetRuntimeError(vm, "Unsupported operand types for operator '-' " - "%s and %s", varTypeName(v1), varTypeName(v2)); + vm->fiber->error = stringFormat(vm, "Unsupported operand types for operator '-' " + "$ and $", varTypeName(v1), varTypeName(v2)); return VAR_NULL; } @@ -347,8 +345,8 @@ Var varMultiply(MSVM* vm, Var v1, Var v2) { return VAR_NULL; } - msSetRuntimeError(vm, "Unsupported operand types for operator '*' " - "%s and %s", varTypeName(v1), varTypeName(v2)); + vm->fiber->error = stringFormat(vm, "Unsupported operand types for operator " + "'*' $ and $", varTypeName(v1), varTypeName(v2)); return VAR_NULL; } @@ -361,8 +359,8 @@ Var varDivide(MSVM* vm, Var v1, Var v2) { return VAR_NULL; } - msSetRuntimeError(vm, "Unsupported operand types for operator '/' " - "%s and %s", varTypeName(v1), varTypeName(v2)); + vm->fiber->error = stringFormat(vm, "Unsupported operand types for operator " + "'/' $ and $", varTypeName(v1), varTypeName(v2)); return VAR_NULL; } @@ -390,14 +388,16 @@ bool varLesser(MSVM* vm, Var v1, Var v2) { #define IS_ATTRIB(name) \ (attrib->length == strlen(name) && strcmp(name, attrib->data) == 0) -#define ERR_NO_ATTRIB() \ - msSetRuntimeError(vm, "'%s' objects has no attribute named '%s'", \ - varTypeName(on), attrib->data); +#define ERR_NO_ATTRIB() \ + vm->fiber->error = stringFormat(vm, "'$' objects has no attribute " \ + "named '$'", \ + varTypeName(on), attrib->data); Var varGetAttrib(MSVM* vm, Var on, String* attrib) { if (!IS_OBJ(on)) { - msSetRuntimeError(vm, "%s type is not subscriptable.", varTypeName(on)); + vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.", + varTypeName(on)); return VAR_NULL; } @@ -429,7 +429,7 @@ Var varGetAttrib(MSVM* vm, Var on, String* attrib) { { Var value = mapGet((Map*)obj, VAR_OBJ(&attrib->_super)); if (IS_UNDEF(value)) { - msSetRuntimeError(vm, "Key (\"%s\") not exists.", attrib->data); + vm->fiber->error = stringFormat(vm, "Key (\"@\") not exists.", attrib); return VAR_NULL; } return value; @@ -475,16 +475,17 @@ Var varGetAttrib(MSVM* vm, Var on, String* attrib) { void varSetAttrib(MSVM* vm, Var on, String* attrib, Var value) { -#define ATTRIB_IMMUTABLE(prop) \ -do { \ - if (IS_ATTRIB(prop)) { \ - msSetRuntimeError(vm, "'%s' attribute is immutable.", prop); \ - return; \ - } \ +#define ATTRIB_IMMUTABLE(prop) \ +do { \ + if (IS_ATTRIB(prop)) { \ + vm->fiber->error = stringFormat(vm, "'$' attribute is immutable.", prop); \ + return; \ + } \ } while (false) if (!IS_OBJ(on)) { - msSetRuntimeError(vm, "%s type is not subscriptable.", varTypeName(on)); + vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.", + varTypeName(on)); return; } @@ -553,7 +554,8 @@ do { \ Var varGetSubscript(MSVM* vm, Var on, Var key) { if (!IS_OBJ(on)) { - msSetRuntimeError(vm, "%s type is not subscriptable.", varTypeName(on)); + vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.", + varTypeName(on)); return VAR_NULL; } @@ -569,7 +571,7 @@ Var varGetSubscript(MSVM* vm, Var on, Var key) { if (!validateIndex(vm, index, str->length, "String")) { return VAR_NULL; } - String* c = newString(vm, str->data + index, 1); + String* c = newStringLength(vm, str->data + index, 1); return VAR_OBJ(c); } @@ -591,9 +593,11 @@ Var varGetSubscript(MSVM* vm, Var on, Var key) { Var value = mapGet((Map*)obj, key); if (IS_UNDEF(value)) { String* key_str = toString(vm, key, true); + vmPushTempRef(vm, &key_str->_super); - msSetRuntimeError(vm, "Key (%s) not exists.", key_str->data); + vm->fiber->error = stringFormat(vm, "Key (@) not exists", key_str); vmPopTempRef(vm); + return VAR_NULL; } return value; @@ -616,14 +620,14 @@ Var varGetSubscript(MSVM* vm, Var on, Var key) { void varsetSubscript(MSVM* vm, Var on, Var key, Var value) { if (!IS_OBJ(on)) { - msSetRuntimeError(vm, "%s type is not subscriptable.", varTypeName(on)); + vm->fiber->error = stringFormat(vm, "$ type is not subscriptable.", varTypeName(on)); return; } Object* obj = AS_OBJ(on); switch (obj->type) { case OBJ_STRING: - msSetRuntimeError(vm, "String objects are immutable."); + vm->fiber->error = newString(vm, "String objects are immutable."); return; case OBJ_LIST: @@ -639,7 +643,7 @@ void varsetSubscript(MSVM* vm, Var on, Var key, Var value) { case OBJ_MAP: { if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) { - msSetRuntimeError(vm, "%s type is not hashable.", varTypeName(key)); + vm->fiber->error = stringFormat(vm, "$ type is not hashable.", varTypeName(key)); } else { mapSet((Map*)obj, vm, key, value); } @@ -672,11 +676,11 @@ bool varIterate(MSVM* vm, Var seq, Var* iterator, Var* value) { // Primitive types are not iterable. if (!IS_OBJ(seq)) { if (IS_NULL(seq)) { - msSetRuntimeError(vm, "Null is not iterable."); + vm->fiber->error = newString(vm, "Null is not iterable."); } else if (IS_BOOL(seq)) { - msSetRuntimeError(vm, "Boolenan is not iterable."); + vm->fiber->error = newString(vm, "Boolenan is not iterable."); } else if (IS_NUM(seq)) { - msSetRuntimeError(vm, "Number is not iterable."); + vm->fiber->error = newString(vm, "Number is not iterable."); } else { UNREACHABLE(); } @@ -699,7 +703,7 @@ bool varIterate(MSVM* vm, Var seq, Var* iterator, Var* value) { return false; //< Stop iteration. } // TODO: Or I could add char as a type for efficiency. - *value = VAR_OBJ(newString(vm, str->data + iter, 1)); + *value = VAR_OBJ(newStringLength(vm, str->data + iter, 1)); *iterator = VAR_NUM((double)iter + 1); return true; } diff --git a/src/debug.c b/src/debug.c index 6eba028..581bf15 100644 --- a/src/debug.c +++ b/src/debug.c @@ -181,9 +181,8 @@ void dumpInstructions(MSVM* vm, Function* func) { } - case OP_POP: - NO_ARGS(); - break; + case OP_POP: NO_ARGS(); break; + case OP_IMPORT: NO_ARGS(); break; case OP_CALL: printf("%5d (argc)\n", READ_SHORT()); diff --git a/src/opcodes.h b/src/opcodes.h index 61cfb2c..b9275c8 100644 --- a/src/opcodes.h +++ b/src/opcodes.h @@ -99,6 +99,11 @@ OPCODE(POP, 0, -1) // marked explicitly since it's performance criticle. // params: n bytes argc. +// Pop the path from the stack, import the module at the path and push the +// script in the script. If the script is imported for the first time (not +// cached) the script's body will be executed. +OPCODE(IMPORT, 0, 0) + // TODO: may be later. //OPCODE(CALL_0, 0, 0) //< Push null call null will be the return value. //OPCODE(CALL_1, 0, -1) //< Push null and arg1. arg1 will be popped. diff --git a/src/var.c b/src/var.c index 398f773..cfdc11c 100644 --- a/src/var.c +++ b/src/var.c @@ -19,7 +19,7 @@ Var msVarNumber(MSVM* vm, double value) { } Var msVarString(MSVM* vm, const char* value) { - return VAR_OBJ(newString(vm, value, (uint32_t)strlen(value))); + return VAR_OBJ(newStringLength(vm, value, (uint32_t)strlen(value))); } bool msAsBool(MSVM* vm, Var value) { @@ -230,7 +230,7 @@ static String* _allocateString(MSVM* vm, size_t length) { return string; } -String* newString(MSVM* vm, const char* text, uint32_t length) { +String* newStringLength(MSVM* vm, const char* text, uint32_t length) { ASSERT(length == 0 || text != NULL, "Unexpected NULL string."); @@ -699,20 +699,20 @@ bool isObjectHashable(ObjectType type) { String* toString(MSVM* vm, Var v, bool recursive) { if (IS_NULL(v)) { - return newString(vm, "null", 4); + return newStringLength(vm, "null", 4); } else if (IS_BOOL(v)) { if (AS_BOOL(v)) { - return newString(vm, "true", 4); + return newStringLength(vm, "true", 4); } else { - return newString(vm, "false", 5); + return newStringLength(vm, "false", 5); } } else if (IS_NUM(v)) { char buff[TO_STRING_BUFF_SIZE]; int length = sprintf(buff, "%.14g", AS_NUM(v)); ASSERT(length < TO_STRING_BUFF_SIZE, "Buffer overflowed."); - return newString(vm, buff, length); + return newStringLength(vm, buff, length); } else if (IS_OBJ(v)) { Object* obj = AS_OBJ(v); @@ -720,14 +720,14 @@ String* toString(MSVM* vm, Var v, bool recursive) { case OBJ_STRING: { // If recursive return with quotes (ex: [42, "hello", 0..10]). - String* string = newString(vm, ((String*)obj)->data, ((String*)obj)->length); + String* string = newStringLength(vm, ((String*)obj)->data, ((String*)obj)->length); if (!recursive) return string; else return stringFormat(vm, "\"@\"", string); } case OBJ_LIST: { List* list = (List*)obj; - String* result = newString(vm, "[", 1); + String* result = newStringLength(vm, "[", 1); for (uint32_t i = 0; i < list->elements.count; i++) { const char* fmt = (i != 0) ? "@, @" : "@@"; @@ -743,7 +743,7 @@ String* toString(MSVM* vm, Var v, bool recursive) { case OBJ_MAP: { Map* map = (Map*)obj; - String* result = newString(vm, "{", 1); + String* result = newStringLength(vm, "{", 1); uint32_t i = 0; bool _first = true; // For first element no ',' required. @@ -774,8 +774,8 @@ String* toString(MSVM* vm, Var v, bool recursive) { return stringFormat(vm, "@}", result); } - case OBJ_RANGE: return newString(vm, "[Range]", 7); // TODO; - case OBJ_SCRIPT: return newString(vm, "[Script]", 8); // TODO; + case OBJ_RANGE: return newStringLength(vm, "[Range]", 7); // TODO; + case OBJ_SCRIPT: return newStringLength(vm, "[Script]", 8); // TODO; case OBJ_FUNC: { const char* name = ((Function*)obj)->name; int length = (int)strlen(name); // TODO: Assert length. @@ -783,9 +783,9 @@ String* toString(MSVM* vm, Var v, bool recursive) { memcpy(buff, "[Func:", 6); memcpy(buff + 6, name, length); buff[6 + length] = ']'; - return newString(vm, buff, 6 + length + 1); + return newStringLength(vm, buff, 6 + length + 1); } - case OBJ_USER: return newString(vm, "[UserObj]", 9); // TODO; + case OBJ_USER: return newStringLength(vm, "[UserObj]", 9); // TODO; break; } @@ -887,7 +887,7 @@ uint32_t scriptAddName(Script* self, MSVM* vm, const char* name, // If we reach here the name doesn't exists in the buffer, so add it and // return the index. - String* new_name = newString(vm, name, length); + String* new_name = newStringLength(vm, name, length); vmPushTempRef(vm, &new_name->_super); stringBufferWrite(&self->names, vm, new_name); vmPopTempRef(vm); diff --git a/src/var.h b/src/var.h index 6104b7c..ca6bfd5 100644 --- a/src/var.h +++ b/src/var.h @@ -307,7 +307,7 @@ struct Function { bool is_native; //< True if Native function. union { - MiniScriptNativeFn native; //< Native function pointer. + msNativeFn native; //< Native function pointer. Fn* fn; //< Script function pointer. }; }; @@ -386,7 +386,10 @@ Var doubleToVar(double value); double varToDouble(Var value); // Allocate new String object and return String*. -String* newString(MSVM* vm, const char* text, uint32_t length); +String* newStringLength(MSVM* vm, const char* text, uint32_t length); +static inline String* newString(MSVM* vm, const char* text) { + return newStringLength(vm, text, (uint32_t)strlen(text)); +} // Allocate new List and return List*. List* newList(MSVM* vm, uint32_t size); diff --git a/src/vm.c b/src/vm.c index 73387f8..8293db2 100644 --- a/src/vm.c +++ b/src/vm.c @@ -46,7 +46,7 @@ void* vmRealloc(MSVM* self, void* memory, size_t old_size, size_t new_size) { return self->config.realloc_fn(memory, new_size, self->config.user_data); } -void msInitConfiguration(MSConfiguration* config) { +void msInitConfiguration(msConfiguration* config) { config->realloc_fn = defaultRealloc; // TODO: Handle Null functions before calling them. @@ -54,18 +54,36 @@ void msInitConfiguration(MSConfiguration* config) { config->write_fn = NULL; config->load_script_fn = NULL; - config->load_script_done_fn = NULL; config->user_data = NULL; } -MSVM* msNewVM(MSConfiguration* config) { - MSVM* vm = (MSVM*)malloc(sizeof(MSVM)); - vmInit(vm, config); +MSVM* msNewVM(msConfiguration* config) { + + msReallocFn realloc_fn = defaultRealloc; + void* user_data = NULL; + if (config != NULL) { + realloc_fn = config->realloc_fn; + user_data = config->user_data; + } + MSVM* vm = (MSVM*)realloc_fn(NULL, sizeof(MSVM), user_data); + memset(vm, 0, sizeof(MSVM)); + + vm->config = *config; + vm->gray_list_count = 0; + vm->gray_list_capacity = MIN_CAPACITY; + vm->gray_list = (Object**)vm->config.realloc_fn( + NULL, sizeof(Object*) * vm->gray_list_capacity, NULL); + vm->next_gc = 1024 * 1024 * 10; // TODO: + + vm->scripts = newMap(vm); + + // TODO: no need to initialize if already done by another vm. + initializeCore(vm); + return vm; } void msFreeVM(MSVM* self) { - // TODO: Check if vm already freed. Object* obj = self->first; while (obj != NULL) { @@ -76,21 +94,8 @@ void msFreeVM(MSVM* self) { self->gray_list = (Object**)self->config.realloc_fn( self->gray_list, 0, self->config.user_data); - self->config.realloc_fn(self, 0, self->config.user_data); -} -void vmInit(MSVM* self, MSConfiguration* config) { - memset(self, 0, sizeof(MSVM)); - self->config = *config; - - self->gray_list_count = 0; - self->gray_list_capacity = 8; // TODO: refactor the magic '8' here. - self->gray_list = (Object**)self->config.realloc_fn( - NULL, sizeof(Object*) * self->gray_list_capacity, NULL); - self->next_gc = 1024 * 1024 * 10; // TODO: - - // TODO: no need to initialize if already done by another vm. - initializeCore(self); + DEALLOCATE(self, self); } void vmPushTempRef(MSVM* self, Object* obj) { @@ -114,10 +119,8 @@ void vmCollectGarbage(MSVM* self) { // Mark core objects (mostlikely builtin functions). markCoreObjects(self); - // Mark all the 'std' scripts. - for (int i = 0; i < self->std_count; i++) { - grayObject(&(self->std_scripts[i]->_super), self); - } + // Mark the scripts cache. + grayObject(&self->scripts->_super, self); // Mark temp references. for (int i = 0; i < self->temp_reference_count; i++) { @@ -143,22 +146,6 @@ void vmCollectGarbage(MSVM* self) { TODO; // Sweep. } -void vmAddStdScript(MSVM* self, Script* script) { - ASSERT(self->std_count < MAX_SCRIPT_CACHE, OOPS); - self->std_scripts[self->std_count++] = script; -} - -Script* vmGetStdScript(MSVM* self, const char* name) { - for (int i = 0; i < self->std_count; i++) { - Script* scr = self->std_scripts[i]; - // +4 to skip "std:". - if (strcmp(name, scr->name + 4) == 0) { - return scr; - } - } - return NULL; -} - void* msGetUserData(MSVM* vm) { return vm->config.user_data; } @@ -171,6 +158,45 @@ void msSetUserData(MSVM* vm, void* user_data) { * RUNTIME * *****************************************************************************/ +static String* resolveScriptPath(MSVM* vm, String* name) { + TODO; + return NULL; +} + +static Var importScript(MSVM* vm, String* name, bool* is_new_script) { + + name = resolveScriptPath(vm, name); + + // Check if the script is already cached (then use it). + Var scr = mapGet(vm->scripts, VAR_OBJ(&name->_super)); + if (!IS_UNDEF(scr)) { + ASSERT(AS_OBJ(scr)->type == OBJ_SCRIPT, OOPS); + *is_new_script = false; + return scr; + } + *is_new_script = true; + + vmPushTempRef(vm, &name->_super); + + msStringResult result = { false, NULL, NULL }; + if (vm->config.load_script_fn != NULL) + result = vm->config.load_script_fn(vm, name->data); + + if (!result.success) { + vmPopTempRef(vm); // name + + String* err_msg = stringFormat(vm, "Cannot import script '@'", name); + vm->fiber->error = err_msg; //< Set the error msg. + + return VAR_NULL; + } + + vmPopTempRef(vm); // name + + + TODO; // Compile the script and ... +} + static void ensureStackSize(MSVM* vm, int size) { if (vm->fiber->stack_size > size) return; TODO; @@ -200,8 +226,8 @@ static inline void pushCallFrame(MSVM* vm, Function* fn) { } void msSetRuntimeError(MSVM* vm, const char* format, ...) { - vm->fiber->error = newString(vm, "TODO:", 5); - TODO; + vm->fiber->error = newString(vm, "TODO:"); + TODO; // Construct String and set to vm->fiber->error. } void vmReportError(MSVM* vm) { @@ -468,6 +494,24 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) { DROP(); DISPATCH(); + OPCODE(IMPORT) : + { + Var path = POP(); + if (!IS_OBJ(path) || AS_OBJ(path)->type != OBJ_STRING) { + RUNTIME_ERROR("Expected a string argument for import statement."); + } + + bool is_new_script = false; + PUSH(importScript(vm, (String*)AS_OBJ(path), &is_new_script)); + CHECK_ERROR(); + + if (is_new_script) { + TODO; // Execute the script. + } + + DISPATCH(); + } + OPCODE(CALL): { uint16_t argc = READ_SHORT(); diff --git a/src/vm.h b/src/vm.h index efc1157..082fbe4 100644 --- a/src/vm.h +++ b/src/vm.h @@ -14,9 +14,6 @@ // garbage collected. #define MAX_TEMP_REFERENCE 16 -// The maximum number of script cache can vm hold at once. -#define MAX_SCRIPT_CACHE 128 - typedef enum { #define OPCODE(name, _, __) OP_##name, #include "opcodes.h" @@ -46,15 +43,15 @@ struct MSVM { int temp_reference_count; // VM's configurations. - MSConfiguration config; + msConfiguration config; // Current compiler reference to mark it's heap allocated objects. Note that // The compiler isn't heap allocated. Compiler* compiler; - // Std scripts array. (TODO: assert "std" scripts doesn't have global vars). - Script* std_scripts[MAX_SCRIPT_CACHE]; - int std_count; + // A cache of the compiled scripts with their path as key and the Scrpit + // object as the value. + Map* scripts; // Execution variables //////////////////////////////////////////////////// @@ -77,10 +74,6 @@ struct MSVM { // going to track deallocated bytes, instead use garbage collector to do it. void* vmRealloc(MSVM* self, void* memory, size_t old_size, size_t new_size); -// Initialize the vm and update the configuration. If config is NULL it'll use -// the default configuration. -void vmInit(MSVM* self, MSConfiguration* config); - // Push the object to temporary references stack. void vmPushTempRef(MSVM* self, Object* obj); @@ -90,14 +83,6 @@ void vmPopTempRef(MSVM* self); // Trigger garbage collection manually. void vmCollectGarbage(MSVM* self); -// Add a std script to vm when initializing core. -void vmAddStdScript(MSVM* self, Script* script); - -// Returns the std script with the name [name]. Note that the name shouldn't -// be start with "std:" but the actual name of the script. If not found -// returns NULL. -Script* vmGetStdScript(MSVM* self, const char* name); - // Runs the script and return result. MSInterpretResult vmRunScript(MSVM* vm, Script* script); diff --git a/test/main.c b/test/main.c index 06c1bf3..79ee61b 100644 --- a/test/main.c +++ b/test/main.c @@ -17,20 +17,30 @@ void writeFunction(MSVM* vm, const char* text) { fprintf(stdout, "%s", text); } -void loadScriptDone(MSVM* vm, const char* path, void* user_data) { - // User data is the allocated source code buffer and it has to be freed +void onResultDone(MSVM* vm, msStringResult result) { + // The result.string is the allocated buffer and it has to be freed // manually since it wasn't allocated by the VM. - free(user_data); + free((void*)result.string); } -MSLoadScriptResult loadScript(MSVM* vm, const char* path) { - MSLoadScriptResult result; - result.is_failed = false; +msStringResult resolvePath(MSVM* vm, const char* from, const char* name) { + msStringResult result; + result.success = true; + result.on_done = onResultDone; + + __debugbreak(); + return result; +} + +msStringResult loadScript(MSVM* vm, const char* path) { + msStringResult result; + result.success = true; + result.on_done = onResultDone; // Open the file. FILE* file = fopen(path, "r"); if (file == NULL) { - result.is_failed = true; + result.success = true; return result; } @@ -47,8 +57,7 @@ MSLoadScriptResult loadScript(MSVM* vm, const char* path) { buff[read] = '\0'; fclose(file); - result.source = buff; - result.user_data = (void*)buff; + result.string = buff; return result; } @@ -67,12 +76,11 @@ int main(int argc, char** argv) { const char* source_path = argv[1]; - MSConfiguration config; + msConfiguration config; msInitConfiguration(&config); config.error_fn = errorPrint; config.write_fn = writeFunction; config.load_script_fn = loadScript; - config.load_script_done_fn = loadScriptDone; MSVM* vm = msNewVM(&config); MSInterpretResult result = msInterpret(vm, source_path);