import statement refactor

This commit is contained in:
Thakee Nathees 2021-05-06 19:49:30 +05:30
parent 0db6cf9780
commit e613203cca
10 changed files with 258 additions and 187 deletions

View File

@ -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);

View File

@ -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++) {

View File

@ -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;
}

View File

@ -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());

View File

@ -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.

View File

@ -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);

View File

@ -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);

128
src/vm.c
View File

@ -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();

View File

@ -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);

View File

@ -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);