mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 07:00:58 +08:00
import statement refactor
This commit is contained in:
parent
0db6cf9780
commit
e613203cca
@ -50,10 +50,10 @@ typedef struct MSVM MSVM;
|
|||||||
//
|
//
|
||||||
// - To free an allocated memory pass [memory] and 0 to [new_size]. The
|
// - To free an allocated memory pass [memory] and 0 to [new_size]. The
|
||||||
// function will return NULL.
|
// 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.
|
// C function pointer which is callable from MiniScript.
|
||||||
typedef void (*MiniScriptNativeFn)(MSVM* vm);
|
typedef void (*msNativeFn)(MSVM* vm);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
|
||||||
@ -69,51 +69,60 @@ typedef enum {
|
|||||||
|
|
||||||
// Error callback function pointer. for runtime error it'll call first with
|
// Error callback function pointer. for runtime error it'll call first with
|
||||||
// MS_ERROR_RUNTIME followed by multiple callbacks with MS_ERROR_STACKTRACE.
|
// MS_ERROR_RUNTIME followed by multiple callbacks with MS_ERROR_STACKTRACE.
|
||||||
typedef void (*MiniScriptErrorFn) (MSVM* vm, MSErrorType type,
|
typedef void (*msErrorFn) (MSVM* vm, MSErrorType type,
|
||||||
const char* file, int line,
|
const char* file, int line,
|
||||||
const char* message);
|
const char* message);
|
||||||
|
|
||||||
// A function callback used by `print()` statement.
|
// 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.
|
// Result of the MiniScriptLoadScriptFn function.
|
||||||
typedef struct {
|
struct msStringResult {
|
||||||
bool is_failed;
|
bool success; //< State of the result.
|
||||||
const char* source;
|
const char* string; //< The string result.
|
||||||
void* user_data;
|
void* user_data; //< User related data.
|
||||||
} MSLoadScriptResult;
|
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
|
// Load and return the script. Called by the compiler to fetch initial source
|
||||||
// code and source for import statements.
|
// code and source for import statements.
|
||||||
typedef MSLoadScriptResult (*MiniScriptLoadScriptFn)(MSVM* vm,
|
typedef msStringResult (*msLoadScriptFn) (MSVM* vm, const char* path);
|
||||||
const char* path);
|
|
||||||
|
|
||||||
// This function will be called once it done with the loaded script.
|
// This function will be called once it done with the loaded script only if
|
||||||
// [user_data] would be be the one returned in MiniScriptLoadScriptFn
|
// it's corresponding MSLoadScriptResult is succeeded (ie. is_failed = false).
|
||||||
// which is useful to free the source memory if needed.
|
//typedef void (*msLoadDoneFn) (MSVM* vm, msStringResult result);
|
||||||
typedef void (*MiniScriptLoadScriptDoneFn) (MSVM* vm, const char* path,
|
|
||||||
void* user_data);
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
||||||
// The callback used to allocate, reallocate, and free. If the function
|
// The callback used to allocate, reallocate, and free. If the function
|
||||||
// pointer is NULL it defaults to the VM's realloc(), free() wrappers.
|
// pointer is NULL it defaults to the VM's realloc(), free() wrappers.
|
||||||
MiniScriptReallocFn realloc_fn;
|
msReallocFn realloc_fn;
|
||||||
|
|
||||||
MiniScriptErrorFn error_fn;
|
msErrorFn error_fn;
|
||||||
MiniScriptWriteFn write_fn;
|
msWriteFn write_fn;
|
||||||
|
|
||||||
MiniScriptLoadScriptFn load_script_fn;
|
msResolvePathFn resolve_path_fn;
|
||||||
MiniScriptLoadScriptDoneFn load_script_done_fn;
|
msLoadScriptFn load_script_fn;
|
||||||
|
|
||||||
// User defined data associated with VM.
|
// User defined data associated with VM.
|
||||||
void* user_data;
|
void* user_data;
|
||||||
|
|
||||||
} MSConfiguration;
|
} msConfiguration;
|
||||||
|
|
||||||
// Initialize the configuration and set ALL of it's values to the defaults.
|
// Initialize the configuration and set ALL of it's values to the defaults.
|
||||||
// Call this before setting any particular field of it.
|
// Call this before setting any particular field of it.
|
||||||
void msInitConfiguration(MSConfiguration* config);
|
void msInitConfiguration(msConfiguration* config);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
RESULT_SUCCESS = 0,
|
RESULT_SUCCESS = 0,
|
||||||
@ -122,7 +131,7 @@ typedef enum {
|
|||||||
} MSInterpretResult;
|
} MSInterpretResult;
|
||||||
|
|
||||||
// Allocate initialize and returns a new VM
|
// 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.
|
// Clean the VM and dispose all the resources allocated by the VM.
|
||||||
void msFreeVM(MSVM* vm);
|
void msFreeVM(MSVM* vm);
|
||||||
|
@ -91,6 +91,7 @@ typedef enum {
|
|||||||
//TK_XOREQ, // ^=
|
//TK_XOREQ, // ^=
|
||||||
|
|
||||||
// Keywords.
|
// Keywords.
|
||||||
|
TK_IMPORT, // import
|
||||||
TK_DEF, // def
|
TK_DEF, // def
|
||||||
TK_NATIVE, // native (C function declaration)
|
TK_NATIVE, // native (C function declaration)
|
||||||
TK_FUNCTION, // function (literal function)
|
TK_FUNCTION, // function (literal function)
|
||||||
@ -150,6 +151,7 @@ typedef struct {
|
|||||||
|
|
||||||
// List of keywords mapped into their identifiers.
|
// List of keywords mapped into their identifiers.
|
||||||
static _Keyword _keywords[] = {
|
static _Keyword _keywords[] = {
|
||||||
|
{ "import", 6, TK_IMPORT },
|
||||||
{ "def", 3, TK_DEF },
|
{ "def", 3, TK_DEF },
|
||||||
{ "native", 6, TK_NATIVE },
|
{ "native", 6, TK_NATIVE },
|
||||||
{ "function", 8, TK_FUNCTION },
|
{ "function", 8, TK_FUNCTION },
|
||||||
@ -409,7 +411,7 @@ static void eatString(Parser* parser, bool single_quote) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// '\0' will be added by varNewSring();
|
// '\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);
|
(uint32_t)buff.count)->_super);
|
||||||
|
|
||||||
byteBufferClear(&buff, parser->vm);
|
byteBufferClear(&buff, parser->vm);
|
||||||
@ -863,6 +865,7 @@ static void parsePrecedence(Compiler* compiler, Precedence precedence);
|
|||||||
static void compileExpression(Compiler* compiler);
|
static void compileExpression(Compiler* compiler);
|
||||||
|
|
||||||
static void exprLiteral(Compiler* compiler, bool can_assign);
|
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 exprFunc(Compiler* compiler, bool can_assign);
|
||||||
static void exprName(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_DIVEQ */ NO_RULE, // exprAssignment, PREC_ASSIGNMENT
|
||||||
/* TK_SRIGHT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
/* TK_SRIGHT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
||||||
/* TK_SLEFT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
/* TK_SLEFT */ { NULL, exprBinaryOp, PREC_BITWISE_SHIFT },
|
||||||
|
/* TK_IMPORT */ { exprImport, NULL, NO_INFIX },
|
||||||
/* TK_DEF */ NO_RULE,
|
/* TK_DEF */ NO_RULE,
|
||||||
/* TK_EXTERN */ NO_RULE,
|
/* TK_EXTERN */ NO_RULE,
|
||||||
/* TK_FUNCTION */ { exprFunc, NULL, NO_INFIX },
|
/* TK_FUNCTION */ { exprFunc, NULL, NO_INFIX },
|
||||||
/* TK_END */ NO_RULE,
|
/* TK_END */ NO_RULE,
|
||||||
/* TK_NULL */ { exprValue, NULL, NO_INFIX },
|
/* TK_NULL */ { exprValue, NULL, NO_INFIX },
|
||||||
/* TK_SELF */ { 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);
|
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) {
|
static void exprFunc(Compiler* compiler, bool can_assign) {
|
||||||
int fn_index = compileFunction(compiler, FN_LITERAL);
|
int fn_index = compileFunction(compiler, FN_LITERAL);
|
||||||
emitOpcode(compiler, OP_PUSH_FN);
|
emitOpcode(compiler, OP_PUSH_FN);
|
||||||
@ -1200,7 +1215,7 @@ static void exprAttrib(Compiler* compiler, bool can_assign) {
|
|||||||
int length = parser->previous.length;
|
int length = parser->previous.length;
|
||||||
|
|
||||||
// Store the name in script's names.
|
// 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);
|
vmPushTempRef(compiler->vm, &string->_super);
|
||||||
stringBufferWrite(&compiler->script->names, compiler->vm, string);
|
stringBufferWrite(&compiler->script->names, compiler->vm, string);
|
||||||
vmPopTempRef(compiler->vm);
|
vmPopTempRef(compiler->vm);
|
||||||
@ -1756,11 +1771,11 @@ static void compileStatement(Compiler* compiler) {
|
|||||||
|
|
||||||
Script* compileSource(MSVM* vm, const char* path) {
|
Script* compileSource(MSVM* vm, const char* path) {
|
||||||
|
|
||||||
MSLoadScriptResult res = vm->config.load_script_fn(vm, path);
|
msStringResult res = vm->config.load_script_fn(vm, path);
|
||||||
if (res.is_failed) // FIXME:
|
if (!res.success) // FIXME:
|
||||||
vm->config.error_fn(vm, MS_ERROR_COMPILE, NULL, -1,
|
vm->config.error_fn(vm, MS_ERROR_COMPILE, NULL, -1,
|
||||||
"file load source failed.");
|
"file load source failed.");
|
||||||
const char* source = res.source;
|
const char* source = res.string;
|
||||||
|
|
||||||
// Skip utf8 BOM if there is any.
|
// Skip utf8 BOM if there is any.
|
||||||
if (strncmp(source, "\xEF\xBB\xBF", 3) == 0) source += 3;
|
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);
|
emitOpcode(&compiler, OP_END);
|
||||||
|
|
||||||
// Source done callback.
|
// Source done callback.
|
||||||
if (vm->config.load_script_done_fn != NULL)
|
if (res.on_done != NULL) res.on_done(vm, res);
|
||||||
vm->config.load_script_done_fn(vm, path, res.user_data);
|
|
||||||
|
|
||||||
// Create script globals.
|
// Create script globals.
|
||||||
for (int i = 0; i < compiler.var_count; i++) {
|
for (int i = 0; i < compiler.var_count; i++) {
|
||||||
|
128
src/core.c
128
src/core.c
@ -28,7 +28,7 @@ static _BuiltinFn builtins[BUILTIN_COUNT];
|
|||||||
static int builtins_count = 0;
|
static int builtins_count = 0;
|
||||||
|
|
||||||
static void initializeBuiltinFN(MSVM* vm, _BuiltinFn* bfn, const char* name,
|
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->name = name;
|
||||||
bfn->length = length;
|
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,
|
static inline bool validateNumeric(MSVM* vm, Var var, double* value,
|
||||||
const char* name) {
|
const char* name) {
|
||||||
if (isNumeric(var, value)) return true;
|
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;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool validateIndex(MSVM* vm, int32_t index, int32_t size,
|
static inline bool validateIndex(MSVM* vm, int32_t index, int32_t size,
|
||||||
const char* container) {
|
const char* container) {
|
||||||
if (index < 0 || size <= index) {
|
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 false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -174,21 +174,21 @@ void corePrint(MSVM* vm) {
|
|||||||
vm->config.write_fn(vm, "\n");
|
vm->config.write_fn(vm, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void coreImport(MSVM* vm) {
|
//void coreImport(MSVM* vm) {
|
||||||
Var arg1 = vm->fiber->ret[1];
|
// Var arg1 = vm->fiber->ret[1];
|
||||||
if (!IS_OBJ(arg1) || AS_OBJ(arg1)->type != OBJ_STRING) {
|
// if (!IS_OBJ(arg1) || AS_OBJ(arg1)->type != OBJ_STRING) {
|
||||||
msSetRuntimeError(vm, "Expected a String argument.");
|
// msSetRuntimeError(vm, "Expected a String argument.");
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
String* path = (String*)AS_OBJ(arg1);
|
// String* path = (String*)AS_OBJ(arg1);
|
||||||
if (path->length > 4 && strncmp(path->data, "std:", 4) == 0) {
|
// if (path->length > 4 && strncmp(path->data, "std:", 4) == 0) {
|
||||||
Script* scr = vmGetStdScript(vm, path->data + 4);
|
// Script* scr = vmGetStdScript(vm, path->data + 4);
|
||||||
ASSERT(scr != NULL, OOPS);
|
// ASSERT(scr != NULL, OOPS);
|
||||||
RET(VAR_OBJ(scr));
|
// RET(VAR_OBJ(scr));
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
TODO;
|
// TODO;
|
||||||
}
|
//}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* STD METHODS */
|
/* STD METHODS */
|
||||||
@ -198,7 +198,7 @@ void coreImport(MSVM* vm) {
|
|||||||
void stdListSort(MSVM* vm) {
|
void stdListSort(MSVM* vm) {
|
||||||
Var list = ARG(1);
|
Var list = ARG(1);
|
||||||
if (!IS_OBJ(list) || AS_OBJ(list)->type != OBJ_LIST) {
|
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.
|
// TODO: sort.
|
||||||
@ -237,15 +237,14 @@ void initializeCore(MSVM* vm) {
|
|||||||
|
|
||||||
INITALIZE_BUILTIN_FN("to_string", coreToString, 1);
|
INITALIZE_BUILTIN_FN("to_string", coreToString, 1);
|
||||||
INITALIZE_BUILTIN_FN("print", corePrint, -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.
|
// Sentinal to mark the end of the array.
|
||||||
//initializeBuiltinFN(vm, &builtins[i], NULL, 0, 0, NULL);
|
//initializeBuiltinFN(vm, &builtins[i], NULL, 0, 0, NULL);
|
||||||
|
|
||||||
// Make STD scripts.
|
// Make STD scripts.
|
||||||
Script* std; // A temporary pointer to the current std script.
|
//Script* std; // A temporary pointer to the current std script.
|
||||||
Function* fn; // A temporary pointer to the allocated function function.
|
//Function* fn; // A temporary pointer to the allocated function function.
|
||||||
|
|
||||||
#define STD_NEW_SCRIPT(_name) \
|
#define STD_NEW_SCRIPT(_name) \
|
||||||
do { \
|
do { \
|
||||||
std = newScript(vm); \
|
std = newScript(vm); \
|
||||||
@ -261,14 +260,13 @@ void initializeCore(MSVM* vm) {
|
|||||||
fn->native = fptr; \
|
fn->native = fptr; \
|
||||||
fn->arity = _arity; \
|
fn->arity = _arity; \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
// TODO: add std scripts to vm.
|
||||||
// std:list script.
|
// STD_NEW_SCRIPT("std:list");
|
||||||
STD_NEW_SCRIPT("std:list");
|
// STD_ADD_FUNCTION("sort", stdListSort, 1);
|
||||||
STD_ADD_FUNCTION("sort", stdListSort, 1);
|
//
|
||||||
|
// // std:os script.
|
||||||
// std:os script.
|
// STD_NEW_SCRIPT("std:os");
|
||||||
STD_NEW_SCRIPT("std:os");
|
// STD_ADD_FUNCTION("clock", stdOsClock, 0);
|
||||||
STD_ADD_FUNCTION("clock", stdOsClock, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void markCoreObjects(MSVM* vm) {
|
void markCoreObjects(MSVM* vm) {
|
||||||
@ -314,8 +312,8 @@ Var varAdd(MSVM* vm, Var v1, Var v2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
msSetRuntimeError(vm, "Unsupported operand types for operator '-' "
|
vm->fiber->error = stringFormat(vm, "Unsupported operand types for operator '-' "
|
||||||
"%s and %s", varTypeName(v1), varTypeName(v2));
|
"$ and $", varTypeName(v1), varTypeName(v2));
|
||||||
|
|
||||||
return VAR_NULL;
|
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).
|
TODO; // for user objects call vm.config.sub_userobj_sub(handles).
|
||||||
|
|
||||||
msSetRuntimeError(vm, "Unsupported operand types for operator '-' "
|
vm->fiber->error = stringFormat(vm, "Unsupported operand types for operator '-' "
|
||||||
"%s and %s", varTypeName(v1), varTypeName(v2));
|
"$ and $", varTypeName(v1), varTypeName(v2));
|
||||||
|
|
||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
@ -347,8 +345,8 @@ Var varMultiply(MSVM* vm, Var v1, Var v2) {
|
|||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
msSetRuntimeError(vm, "Unsupported operand types for operator '*' "
|
vm->fiber->error = stringFormat(vm, "Unsupported operand types for operator "
|
||||||
"%s and %s", varTypeName(v1), varTypeName(v2));
|
"'*' $ and $", varTypeName(v1), varTypeName(v2));
|
||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,8 +359,8 @@ Var varDivide(MSVM* vm, Var v1, Var v2) {
|
|||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
msSetRuntimeError(vm, "Unsupported operand types for operator '/' "
|
vm->fiber->error = stringFormat(vm, "Unsupported operand types for operator "
|
||||||
"%s and %s", varTypeName(v1), varTypeName(v2));
|
"'/' $ and $", varTypeName(v1), varTypeName(v2));
|
||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -390,14 +388,16 @@ bool varLesser(MSVM* vm, Var v1, Var v2) {
|
|||||||
#define IS_ATTRIB(name) \
|
#define IS_ATTRIB(name) \
|
||||||
(attrib->length == strlen(name) && strcmp(name, attrib->data) == 0)
|
(attrib->length == strlen(name) && strcmp(name, attrib->data) == 0)
|
||||||
|
|
||||||
#define ERR_NO_ATTRIB() \
|
#define ERR_NO_ATTRIB() \
|
||||||
msSetRuntimeError(vm, "'%s' objects has no attribute named '%s'", \
|
vm->fiber->error = stringFormat(vm, "'$' objects has no attribute " \
|
||||||
varTypeName(on), attrib->data);
|
"named '$'", \
|
||||||
|
varTypeName(on), attrib->data);
|
||||||
|
|
||||||
Var varGetAttrib(MSVM* vm, Var on, String* attrib) {
|
Var varGetAttrib(MSVM* vm, Var on, String* attrib) {
|
||||||
|
|
||||||
if (!IS_OBJ(on)) {
|
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;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -429,7 +429,7 @@ Var varGetAttrib(MSVM* vm, Var on, String* attrib) {
|
|||||||
{
|
{
|
||||||
Var value = mapGet((Map*)obj, VAR_OBJ(&attrib->_super));
|
Var value = mapGet((Map*)obj, VAR_OBJ(&attrib->_super));
|
||||||
if (IS_UNDEF(value)) {
|
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 VAR_NULL;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
@ -475,16 +475,17 @@ Var varGetAttrib(MSVM* vm, Var on, String* attrib) {
|
|||||||
|
|
||||||
void varSetAttrib(MSVM* vm, Var on, String* attrib, Var value) {
|
void varSetAttrib(MSVM* vm, Var on, String* attrib, Var value) {
|
||||||
|
|
||||||
#define ATTRIB_IMMUTABLE(prop) \
|
#define ATTRIB_IMMUTABLE(prop) \
|
||||||
do { \
|
do { \
|
||||||
if (IS_ATTRIB(prop)) { \
|
if (IS_ATTRIB(prop)) { \
|
||||||
msSetRuntimeError(vm, "'%s' attribute is immutable.", prop); \
|
vm->fiber->error = stringFormat(vm, "'$' attribute is immutable.", prop); \
|
||||||
return; \
|
return; \
|
||||||
} \
|
} \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
if (!IS_OBJ(on)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -553,7 +554,8 @@ do { \
|
|||||||
|
|
||||||
Var varGetSubscript(MSVM* vm, Var on, Var key) {
|
Var varGetSubscript(MSVM* vm, Var on, Var key) {
|
||||||
if (!IS_OBJ(on)) {
|
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;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -569,7 +571,7 @@ Var varGetSubscript(MSVM* vm, Var on, Var key) {
|
|||||||
if (!validateIndex(vm, index, str->length, "String")) {
|
if (!validateIndex(vm, index, str->length, "String")) {
|
||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
String* c = newString(vm, str->data + index, 1);
|
String* c = newStringLength(vm, str->data + index, 1);
|
||||||
return VAR_OBJ(c);
|
return VAR_OBJ(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,9 +593,11 @@ Var varGetSubscript(MSVM* vm, Var on, Var key) {
|
|||||||
Var value = mapGet((Map*)obj, key);
|
Var value = mapGet((Map*)obj, key);
|
||||||
if (IS_UNDEF(value)) {
|
if (IS_UNDEF(value)) {
|
||||||
String* key_str = toString(vm, key, true);
|
String* key_str = toString(vm, key, true);
|
||||||
|
|
||||||
vmPushTempRef(vm, &key_str->_super);
|
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);
|
vmPopTempRef(vm);
|
||||||
|
|
||||||
return VAR_NULL;
|
return VAR_NULL;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
@ -616,14 +620,14 @@ Var varGetSubscript(MSVM* vm, Var on, Var key) {
|
|||||||
|
|
||||||
void varsetSubscript(MSVM* vm, Var on, Var key, Var value) {
|
void varsetSubscript(MSVM* vm, Var on, Var key, Var value) {
|
||||||
if (!IS_OBJ(on)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object* obj = AS_OBJ(on);
|
Object* obj = AS_OBJ(on);
|
||||||
switch (obj->type) {
|
switch (obj->type) {
|
||||||
case OBJ_STRING:
|
case OBJ_STRING:
|
||||||
msSetRuntimeError(vm, "String objects are immutable.");
|
vm->fiber->error = newString(vm, "String objects are immutable.");
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case OBJ_LIST:
|
case OBJ_LIST:
|
||||||
@ -639,7 +643,7 @@ void varsetSubscript(MSVM* vm, Var on, Var key, Var value) {
|
|||||||
case OBJ_MAP:
|
case OBJ_MAP:
|
||||||
{
|
{
|
||||||
if (IS_OBJ(key) && !isObjectHashable(AS_OBJ(key)->type)) {
|
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 {
|
} else {
|
||||||
mapSet((Map*)obj, vm, key, value);
|
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.
|
// Primitive types are not iterable.
|
||||||
if (!IS_OBJ(seq)) {
|
if (!IS_OBJ(seq)) {
|
||||||
if (IS_NULL(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)) {
|
} 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)) {
|
} else if (IS_NUM(seq)) {
|
||||||
msSetRuntimeError(vm, "Number is not iterable.");
|
vm->fiber->error = newString(vm, "Number is not iterable.");
|
||||||
} else {
|
} else {
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
@ -699,7 +703,7 @@ bool varIterate(MSVM* vm, Var seq, Var* iterator, Var* value) {
|
|||||||
return false; //< Stop iteration.
|
return false; //< Stop iteration.
|
||||||
}
|
}
|
||||||
// TODO: Or I could add char as a type for efficiency.
|
// 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);
|
*iterator = VAR_NUM((double)iter + 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -181,9 +181,8 @@ void dumpInstructions(MSVM* vm, Function* func) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
case OP_POP:
|
case OP_POP: NO_ARGS(); break;
|
||||||
NO_ARGS();
|
case OP_IMPORT: NO_ARGS(); break;
|
||||||
break;
|
|
||||||
|
|
||||||
case OP_CALL:
|
case OP_CALL:
|
||||||
printf("%5d (argc)\n", READ_SHORT());
|
printf("%5d (argc)\n", READ_SHORT());
|
||||||
|
@ -99,6 +99,11 @@ OPCODE(POP, 0, -1)
|
|||||||
// marked explicitly since it's performance criticle.
|
// marked explicitly since it's performance criticle.
|
||||||
// params: n bytes argc.
|
// 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.
|
// TODO: may be later.
|
||||||
//OPCODE(CALL_0, 0, 0) //< Push null call null will be the return value.
|
//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.
|
//OPCODE(CALL_1, 0, -1) //< Push null and arg1. arg1 will be popped.
|
||||||
|
28
src/var.c
28
src/var.c
@ -19,7 +19,7 @@ Var msVarNumber(MSVM* vm, double value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Var msVarString(MSVM* vm, const char* 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) {
|
bool msAsBool(MSVM* vm, Var value) {
|
||||||
@ -230,7 +230,7 @@ static String* _allocateString(MSVM* vm, size_t length) {
|
|||||||
return string;
|
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.");
|
ASSERT(length == 0 || text != NULL, "Unexpected NULL string.");
|
||||||
|
|
||||||
@ -699,20 +699,20 @@ bool isObjectHashable(ObjectType type) {
|
|||||||
String* toString(MSVM* vm, Var v, bool recursive) {
|
String* toString(MSVM* vm, Var v, bool recursive) {
|
||||||
|
|
||||||
if (IS_NULL(v)) {
|
if (IS_NULL(v)) {
|
||||||
return newString(vm, "null", 4);
|
return newStringLength(vm, "null", 4);
|
||||||
|
|
||||||
} else if (IS_BOOL(v)) {
|
} else if (IS_BOOL(v)) {
|
||||||
if (AS_BOOL(v)) {
|
if (AS_BOOL(v)) {
|
||||||
return newString(vm, "true", 4);
|
return newStringLength(vm, "true", 4);
|
||||||
} else {
|
} else {
|
||||||
return newString(vm, "false", 5);
|
return newStringLength(vm, "false", 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (IS_NUM(v)) {
|
} else if (IS_NUM(v)) {
|
||||||
char buff[TO_STRING_BUFF_SIZE];
|
char buff[TO_STRING_BUFF_SIZE];
|
||||||
int length = sprintf(buff, "%.14g", AS_NUM(v));
|
int length = sprintf(buff, "%.14g", AS_NUM(v));
|
||||||
ASSERT(length < TO_STRING_BUFF_SIZE, "Buffer overflowed.");
|
ASSERT(length < TO_STRING_BUFF_SIZE, "Buffer overflowed.");
|
||||||
return newString(vm, buff, length);
|
return newStringLength(vm, buff, length);
|
||||||
|
|
||||||
} else if (IS_OBJ(v)) {
|
} else if (IS_OBJ(v)) {
|
||||||
Object* obj = AS_OBJ(v);
|
Object* obj = AS_OBJ(v);
|
||||||
@ -720,14 +720,14 @@ String* toString(MSVM* vm, Var v, bool recursive) {
|
|||||||
case OBJ_STRING:
|
case OBJ_STRING:
|
||||||
{
|
{
|
||||||
// If recursive return with quotes (ex: [42, "hello", 0..10]).
|
// 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;
|
if (!recursive) return string;
|
||||||
else return stringFormat(vm, "\"@\"", string);
|
else return stringFormat(vm, "\"@\"", string);
|
||||||
}
|
}
|
||||||
|
|
||||||
case OBJ_LIST: {
|
case OBJ_LIST: {
|
||||||
List* list = (List*)obj;
|
List* list = (List*)obj;
|
||||||
String* result = newString(vm, "[", 1);
|
String* result = newStringLength(vm, "[", 1);
|
||||||
|
|
||||||
for (uint32_t i = 0; i < list->elements.count; i++) {
|
for (uint32_t i = 0; i < list->elements.count; i++) {
|
||||||
const char* fmt = (i != 0) ? "@, @" : "@@";
|
const char* fmt = (i != 0) ? "@, @" : "@@";
|
||||||
@ -743,7 +743,7 @@ String* toString(MSVM* vm, Var v, bool recursive) {
|
|||||||
case OBJ_MAP:
|
case OBJ_MAP:
|
||||||
{
|
{
|
||||||
Map* map = (Map*)obj;
|
Map* map = (Map*)obj;
|
||||||
String* result = newString(vm, "{", 1);
|
String* result = newStringLength(vm, "{", 1);
|
||||||
|
|
||||||
uint32_t i = 0;
|
uint32_t i = 0;
|
||||||
bool _first = true; // For first element no ',' required.
|
bool _first = true; // For first element no ',' required.
|
||||||
@ -774,8 +774,8 @@ String* toString(MSVM* vm, Var v, bool recursive) {
|
|||||||
return stringFormat(vm, "@}", result);
|
return stringFormat(vm, "@}", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
case OBJ_RANGE: return newString(vm, "[Range]", 7); // TODO;
|
case OBJ_RANGE: return newStringLength(vm, "[Range]", 7); // TODO;
|
||||||
case OBJ_SCRIPT: return newString(vm, "[Script]", 8); // TODO;
|
case OBJ_SCRIPT: return newStringLength(vm, "[Script]", 8); // TODO;
|
||||||
case OBJ_FUNC: {
|
case OBJ_FUNC: {
|
||||||
const char* name = ((Function*)obj)->name;
|
const char* name = ((Function*)obj)->name;
|
||||||
int length = (int)strlen(name); // TODO: Assert length.
|
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, "[Func:", 6);
|
||||||
memcpy(buff + 6, name, length);
|
memcpy(buff + 6, name, length);
|
||||||
buff[6 + 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;
|
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
|
// If we reach here the name doesn't exists in the buffer, so add it and
|
||||||
// return the index.
|
// return the index.
|
||||||
String* new_name = newString(vm, name, length);
|
String* new_name = newStringLength(vm, name, length);
|
||||||
vmPushTempRef(vm, &new_name->_super);
|
vmPushTempRef(vm, &new_name->_super);
|
||||||
stringBufferWrite(&self->names, vm, new_name);
|
stringBufferWrite(&self->names, vm, new_name);
|
||||||
vmPopTempRef(vm);
|
vmPopTempRef(vm);
|
||||||
|
@ -307,7 +307,7 @@ struct Function {
|
|||||||
|
|
||||||
bool is_native; //< True if Native function.
|
bool is_native; //< True if Native function.
|
||||||
union {
|
union {
|
||||||
MiniScriptNativeFn native; //< Native function pointer.
|
msNativeFn native; //< Native function pointer.
|
||||||
Fn* fn; //< Script function pointer.
|
Fn* fn; //< Script function pointer.
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -386,7 +386,10 @@ Var doubleToVar(double value);
|
|||||||
double varToDouble(Var value);
|
double varToDouble(Var value);
|
||||||
|
|
||||||
// Allocate new String object and return String*.
|
// 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*.
|
// Allocate new List and return List*.
|
||||||
List* newList(MSVM* vm, uint32_t size);
|
List* newList(MSVM* vm, uint32_t size);
|
||||||
|
128
src/vm.c
128
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);
|
return self->config.realloc_fn(memory, new_size, self->config.user_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void msInitConfiguration(MSConfiguration* config) {
|
void msInitConfiguration(msConfiguration* config) {
|
||||||
config->realloc_fn = defaultRealloc;
|
config->realloc_fn = defaultRealloc;
|
||||||
|
|
||||||
// TODO: Handle Null functions before calling them.
|
// TODO: Handle Null functions before calling them.
|
||||||
@ -54,18 +54,36 @@ void msInitConfiguration(MSConfiguration* config) {
|
|||||||
config->write_fn = NULL;
|
config->write_fn = NULL;
|
||||||
|
|
||||||
config->load_script_fn = NULL;
|
config->load_script_fn = NULL;
|
||||||
config->load_script_done_fn = NULL;
|
|
||||||
config->user_data = NULL;
|
config->user_data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
MSVM* msNewVM(MSConfiguration* config) {
|
MSVM* msNewVM(msConfiguration* config) {
|
||||||
MSVM* vm = (MSVM*)malloc(sizeof(MSVM));
|
|
||||||
vmInit(vm, 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;
|
return vm;
|
||||||
}
|
}
|
||||||
|
|
||||||
void msFreeVM(MSVM* self) {
|
void msFreeVM(MSVM* self) {
|
||||||
// TODO: Check if vm already freed.
|
|
||||||
|
|
||||||
Object* obj = self->first;
|
Object* obj = self->first;
|
||||||
while (obj != NULL) {
|
while (obj != NULL) {
|
||||||
@ -76,21 +94,8 @@ void msFreeVM(MSVM* self) {
|
|||||||
|
|
||||||
self->gray_list = (Object**)self->config.realloc_fn(
|
self->gray_list = (Object**)self->config.realloc_fn(
|
||||||
self->gray_list, 0, self->config.user_data);
|
self->gray_list, 0, self->config.user_data);
|
||||||
self->config.realloc_fn(self, 0, self->config.user_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
void vmInit(MSVM* self, MSConfiguration* config) {
|
DEALLOCATE(self, self);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void vmPushTempRef(MSVM* self, Object* obj) {
|
void vmPushTempRef(MSVM* self, Object* obj) {
|
||||||
@ -114,10 +119,8 @@ void vmCollectGarbage(MSVM* self) {
|
|||||||
// Mark core objects (mostlikely builtin functions).
|
// Mark core objects (mostlikely builtin functions).
|
||||||
markCoreObjects(self);
|
markCoreObjects(self);
|
||||||
|
|
||||||
// Mark all the 'std' scripts.
|
// Mark the scripts cache.
|
||||||
for (int i = 0; i < self->std_count; i++) {
|
grayObject(&self->scripts->_super, self);
|
||||||
grayObject(&(self->std_scripts[i]->_super), self);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark temp references.
|
// Mark temp references.
|
||||||
for (int i = 0; i < self->temp_reference_count; i++) {
|
for (int i = 0; i < self->temp_reference_count; i++) {
|
||||||
@ -143,22 +146,6 @@ void vmCollectGarbage(MSVM* self) {
|
|||||||
TODO; // Sweep.
|
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) {
|
void* msGetUserData(MSVM* vm) {
|
||||||
return vm->config.user_data;
|
return vm->config.user_data;
|
||||||
}
|
}
|
||||||
@ -171,6 +158,45 @@ void msSetUserData(MSVM* vm, void* user_data) {
|
|||||||
* RUNTIME *
|
* 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) {
|
static void ensureStackSize(MSVM* vm, int size) {
|
||||||
if (vm->fiber->stack_size > size) return;
|
if (vm->fiber->stack_size > size) return;
|
||||||
TODO;
|
TODO;
|
||||||
@ -200,8 +226,8 @@ static inline void pushCallFrame(MSVM* vm, Function* fn) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void msSetRuntimeError(MSVM* vm, const char* format, ...) {
|
void msSetRuntimeError(MSVM* vm, const char* format, ...) {
|
||||||
vm->fiber->error = newString(vm, "TODO:", 5);
|
vm->fiber->error = newString(vm, "TODO:");
|
||||||
TODO;
|
TODO; // Construct String and set to vm->fiber->error.
|
||||||
}
|
}
|
||||||
|
|
||||||
void vmReportError(MSVM* vm) {
|
void vmReportError(MSVM* vm) {
|
||||||
@ -468,6 +494,24 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
|
|||||||
DROP();
|
DROP();
|
||||||
DISPATCH();
|
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):
|
OPCODE(CALL):
|
||||||
{
|
{
|
||||||
uint16_t argc = READ_SHORT();
|
uint16_t argc = READ_SHORT();
|
||||||
|
23
src/vm.h
23
src/vm.h
@ -14,9 +14,6 @@
|
|||||||
// garbage collected.
|
// garbage collected.
|
||||||
#define MAX_TEMP_REFERENCE 16
|
#define MAX_TEMP_REFERENCE 16
|
||||||
|
|
||||||
// The maximum number of script cache can vm hold at once.
|
|
||||||
#define MAX_SCRIPT_CACHE 128
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
#define OPCODE(name, _, __) OP_##name,
|
#define OPCODE(name, _, __) OP_##name,
|
||||||
#include "opcodes.h"
|
#include "opcodes.h"
|
||||||
@ -46,15 +43,15 @@ struct MSVM {
|
|||||||
int temp_reference_count;
|
int temp_reference_count;
|
||||||
|
|
||||||
// VM's configurations.
|
// VM's configurations.
|
||||||
MSConfiguration config;
|
msConfiguration config;
|
||||||
|
|
||||||
// Current compiler reference to mark it's heap allocated objects. Note that
|
// Current compiler reference to mark it's heap allocated objects. Note that
|
||||||
// The compiler isn't heap allocated.
|
// The compiler isn't heap allocated.
|
||||||
Compiler* compiler;
|
Compiler* compiler;
|
||||||
|
|
||||||
// Std scripts array. (TODO: assert "std" scripts doesn't have global vars).
|
// A cache of the compiled scripts with their path as key and the Scrpit
|
||||||
Script* std_scripts[MAX_SCRIPT_CACHE];
|
// object as the value.
|
||||||
int std_count;
|
Map* scripts;
|
||||||
|
|
||||||
// Execution variables ////////////////////////////////////////////////////
|
// Execution variables ////////////////////////////////////////////////////
|
||||||
|
|
||||||
@ -77,10 +74,6 @@ struct MSVM {
|
|||||||
// going to track deallocated bytes, instead use garbage collector to do it.
|
// 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);
|
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.
|
// Push the object to temporary references stack.
|
||||||
void vmPushTempRef(MSVM* self, Object* obj);
|
void vmPushTempRef(MSVM* self, Object* obj);
|
||||||
|
|
||||||
@ -90,14 +83,6 @@ void vmPopTempRef(MSVM* self);
|
|||||||
// Trigger garbage collection manually.
|
// Trigger garbage collection manually.
|
||||||
void vmCollectGarbage(MSVM* self);
|
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.
|
// Runs the script and return result.
|
||||||
MSInterpretResult vmRunScript(MSVM* vm, Script* script);
|
MSInterpretResult vmRunScript(MSVM* vm, Script* script);
|
||||||
|
|
||||||
|
30
test/main.c
30
test/main.c
@ -17,20 +17,30 @@ void writeFunction(MSVM* vm, const char* text) {
|
|||||||
fprintf(stdout, "%s", text);
|
fprintf(stdout, "%s", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadScriptDone(MSVM* vm, const char* path, void* user_data) {
|
void onResultDone(MSVM* vm, msStringResult result) {
|
||||||
// User data is the allocated source code buffer and it has to be freed
|
// The result.string is the allocated buffer and it has to be freed
|
||||||
// manually since it wasn't allocated by the VM.
|
// manually since it wasn't allocated by the VM.
|
||||||
free(user_data);
|
free((void*)result.string);
|
||||||
}
|
}
|
||||||
|
|
||||||
MSLoadScriptResult loadScript(MSVM* vm, const char* path) {
|
msStringResult resolvePath(MSVM* vm, const char* from, const char* name) {
|
||||||
MSLoadScriptResult result;
|
msStringResult result;
|
||||||
result.is_failed = false;
|
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.
|
// Open the file.
|
||||||
FILE* file = fopen(path, "r");
|
FILE* file = fopen(path, "r");
|
||||||
if (file == NULL) {
|
if (file == NULL) {
|
||||||
result.is_failed = true;
|
result.success = true;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,8 +57,7 @@ MSLoadScriptResult loadScript(MSVM* vm, const char* path) {
|
|||||||
buff[read] = '\0';
|
buff[read] = '\0';
|
||||||
fclose(file);
|
fclose(file);
|
||||||
|
|
||||||
result.source = buff;
|
result.string = buff;
|
||||||
result.user_data = (void*)buff;
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,12 +76,11 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
const char* source_path = argv[1];
|
const char* source_path = argv[1];
|
||||||
|
|
||||||
MSConfiguration config;
|
msConfiguration config;
|
||||||
msInitConfiguration(&config);
|
msInitConfiguration(&config);
|
||||||
config.error_fn = errorPrint;
|
config.error_fn = errorPrint;
|
||||||
config.write_fn = writeFunction;
|
config.write_fn = writeFunction;
|
||||||
config.load_script_fn = loadScript;
|
config.load_script_fn = loadScript;
|
||||||
config.load_script_done_fn = loadScriptDone;
|
|
||||||
|
|
||||||
MSVM* vm = msNewVM(&config);
|
MSVM* vm = msNewVM(&config);
|
||||||
MSInterpretResult result = msInterpret(vm, source_path);
|
MSInterpretResult result = msInterpret(vm, source_path);
|
||||||
|
Loading…
Reference in New Issue
Block a user