mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-11 07:00:58 +08:00
Merge pull request #44 from ThakeeNathees/repl-refactor-3
REPL refactor [3/3]
This commit is contained in:
commit
63d3039eeb
@ -28,6 +28,8 @@
|
|||||||
- Hex, binary literals and floats like ".5".
|
- Hex, binary literals and floats like ".5".
|
||||||
- Docs build to a single directory, and replace url space (%20) with a hyphen.
|
- Docs build to a single directory, and replace url space (%20) with a hyphen.
|
||||||
- Complete all the TODO; macros.
|
- Complete all the TODO; macros.
|
||||||
|
- implement MAX_ARGC checks (would cause a buffer overflow if not)
|
||||||
|
when compiling and calling a function (also in fibers).
|
||||||
|
|
||||||
// Low priority.
|
// Low priority.
|
||||||
- Ignore line with '\' character.
|
- Ignore line with '\' character.
|
||||||
|
64
cli/main.c
64
cli/main.c
@ -14,7 +14,7 @@
|
|||||||
void registerModules(PKVM* vm);
|
void registerModules(PKVM* vm);
|
||||||
|
|
||||||
// Path public functions (TODO: should I add a header for that?)
|
// Path public functions (TODO: should I add a header for that?)
|
||||||
void pathInit();
|
void pathInit(void);
|
||||||
bool pathIsAbsolute(const char* path);
|
bool pathIsAbsolute(const char* path);
|
||||||
void pathGetDirName(const char* path, size_t* length);
|
void pathGetDirName(const char* path, size_t* length);
|
||||||
size_t pathNormalize(const char* path, char* buff, size_t buff_size);
|
size_t pathNormalize(const char* path, char* buff, size_t buff_size);
|
||||||
@ -23,6 +23,11 @@ size_t pathJoin(const char* from, const char* path, char* buffer,
|
|||||||
|
|
||||||
// ---------------------------------------
|
// ---------------------------------------
|
||||||
|
|
||||||
|
// FIXME:
|
||||||
|
typedef struct {
|
||||||
|
bool repl_mode;
|
||||||
|
} VmUserData;
|
||||||
|
|
||||||
void onResultDone(PKVM* vm, PkStringPtr result) {
|
void onResultDone(PKVM* vm, PkStringPtr result) {
|
||||||
|
|
||||||
if ((bool)result.user_data) {
|
if ((bool)result.user_data) {
|
||||||
@ -31,15 +36,22 @@ void onResultDone(PKVM* vm, PkStringPtr result) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void errorFunction(PKVM* vm, PkErrorType type, const char* file, int line,
|
void errorFunction(PKVM* vm, PkErrorType type, const char* file, int line,
|
||||||
const char* message) {
|
const char* message) {
|
||||||
|
|
||||||
|
VmUserData* ud = (VmUserData*)pkGetUserData(vm);
|
||||||
|
bool repl = (ud) ? ud->repl_mode : false;
|
||||||
|
|
||||||
if (type == PK_ERROR_COMPILE) {
|
if (type == PK_ERROR_COMPILE) {
|
||||||
fprintf(stderr, "Error: %s\n at \"%s\":%i\n", message, file, line);
|
|
||||||
|
if (repl) fprintf(stderr, "Error: %s\n", message);
|
||||||
|
else fprintf(stderr, "Error: %s\n at \"%s\":%i\n", message, file, line);
|
||||||
|
|
||||||
} else if (type == PK_ERROR_RUNTIME) {
|
} else if (type == PK_ERROR_RUNTIME) {
|
||||||
fprintf(stderr, "Error: %s\n", message);
|
fprintf(stderr, "Error: %s\n", message);
|
||||||
|
|
||||||
} else if (type == PK_ERROR_STACKTRACE) {
|
} else if (type == PK_ERROR_STACKTRACE) {
|
||||||
fprintf(stderr, " %s() [\"%s\":%i]\n", message, file, line);
|
if (repl) fprintf(stderr, " %s() [line:%i]\n", message, line);
|
||||||
|
else fprintf(stderr, " %s() [\"%s\":%i]\n", message, file, line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +59,7 @@ void writeFunction(PKVM* vm, const char* text) {
|
|||||||
fprintf(stdout, "%s", text);
|
fprintf(stdout, "%s", text);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* read_line() {
|
static const char* read_line(void) {
|
||||||
// FIXME: use fgetc char by char till reach a new line.
|
// FIXME: use fgetc char by char till reach a new line.
|
||||||
const int size = 1024;
|
const int size = 1024;
|
||||||
char* mem = (char*) malloc(size);
|
char* mem = (char*) malloc(size);
|
||||||
@ -141,15 +153,15 @@ int main(int argc, char** argv) {
|
|||||||
"PocketLang " PK_VERSION_STRING " (https://github.com/ThakeeNathees/pocketlang/)\n"
|
"PocketLang " PK_VERSION_STRING " (https://github.com/ThakeeNathees/pocketlang/)\n"
|
||||||
"Copyright(c) 2020 - 2021 ThakeeNathees.\n"
|
"Copyright(c) 2020 - 2021 ThakeeNathees.\n"
|
||||||
"Free and open source software under the terms of the MIT license.\n";
|
"Free and open source software under the terms of the MIT license.\n";
|
||||||
const char* help = "usage: pocket [-c cmd | file]\n";
|
|
||||||
|
|
||||||
|
const char* usage = "usage: pocket [-c cmd | file]\n";
|
||||||
|
|
||||||
// TODO: implement arg parse, REPL.
|
// TODO: implement arg parse, REPL.
|
||||||
|
|
||||||
if (argc < 2) {
|
//if (argc < 2) {
|
||||||
printf("%s\n%s", notice, help);
|
// printf("%s\n%s", notice, help);
|
||||||
return 0;
|
// return 0;
|
||||||
}
|
//}
|
||||||
|
|
||||||
// Initialize cli.
|
// Initialize cli.
|
||||||
pathInit();
|
pathInit();
|
||||||
@ -166,13 +178,39 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
PKVM* vm = pkNewVM(&config);
|
PKVM* vm = pkNewVM(&config);
|
||||||
registerModules(vm);
|
registerModules(vm);
|
||||||
PkResult result;
|
|
||||||
|
|
||||||
// FIXME: this is temp till arg parse implemented.
|
// FIXME: this is temp till arg parse implemented.
|
||||||
|
PkResult result;
|
||||||
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
// TODO:
|
PkHandle* module = pkNewModule(vm, "$(REPL)");
|
||||||
//PkHandle* module = pkNewModule(vm, "$(REPL)");
|
options.repl_mode = true;
|
||||||
|
|
||||||
|
VmUserData user_data;
|
||||||
|
user_data.repl_mode = true;
|
||||||
|
pkSetUserData(vm, &user_data);
|
||||||
|
|
||||||
|
printf("%s\n", notice);
|
||||||
|
bool done = false;
|
||||||
|
do {
|
||||||
|
printf(">>> ");
|
||||||
|
PkStringPtr line = { read_line(), onResultDone, (void*)true };
|
||||||
|
// TODO: if line is empty continue.
|
||||||
|
|
||||||
|
result = pkCompileModule(vm, module, line, &options);
|
||||||
|
if (result != PK_RESULT_SUCCESS) continue;
|
||||||
|
|
||||||
|
PkHandle* main_fn = pkGetFunction(vm, module, PK_IMPLICIT_MAIN_NAME);
|
||||||
|
PkHandle* fiber = pkNewFiber(vm, main_fn);
|
||||||
|
result = pkRunFiber(vm, fiber, 0, NULL);
|
||||||
|
|
||||||
|
pkReleaseHandle(vm, main_fn);
|
||||||
|
pkReleaseHandle(vm, fiber);
|
||||||
|
|
||||||
|
} while (!done);
|
||||||
|
|
||||||
|
pkReleaseHandle(vm, module);
|
||||||
|
|
||||||
|
|
||||||
} if (argc >= 3 && strcmp(argv[1], "-c") == 0) {
|
} if (argc >= 3 && strcmp(argv[1], "-c") == 0) {
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@
|
|||||||
/* PUBLIC FUNCTIONS */
|
/* PUBLIC FUNCTIONS */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
|
|
||||||
void pathInit() {
|
void pathInit(void) {
|
||||||
cwk_path_set_style(CWK_STYLE_UNIX);
|
cwk_path_set_style(CWK_STYLE_UNIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,6 +41,11 @@
|
|||||||
// header for more information on Nan-tagging.
|
// header for more information on Nan-tagging.
|
||||||
#define VAR_NAN_TAGGING 1
|
#define VAR_NAN_TAGGING 1
|
||||||
|
|
||||||
|
// The maximum number of argument a pocketlang function supported to call. This
|
||||||
|
// value is arbitary and feel free to change it. (Just used this limit for an
|
||||||
|
// internal buffer to store values before calling a new fiber).
|
||||||
|
#define MAX_ARGC 32
|
||||||
|
|
||||||
// The factor by which a buffer will grow when it's capacity reached.
|
// The factor by which a buffer will grow when it's capacity reached.
|
||||||
#define GROW_FACTOR 2
|
#define GROW_FACTOR 2
|
||||||
|
|
||||||
|
102
src/compiler.c
102
src/compiler.c
@ -251,7 +251,7 @@ typedef enum {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char* name; //< Directly points into the source string.
|
const char* name; //< Directly points into the source string.
|
||||||
int length; //< Length of the name.
|
uint32_t length; //< Length of the name.
|
||||||
int depth; //< The depth the local is defined in.
|
int depth; //< The depth the local is defined in.
|
||||||
int line; //< The line variable declared for debugging.
|
int line; //< The line variable declared for debugging.
|
||||||
} Local;
|
} Local;
|
||||||
@ -643,7 +643,7 @@ static void lexToken(Compiler* compiler) {
|
|||||||
case ' ':
|
case ' ':
|
||||||
case '\t':
|
case '\t':
|
||||||
case '\r': {
|
case '\r': {
|
||||||
char c = peekChar(compiler);
|
c = peekChar(compiler);
|
||||||
while (c == ' ' || c == '\t' || c == '\r') {
|
while (c == ' ' || c == '\t' || c == '\r') {
|
||||||
eatChar(compiler);
|
eatChar(compiler);
|
||||||
c = peekChar(compiler);
|
c = peekChar(compiler);
|
||||||
@ -735,11 +735,6 @@ static TokenType peek(Compiler* self) {
|
|||||||
return self->current.type;
|
return self->current.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns next token type without lexing a new token.
|
|
||||||
static TokenType peekNext(Compiler* self) {
|
|
||||||
return self->next.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Consume the current token if it's expected and lex for the next token
|
// Consume the current token if it's expected and lex for the next token
|
||||||
// and return true otherwise reutrn false.
|
// and return true otherwise reutrn false.
|
||||||
static bool match(Compiler* self, TokenType expected) {
|
static bool match(Compiler* self, TokenType expected) {
|
||||||
@ -794,7 +789,7 @@ static bool matchEndStatement(Compiler* compiler) {
|
|||||||
// Consume semi collon, multiple new lines or peek 'end' keyword.
|
// Consume semi collon, multiple new lines or peek 'end' keyword.
|
||||||
static void consumeEndStatement(Compiler* compiler) {
|
static void consumeEndStatement(Compiler* compiler) {
|
||||||
if (!matchEndStatement(compiler)) {
|
if (!matchEndStatement(compiler)) {
|
||||||
parseError(compiler, "Expected statement end with newline or ';'.");
|
parseError(compiler, "Expected statement end with '\\n' or ';'.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -924,9 +919,9 @@ static void patchForward(Compiler* compiler, Fn* fn, int index, int name);
|
|||||||
|
|
||||||
static int compilerAddConstant(Compiler* compiler, Var value);
|
static int compilerAddConstant(Compiler* compiler, Var value);
|
||||||
static int compilerGetVariable(Compiler* compiler, const char* name,
|
static int compilerGetVariable(Compiler* compiler, const char* name,
|
||||||
int length);
|
uint32_t length);
|
||||||
static int compilerAddVariable(Compiler* compiler, const char* name,
|
static int compilerAddVariable(Compiler* compiler, const char* name,
|
||||||
int length, int line);
|
uint32_t length, int line);
|
||||||
static void compilerAddForward(Compiler* compiler, int instruction, Fn* fn,
|
static void compilerAddForward(Compiler* compiler, int instruction, Fn* fn,
|
||||||
const char* name, int length, int line);
|
const char* name, int length, int line);
|
||||||
|
|
||||||
@ -1491,7 +1486,7 @@ static void compilerInit(Compiler* compiler, PKVM* vm, const char* source,
|
|||||||
// Return the index of the variable if it's already defined in the current
|
// Return the index of the variable if it's already defined in the current
|
||||||
// scope otherwise returns -1.
|
// scope otherwise returns -1.
|
||||||
static int compilerGetVariable(Compiler* compiler, const char* name,
|
static int compilerGetVariable(Compiler* compiler, const char* name,
|
||||||
int length) {
|
uint32_t length) {
|
||||||
for (int i = compiler->local_count - 1; i >= 0; i--) {
|
for (int i = compiler->local_count - 1; i >= 0; i--) {
|
||||||
Local* local = &compiler->locals[i];
|
Local* local = &compiler->locals[i];
|
||||||
if (length == local->length && strncmp(name, local->name, length) == 0) {
|
if (length == local->length && strncmp(name, local->name, length) == 0) {
|
||||||
@ -1504,13 +1499,13 @@ static int compilerGetVariable(Compiler* compiler, const char* name,
|
|||||||
// Add a variable and return it's index to the context. Assumes that the
|
// Add a variable and return it's index to the context. Assumes that the
|
||||||
// variable name is unique and not defined before in the current scope.
|
// variable name is unique and not defined before in the current scope.
|
||||||
static int compilerAddVariable(Compiler* compiler, const char* name,
|
static int compilerAddVariable(Compiler* compiler, const char* name,
|
||||||
int length, int line) {
|
uint32_t length, int line) {
|
||||||
|
|
||||||
// TODO: should I validate the name for pre-defined, etc?
|
// TODO: should I validate the name for pre-defined, etc?
|
||||||
|
|
||||||
// Check if maximum variable count is reached.
|
// Check if maximum variable count is reached.
|
||||||
bool max_vars_reached = false;
|
bool max_vars_reached = false;
|
||||||
const char* var_type; // For max variables reached error message.
|
const char* var_type = ""; // For max variables reached error message.
|
||||||
if (compiler->scope_depth == DEPTH_GLOBAL) {
|
if (compiler->scope_depth == DEPTH_GLOBAL) {
|
||||||
if (compiler->local_count == MAX_VARIABLES) {
|
if (compiler->local_count == MAX_VARIABLES) {
|
||||||
max_vars_reached = true;
|
max_vars_reached = true;
|
||||||
@ -1726,7 +1721,7 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
|
|||||||
argc++;
|
argc++;
|
||||||
|
|
||||||
const char* param_name = compiler->previous.start;
|
const char* param_name = compiler->previous.start;
|
||||||
int param_len = compiler->previous.length;
|
uint32_t param_len = compiler->previous.length;
|
||||||
|
|
||||||
// TODO: move this to a functions.
|
// TODO: move this to a functions.
|
||||||
bool predefined = false;
|
bool predefined = false;
|
||||||
@ -1757,7 +1752,12 @@ static int compileFunction(Compiler* compiler, FuncType fn_type) {
|
|||||||
compileBlockBody(compiler, BLOCK_FUNC);
|
compileBlockBody(compiler, BLOCK_FUNC);
|
||||||
consume(compiler, TK_END, "Expected 'end' after function definition end.");
|
consume(compiler, TK_END, "Expected 'end' after function definition end.");
|
||||||
|
|
||||||
emitOpcode(compiler, OP_PUSH_NULL);
|
// TODO: This is the function end return, if we pop all the parameters the
|
||||||
|
// below push_null is redundent (because we always have a null at the rbp
|
||||||
|
// of the call frame. (for i in argc : emit(pop)) emit(return); but this
|
||||||
|
// might be faster (right?).
|
||||||
|
|
||||||
|
emitOpcode(compiler, OP_PUSH_NULL);
|
||||||
emitOpcode(compiler, OP_RETURN);
|
emitOpcode(compiler, OP_RETURN);
|
||||||
emitOpcode(compiler, OP_END);
|
emitOpcode(compiler, OP_END);
|
||||||
}
|
}
|
||||||
@ -1931,6 +1931,8 @@ static inline Script* compilerImport(Compiler* compiler) {
|
|||||||
// before executing the below instructions.
|
// before executing the below instructions.
|
||||||
static void compilerImportAll(Compiler* compiler, Script* script) {
|
static void compilerImportAll(Compiler* compiler, Script* script) {
|
||||||
|
|
||||||
|
ASSERT(script != NULL, OOPS);
|
||||||
|
|
||||||
// Line number of the variables which will be bind to the imported sybmols.
|
// Line number of the variables which will be bind to the imported sybmols.
|
||||||
int line = compiler->previous.line;
|
int line = compiler->previous.line;
|
||||||
|
|
||||||
@ -2048,7 +2050,7 @@ static void compileFromImport(Compiler* compiler) {
|
|||||||
emitStoreVariable(compiler, var_index, true);
|
emitStoreVariable(compiler, var_index, true);
|
||||||
emitOpcode(compiler, OP_POP);
|
emitOpcode(compiler, OP_POP);
|
||||||
|
|
||||||
} while (match(compiler, TK_COMMA));
|
} while (match(compiler, TK_COMMA) && (skipNewLines(compiler), true));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Done getting all the attributes, now pop the lib from the stack.
|
// Done getting all the attributes, now pop the lib from the stack.
|
||||||
@ -2115,12 +2117,11 @@ static void compileRegularImport(Compiler* compiler) {
|
|||||||
emitOpcode(compiler, OP_POP);
|
emitOpcode(compiler, OP_POP);
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (match(compiler, TK_COMMA));
|
} while (match(compiler, TK_COMMA) && (skipNewLines(compiler), true));
|
||||||
|
|
||||||
consumeEndStatement(compiler);
|
consumeEndStatement(compiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Compiles an expression. An expression will result a value on top of the
|
// Compiles an expression. An expression will result a value on top of the
|
||||||
// stack.
|
// stack.
|
||||||
static void compileExpression(Compiler* compiler) {
|
static void compileExpression(Compiler* compiler) {
|
||||||
@ -2260,10 +2261,13 @@ static void compileForStatement(Compiler* compiler) {
|
|||||||
static void compileStatement(Compiler* compiler) {
|
static void compileStatement(Compiler* compiler) {
|
||||||
|
|
||||||
// is_temproary will be set to true if the statement is an temporary
|
// is_temproary will be set to true if the statement is an temporary
|
||||||
// expression, it'll used to be pop from the stack. If running REPL mode used
|
// expression, it'll used to be pop from the stack.
|
||||||
// to print it's value.
|
|
||||||
bool is_temproary = false;
|
bool is_temproary = false;
|
||||||
|
|
||||||
|
// This will be set to true if the statement is an expression. It'll used to
|
||||||
|
// print it's value when running in REPL mode.
|
||||||
|
bool is_expression = false;
|
||||||
|
|
||||||
if (match(compiler, TK_BREAK)) {
|
if (match(compiler, TK_BREAK)) {
|
||||||
if (compiler->loop == NULL) {
|
if (compiler->loop == NULL) {
|
||||||
parseError(compiler, "Cannot use 'break' outside a loop.");
|
parseError(compiler, "Cannot use 'break' outside a loop.");
|
||||||
@ -2321,19 +2325,23 @@ static void compileStatement(Compiler* compiler) {
|
|||||||
compiler->new_local = false;
|
compiler->new_local = false;
|
||||||
compileExpression(compiler);
|
compileExpression(compiler);
|
||||||
consumeEndStatement(compiler);
|
consumeEndStatement(compiler);
|
||||||
|
|
||||||
|
is_expression = true;
|
||||||
if (!compiler->new_local) is_temproary = true;
|
if (!compiler->new_local) is_temproary = true;
|
||||||
|
|
||||||
compiler->new_local = false;
|
compiler->new_local = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If running REPL mode, print the expression's evaluvated value. Python
|
// If running REPL mode, print the expression's evaluvated value. Only if
|
||||||
// does print local depth expression too. (it's just a design decision).
|
// we're at the top level. Python does print local depth expression too.
|
||||||
|
// (it's just a design decision).
|
||||||
if (compiler->options && compiler->options->repl_mode &&
|
if (compiler->options && compiler->options->repl_mode &&
|
||||||
is_temproary /*&& compiler->scope_depth == DEPTH_GLOBAL*/) {
|
compiler->func->ptr == compiler->script->body &&
|
||||||
|
is_expression /*&& compiler->scope_depth == DEPTH_GLOBAL*/) {
|
||||||
emitOpcode(compiler, OP_REPL_PRINT);
|
emitOpcode(compiler, OP_REPL_PRINT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_temproary) emitOpcode(compiler, OP_POP);
|
if (is_temproary) emitOpcode(compiler, OP_POP);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile statements that are only valid at the top level of the script. Such
|
// Compile statements that are only valid at the top level of the script. Such
|
||||||
@ -2377,11 +2385,16 @@ bool compile(PKVM* vm, Script* script, const char* source,
|
|||||||
compiler->next_compiler = vm->compiler;
|
compiler->next_compiler = vm->compiler;
|
||||||
vm->compiler = compiler;
|
vm->compiler = compiler;
|
||||||
|
|
||||||
// Remember the count of the (valid) code of the provided script body. For
|
// If we're compiling for a script that was already compiled (when running
|
||||||
// new scripts it'll be null, but if we're compiling it multiple times we
|
// REPL or evaluvating an expression) we don't need the old main anymore.
|
||||||
// already have some code before. And if the compilation failed we discard
|
// just use the globals and functions of the script and use a new body func.
|
||||||
// all the compiled opcodes and jump back to the valid_code.
|
ASSERT(script->body != NULL, OOPS);
|
||||||
uint32_t valid_code = script->body->fn->opcodes.count;
|
byteBufferClear(&script->body->fn->opcodes, vm);
|
||||||
|
|
||||||
|
// Remember the count of the globals and functions, If the compilation failed
|
||||||
|
// discard all the globals and functions added by the compilation.
|
||||||
|
uint32_t globals_count = script->globals.count;
|
||||||
|
uint32_t functions_count = script->functions.count;
|
||||||
|
|
||||||
Func curr_fn;
|
Func curr_fn;
|
||||||
curr_fn.depth = DEPTH_SCRIPT;
|
curr_fn.depth = DEPTH_SCRIPT;
|
||||||
@ -2416,14 +2429,9 @@ bool compile(PKVM* vm, Script* script, const char* source,
|
|||||||
skipNewLines(compiler);
|
skipNewLines(compiler);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compiler->options == NULL || !compiler->options->repl_mode) {
|
// Already a null at the stack top, added when the fiber for the function created.
|
||||||
emitOpcode(compiler, OP_PUSH_NULL);
|
emitOpcode(compiler, OP_RETURN);
|
||||||
emitOpcode(compiler, OP_RETURN);
|
emitOpcode(compiler, OP_END);
|
||||||
emitOpcode(compiler, OP_END);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
emitOpcode(compiler, OP_YIELD);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve forward names (function names that are used before defined).
|
// Resolve forward names (function names that are used before defined).
|
||||||
for (int i = 0; i < compiler->forwards_count; i++) {
|
for (int i = 0; i < compiler->forwards_count; i++) {
|
||||||
@ -2441,23 +2449,21 @@ bool compile(PKVM* vm, Script* script, const char* source,
|
|||||||
|
|
||||||
vm->compiler = compiler->next_compiler;
|
vm->compiler = compiler->next_compiler;
|
||||||
|
|
||||||
|
// If compilation failed, discard all the invalid functions and globals.
|
||||||
|
if (compiler->has_errors) {
|
||||||
|
script->globals.count = script->global_names.count = globals_count;
|
||||||
|
script->functions.count = script->function_names.count = functions_count;
|
||||||
|
}
|
||||||
|
|
||||||
#if DEBUG_DUMP_COMPILED_CODE
|
#if DEBUG_DUMP_COMPILED_CODE
|
||||||
dumpFunctionCode(vm, script->body);
|
dumpFunctionCode(vm, script->body);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// If the compilation failed discard all the compiled invalid code.
|
return !compiler->has_errors;
|
||||||
// TODO: May be shrink the buffer.
|
|
||||||
if (compiler->has_errors) {
|
|
||||||
script->body->fn->opcodes.count = valid_code;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we reach here, it means the compilation is success and return true.
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PkResult pkCompileModule(PKVM* vm, PkHandle* module, PkStringPtr source,
|
PkResult pkCompileModule(PKVM* vm, PkHandle* module, PkStringPtr source,
|
||||||
const PkCompileOptions* options) {
|
const PkCompileOptions* options) {
|
||||||
__ASSERT(module != NULL, "Argument module was NULL.");
|
__ASSERT(module != NULL, "Argument module was NULL.");
|
||||||
Var scr = module->value;
|
Var scr = module->value;
|
||||||
__ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module");
|
__ASSERT(IS_OBJ_TYPE(scr, OBJ_SCRIPT), "Given handle is not a module");
|
||||||
|
126
src/core.c
126
src/core.c
@ -218,6 +218,28 @@ void pkReturnValue(PKVM* vm, PkVar value) {
|
|||||||
RET(*(Var*)value);
|
RET(*(Var*)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* pkStringGetData(const PkVar value) {
|
||||||
|
const Var str = (*(const Var*)value);
|
||||||
|
__ASSERT(IS_OBJ_TYPE(str, OBJ_STRING), "Value should be of type string.");
|
||||||
|
return ((String*)AS_OBJ(str))->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
PkVar pkFiberGetReturnValue(const PkHandle* fiber) {
|
||||||
|
__ASSERT(fiber != NULL, "Handle fiber was NULL.");
|
||||||
|
Var fb = fiber->value;
|
||||||
|
__ASSERT(IS_OBJ_TYPE(fb, OBJ_FIBER), "Given handle is not a fiber");
|
||||||
|
Fiber* _fiber = (Fiber*)AS_OBJ(fb);
|
||||||
|
return (PkVar)_fiber->ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pkFiberIsDone(const PkHandle* fiber) {
|
||||||
|
__ASSERT(fiber != NULL, "Handle fiber was NULL.");
|
||||||
|
Var fb = fiber->value;
|
||||||
|
__ASSERT(IS_OBJ_TYPE(fb, OBJ_FIBER), "Given handle is not a fiber");
|
||||||
|
Fiber* _fiber = (Fiber*)AS_OBJ(fb);
|
||||||
|
return _fiber->state == FIBER_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* VALIDATORS */
|
/* VALIDATORS */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -294,7 +316,7 @@ static inline bool validateIndex(PKVM* vm, int32_t index, int32_t size,
|
|||||||
|
|
||||||
// findBuiltinFunction implementation (see core.h for description).
|
// findBuiltinFunction implementation (see core.h for description).
|
||||||
int findBuiltinFunction(const PKVM* vm, const char* name, uint32_t length) {
|
int findBuiltinFunction(const PKVM* vm, const char* name, uint32_t length) {
|
||||||
for (int i = 0; i < vm->builtins_count; i++) {
|
for (uint32_t i = 0; i < vm->builtins_count; i++) {
|
||||||
if (length == vm->builtins[i].length &&
|
if (length == vm->builtins[i].length &&
|
||||||
strncmp(name, vm->builtins[i].name, length) == 0) {
|
strncmp(name, vm->builtins[i].name, length) == 0) {
|
||||||
return i;
|
return i;
|
||||||
@ -456,7 +478,7 @@ PK_DOC(coreStrLower,
|
|||||||
|
|
||||||
String* result = newStringLength(vm, str->data, str->length);
|
String* result = newStringLength(vm, str->data, str->length);
|
||||||
char* data = result->data;
|
char* data = result->data;
|
||||||
for (; *data; ++data) *data = tolower(*data);
|
for (; *data; ++data) *data = (char)tolower(*data);
|
||||||
// Since the string is modified re-hash it.
|
// Since the string is modified re-hash it.
|
||||||
result->hash = utilHashString(result->data);
|
result->hash = utilHashString(result->data);
|
||||||
|
|
||||||
@ -471,7 +493,7 @@ PK_DOC(coreStrUpper,
|
|||||||
|
|
||||||
String* result = newStringLength(vm, str->data, str->length);
|
String* result = newStringLength(vm, str->data, str->length);
|
||||||
char* data = result->data;
|
char* data = result->data;
|
||||||
for (; *data; ++data) *data = toupper(*data);
|
for (; *data; ++data) *data = (char)toupper(*data);
|
||||||
// Since the string is modified re-hash it.
|
// Since the string is modified re-hash it.
|
||||||
result->hash = utilHashString(result->data);
|
result->hash = utilHashString(result->data);
|
||||||
|
|
||||||
@ -586,56 +608,19 @@ PK_DOC(coreFiberRun,
|
|||||||
Fiber* fb;
|
Fiber* fb;
|
||||||
if (!validateArgFiber(vm, 1, &fb)) return;
|
if (!validateArgFiber(vm, 1, &fb)) return;
|
||||||
|
|
||||||
ASSERT(fb->func->arity >= -1 , OOPS " (Forget to initialize arity.)");
|
// Buffer of argument to call vmPrepareFiber().
|
||||||
|
Var* args[MAX_ARGC];
|
||||||
|
|
||||||
if (argc - 1 != fb->func->arity) {
|
// ARG(1) is fiber, function arguments are ARG(2), ARG(3), ... ARG(argc).
|
||||||
char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", fb->func->arity);
|
|
||||||
RET_ERR(stringFormat(vm, "Expected excatly $ argument(s).", buff));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fb->state != FIBER_NEW) {
|
|
||||||
switch (fb->state) {
|
|
||||||
case FIBER_NEW: UNREACHABLE();
|
|
||||||
case FIBER_RUNNING:
|
|
||||||
RET_ERR(newString(vm, "The fiber has already been running."));
|
|
||||||
case FIBER_YIELDED:
|
|
||||||
RET_ERR(newString(vm, "Cannot run a fiber which is yielded, use "
|
|
||||||
"fiber_resume() instead."));
|
|
||||||
case FIBER_DONE:
|
|
||||||
RET_ERR(newString(vm, "The fiber has done running."));
|
|
||||||
}
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT(fb->stack != NULL && fb->sp == fb->stack, OOPS);
|
|
||||||
ASSERT(fb->ret == fb->sp, OOPS);
|
|
||||||
|
|
||||||
fb->state = FIBER_RUNNING;
|
|
||||||
fb->caller = vm->fiber;
|
|
||||||
|
|
||||||
// Pass the function arguments.
|
|
||||||
|
|
||||||
// Assert we have the first frame (to push the arguments). And assert we have
|
|
||||||
// enought stack space for parameters.
|
|
||||||
ASSERT(fb->frame_count == 1, OOPS);
|
|
||||||
ASSERT(fb->frames[0].rbp == fb->ret, OOPS);
|
|
||||||
ASSERT((fb->stack + fb->stack_size) - fb->sp >= argc, OOPS);
|
|
||||||
|
|
||||||
// ARG1 is fiber, function arguments are ARG(2), ARG(3), ... ARG(argc).
|
|
||||||
// And ret[0] is the return value, parameters starts at ret[1], ...
|
|
||||||
for (int i = 1; i < argc; i++) {
|
for (int i = 1; i < argc; i++) {
|
||||||
fb->ret[i] = ARG(i + 1);
|
args[i - 1] = &ARG(i + 1);
|
||||||
}
|
}
|
||||||
fb->sp += argc; // Parameters and return value.
|
|
||||||
|
|
||||||
// Set the new fiber as the vm's fiber.
|
// Switch fiber and start execution.
|
||||||
vm->fiber = fb;
|
if (vmPrepareFiber(vm, fb, argc - 1, args)) {
|
||||||
|
ASSERT(fb == vm->fiber, OOPS);
|
||||||
// fb->ret is "un initialized" and will be initialized by the fiber_resume()
|
fb->state = FIBER_RUNNING;
|
||||||
// call. But we're setting the value to VAR_NULL below to make it initialized
|
}
|
||||||
// for the debugger, it'll prevent from crashing when we're trying to read
|
|
||||||
// the value to dump.
|
|
||||||
RET(VAR_NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PK_DOC(coreFiberResume,
|
PK_DOC(coreFiberResume,
|
||||||
@ -652,36 +637,13 @@ PK_DOC(coreFiberResume,
|
|||||||
Fiber* fb;
|
Fiber* fb;
|
||||||
if (!validateArgFiber(vm, 1, &fb)) return;
|
if (!validateArgFiber(vm, 1, &fb)) return;
|
||||||
|
|
||||||
if (fb->state != FIBER_YIELDED) {
|
Var value = (argc == 1) ? VAR_NULL : ARG(2);
|
||||||
switch (fb->state) {
|
|
||||||
case FIBER_NEW:
|
// Switch fiber and resume execution.
|
||||||
RET_ERR(newString(vm, "The fiber hasn't started. call fiber_run() to "
|
if (vmSwitchFiber(vm, fb, &value)) {
|
||||||
"start."));
|
ASSERT(fb == vm->fiber, OOPS);
|
||||||
case FIBER_RUNNING:
|
fb->state = FIBER_RUNNING;
|
||||||
RET_ERR(newString(vm, "The fiber has already been running."));
|
|
||||||
case FIBER_YIELDED: UNREACHABLE();
|
|
||||||
case FIBER_DONE:
|
|
||||||
RET_ERR(newString(vm, "The fiber has done running."));
|
|
||||||
}
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fb->state = FIBER_RUNNING;
|
|
||||||
fb->caller = vm->fiber;
|
|
||||||
|
|
||||||
// Pass the resume argument if it has any.
|
|
||||||
|
|
||||||
// Assert if we have a call frame and the stack size enough for the return
|
|
||||||
// value and the resumed value.
|
|
||||||
ASSERT(fb->frame_count != 0, OOPS);
|
|
||||||
ASSERT((fb->stack + fb->stack_size) - fb->sp >= 2, OOPS);
|
|
||||||
|
|
||||||
// fb->ret will points to the return value of the 'yield()' call.
|
|
||||||
if (argc == 1) *fb->ret = VAR_NULL;
|
|
||||||
else *fb->ret = ARG(2);
|
|
||||||
|
|
||||||
// Set the new fiber as the vm's fiber.
|
|
||||||
vm->fiber = fb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -1126,7 +1088,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
|||||||
varBufferWrite(&list->elements, vm, VAR_NUM(i));
|
varBufferWrite(&list->elements, vm, VAR_NUM(i));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newList(vm, 0);
|
list = newList(vm, 0);
|
||||||
}
|
}
|
||||||
return VAR_OBJ(list);
|
return VAR_OBJ(list);
|
||||||
}
|
}
|
||||||
@ -1139,7 +1101,7 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
|||||||
Script* scr = (Script*)obj;
|
Script* scr = (Script*)obj;
|
||||||
|
|
||||||
// Search in functions.
|
// Search in functions.
|
||||||
uint32_t index = scriptGetFunc(scr, attrib->data, attrib->length);
|
int index = scriptGetFunc(scr, attrib->data, attrib->length);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
ASSERT_INDEX(index, scr->functions.count);
|
ASSERT_INDEX(index, scr->functions.count);
|
||||||
return VAR_OBJ(scr->functions.data[index]);
|
return VAR_OBJ(scr->functions.data[index]);
|
||||||
@ -1167,7 +1129,6 @@ Var varGetAttrib(PKVM* vm, Var on, String* attrib) {
|
|||||||
CHECK_MISSING_OBJ_TYPE(7);
|
CHECK_MISSING_OBJ_TYPE(7);
|
||||||
|
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
return VAR_NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void varSetAttrib(PKVM* vm, Var on, String* attrib, Var value) {
|
void varSetAttrib(PKVM* vm, Var on, String* attrib, Var value) {
|
||||||
@ -1211,7 +1172,7 @@ do { \
|
|||||||
Script* scr = (Script*)obj;
|
Script* scr = (Script*)obj;
|
||||||
|
|
||||||
// Check globals.
|
// Check globals.
|
||||||
uint32_t index = scriptGetGlobals(scr, attrib->data, attrib->length);
|
int index = scriptGetGlobals(scr, attrib->data, attrib->length);
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
ASSERT_INDEX(index, scr->globals.count);
|
ASSERT_INDEX(index, scr->globals.count);
|
||||||
scr->globals.data[index] = value;
|
scr->globals.data[index] = value;
|
||||||
@ -1315,7 +1276,6 @@ Var varGetSubscript(PKVM* vm, Var on, Var key) {
|
|||||||
|
|
||||||
CHECK_MISSING_OBJ_TYPE(7);
|
CHECK_MISSING_OBJ_TYPE(7);
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
return VAR_NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
|
void varsetSubscript(PKVM* vm, Var on, Var key, Var value) {
|
||||||
|
@ -26,7 +26,7 @@ static void _dumpValue(PKVM* vm, Var value, bool recursive) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (IS_BOOL(value)) {
|
if (IS_BOOL(value)) {
|
||||||
printf((AS_BOOL(value)) ? "true" : "false");
|
printf("%s", (AS_BOOL(value)) ? "true" : "false");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (IS_NUM(value)) {
|
if (IS_NUM(value)) {
|
||||||
@ -199,7 +199,7 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case OP_PUSH_LOCAL_N:
|
case OP_PUSH_LOCAL_N:
|
||||||
READ_BYTE();
|
BYTE_ARG();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OP_STORE_LOCAL_0:
|
case OP_STORE_LOCAL_0:
|
||||||
@ -215,7 +215,7 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case OP_STORE_LOCAL_N:
|
case OP_STORE_LOCAL_N:
|
||||||
READ_BYTE();
|
BYTE_ARG();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OP_PUSH_GLOBAL:
|
case OP_PUSH_GLOBAL:
|
||||||
@ -315,7 +315,6 @@ void dumpFunctionCode(PKVM* vm, Function* func) {
|
|||||||
case OP_RANGE:
|
case OP_RANGE:
|
||||||
case OP_IN:
|
case OP_IN:
|
||||||
case OP_REPL_PRINT:
|
case OP_REPL_PRINT:
|
||||||
case OP_YIELD:
|
|
||||||
case OP_END:
|
case OP_END:
|
||||||
NO_ARGS();
|
NO_ARGS();
|
||||||
break;
|
break;
|
||||||
|
@ -67,7 +67,7 @@ extern "C" {
|
|||||||
|
|
||||||
// Name of the implicit function for a module. When a module is parsed all of
|
// Name of the implicit function for a module. When a module is parsed all of
|
||||||
// it's statements are wrapped around an implicit function with this name.
|
// it's statements are wrapped around an implicit function with this name.
|
||||||
#define PK_BODY_FN_NAME "$(SourceBody)"
|
#define PK_IMPLICIT_MAIN_NAME "$(SourceBody)"
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* POCKETLANG TYPES */
|
/* POCKETLANG TYPES */
|
||||||
@ -177,12 +177,12 @@ typedef PkStringPtr (*pkLoadScriptFn) (PKVM* vm, const char* path);
|
|||||||
// Create a new pkConfiguraition with the default values and return it.
|
// Create a new pkConfiguraition with the default values and return it.
|
||||||
// Override those default configuration to adopt to another hosting
|
// Override those default configuration to adopt to another hosting
|
||||||
// application.
|
// application.
|
||||||
PK_PUBLIC PkConfiguration pkNewConfiguration();
|
PK_PUBLIC PkConfiguration pkNewConfiguration(void);
|
||||||
|
|
||||||
// Create a new pkCompilerOptions with the default values and return it.
|
// Create a new pkCompilerOptions with the default values and return it.
|
||||||
// Override those default configuration to adopt to another hosting
|
// Override those default configuration to adopt to another hosting
|
||||||
// application.
|
// application.
|
||||||
PK_PUBLIC PkCompileOptions pkNewCompilerOptions();
|
PK_PUBLIC PkCompileOptions pkNewCompilerOptions(void);
|
||||||
|
|
||||||
// Allocate initialize and returns a new VM
|
// Allocate initialize and returns a new VM
|
||||||
PK_PUBLIC PKVM* pkNewVM(PkConfiguration* config);
|
PK_PUBLIC PKVM* pkNewVM(PkConfiguration* config);
|
||||||
@ -221,9 +221,8 @@ PK_PUBLIC void pkModuleAddFunction(PKVM* vm, PkHandle* module,
|
|||||||
PK_PUBLIC PkHandle* pkGetFunction(PKVM* vm, PkHandle* module,
|
PK_PUBLIC PkHandle* pkGetFunction(PKVM* vm, PkHandle* module,
|
||||||
const char* name);
|
const char* name);
|
||||||
|
|
||||||
// Compile the [module] with the provided [source] and return true if the
|
// Compile the [module] with the provided [source]. Set the compiler options
|
||||||
// compilation is success. Set the compiler options with the the [options]
|
// with the the [options] argument or set to NULL for default options.
|
||||||
// argument or it can be set to NULL for default options.
|
|
||||||
PK_PUBLIC PkResult pkCompileModule(PKVM* vm, PkHandle* module,
|
PK_PUBLIC PkResult pkCompileModule(PKVM* vm, PkHandle* module,
|
||||||
PkStringPtr source,
|
PkStringPtr source,
|
||||||
const PkCompileOptions* options);
|
const PkCompileOptions* options);
|
||||||
@ -237,9 +236,18 @@ PK_PUBLIC PkResult pkInterpretSource(PKVM* vm,
|
|||||||
PkStringPtr path,
|
PkStringPtr path,
|
||||||
const PkCompileOptions* options);
|
const PkCompileOptions* options);
|
||||||
|
|
||||||
|
// Runs the fiber's function with the provided arguments (param [arc] is the
|
||||||
|
// argument count and [argv] are the values). It'll returns it's run status
|
||||||
|
// reslt (success or failure) if you need the yielded or returned value use the
|
||||||
|
// pkFiberGetReturnValue() function, and use pkFiberIsDone() function to check
|
||||||
|
// if the fiber can be resumed with pkFiberResume() function.
|
||||||
|
PK_PUBLIC PkResult pkRunFiber(PKVM* vm, PkHandle* fiber,
|
||||||
|
int argc, PkHandle** argv);
|
||||||
|
|
||||||
//PK_PUBLIC PkResult pkRunFiber(PKVM* vm, PkHandle* fiber,
|
// Resume a yielded fiber with an optional [value]. (could be set to NULL)
|
||||||
// int argc, PkHandle** argv);
|
// It'll returns it's run status reslt (success or failure) if you need the
|
||||||
|
// yielded or returned value use the pkFiberGetReturnValue() function.
|
||||||
|
PK_PUBLIC PkResult pkResumeFiber(PKVM* vm, PkHandle* fiber, PkVar value);
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* POCKETLANG PUBLIC TYPE DEFINES */
|
/* POCKETLANG PUBLIC TYPE DEFINES */
|
||||||
@ -336,6 +344,19 @@ PK_PUBLIC void pkReturnString(PKVM* vm, const char* value);
|
|||||||
PK_PUBLIC void pkReturnStringLength(PKVM* vm, const char* value, size_t len);
|
PK_PUBLIC void pkReturnStringLength(PKVM* vm, const char* value, size_t len);
|
||||||
PK_PUBLIC void pkReturnValue(PKVM* vm, PkVar value);
|
PK_PUBLIC void pkReturnValue(PKVM* vm, PkVar value);
|
||||||
|
|
||||||
|
// Returns the cstring pointer of the given string. Make sure if the [value] is
|
||||||
|
// a string before calling this function, otherwise it'll fail an assertion.
|
||||||
|
PK_PUBLIC const char* pkStringGetData(const PkVar value);
|
||||||
|
|
||||||
|
// Returns the return value or if it's yielded, the yielded value of the fiber
|
||||||
|
// as PkVar, this value lives on stack and will die (popped) once the fiber
|
||||||
|
// resumed use handle to keep it alive.
|
||||||
|
PK_PUBLIC PkVar pkFiberGetReturnValue(const PkHandle* fiber);
|
||||||
|
|
||||||
|
// Returns true if the fiber is finished it's execution and cannot be resumed
|
||||||
|
// anymore.
|
||||||
|
PK_PUBLIC bool pkFiberIsDone(const PkHandle* fiber);
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* POCKETLANG TYPE FUNCTIONS */
|
/* POCKETLANG TYPE FUNCTIONS */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -355,8 +376,6 @@ PK_PUBLIC PkHandle* pkNewModule(PKVM* vm, const char* name);
|
|||||||
// Create and return a new fiber around the function [fn].
|
// Create and return a new fiber around the function [fn].
|
||||||
PK_PUBLIC PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn);
|
PK_PUBLIC PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn);
|
||||||
|
|
||||||
PK_PUBLIC const char* pkStringGetData(const PkVar value);
|
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// The below functions will push the primitive values on the stack and return
|
// The below functions will push the primitive values on the stack and return
|
||||||
// it's pointer as a PkVar it's usefull to convert your primitive values as
|
// it's pointer as a PkVar it's usefull to convert your primitive values as
|
||||||
|
@ -192,9 +192,6 @@ OPCODE(IN, 0, -1)
|
|||||||
// This will not pop the value.
|
// This will not pop the value.
|
||||||
OPCODE(REPL_PRINT, 0, 0)
|
OPCODE(REPL_PRINT, 0, 0)
|
||||||
|
|
||||||
// Yield the current fiber. Used in REPL mode.
|
|
||||||
OPCODE(YIELD, 0, 0)
|
|
||||||
|
|
||||||
// A sudo instruction which will never be called. A function's last opcode
|
// A sudo instruction which will never be called. A function's last opcode
|
||||||
// used for debugging.
|
// used for debugging.
|
||||||
OPCODE(END, 0, 0)
|
OPCODE(END, 0, 0)
|
||||||
|
18
src/utils.c
18
src/utils.c
@ -128,17 +128,17 @@ int utf8_encodeValue(int value, uint8_t* bytes) {
|
|||||||
// 2 byte character 110xxxxx 10xxxxxx -> last 6 bits write to 2nd byte and
|
// 2 byte character 110xxxxx 10xxxxxx -> last 6 bits write to 2nd byte and
|
||||||
// first 5 bit write to first byte
|
// first 5 bit write to first byte
|
||||||
if (value <= 0x7ff) {
|
if (value <= 0x7ff) {
|
||||||
*(bytes++) = 0b11000000 | ((value & 0b11111000000) >> 6);
|
*(bytes++) = (uint8_t)(0b11000000 | ((value & 0b11111000000) >> 6));
|
||||||
*(bytes) = 0b10000000 | ((value & 111111));
|
*(bytes) = (uint8_t)(0b10000000 | ((value & 111111)));
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3 byte character 1110xxxx 10xxxxxx 10xxxxxx -> from last, 6 bits write
|
// 3 byte character 1110xxxx 10xxxxxx 10xxxxxx -> from last, 6 bits write
|
||||||
// to 3rd byte, next 6 bits write to 2nd byte, and 4 bits to first byte.
|
// to 3rd byte, next 6 bits write to 2nd byte, and 4 bits to first byte.
|
||||||
if (value <= 0xffff) {
|
if (value <= 0xffff) {
|
||||||
*(bytes++) = 0b11100000 | ((value & 0b1111000000000000) >> 12);
|
*(bytes++) = (uint8_t)(0b11100000 | ((value & 0b1111000000000000) >> 12));
|
||||||
*(bytes++) = 0b10000000 | ((value & 0b111111000000) >> 6);
|
*(bytes++) = (uint8_t)(0b10000000 | ((value & 0b111111000000) >> 6));
|
||||||
*(bytes) = 0b10000000 | ((value & 0b111111));
|
*(bytes) = (uint8_t)(0b10000000 | ((value & 0b111111)));
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,10 +146,10 @@ int utf8_encodeValue(int value, uint8_t* bytes) {
|
|||||||
// to 4th byte, next 6 bits to 3rd byte, next 6 bits to 2nd byte, 3 bits
|
// to 4th byte, next 6 bits to 3rd byte, next 6 bits to 2nd byte, 3 bits
|
||||||
// first byte.
|
// first byte.
|
||||||
if (value <= 0x10ffff) {
|
if (value <= 0x10ffff) {
|
||||||
*(bytes++) = 0b11110000 | ((value & 0b111000000000000000000) >> 18);
|
*(bytes++) = (uint8_t)(0b11110000 | ((value & 0b111000000000000000000) >> 18));
|
||||||
*(bytes++) = 0b10000000 | ((value & 0b111111000000000000) >> 12);
|
*(bytes++) = (uint8_t)(0b10000000 | ((value & 0b111111000000000000) >> 12));
|
||||||
*(bytes++) = 0b10000000 | ((value & 0b111111000000) >> 6);
|
*(bytes++) = (uint8_t)(0b10000000 | ((value & 0b111111000000) >> 6));
|
||||||
*(bytes) = 0b10000000 | ((value & 0b111111));
|
*(bytes) = (uint8_t)(0b10000000 | ((value & 0b111111)));
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
51
src/var.c
51
src/var.c
@ -36,7 +36,6 @@ PkVarType pkGetValueType(const PkVar value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
return (PkVarType)0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PkHandle* pkNewString(PKVM* vm, const char* value) {
|
PkHandle* pkNewString(PKVM* vm, const char* value) {
|
||||||
@ -62,12 +61,6 @@ PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn) {
|
|||||||
return vmNewHandle(vm, VAR_OBJ(fiber));
|
return vmNewHandle(vm, VAR_OBJ(fiber));
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* pkStringGetData(const PkVar value) {
|
|
||||||
const Var str = (*(const Var*)value);
|
|
||||||
__ASSERT(IS_OBJ_TYPE(str, OBJ_STRING), "Value should be of type string.");
|
|
||||||
return ((String*)AS_OBJ(str))->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
/* VAR INTERNALS */
|
/* VAR INTERNALS */
|
||||||
/*****************************************************************************/
|
/*****************************************************************************/
|
||||||
@ -340,7 +333,7 @@ Script* newScript(PKVM* vm, String* path) {
|
|||||||
stringBufferInit(&script->names);
|
stringBufferInit(&script->names);
|
||||||
|
|
||||||
vmPushTempRef(vm, &script->_super);
|
vmPushTempRef(vm, &script->_super);
|
||||||
const char* fn_name = PK_BODY_FN_NAME;
|
const char* fn_name = PK_IMPLICIT_MAIN_NAME;
|
||||||
script->body = newFunction(vm, fn_name, (int)strlen(fn_name), script, false);
|
script->body = newFunction(vm, fn_name, (int)strlen(fn_name), script, false);
|
||||||
script->body->arity = 0; // TODO: should it be 1 (ARGV)?.
|
script->body->arity = 0; // TODO: should it be 1 (ARGV)?.
|
||||||
vmPopTempRef(vm);
|
vmPopTempRef(vm);
|
||||||
@ -401,8 +394,8 @@ Fiber* newFiber(PKVM* vm, Function* fn) {
|
|||||||
int stack_size = utilPowerOf2Ceil(fn->arity + 1);
|
int stack_size = utilPowerOf2Ceil(fn->arity + 1);
|
||||||
fiber->stack = ALLOCATE_ARRAY(vm, Var, stack_size);
|
fiber->stack = ALLOCATE_ARRAY(vm, Var, stack_size);
|
||||||
fiber->stack_size = stack_size;
|
fiber->stack_size = stack_size;
|
||||||
fiber->sp = fiber->stack;
|
|
||||||
fiber->ret = fiber->stack;
|
fiber->ret = fiber->stack;
|
||||||
|
fiber->sp = fiber->stack + 1;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Allocate stack.
|
// Allocate stack.
|
||||||
@ -410,8 +403,8 @@ Fiber* newFiber(PKVM* vm, Function* fn) {
|
|||||||
if (stack_size < MIN_STACK_SIZE) stack_size = MIN_STACK_SIZE;
|
if (stack_size < MIN_STACK_SIZE) stack_size = MIN_STACK_SIZE;
|
||||||
fiber->stack = ALLOCATE_ARRAY(vm, Var, stack_size);
|
fiber->stack = ALLOCATE_ARRAY(vm, Var, stack_size);
|
||||||
fiber->stack_size = stack_size;
|
fiber->stack_size = stack_size;
|
||||||
fiber->sp = fiber->stack;
|
|
||||||
fiber->ret = fiber->stack;
|
fiber->ret = fiber->stack;
|
||||||
|
fiber->sp = fiber->stack + 1;
|
||||||
|
|
||||||
// Allocate call frames.
|
// Allocate call frames.
|
||||||
fiber->frame_capacity = INITIAL_CALL_FRAMES;
|
fiber->frame_capacity = INITIAL_CALL_FRAMES;
|
||||||
@ -424,6 +417,10 @@ Fiber* newFiber(PKVM* vm, Function* fn) {
|
|||||||
fiber->frames[0].rbp = fiber->ret;
|
fiber->frames[0].rbp = fiber->ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize the return value to null (doesn't really have to do that here
|
||||||
|
// but if we're trying to debut it may crash when dumping the return vaue).
|
||||||
|
*fiber->ret = VAR_NULL;
|
||||||
|
|
||||||
return fiber;
|
return fiber;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,7 +497,6 @@ static uint32_t _hashObject(Object* obj) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t varHashValue(Var v) {
|
uint32_t varHashValue(Var v) {
|
||||||
@ -754,7 +750,6 @@ const char* getPkVarTypeName(PkVarType type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UNREACHABLE();
|
UNREACHABLE();
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* getObjectTypeName(ObjectType type) {
|
const char* getObjectTypeName(ObjectType type) {
|
||||||
@ -767,9 +762,8 @@ const char* getObjectTypeName(ObjectType type) {
|
|||||||
case OBJ_FUNC: return "Func";
|
case OBJ_FUNC: return "Func";
|
||||||
case OBJ_FIBER: return "Fiber";
|
case OBJ_FIBER: return "Fiber";
|
||||||
case OBJ_USER: return "UserObj";
|
case OBJ_USER: return "UserObj";
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* varTypeName(Var v) {
|
const char* varTypeName(Var v) {
|
||||||
@ -821,11 +815,11 @@ bool isValuesEqual(Var v1, Var v2) {
|
|||||||
*/
|
*/
|
||||||
List *l1 = (List*)o1, *l2 = (List*)o2;
|
List *l1 = (List*)o1, *l2 = (List*)o2;
|
||||||
if (l1->elements.count != l2->elements.count) return false;
|
if (l1->elements.count != l2->elements.count) return false;
|
||||||
Var* v1 = l1->elements.data;
|
Var* _v1 = l1->elements.data;
|
||||||
Var* v2 = l2->elements.data;
|
Var* _v2 = l2->elements.data;
|
||||||
for (uint32_t i = 0; i < l1->elements.count; i++) {
|
for (uint32_t i = 0; i < l1->elements.count; i++) {
|
||||||
if (!isValuesEqual(*v1, *v2)) return false;
|
if (!isValuesEqual(*_v1, *_v2)) return false;
|
||||||
v1++, v2++;
|
_v1++, _v2++;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -918,12 +912,7 @@ static void _toStringInternal(PKVM* vm, const Var v, ByteBuffer* buff,
|
|||||||
byteBufferWrite(buff, vm, '"');
|
byteBufferWrite(buff, vm, '"');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
UNREACHABLE();
|
||||||
// If recursive return with quotes (ex: [42, "hello", 0..10]).
|
|
||||||
byteBufferWrite(buff, vm, '"');
|
|
||||||
byteBufferAddString(buff, vm, str->data, str->length);
|
|
||||||
byteBufferWrite(buff, vm, '"');
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case OBJ_LIST:
|
case OBJ_LIST:
|
||||||
@ -994,14 +983,13 @@ static void _toStringInternal(PKVM* vm, const Var v, ByteBuffer* buff,
|
|||||||
}
|
}
|
||||||
if (_done) break;
|
if (_done) break;
|
||||||
|
|
||||||
if (!_first) {
|
if (!_first) byteBufferAddString(buff, vm, ", ", 2);
|
||||||
byteBufferAddString(buff, vm, ", ", 2);
|
|
||||||
_first = false;
|
|
||||||
}
|
|
||||||
_toStringInternal(vm, map->entries[i].key, buff, &seq_map, true);
|
_toStringInternal(vm, map->entries[i].key, buff, &seq_map, true);
|
||||||
byteBufferWrite(buff, vm, ':');
|
byteBufferWrite(buff, vm, ':');
|
||||||
_toStringInternal(vm, map->entries[i].value, buff, &seq_map, true);
|
_toStringInternal(vm, map->entries[i].value, buff, &seq_map, true);
|
||||||
i++;
|
|
||||||
|
i++; _first = false;
|
||||||
} while (i < map->capacity);
|
} while (i < map->capacity);
|
||||||
|
|
||||||
byteBufferWrite(buff, vm, '}');
|
byteBufferWrite(buff, vm, '}');
|
||||||
@ -1106,13 +1094,12 @@ bool toBool(Var v) {
|
|||||||
case OBJ_RANGE: // [[FALLTHROUGH]]
|
case OBJ_RANGE: // [[FALLTHROUGH]]
|
||||||
case OBJ_SCRIPT:
|
case OBJ_SCRIPT:
|
||||||
case OBJ_FUNC:
|
case OBJ_FUNC:
|
||||||
|
case OBJ_FIBER:
|
||||||
case OBJ_USER:
|
case OBJ_USER:
|
||||||
return true;
|
return true;
|
||||||
default:
|
|
||||||
UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
String* stringFormat(PKVM* vm, const char* fmt, ...) {
|
String* stringFormat(PKVM* vm, const char* fmt, ...) {
|
||||||
|
@ -24,10 +24,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// To use dynamic variably-sized struct with a tail array add an array at the
|
// To use dynamic variably-sized struct with a tail array add an array at the
|
||||||
// end of the struct with size \ref DYNAMIC_TAIL_ARRAY. This method was a
|
// end of the struct with size DYNAMIC_TAIL_ARRAY. This method was a legacy
|
||||||
// legacy standard called "struct hack".
|
// standard called "struct hack".
|
||||||
#if __STDC_VERSION__ >= 199901L
|
#if defined(_MSC_VER) || __STDC_VERSION__ >= 199901L // std >= c99
|
||||||
/** for std >= c99 it's just `arr[]` */
|
|
||||||
#define DYNAMIC_TAIL_ARRAY
|
#define DYNAMIC_TAIL_ARRAY
|
||||||
#else
|
#else
|
||||||
#define DYNAMIC_TAIL_ARRAY 0
|
#define DYNAMIC_TAIL_ARRAY 0
|
||||||
@ -218,7 +217,7 @@ typedef enum {
|
|||||||
// This will terminate compiler (because of 1/0 evaluvated) if ObjectType max
|
// This will terminate compiler (because of 1/0 evaluvated) if ObjectType max
|
||||||
// is not [count]. Use this to ensure every time switching ObjectType will
|
// is not [count]. Use this to ensure every time switching ObjectType will
|
||||||
// cover all object types.
|
// cover all object types.
|
||||||
#if DEBUG
|
#ifdef DEBUG
|
||||||
#define CHECK_MISSING_OBJ_TYPE(count) (1/ ((int)(!(count ^ OBJ_USER))) )
|
#define CHECK_MISSING_OBJ_TYPE(count) (1/ ((int)(!(count ^ OBJ_USER))) )
|
||||||
#else
|
#else
|
||||||
#define CHECK_MISSING_OBJ_TYPE(count) do {} while (false)
|
#define CHECK_MISSING_OBJ_TYPE(count) do {} while (false)
|
||||||
|
191
src/vm.c
191
src/vm.c
@ -25,7 +25,7 @@ static void* defaultRealloc(void* memory, size_t new_size, void* user_data);
|
|||||||
// till the next yield or return statement, and return result.
|
// till the next yield or return statement, and return result.
|
||||||
static PkResult runFiber(PKVM* vm, Fiber* fiber);
|
static PkResult runFiber(PKVM* vm, Fiber* fiber);
|
||||||
|
|
||||||
PkConfiguration pkNewConfiguration() {
|
PkConfiguration pkNewConfiguration(void) {
|
||||||
PkConfiguration config;
|
PkConfiguration config;
|
||||||
config.realloc_fn = defaultRealloc;
|
config.realloc_fn = defaultRealloc;
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ PkConfiguration pkNewConfiguration() {
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
PkCompileOptions pkNewCompilerOptions() {
|
PkCompileOptions pkNewCompilerOptions(void) {
|
||||||
PkCompileOptions options;
|
PkCompileOptions options;
|
||||||
options.debug = false;
|
options.debug = false;
|
||||||
// TODO:
|
// TODO:
|
||||||
@ -161,6 +161,39 @@ PkResult pkInterpretSource(PKVM* vm, PkStringPtr source, PkStringPtr path,
|
|||||||
return runFiber(vm, newFiber(vm, scr->body));
|
return runFiber(vm, newFiber(vm, scr->body));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PkResult pkRunFiber(PKVM* vm, PkHandle* fiber,
|
||||||
|
int argc, PkHandle** argv) {
|
||||||
|
__ASSERT(fiber != NULL, "Handle fiber was NULL.");
|
||||||
|
Var fb = fiber->value;
|
||||||
|
__ASSERT(IS_OBJ_TYPE(fb, OBJ_FIBER), "Given handle is not a fiber.");
|
||||||
|
Fiber* _fiber = (Fiber*)AS_OBJ(fb);
|
||||||
|
|
||||||
|
Var* args[MAX_ARGC];
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
args[i] = &(argv[i]->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vmPrepareFiber(vm, _fiber, argc, args)) {
|
||||||
|
return PK_RESULT_RUNTIME_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(_fiber->frame_count == 1, OOPS);
|
||||||
|
return runFiber(vm, _fiber);
|
||||||
|
}
|
||||||
|
|
||||||
|
PkResult pkResumeFiber(PKVM* vm, PkHandle* fiber, PkVar value) {
|
||||||
|
__ASSERT(fiber != NULL, "Handle fiber was NULL.");
|
||||||
|
Var fb = fiber->value;
|
||||||
|
__ASSERT(IS_OBJ_TYPE(fb, OBJ_FIBER), "Given handle is not a fiber.");
|
||||||
|
Fiber* _fiber = (Fiber*)AS_OBJ(fb);
|
||||||
|
|
||||||
|
if (!vmSwitchFiber(vm, _fiber, (Var*)value)) {
|
||||||
|
return PK_RESULT_RUNTIME_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return runFiber(vm, _fiber);
|
||||||
|
}
|
||||||
|
|
||||||
void pkSetRuntimeError(PKVM* vm, const char* message) {
|
void pkSetRuntimeError(PKVM* vm, const char* message) {
|
||||||
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
|
__ASSERT(vm->fiber != NULL, "This function can only be called at runtime.");
|
||||||
vm->fiber->error = newString(vm, message);
|
vm->fiber->error = newString(vm, message);
|
||||||
@ -224,7 +257,7 @@ void vmCollectGarbage(PKVM* vm) {
|
|||||||
|
|
||||||
// Mark the core libs and builtin functions.
|
// Mark the core libs and builtin functions.
|
||||||
grayObject(vm, &vm->core_libs->_super);
|
grayObject(vm, &vm->core_libs->_super);
|
||||||
for (int i = 0; i < vm->builtins_count; i++) {
|
for (uint32_t i = 0; i < vm->builtins_count; i++) {
|
||||||
grayObject(vm, &vm->builtins[i].fn->_super);
|
grayObject(vm, &vm->builtins[i].fn->_super);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,6 +314,96 @@ void vmCollectGarbage(PKVM* vm) {
|
|||||||
if (vm->next_gc < vm->min_heap_size) vm->next_gc = vm->min_heap_size;
|
if (vm->next_gc < vm->min_heap_size) vm->next_gc = vm->min_heap_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define _ERR_FAIL(msg) \
|
||||||
|
do { \
|
||||||
|
if (vm->fiber != NULL) vm->fiber->error = msg; \
|
||||||
|
return false; \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var** argv) {
|
||||||
|
ASSERT(fiber->func->arity >= -1, OOPS " (Forget to initialize arity.)");
|
||||||
|
|
||||||
|
if (argc != fiber->func->arity) {
|
||||||
|
char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", fiber->func->arity);
|
||||||
|
_ERR_FAIL(stringFormat(vm, "Expected excatly $ argument(s).", buff));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fiber->state != FIBER_NEW) {
|
||||||
|
switch (fiber->state) {
|
||||||
|
case FIBER_NEW: UNREACHABLE();
|
||||||
|
case FIBER_RUNNING:
|
||||||
|
_ERR_FAIL(newString(vm, "The fiber has already been running."));
|
||||||
|
case FIBER_YIELDED:
|
||||||
|
_ERR_FAIL(newString(vm, "Cannot run a fiber which is yielded, use "
|
||||||
|
"fiber_resume() instead."));
|
||||||
|
case FIBER_DONE:
|
||||||
|
_ERR_FAIL(newString(vm, "The fiber has done running."));
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
ASSERT(fiber->stack != NULL && fiber->sp == fiber->stack + 1, OOPS);
|
||||||
|
ASSERT(fiber->ret + 1 == fiber->sp, OOPS);
|
||||||
|
|
||||||
|
// Pass the function arguments.
|
||||||
|
|
||||||
|
// Assert we have the first frame (to push the arguments). And assert we have
|
||||||
|
// enought stack space for parameters.
|
||||||
|
ASSERT(fiber->frame_count == 1, OOPS);
|
||||||
|
ASSERT(fiber->frames[0].rbp == fiber->ret, OOPS);
|
||||||
|
ASSERT((fiber->stack + fiber->stack_size) - fiber->sp >= argc, OOPS);
|
||||||
|
|
||||||
|
// ARG1 is fiber, function arguments are ARG(2), ARG(3), ... ARG(argc).
|
||||||
|
// And ret[0] is the return value, parameters starts at ret[1], ...
|
||||||
|
for (int i = 0; i < argc; i++) {
|
||||||
|
fiber->ret[1 + i] = *argv[i]; // +1: ret[0] is return value.
|
||||||
|
}
|
||||||
|
fiber->sp += argc; // Parameters.
|
||||||
|
|
||||||
|
// Set the new fiber as the vm's fiber.
|
||||||
|
fiber->caller = vm->fiber;
|
||||||
|
vm->fiber = fiber;
|
||||||
|
|
||||||
|
// On success return true.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool vmSwitchFiber(PKVM* vm, Fiber* fiber, Var* value) {
|
||||||
|
if (fiber->state != FIBER_YIELDED) {
|
||||||
|
switch (fiber->state) {
|
||||||
|
case FIBER_NEW:
|
||||||
|
_ERR_FAIL(newString(vm, "The fiber hasn't started. call fiber_run() "
|
||||||
|
"to start."));
|
||||||
|
case FIBER_RUNNING:
|
||||||
|
_ERR_FAIL(newString(vm, "The fiber has already been running."));
|
||||||
|
case FIBER_YIELDED: UNREACHABLE();
|
||||||
|
case FIBER_DONE:
|
||||||
|
_ERR_FAIL(newString(vm, "The fiber has done running."));
|
||||||
|
}
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass the resume argument if it has any.
|
||||||
|
|
||||||
|
// Assert if we have a call frame and the stack size enough for the return
|
||||||
|
// value and the resumed value.
|
||||||
|
ASSERT(fiber->frame_count != 0, OOPS);
|
||||||
|
ASSERT((fiber->stack + fiber->stack_size) - fiber->sp >= 2, OOPS);
|
||||||
|
|
||||||
|
// fb->ret will points to the return value of the 'yield()' call.
|
||||||
|
if (value == NULL) *fiber->ret = VAR_NULL;
|
||||||
|
else *fiber->ret = *value;
|
||||||
|
|
||||||
|
// Switch fiber.
|
||||||
|
fiber->caller = vm->fiber;
|
||||||
|
vm->fiber = fiber;
|
||||||
|
|
||||||
|
// On success return true.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef _ERR_FAIL
|
||||||
|
|
||||||
void vmYieldFiber(PKVM* vm, Var* value) {
|
void vmYieldFiber(PKVM* vm, Var* value) {
|
||||||
|
|
||||||
Fiber* caller = vm->fiber->caller;
|
Fiber* caller = vm->fiber->caller;
|
||||||
@ -454,7 +577,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
|||||||
// it from garbage collection and get the reference from native functions.
|
// it from garbage collection and get the reference from native functions.
|
||||||
vm->fiber = fiber;
|
vm->fiber = fiber;
|
||||||
|
|
||||||
ASSERT(fiber->state == FIBER_NEW, OOPS);
|
ASSERT(fiber->state == FIBER_NEW || fiber->state == FIBER_YIELDED, OOPS);
|
||||||
fiber->state = FIBER_RUNNING;
|
fiber->state = FIBER_RUNNING;
|
||||||
|
|
||||||
// The instruction pointer.
|
// The instruction pointer.
|
||||||
@ -471,12 +594,24 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
|||||||
#define READ_BYTE() (*ip++)
|
#define READ_BYTE() (*ip++)
|
||||||
#define READ_SHORT() (ip+=2, (uint16_t)((ip[-2] << 8) | ip[-1]))
|
#define READ_SHORT() (ip+=2, (uint16_t)((ip[-2] << 8) | ip[-1]))
|
||||||
|
|
||||||
|
// Switch back to the caller of the current fiber, will be called when we're
|
||||||
|
// done with the fiber or aborting it for runtime errors.
|
||||||
|
#define FIBER_SWITCH_BACK() \
|
||||||
|
do { \
|
||||||
|
Fiber* caller = vm->fiber->caller; \
|
||||||
|
ASSERT(caller == NULL || caller->state == FIBER_RUNNING, OOPS); \
|
||||||
|
vm->fiber->state = FIBER_DONE; \
|
||||||
|
vm->fiber->caller = NULL; \
|
||||||
|
vm->fiber = caller; \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
// Check if any runtime error exists and if so returns RESULT_RUNTIME_ERROR.
|
// Check if any runtime error exists and if so returns RESULT_RUNTIME_ERROR.
|
||||||
#define CHECK_ERROR() \
|
#define CHECK_ERROR() \
|
||||||
do { \
|
do { \
|
||||||
if (HAS_ERROR()) { \
|
if (HAS_ERROR()) { \
|
||||||
UPDATE_FRAME(); \
|
UPDATE_FRAME(); \
|
||||||
reportError(vm); \
|
reportError(vm); \
|
||||||
|
FIBER_SWITCH_BACK(); \
|
||||||
return PK_RESULT_RUNTIME_ERROR; \
|
return PK_RESULT_RUNTIME_ERROR; \
|
||||||
} \
|
} \
|
||||||
} while (false)
|
} while (false)
|
||||||
@ -487,6 +622,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
|||||||
vm->fiber->error = err_msg; \
|
vm->fiber->error = err_msg; \
|
||||||
UPDATE_FRAME(); \
|
UPDATE_FRAME(); \
|
||||||
reportError(vm); \
|
reportError(vm); \
|
||||||
|
FIBER_SWITCH_BACK(); \
|
||||||
return PK_RESULT_RUNTIME_ERROR; \
|
return PK_RESULT_RUNTIME_ERROR; \
|
||||||
} while (false)
|
} while (false)
|
||||||
|
|
||||||
@ -522,8 +658,7 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
|||||||
#define OPCODE(code) case OP_##code
|
#define OPCODE(code) case OP_##code
|
||||||
#define DISPATCH() goto L_vm_main_loop
|
#define DISPATCH() goto L_vm_main_loop
|
||||||
|
|
||||||
// TODO: remove the below push null and add it from the compiler.
|
// Load the fiber's top call frame to the vm's execution variables.
|
||||||
PUSH(VAR_NULL); // Return value of the script body.
|
|
||||||
LOAD_FRAME();
|
LOAD_FRAME();
|
||||||
|
|
||||||
L_vm_main_loop:
|
L_vm_main_loop:
|
||||||
@ -689,14 +824,14 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
|||||||
OPCODE(IMPORT):
|
OPCODE(IMPORT):
|
||||||
{
|
{
|
||||||
String* name = script->names.data[READ_SHORT()];
|
String* name = script->names.data[READ_SHORT()];
|
||||||
Var script = importScript(vm, name);
|
Var scr = importScript(vm, name);
|
||||||
|
|
||||||
// TODO: implement fiber bsed execution.
|
// TODO: implement fiber bsed execution.
|
||||||
//ASSERT(IS_OBJ_TYPE(script, OBJ_SCRIPT), OOPS);
|
//ASSERT(IS_OBJ_TYPE(script, OBJ_SCRIPT), OOPS);
|
||||||
//Script* scr = (Script*)AS_OBJ(script);
|
//Script* scr = (Script*)AS_OBJ(script);
|
||||||
//if (!scr->initialized) vmRunScript(vm, scr);
|
//if (!scr->initialized) vmRunScript(vm, scr);
|
||||||
|
|
||||||
PUSH(script);
|
PUSH(scr);
|
||||||
CHECK_ERROR();
|
CHECK_ERROR();
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
@ -914,21 +1049,20 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
|||||||
// Pop the last frame, and if no more call frames, we're done with the
|
// Pop the last frame, and if no more call frames, we're done with the
|
||||||
// current fiber.
|
// current fiber.
|
||||||
if (--vm->fiber->frame_count == 0) {
|
if (--vm->fiber->frame_count == 0) {
|
||||||
vm->fiber->state = FIBER_DONE;
|
// TODO: if we're evaluvating an expressoin we need to set it's
|
||||||
|
// value on the stack.
|
||||||
|
//vm->fiber->sp = vm->fiber->stack; ??
|
||||||
|
|
||||||
// TODO:
|
// Assert all the stack locals were popped.
|
||||||
//vm->fiber->sp = vm->fiber->stack;.
|
ASSERT(vm->fiber->sp == vm->fiber->stack, OOPS);
|
||||||
|
|
||||||
if (vm->fiber->caller == NULL) {
|
FIBER_SWITCH_BACK();
|
||||||
|
|
||||||
|
if (vm->fiber == NULL) {
|
||||||
return PK_RESULT_SUCCESS;
|
return PK_RESULT_SUCCESS;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Fiber* caller = vm->fiber->caller;
|
*vm->fiber->ret = ret_value;
|
||||||
ASSERT(caller->state == FIBER_RUNNING, OOPS);
|
|
||||||
|
|
||||||
vm->fiber->caller = NULL;
|
|
||||||
vm->fiber = caller;
|
|
||||||
*caller->ret = ret_value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -1186,27 +1320,14 @@ static PkResult runFiber(PKVM* vm, Fiber* fiber) {
|
|||||||
{
|
{
|
||||||
if (vm->config.write_fn != NULL) {
|
if (vm->config.write_fn != NULL) {
|
||||||
Var tmp = PEEK(-1);
|
Var tmp = PEEK(-1);
|
||||||
vm->config.write_fn(vm, toRepr(vm, tmp)->data);
|
if (!IS_NULL(tmp)) {
|
||||||
vm->config.write_fn(vm, "\n");
|
vm->config.write_fn(vm, toRepr(vm, tmp)->data);
|
||||||
|
vm->config.write_fn(vm, "\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
DISPATCH();
|
DISPATCH();
|
||||||
}
|
}
|
||||||
|
|
||||||
OPCODE(YIELD):
|
|
||||||
{
|
|
||||||
Fiber* call_fiber = vm->fiber;
|
|
||||||
|
|
||||||
UPDATE_FRAME();
|
|
||||||
vmYieldFiber(vm, NULL);
|
|
||||||
if (vm->fiber == NULL) return PK_RESULT_SUCCESS;
|
|
||||||
LOAD_FRAME();
|
|
||||||
|
|
||||||
// Pop function arguments except for the return value of the call fiber.
|
|
||||||
call_fiber->sp = call_fiber->ret + 1;
|
|
||||||
|
|
||||||
DISPATCH();
|
|
||||||
}
|
|
||||||
|
|
||||||
OPCODE(END):
|
OPCODE(END):
|
||||||
TODO;
|
TODO;
|
||||||
break;
|
break;
|
||||||
|
17
src/vm.h
17
src/vm.h
@ -40,7 +40,7 @@
|
|||||||
// entry of the array.
|
// entry of the array.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char* name; //< Name of the function.
|
const char* name; //< Name of the function.
|
||||||
int length; //< Length of the name.
|
uint32_t length; //< Length of the name.
|
||||||
Function* fn; //< Native function pointer.
|
Function* fn; //< Native function pointer.
|
||||||
} BuiltinFn;
|
} BuiltinFn;
|
||||||
|
|
||||||
@ -108,7 +108,7 @@ struct PKVM {
|
|||||||
|
|
||||||
// Array of all builtin functions.
|
// Array of all builtin functions.
|
||||||
BuiltinFn builtins[BUILTIN_FN_CAPACITY];
|
BuiltinFn builtins[BUILTIN_FN_CAPACITY];
|
||||||
int builtins_count;
|
uint32_t builtins_count;
|
||||||
|
|
||||||
// Current fiber.
|
// Current fiber.
|
||||||
Fiber* fiber;
|
Fiber* fiber;
|
||||||
@ -180,6 +180,19 @@ void vmPopTempRef(PKVM* vm);
|
|||||||
// cache. If not found itll return NULL.
|
// cache. If not found itll return NULL.
|
||||||
Script* vmGetScript(PKVM* vm, String* path);
|
Script* vmGetScript(PKVM* vm, String* path);
|
||||||
|
|
||||||
|
// ((Context switching - start))
|
||||||
|
// Prepare a new fiber for execution with the given arguments. That can be used
|
||||||
|
// different fiber_run apis. Return true on success, otherwise it'll set the
|
||||||
|
// error to the vm's current fiber (if it has any).
|
||||||
|
bool vmPrepareFiber(PKVM* vm, Fiber* fiber, int argc, Var** argv);
|
||||||
|
|
||||||
|
// ((Context switching - resume))
|
||||||
|
// Switch the running fiber of the vm from the current fiber to the provided
|
||||||
|
// [fiber]. with an optional [value] (could be set to NULL). used in different
|
||||||
|
// fiber_resume apis. Return true on success, otherwise it'll set the error to
|
||||||
|
// the vm's current fiber (if it has any).
|
||||||
|
bool vmSwitchFiber(PKVM* vm, Fiber* fiber, Var* value);
|
||||||
|
|
||||||
// Yield from the current fiber. If the [value] isn't NULL it'll set it as the
|
// Yield from the current fiber. If the [value] isn't NULL it'll set it as the
|
||||||
// yield value.
|
// yield value.
|
||||||
void vmYieldFiber(PKVM* vm, Var* value);
|
void vmYieldFiber(PKVM* vm, Var* value);
|
||||||
|
Loading…
Reference in New Issue
Block a user