diff --git a/TODO.txt b/TODO.txt
index 769ff84..0ced204 100644
--- a/TODO.txt
+++ b/TODO.txt
@@ -22,5 +22,5 @@
// Bugs.
-[ ] `function() "do"
end` make 'do' keyword optional here.
[ ] Update cache on each recompilation instead of making a new cache.
+[*] `function() "do" end` make 'do' keyword optional here.
diff --git a/include/miniscript.h b/include/miniscript.h
index a177dda..0f22deb 100644
--- a/include/miniscript.h
+++ b/include/miniscript.h
@@ -26,6 +26,20 @@ extern "C" {
// allocations.
typedef struct MSVM MSVM;
+// Nan-Tagging could be disable for debugging/portability purposes only when
+// compiling the compiler. Do not change this if using the miniscript library
+// for embedding. To disable when compiling the compiler, define
+// `VAR_NAN_TAGGING 0`, otherwise it defaults to Nan-Tagging.
+#ifndef VAR_NAN_TAGGING
+ #define VAR_NAN_TAGGING 1
+#endif
+
+#if VAR_NAN_TAGGING
+ typedef uint64_t Var;
+#else
+ typedef struct Var Var;
+#endif
+
// C function pointer which is callable from MiniScript.
typedef void (*MiniScriptNativeFn)(MSVM* vm);
@@ -102,6 +116,18 @@ void* msGetUserData(MSVM* vm);
// Update the user data of the vm.
void msSetUserData(MSVM* vm, void* user_data);
+// Encode types to var.
+// TODO: user need to use vmPushTempRoot() for strings.
+Var msVarBool(MSVM* vm, bool value);
+Var msVarNumber(MSVM* vm, double value);
+Var msVarString(MSVM* vm, const char* value);
+
+// Decode var types.
+// TODO: const char* should be copied otherwise it'll become dangling pointer.
+bool msAsBool(MSVM* vm, Var value);
+double msAsNumber(MSVM* vm, Var value);
+const char* msAsString(MSVM* vm, Var value);
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/src/common.h b/src/common.h
index 8f5277b..81a7b2a 100644
--- a/src/common.h
+++ b/src/common.h
@@ -6,6 +6,8 @@
#ifndef MS_COMMON_H
#define MS_COMMON_H
+#include "miniscript.h"
+
#include
#include
#include
@@ -38,7 +40,7 @@
#endif
// Set this to dump compiled opcodes of each functions.
-#define DEBUG_DUMP_COMPILED_CODE 1
+#define DEBUG_DUMP_COMPILED_CODE 0
#ifdef DEBUG
@@ -73,8 +75,8 @@
#else
#define DEBUG_BREAK()
-
#define ASSERT(condition, message) do { } while (false)
+#define ASSERT_INDEX(index, size) do {} while (false)
// Reference : https://github.com/wren-lang/
#if defined( _MSC_VER )
@@ -116,19 +118,6 @@
#define DEALLOCATE(vm, pointer) \
vmRealloc(vm, pointer, 0, 0)
-
-// Nan-Tagging could be disable for debugging/portability purposes.
-// To disable define `VAR_NAN_TAGGING 0`, otherwise it defaults to Nan-Tagging.
-#ifndef VAR_NAN_TAGGING
- #define VAR_NAN_TAGGING 1
-#endif
-
-#if VAR_NAN_TAGGING
-typedef uint64_t Var;
-#else
-typedef struct Var Var;
-#endif
-
typedef struct Object Object;
typedef struct String String;
typedef struct List List;
@@ -140,4 +129,7 @@ typedef struct Function Function;
// Unique number to identify for various cases.
typedef uint32_t ID;
+// VM's fiber type.
+typedef struct Fiber Fiber;
+
#endif //MS_COMMON_H
diff --git a/src/compiler.c b/src/compiler.c
index fcd0fe2..b5d305d 100644
--- a/src/compiler.c
+++ b/src/compiler.c
@@ -306,6 +306,12 @@ struct Compiler {
Script* script; //< Current script.
Loop* loop; //< Current loop.
Func* func; //< Current function.
+
+ // True if the last statement is a new local variable assignment. Because
+ // the assignment is different than reqular assignment and use this boolean
+ // to tell the compiler that dont pop it's assigned value because the value
+ // itself is the local.
+ bool new_local;
};
typedef struct {
@@ -1009,7 +1015,13 @@ static void exprName(Compiler* compiler, bool can_assign) {
int index = compilerAddVariable(compiler, name_start, name_len,
name_line);
compileExpression(compiler);
- emitStoreVariable(compiler, index, compiler->scope_depth == DEPTH_GLOBAL);
+ if (compiler->scope_depth == DEPTH_GLOBAL) {
+ emitStoreVariable(compiler, index, true);
+ } else {
+ // This will prevent the assignment from poped out from the stack
+ // since the assigned value itself is the local and not a temp.
+ compiler->new_local = true;
+ }
} else {
parseError(parser, "Name \"%.*s\" is not defined.", name_len, name_start);
}
@@ -1277,8 +1289,10 @@ static void compilerInit(Compiler* compiler, MSVM* vm, const char* source,
compiler->var_count = 0;
compiler->global_count = 0;
compiler->stack_size = 0;
- Loop* loop = NULL;
- Function* fn = NULL;
+ compiler->loop = NULL;
+ compiler->func = NULL;
+ compiler->script = NULL;
+ compiler->new_local = false;
}
// Add a variable and return it's index to the context. Assumes that the
@@ -1499,7 +1513,7 @@ static void compileBlockBody(Compiler* compiler, BlockType type) {
compilerEnterBlock(compiler);
- if (type != BLOCK_ELIF) {
+ if (type != BLOCK_ELSE && type != BLOCK_ELIF) {
consumeStartBlock(&compiler->parser);
skipNewLines(&compiler->parser);
}
@@ -1707,9 +1721,13 @@ static void compileStatement(Compiler* compiler) {
compileForStatement(compiler);
} else {
+ compiler->new_local = false;
compileExpression(compiler);
consumeEndStatement(parser);
- emitOpcode(compiler, OP_POP);
+ if (!compiler->new_local) {
+ emitOpcode(compiler, OP_POP);
+ }
+ compiler->new_local = false;
}
}
diff --git a/src/compiler.h b/src/compiler.h
index a856a85..19affa5 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -6,8 +6,6 @@
#ifndef COMPILER_H
#define COMPILER_H
-#include "miniscript.h"
-
#include "common.h"
#include "var.h"
diff --git a/src/core.c b/src/core.c
index 5273b80..959cf15 100644
--- a/src/core.c
+++ b/src/core.c
@@ -105,16 +105,16 @@ static inline bool validateIndex(MSVM* vm, int32_t index, int32_t size,
/*****************************************************************************/
// Argument getter (1 based).
-#define ARG(n) vm->rbp[n]
+#define ARG(n) vm->fiber->ret[n]
// Argument count used in variadic functions.
-#define ARGC ((int)(vm->sp - vm->rbp) - 1)
+#define ARGC ((int)(vm->fiber->sp - vm->fiber->ret) - 1)
// Set return value.
-#define RET(value) \
- do { \
- vm->rbp[0] = value; \
- return; \
+#define RET(value) \
+ do { \
+ *(vm->fiber->ret) = value; \
+ return; \
} while (false)
Function* getBuiltinFunction(int index) {
@@ -178,7 +178,7 @@ void corePrint(MSVM* vm) {
}
void coreImport(MSVM* vm) {
- Var arg1 = vm->rbp[1];
+ Var arg1 = vm->fiber->ret[1];
if (!IS_OBJ(arg1) || AS_OBJ(arg1)->type != OBJ_STRING) {
msSetRuntimeError(vm, "Expected a String argument.");
}
@@ -252,7 +252,6 @@ void initializeCore(MSVM* vm) {
do { \
std = newScript(vm); \
std->name = _name; \
- std->name_length = (int)strlen(_name); \
vmPushTempRef(vm, &std->_super); \
vmAddStdScript(vm, std); \
vmPopTempRef(vm); \
@@ -417,12 +416,14 @@ Var varGetAttrib(MSVM* vm, Var on, String* attrib) {
}
case OBJ_FUNC:
+ case OBJ_FIBER:
case OBJ_USER:
TODO;
default:
UNREACHABLE();
}
+ CHECK_MISSING_OBJ_TYPE(7);
UNREACHABLE();
return VAR_NULL;
@@ -486,6 +487,10 @@ do { \
ERR_NO_ATTRIB();
return;
+ case OBJ_FIBER:
+ ERR_NO_ATTRIB();
+ return;
+
case OBJ_USER:
ERR_NO_ATTRIB();
return;
@@ -493,7 +498,7 @@ do { \
default:
UNREACHABLE();
}
-
+ CHECK_MISSING_OBJ_TYPE(7);
UNREACHABLE();
}
@@ -536,12 +541,14 @@ Var varGetSubscript(MSVM* vm, Var on, Var key) {
case OBJ_RANGE:
case OBJ_SCRIPT:
case OBJ_FUNC:
+ case OBJ_FIBER:
case OBJ_USER:
TODO;
default:
UNREACHABLE();
}
+ CHECK_MISSING_OBJ_TYPE(7);
UNREACHABLE();
return VAR_NULL;
}
@@ -570,12 +577,13 @@ void varsetSubscript(MSVM* vm, Var on, Var key, Var value) {
case OBJ_RANGE:
case OBJ_SCRIPT:
case OBJ_FUNC:
+ case OBJ_FIBER:
case OBJ_USER:
TODO;
default:
UNREACHABLE();
}
-
+ CHECK_MISSING_OBJ_TYPE(7);
UNREACHABLE();
}
@@ -615,7 +623,7 @@ bool varIterate(MSVM* vm, Var seq, Var* iterator, Var* value) {
case OBJ_STRING: {
// TODO: // Need to consider utf8.
String* str = ((String*)obj);
- if (iter < 0 || iter >= str->length) {
+ if (iter < 0 || iter >= (int)str->length) {
return false; //< Stop iteration.
}
// TODO: Or I could add char as a type for efficiency.
@@ -656,13 +664,14 @@ bool varIterate(MSVM* vm, Var seq, Var* iterator, Var* value) {
case OBJ_SCRIPT:
case OBJ_FUNC:
+ case OBJ_FIBER:
case OBJ_USER:
TODO;
break;
default:
UNREACHABLE();
}
-
+ CHECK_MISSING_OBJ_TYPE(7);
UNREACHABLE();
return false;
}
\ No newline at end of file
diff --git a/src/core.h b/src/core.h
index 0e6fd4b..fdbec1b 100644
--- a/src/core.h
+++ b/src/core.h
@@ -7,7 +7,7 @@
#define CORE_H
#include "var.h"
-#include "miniscript.h"
+#include "common.h"
void initializeCore(MSVM* vm);
diff --git a/src/debug.c b/src/debug.c
index db15c66..8cfa06e 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -3,84 +3,91 @@
* Licensed under: MIT License
*/
+#include
+
#include "core.h"
#include "debug.h"
#include "vm.h"
static const char* op_name[] = {
- #define OPCODE(name, params, stack) #name,
- #include "opcodes.h"
- #undef OPCODE
- NULL,
+ #define OPCODE(name, params, stack) #name,
+ #include "opcodes.h"
+ #undef OPCODE
+ NULL,
};
+
static void _dumpValue(MSVM* vm, Var value, bool recursive) {
- if (IS_NULL(value)) {
- printf("null");
- return;
- }
- if (IS_BOOL(value)) {
- printf((AS_BOOL(value)) ? "true" : "false");
- return;
- }
- if (IS_NUM(value)) {
- printf("%.14g", AS_NUM(value));
- return;
- }
- ASSERT(IS_OBJ(value), OOPS);
- Object* obj = AS_OBJ(value);
- switch (obj->type) {
- case OBJ_STRING:
- printf("\"%s\"", ((String*)obj)->data);
- return;
- case OBJ_LIST:
- {
- List* list = ((List*)obj);
- if (recursive) {
- printf("[...]");
- } else {
- printf("[");
- for (int i = 0; i < list->elements.count; i++) {
- if (i != 0) printf(", ");
- _dumpValue(vm, list->elements.data[i], true);
- }
- printf("]");
- }
- return;
- }
+ if (IS_NULL(value)) {
+ printf("null");
+ return;
+ }
+ if (IS_BOOL(value)) {
+ printf((AS_BOOL(value)) ? "true" : "false");
+ return;
+ }
+ if (IS_NUM(value)) {
+ printf("%.14g", AS_NUM(value));
+ return;
+ }
+ ASSERT(IS_OBJ(value), OOPS);
+ Object* obj = AS_OBJ(value);
+ switch (obj->type) {
+ case OBJ_STRING:
+ printf("\"%s\"", ((String*)obj)->data);
+ return;
+ case OBJ_LIST:
+ {
+ List* list = ((List*)obj);
+ if (recursive) {
+ printf("[...]");
+ } else {
+ printf("[");
+ for (int i = 0; i < list->elements.count; i++) {
+ if (i != 0) printf(", ");
+ _dumpValue(vm, list->elements.data[i], true);
+ }
+ printf("]");
+ }
+ return;
+ }
- case OBJ_MAP:
- TODO;
- return;
+ case OBJ_MAP:
+ TODO;
+ return;
- case OBJ_RANGE:
- {
- Range* range = ((Range*)obj);
- printf("%.2g..%.2g", range->from, range->to);
- }
+ case OBJ_RANGE:
+ {
+ Range* range = ((Range*)obj);
+ printf("%.2g..%.2g", range->from, range->to);
+ return;
+ }
- case OBJ_SCRIPT:
- printf("[Script:%p]", obj);
- case OBJ_FUNC:
- printf("[Fn:%p]", obj);
- case OBJ_USER:
- printf("[UserObj:%p]", obj);
- }
+ case OBJ_SCRIPT:
+ printf("[Script:%p]", obj);
+ return;
+ case OBJ_FUNC:
+ printf("[Fn:%p]", obj);
+ return;
+ case OBJ_USER:
+ printf("[UserObj:%p]", obj);
+ return;
+ }
}
void dumpValue(MSVM* vm, Var value) {
- _dumpValue(vm, value, false);
+ _dumpValue(vm, value, false);
}
-void dumpInstructions(MSVM* vm, Function* func) {
+void dumpInstructions(MSVM* vm, Function* func) {
- int i = 0;
- uint8_t* opcodes = func->fn->opcodes.data;
- int* lines = func->fn->oplines.data;
- int line = 1, last_line = 0;
+ int i = 0;
+ uint8_t* opcodes = func->fn->opcodes.data;
+ int* lines = func->fn->oplines.data;
+ int line = 1, last_line = 0;
- printf("Instruction Dump of function '%s'\n", func->name);
+ printf("Instruction Dump of function '%s'\n", func->name);
#define READ_BYTE() (opcodes[i++])
#define READ_SHORT() (i += 2, opcodes[i - 2] << 8 | opcodes[i-1])
@@ -88,163 +95,178 @@ void dumpInstructions(MSVM* vm, Function* func) {
#define SHORT_ARG() printf("%5d\n", READ_SHORT())
#define INDENTATION " "
- while (i < func->fn->opcodes.count) {
- ASSERT_INDEX(i, func->fn->opcodes.count);
+ while (i < func->fn->opcodes.count) {
+ ASSERT_INDEX(i, func->fn->opcodes.count);
- // Print the line number.
- line = lines[i];
- if (line != last_line) {
- printf(INDENTATION "%4d:", line);
- last_line = line;
- } else {
- printf(INDENTATION " ");
- }
+ // Print the line number.
+ line = lines[i];
+ if (line != last_line) {
+ printf(INDENTATION "%4d:", line);
+ last_line = line;
+ } else {
+ printf(INDENTATION " ");
+ }
- printf(INDENTATION "%4d %-16s", i, op_name[opcodes[i]]);
+ printf(INDENTATION "%4d %-16s", i, op_name[opcodes[i]]);
- Opcode op = (Opcode)func->fn->opcodes.data[i++];
- switch (op) {
- case OP_CONSTANT:
- {
- int index = READ_SHORT();
- printf("%5d ", index);
- ASSERT_INDEX(index, func->owner->literals.count);
- Var value = func->owner->literals.data[index];
- dumpValue(vm, value);
- printf("\n");
- break;
- }
+ Opcode op = (Opcode)func->fn->opcodes.data[i++];
+ switch (op) {
+ case OP_CONSTANT:
+ {
+ int index = READ_SHORT();
+ printf("%5d ", index);
+ ASSERT_INDEX(index, func->owner->literals.count);
+ Var value = func->owner->literals.data[index];
+ dumpValue(vm, value);
+ printf("\n");
+ break;
+ }
- case OP_PUSH_NULL:
- case OP_PUSH_SELF:
- case OP_PUSH_TRUE:
- case OP_PUSH_FALSE:
- NO_ARGS();
- break;
+ case OP_PUSH_NULL:
+ case OP_PUSH_SELF:
+ case OP_PUSH_TRUE:
+ case OP_PUSH_FALSE:
+ NO_ARGS();
+ break;
- case OP_PUSH_LIST:
- SHORT_ARG();
- break;
+ case OP_PUSH_LIST:
+ SHORT_ARG();
+ break;
- case OP_LIST_APPEND: NO_ARGS(); break;
+ case OP_LIST_APPEND: NO_ARGS(); break;
- case OP_PUSH_LOCAL_0:
- case OP_PUSH_LOCAL_1:
- case OP_PUSH_LOCAL_2:
- case OP_PUSH_LOCAL_3:
- case OP_PUSH_LOCAL_4:
- case OP_PUSH_LOCAL_5:
- case OP_PUSH_LOCAL_6:
- case OP_PUSH_LOCAL_7:
- case OP_PUSH_LOCAL_8:
- NO_ARGS();
- break;
+ case OP_PUSH_LOCAL_0:
+ case OP_PUSH_LOCAL_1:
+ case OP_PUSH_LOCAL_2:
+ case OP_PUSH_LOCAL_3:
+ case OP_PUSH_LOCAL_4:
+ case OP_PUSH_LOCAL_5:
+ case OP_PUSH_LOCAL_6:
+ case OP_PUSH_LOCAL_7:
+ case OP_PUSH_LOCAL_8:
+ NO_ARGS();
+ break;
- case OP_PUSH_LOCAL_N:
- SHORT_ARG();
- break;
+ case OP_PUSH_LOCAL_N:
+ SHORT_ARG();
+ break;
- case OP_STORE_LOCAL_0:
- case OP_STORE_LOCAL_1:
- case OP_STORE_LOCAL_2:
- case OP_STORE_LOCAL_3:
- case OP_STORE_LOCAL_4:
- case OP_STORE_LOCAL_5:
- case OP_STORE_LOCAL_6:
- case OP_STORE_LOCAL_7:
- case OP_STORE_LOCAL_8:
- NO_ARGS();
- break;
+ case OP_STORE_LOCAL_0:
+ case OP_STORE_LOCAL_1:
+ case OP_STORE_LOCAL_2:
+ case OP_STORE_LOCAL_3:
+ case OP_STORE_LOCAL_4:
+ case OP_STORE_LOCAL_5:
+ case OP_STORE_LOCAL_6:
+ case OP_STORE_LOCAL_7:
+ case OP_STORE_LOCAL_8:
+ NO_ARGS();
+ break;
- case OP_STORE_LOCAL_N:
- SHORT_ARG();
- break;
+ case OP_STORE_LOCAL_N:
+ SHORT_ARG();
+ break;
- case OP_PUSH_GLOBAL:
- case OP_STORE_GLOBAL:
- case OP_PUSH_FN:
- SHORT_ARG();
- break;
+ case OP_PUSH_GLOBAL:
+ case OP_STORE_GLOBAL:
+ case OP_PUSH_FN:
+ SHORT_ARG();
+ break;
- case OP_PUSH_BUILTIN_FN:
- {
- int index = READ_SHORT();
- printf("%5d [Fn:%s]\n", index, getBuiltinFunctionName(index));
- break;
- }
-
+ case OP_PUSH_BUILTIN_FN:
+ {
+ int index = READ_SHORT();
+ printf("%5d [Fn:%s]\n", index, getBuiltinFunctionName(index));
+ break;
+ }
+
- case OP_POP:
- NO_ARGS();
- break;
+ case OP_POP:
+ NO_ARGS();
+ break;
- case OP_CALL:
- printf("%5d (argc)\n", READ_SHORT());
- break;
+ case OP_CALL:
+ printf("%5d (argc)\n", READ_SHORT());
+ break;
- case OP_ITER:
- case OP_JUMP:
- case OP_JUMP_IF:
- case OP_JUMP_IF_NOT:
- {
- int offset = READ_SHORT();
- printf("%5d (ip:%d)\n", offset, i + offset);
- break;
- }
+ case OP_ITER:
+ case OP_JUMP:
+ case OP_JUMP_IF:
+ case OP_JUMP_IF_NOT:
+ {
+ int offset = READ_SHORT();
+ printf("%5d (ip:%d)\n", offset, i + offset);
+ break;
+ }
- case OP_LOOP:
- {
- int offset = READ_SHORT();
- printf("%5d (ip:%d)\n", -offset, i - offset);
- break;
- }
+ case OP_LOOP:
+ {
+ int offset = READ_SHORT();
+ printf("%5d (ip:%d)\n", -offset, i - offset);
+ break;
+ }
- case OP_RETURN: NO_ARGS(); break;
+ case OP_RETURN: NO_ARGS(); break;
- case OP_GET_ATTRIB:
- case OP_GET_ATTRIB_AOP:
- case OP_SET_ATTRIB:
- SHORT_ARG();
- break;
+ case OP_GET_ATTRIB:
+ case OP_GET_ATTRIB_AOP:
+ case OP_SET_ATTRIB:
+ SHORT_ARG();
+ break;
- case OP_GET_SUBSCRIPT:
- case OP_GET_SUBSCRIPT_AOP:
- case OP_SET_SUBSCRIPT:
- NO_ARGS();
- break;
+ case OP_GET_SUBSCRIPT:
+ case OP_GET_SUBSCRIPT_AOP:
+ case OP_SET_SUBSCRIPT:
+ NO_ARGS();
+ break;
- case OP_NEGATIVE:
- case OP_NOT:
- case OP_BIT_NOT:
- case OP_ADD:
- case OP_SUBTRACT:
- case OP_MULTIPLY:
- case OP_DIVIDE:
- case OP_MOD:
- case OP_BIT_AND:
- case OP_BIT_OR:
- case OP_BIT_XOR:
- case OP_BIT_LSHIFT:
- case OP_BIT_RSHIFT:
- case OP_AND:
- case OP_OR:
- case OP_EQEQ:
- case OP_NOTEQ:
- case OP_LT:
- case OP_LTEQ:
- case OP_GT:
- case OP_GTEQ:
- case OP_RANGE:
- case OP_IN:
- case OP_END:
- NO_ARGS();
- break;
+ case OP_NEGATIVE:
+ case OP_NOT:
+ case OP_BIT_NOT:
+ case OP_ADD:
+ case OP_SUBTRACT:
+ case OP_MULTIPLY:
+ case OP_DIVIDE:
+ case OP_MOD:
+ case OP_BIT_AND:
+ case OP_BIT_OR:
+ case OP_BIT_XOR:
+ case OP_BIT_LSHIFT:
+ case OP_BIT_RSHIFT:
+ case OP_AND:
+ case OP_OR:
+ case OP_EQEQ:
+ case OP_NOTEQ:
+ case OP_LT:
+ case OP_LTEQ:
+ case OP_GT:
+ case OP_GTEQ:
+ case OP_RANGE:
+ case OP_IN:
+ case OP_END:
+ NO_ARGS();
+ break;
- default:
- UNREACHABLE();
- break;
- }
- }
+ default:
+ UNREACHABLE();
+ break;
+ }
+ }
}
+void reportStackTrace(MSVM* vm) {
+ Fiber* fiber = vm->fiber;
+ Script* script = fiber->func->owner;
+
+ //vm->config.error_fn(vm, MS_ERROR_RUNTIME, NULL, -1, fiber->error )
+ //
+ //// TODO: I'm not confident about this approach.
+ //if (script->path != NULL) { // User script.
+ //
+ //
+ //} else { // "std" script.
+ //
+ //}
+
+}
diff --git a/src/debug.h b/src/debug.h
index bddc83a..5a4cb5d 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -7,7 +7,6 @@
#define DEBUG_H
#include "common.h"
-#include "miniscript.h"
// Dump the value of the [value] without a new line at the end.
void dumpValue(MSVM* vm, Var value);
@@ -15,4 +14,7 @@ void dumpValue(MSVM* vm, Var value);
// Dump opcodes of the given function.
void dumpInstructions(MSVM* vm, Function* func);
+// Print stack track.
+void reportStackTrace(MSVM* vm);
+
#endif // DEBUG_H
diff --git a/src/var.c b/src/var.c
index 60c2a05..721b393 100644
--- a/src/var.c
+++ b/src/var.c
@@ -8,7 +8,34 @@
#include "var.h"
#include "vm.h"
- // Number of maximum digits for to_string buffer.
+// Public Api /////////////////////////////////////////////////////////////////
+Var msVarBool(MSVM* vm, bool value) {
+ return VAR_BOOL(value);
+}
+
+Var msVarNumber(MSVM* vm, double value) {
+ return VAR_NUM(value);
+}
+
+Var msVarString(MSVM* vm, const char* value) {
+ return VAR_OBJ(newString(vm, value, (uint32_t)strlen(value)));
+}
+
+bool msAsBool(MSVM* vm, Var value) {
+ return AS_BOOL(value);
+}
+
+double msAsNumber(MSVM* vm, Var value) {
+ return AS_NUM(value);
+}
+
+const char* msAsString(MSVM* vm, Var value) {
+ return AS_STRING(value)->data;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Number of maximum digits for to_string buffer.
#define TO_STRING_BUFF_SIZE 128
void varInitObject(Object* self, MSVM* vm, ObjectType type) {
@@ -84,7 +111,6 @@ Script* newScript(MSVM* vm) {
varInitObject(&script->_super, vm, OBJ_SCRIPT);
script->name = NULL;
- script->name_length = 0;
script->path = NULL;
varBufferInit(&script->globals);
diff --git a/src/var.h b/src/var.h
index da97fcd..a1efdc6 100644
--- a/src/var.h
+++ b/src/var.h
@@ -30,8 +30,7 @@
#include
#include
-#include "miniscript.h"
-
+#include "common.h"
#include "types/gen/byte_buffer.h"
#include "types/gen/function_buffer.h"
#include "types/gen/int_buffer.h"
@@ -157,8 +156,6 @@
#define AS_MAP(value) ((Map*)AS_OBJ(value))
#define AS_RANGE(value) ((Range*)AS_OBJ(value))
-typedef uint64_t Var;
-
#else
// TODO: Union tagging implementation of all the above macros ignore macros
@@ -196,9 +193,20 @@ typedef enum /* ObjectType */ {
OBJ_SCRIPT,
OBJ_FUNC,
+ OBJ_FIBER,
+
OBJ_USER,
} ObjectType;
+// This will terminate compiler (because of 1/0 evaluvated) if ObjectType max
+// is not [count]. Use this to ensure every time switching ObjectType will
+// cover all object types.
+#if DEBUG
+ #define CHECK_MISSING_OBJ_TYPE(count) (1/ ((int)(!(count ^ OBJ_USER))) )
+#else
+ #define CHECK_MISSING_OBJ_TYPE(count) do {} while (false)
+#endif
+
// Base struct for all heap allocated objects.
struct Object {
ObjectType type; //< Type of the object in \ref var_Object_Type.
@@ -233,8 +241,9 @@ struct Range {
struct Script {
Object _super;
+ // One of the below is null and other one is not. Since "std" script names
+ // are hardcoded and user script names are constructed.
const char* name; //< Std script's name. Null for user script.
- int name_length; //< Length of the name.
String* path; //< Absolute path of the script. Null for std scripts.
ID imports[MAX_IMPORT_SCRIPTS]; //< Imported script IDs.
@@ -271,7 +280,7 @@ struct Function {
};
};
-// Methods.
+// Methods ////////////////////////////////////////////////////////////////////
void varInitObject(Object* self, MSVM* vm, ObjectType type);
@@ -305,7 +314,7 @@ const char* varTypeName(Var v);
bool isVauesSame(Var v1, Var v2);
// Returns the string version of the value. Note: pass false as [_recursive]
-// It's an internal use (or may be I could make a wrapper around).
+// It's for internal use (or may be I could make a wrapper around).
String* toString(MSVM* vm, Var v, bool _recursive);
// Returns the truthy value of the var.
diff --git a/src/vm.c b/src/vm.c
index bd229b4..b3a4f4e 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -9,7 +9,7 @@
#include "debug.h"
#include "utils.h"
-#define HAS_ERROR() (vm->error != NULL)
+#define HAS_ERROR() (vm->fiber->error != NULL)
// Initially allocated call frame capacity. Will grow dynamically.
#define INITIAL_CALL_FRAMES 4
@@ -17,6 +17,13 @@
// Minimum size of the stack.
#define MIN_STACK_SIZE 128
+Fiber* newFiber(MSVM* vm) {
+ Fiber* fiber = ALLOCATE(vm, Fiber);
+ memset(fiber, 0, sizeof(Fiber));
+ varInitObject(&fiber->_super, vm, OBJ_FIBER);
+ return fiber;
+}
+
void* vmRealloc(MSVM* self, void* memory, size_t old_size, size_t new_size) {
// Track the total allocated memory of the VM to trigger the GC.
@@ -82,19 +89,8 @@ void msSetUserData(MSVM* vm, void* user_data) {
* RUNTIME *
*****************************************************************************/
-#ifdef DEBUG
-#include
-// TODO: A function for quick debug. REMOVE.
-void _printStackTop(MSVM* vm) {
- if (vm->sp != vm->stack) {
- Var v = *(vm->sp - 1);
- printf("%s\n", toString(vm, v, false)->data);
- }
-}
-#endif
-
static void ensureStackSize(MSVM* vm, int size) {
- if (vm->stack_size > size) return;
+ if (vm->fiber->stack_size > size) return;
TODO;
}
@@ -102,27 +98,27 @@ static inline void pushCallFrame(MSVM* vm, Function* fn) {
ASSERT(!fn->is_native, "Native function shouldn't use call frames.");
// Grow the stack frame if needed.
- if (vm->frame_count + 1 > vm->frame_capacity) {
- int new_capacity = vm->frame_capacity * 2;
- vm->frames = (CallFrame*)vmRealloc(vm, vm->frames,
- sizeof(CallFrame) * vm->frame_capacity,
+ if (vm->fiber->frame_count + 1 > vm->fiber->frame_capacity) {
+ int new_capacity = vm->fiber->frame_capacity * 2;
+ vm->fiber->frames = (CallFrame*)vmRealloc(vm, vm->fiber->frames,
+ sizeof(CallFrame) * vm->fiber->frame_capacity,
sizeof(CallFrame) * new_capacity);
- vm->frame_capacity = new_capacity;
+ vm->fiber->frame_capacity = new_capacity;
}
// Grow the stack if needed.
- int stack_size = (int)(vm->sp - vm->stack);
+ int stack_size = (int)(vm->fiber->sp - vm->fiber->stack);
int needed = stack_size + fn->fn->stack_size;
ensureStackSize(vm, needed);
- CallFrame* frame = &vm->frames[vm->frame_count++];
- frame->rbp = vm->rbp + 1; // vm->rbp is the return value.
+ CallFrame* frame = &vm->fiber->frames[vm->fiber->frame_count++];
+ frame->rbp = vm->fiber->ret;
frame->fn = fn;
frame->ip = fn->fn->opcodes.data;
}
void msSetRuntimeError(MSVM* vm, const char* format, ...) {
- vm->error = newString(vm, "TODO:", 5);
+ vm->fiber->error = newString(vm, "TODO:", 5);
TODO;
}
@@ -146,6 +142,34 @@ MSInterpretResult msInterpret(MSVM* vm, const char* file) {
return vmRunScript(vm, script);
}
+#ifdef DEBUG
+#include
+
+// FIXME: for temp debugging. (implement dump stack frames).
+void _debugRuntime(MSVM* vm) {
+ return;
+ system("cls");
+ Fiber* fiber = vm->fiber;
+
+ for (int i = fiber->frame_count - 1; i >= 0; i--) {
+ CallFrame frame = fiber->frames[i];
+
+ Var* top = fiber->sp - 1;
+ if (i != fiber->frame_count - 1) {
+ top = fiber->frames[i + 1].rbp - 1;
+ }
+
+ for (; top >= frame.rbp; top--) {
+ printf("[*]: ");
+ dumpValue(vm, *top); printf("\n");
+ }
+
+ printf("----------------\n");
+ }
+}
+
+#endif
+
MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
register uint8_t* ip; //< Current instruction pointer.
@@ -153,11 +177,32 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
register CallFrame* frame; //< Current call frame.
register Script* script; //< Currently executing script.
-#define PUSH(value) (*vm->sp++ = (value))
-#define POP() (*(--vm->sp))
-#define DROP() (--vm->sp)
-#define PEEK() (*(vm->sp - 1))
-#define READ_BYTE() (*ip++)
+ vm->fiber = newFiber(vm);
+ vm->fiber->func = _script->body;
+
+ // Allocate stack.
+ int stack_size = utilPowerOf2Ceil(vm->fiber->func->fn->stack_size + 1);
+ if (stack_size < MIN_STACK_SIZE) stack_size = MIN_STACK_SIZE;
+ vm->fiber->stack_size = stack_size;
+ vm->fiber->stack = ALLOCATE_ARRAY(vm, Var, vm->fiber->stack_size);
+ vm->fiber->sp = vm->fiber->stack;
+ vm->fiber->ret = vm->fiber->stack;
+
+ // Allocate call frames.
+ vm->fiber->frame_capacity = INITIAL_CALL_FRAMES;
+ vm->fiber->frames = ALLOCATE_ARRAY(vm, CallFrame, vm->fiber->frame_capacity);
+ vm->fiber->frame_count = 1;
+
+ // Initialize VM's first frame.
+ vm->fiber->frames[0].ip = _script->body->fn->opcodes.data;
+ vm->fiber->frames[0].fn = _script->body;
+ vm->fiber->frames[0].rbp = vm->fiber->stack;
+
+#define PUSH(value) (*vm->fiber->sp++ = (value))
+#define POP() (*(--vm->fiber->sp))
+#define DROP() (--vm->fiber->sp)
+#define PEEK() (*(vm->fiber->sp - 1))
+#define READ_BYTE() (*ip++)
#define READ_SHORT() (ip+=2, (uint16_t)((ip[-2] << 8) | ip[-1]))
// Check if any runtime error exists and if so returns RESULT_RUNTIME_ERROR.
@@ -184,19 +229,24 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
// Update the call frame and ip once vm's call frame pushed or popped.
// fuction call, return or done running imported script.
-#define LOAD_FRAME() \
- do { \
- frame = &vm->frames[vm->frame_count-1]; \
- ip = frame->ip; \
- rbp = frame->rbp; \
- script = frame->fn->owner; \
+#define LOAD_FRAME() \
+ do { \
+ frame = &vm->fiber->frames[vm->fiber->frame_count-1]; \
+ ip = frame->ip; \
+ rbp = frame->rbp; \
+ script = frame->fn->owner; \
} while (false)
#ifdef OPCODE
#error "OPCODE" should not be deifined here.
#endif
-#define DEBUG_INSTRUCTION() //_printStackTop(vm)
+#if DEBUG
+ #define DEBUG_INSTRUCTION() _debugRuntime(vm)
+#else
+ #define DEBUG_INSTRUCTION() do { } while (false)
+#endif
+
#define SWITCH(code) \
L_vm_main_loop: \
@@ -205,25 +255,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
#define OPCODE(code) case OP_##code
#define DISPATCH() goto L_vm_main_loop
- // Allocate stack.
- int stack_size = utilPowerOf2Ceil(_script->body->fn->stack_size + 1);
- if (stack_size < MIN_STACK_SIZE) stack_size = MIN_STACK_SIZE;
- vm->stack_size = stack_size;
- vm->stack = ALLOCATE_ARRAY(vm, Var, vm->stack_size);
- vm->sp = vm->stack;
- vm->rbp = vm->stack;
PUSH(VAR_NULL); // Return value of the script body.
-
- // Allocate call frames.
- vm->frame_capacity = INITIAL_CALL_FRAMES;
- vm->frames = ALLOCATE_ARRAY(vm, CallFrame, vm->frame_capacity);
- vm->frame_count = 1;
-
- // Initialize VM's first frame.
- vm->frames[0].ip = _script->body->fn->opcodes.data;
- vm->frames[0].fn = _script->body;
- vm->frames[0].rbp = vm->rbp + 1; // +1 to skip script's null return value.
-
LOAD_FRAME();
Opcode instruction;
@@ -259,7 +291,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(LIST_APPEND):
{
Var elem = POP();
- Var list = *(vm->sp - 1);
+ Var list = *(vm->fiber->sp - 1);
ASSERT(IS_OBJ(list) && AS_OBJ(list)->type == OBJ_LIST, OOPS);
varBufferWrite(&((List*)AS_OBJ(list))->elements, vm, elem);
DISPATCH();
@@ -276,13 +308,13 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(PUSH_LOCAL_8):
{
int index = (int)(instruction - OP_PUSH_LOCAL_0);
- PUSH(rbp[index]);
+ PUSH(rbp[index + 1]); // +1: rbp[0] is return value.
DISPATCH();
}
OPCODE(PUSH_LOCAL_N):
{
int index = READ_SHORT();
- PUSH(rbp[index]);
+ PUSH(rbp[index + 1]); // +1: rbp[0] is return value.
DISPATCH();
}
@@ -297,13 +329,13 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(STORE_LOCAL_8):
{
int index = (int)(instruction - OP_STORE_LOCAL_0);
- rbp[index] = PEEK();
+ rbp[index + 1] = PEEK(); // +1: rbp[0] is return value.
DISPATCH();
}
OPCODE(STORE_LOCAL_N):
{
int index = READ_SHORT();
- rbp[index] = PEEK();
+ rbp[index + 1] = PEEK(); // +1: rbp[0] is return value.
DISPATCH();
}
@@ -346,7 +378,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(CALL):
{
int argc = READ_SHORT();
- Var* callable = vm->sp - argc - 1;
+ Var* callable = vm->fiber->sp - argc - 1;
if (IS_OBJ(*callable) && AS_OBJ(*callable)->type == OBJ_FUNC) {
@@ -362,12 +394,12 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
*callable = VAR_NULL;
// Next call frame starts here. (including return value).
- vm->rbp = callable;
+ vm->fiber->ret = callable;
if (fn->is_native) {
fn->native(vm);
// Pop function arguments except for the return value.
- vm->sp = vm->rbp + 1;
+ vm->fiber->sp = vm->fiber->ret + 1;
CHECK_ERROR();
} else {
@@ -384,9 +416,9 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(ITER) :
{
- Var* iter_value = (vm->sp - 1);
- Var* iterator = (vm->sp - 2);
- Var* container = (vm->sp - 3);
+ Var* iter_value = (vm->fiber->sp - 1);
+ Var* iterator = (vm->fiber->sp - 2);
+ Var* container = (vm->fiber->sp - 3);
int jump_offset = READ_SHORT();
if (!varIterate(vm, *container, iterator, iter_value)) {
DROP(); //< Iter value.
@@ -433,20 +465,20 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(RETURN):
{
Var ret = POP();
- vm->frame_count--;
+ vm->fiber->frame_count--;
// If no more call frames. We're done.
- if (vm->frame_count == 0) {
- vm->sp = vm->stack;
+ if (vm->fiber->frame_count == 0) {
+ vm->fiber->sp = vm->fiber->stack;
PUSH(ret);
return RESULT_SUCCESS;
}
// Set the return value.
- *(frame->rbp - 1) = ret;
+ *(frame->rbp) = ret;
// Pop the locals and update stack pointer.
- vm->sp = frame->rbp;
+ vm->fiber->sp = frame->rbp + 1; // +1: rbp is returned value.
LOAD_FRAME();
DISPATCH();
@@ -462,7 +494,7 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(GET_ATTRIB_AOP):
{
- Var on = *(vm->sp - 1);
+ Var on = *(vm->fiber->sp - 1);
String* name = script->names.data[READ_SHORT()];
PUSH(varGetAttrib(vm, on, name));
DISPATCH();
@@ -482,8 +514,8 @@ MSInterpretResult vmRunScript(MSVM* vm, Script* _script) {
OPCODE(GET_SUBSCRIPT_AOP):
{
- Var key = *(vm->sp - 1);
- Var on = *(vm->sp - 2);
+ Var key = *(vm->fiber->sp - 1);
+ Var on = *(vm->fiber->sp - 2);
PUSH(varGetSubscript(vm, on, key));
CHECK_ERROR();
DISPATCH();
diff --git a/src/vm.h b/src/vm.h
index 7513e1e..565c6cc 100644
--- a/src/vm.h
+++ b/src/vm.h
@@ -6,8 +6,6 @@
#ifndef VM_H
#define VM_H
-#include "miniscript.h"
-
#include "common.h"
#include "compiler.h"
#include "var.h"
@@ -31,6 +29,40 @@ typedef struct {
Var* rbp; //< Stack base pointer. (%rbp)
} CallFrame;
+struct Fiber {
+ Object _super;
+
+ // The root function of the fiber. (For script it'll be the script's implicit
+ // body function).
+ Function* func;
+
+ // The stack of the execution holding locals and temps. A heap allocated
+ // Will and grow as needed.
+ Var* stack;
+
+ // The stack pointer (%rsp) pointing to the stack top.
+ Var* sp;
+
+ // The stack base pointer of the current frame. It'll be updated before
+ // calling a native function.
+ Var* ret;
+
+ // Size of the allocated stack.
+ int stack_size;
+
+ // Heap allocated array of call frames will grow as needed.
+ CallFrame* frames;
+
+ // Capacity of the frames array.
+ int frame_capacity;
+
+ // Number of frame entry in frames.
+ int frame_count;
+
+ // Runtime error initially NULL, heap allocated.
+ String* error;
+};
+
struct MSVM {
// The first object in the link list of all heap allocated objects.
@@ -63,33 +95,12 @@ struct MSVM {
// Number of script cache.
int script_count;
- // The stack of the execution holding locals and temps. A heap allocated
- // Will and grow as needed.
- Var* stack;
-
- // The stack pointer (%rsp) pointing to the stack top.
- Var* sp;
-
- // The stack base pointer of the current frame. It'll be updated before
- // calling a native function.
- Var* rbp;
-
- // Size of the allocated stack.
- int stack_size;
-
- // Heap allocated array of call frames will grow as needed.
- CallFrame* frames;
-
- // Capacity of the frames array.
- int frame_capacity;
-
- // Number of frame entry in frames.
- int frame_count;
-
- // Runtime error initially NULL, heap allocated.
- String* error;
+ // Current fiber.
+ Fiber* fiber;
};
+Fiber* newFiber(MSVM* vm);
+
// A realloc wrapper which handles memory allocations of the VM.
// - To allocate new memory pass NULL to parameter [memory] and 0 to
// parameter [old_size] on failure it'll return NULL.