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
|
||||
// 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);
|
||||
|
@ -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++) {
|
||||
|
128
src/core.c
128
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;
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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.
|
||||
|
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) {
|
||||
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);
|
||||
|
@ -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
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);
|
||||
}
|
||||
|
||||
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();
|
||||
|
23
src/vm.h
23
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);
|
||||
|
||||
|
30
test/main.c
30
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);
|
||||
|
Loading…
Reference in New Issue
Block a user