diff --git a/cli/main.c b/cli/main.c index 17beff9..b877b9b 100644 --- a/cli/main.c +++ b/cli/main.c @@ -131,6 +131,8 @@ static PKVM* intializePocketVM() { config.resolve_path_fn = resolvePath; // FIXME: +// Refactor and make it portable. Maybe custom is_tty() function?. +// Windows isatty depricated -- use _isatty. if (!!isatty(fileno(stderr))) { #ifdef _WIN32 DWORD outmode = 0; diff --git a/cli/modules/std_io.c b/cli/modules/std_io.c index 84030c5..c452fda 100644 --- a/cli/modules/std_io.c +++ b/cli/modules/std_io.c @@ -62,13 +62,13 @@ DEF(_fileOpen, "") { if (!pkCheckArgcRange(vm, argc, 1, 2)) return; const char* path; - if (!pkGetArgString(vm, 1, &path, NULL)) return; + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; const char* mode_str = "r"; FileAccessMode mode = FMODE_READ; if (argc == 2) { - if (!pkGetArgString(vm, 2, &mode_str, NULL)) return; + if (!pkValidateSlotString(vm, 2, &mode_str, NULL)) return; // Check if the mode string is valid, and update the mode value. do { @@ -123,7 +123,7 @@ DEF(_fileRead, "") { // TODO: this is temporary. char buff[2048]; fread((void*)buff, sizeof(char), sizeof(buff), file->fp); - pkReturnString(vm, (const char*)buff); + pkSetSlotString(vm, 0, (const char*)buff); } DEF(_fileWrite, "") { @@ -133,7 +133,7 @@ DEF(_fileWrite, "") { File* file = (File*)pkGetSelf(vm); const char* text; uint32_t length; - if (!pkGetArgString(vm, 1, &text, &length)) return; + if (!pkValidateSlotString(vm, 1, &text, &length)) return; if (file->closed) { pkSetRuntimeError(vm, "Cannot write to a closed file."); diff --git a/cli/modules/std_math.c b/cli/modules/std_math.c index d27ae20..7afe746 100644 --- a/cli/modules/std_math.c +++ b/cli/modules/std_math.c @@ -19,53 +19,53 @@ DEF(stdMathFloor, "floor(value:num) -> num\n") { double num; - if (!pkGetArgNumber(vm, 1, &num)) return; - pkReturnNumber(vm, floor(num)); + if (!pkValidateSlotNumber(vm, 1, &num)) return; + pkSetSlotNumber(vm, 0, floor(num)); } DEF(stdMathCeil, "ceil(value:num) -> num\n") { double num; - if (!pkGetArgNumber(vm, 1, &num)) return; - pkReturnNumber(vm, ceil(num)); + if (!pkValidateSlotNumber(vm, 1, &num)) return; + pkSetSlotNumber(vm, 0, ceil(num)); } DEF(stdMathPow, "pow(value:num) -> num\n") { double num, ex; - if (!pkGetArgNumber(vm, 1, &num)) return; - if (!pkGetArgNumber(vm, 2, &ex)) return; - pkReturnNumber(vm, pow(num, ex)); + if (!pkValidateSlotNumber(vm, 1, &num)) return; + if (!pkValidateSlotNumber(vm, 2, &ex)) return; + pkSetSlotNumber(vm, 0, pow(num, ex)); } DEF(stdMathSqrt, "sqrt(value:num) -> num\n") { double num; - if (!pkGetArgNumber(vm, 1, &num)) return; - pkReturnNumber(vm, sqrt(num)); + if (!pkValidateSlotNumber(vm, 1, &num)) return; + pkSetSlotNumber(vm, 0, sqrt(num)); } DEF(stdMathAbs, "abs(value:num) -> num\n") { double num; - if (!pkGetArgNumber(vm, 1, &num)) return; + if (!pkValidateSlotNumber(vm, 1, &num)) return; if (num < 0) num = -num; - pkReturnNumber(vm, num); + pkSetSlotNumber(vm, 0, num); } DEF(stdMathSign, "sign(value:num) -> num\n") { double num; - if (!pkGetArgNumber(vm, 1, &num)) return; + if (!pkValidateSlotNumber(vm, 1, &num)) return; if (num < 0) num = -1; else if (num > 0) num = +1; else num = 0; - pkReturnNumber(vm, num); + pkSetSlotNumber(vm, 0, num); } DEF(stdMathSine, @@ -74,8 +74,8 @@ DEF(stdMathSine, "in radians.") { double rad; - if (!pkGetArgNumber(vm, 1, &rad)) return; - pkReturnNumber(vm, sin(rad)); + if (!pkValidateSlotNumber(vm, 1, &rad)) return; + pkSetSlotNumber(vm, 0, sin(rad)); } DEF(stdMathCosine, @@ -84,8 +84,8 @@ DEF(stdMathCosine, "in radians.") { double rad; - if (!pkGetArgNumber(vm, 1, &rad)) return; - pkReturnNumber(vm, cos(rad)); + if (!pkValidateSlotNumber(vm, 1, &rad)) return; + pkSetSlotNumber(vm, 0, cos(rad)); } DEF(stdMathTangent, @@ -94,8 +94,8 @@ DEF(stdMathTangent, "in radians.") { double rad; - if (!pkGetArgNumber(vm, 1, &rad)) return; - pkReturnNumber(vm, tan(rad)); + if (!pkValidateSlotNumber(vm, 1, &rad)) return; + pkSetSlotNumber(vm, 0, tan(rad)); } DEF(stdMathSinh, @@ -103,8 +103,8 @@ DEF(stdMathSinh, "Return the hyperbolic sine value of the argument [val].") { double val; - if (!pkGetArgNumber(vm, 1, &val)) return; - pkReturnNumber(vm, sinh(val)); + if (!pkValidateSlotNumber(vm, 1, &val)) return; + pkSetSlotNumber(vm, 0, sinh(val)); } DEF(stdMathCosh, @@ -112,8 +112,8 @@ DEF(stdMathCosh, "Return the hyperbolic cosine value of the argument [val].") { double val; - if (!pkGetArgNumber(vm, 1, &val)) return; - pkReturnNumber(vm, cosh(val)); + if (!pkValidateSlotNumber(vm, 1, &val)) return; + pkSetSlotNumber(vm, 0, cosh(val)); } DEF(stdMathTanh, @@ -121,8 +121,8 @@ DEF(stdMathTanh, "Return the hyperbolic tangent value of the argument [val].") { double val; - if (!pkGetArgNumber(vm, 1, &val)) return; - pkReturnNumber(vm, tanh(val)); + if (!pkValidateSlotNumber(vm, 1, &val)) return; + pkSetSlotNumber(vm, 0, tanh(val)); } DEF(stdMathArcSine, @@ -131,13 +131,13 @@ DEF(stdMathArcSine, "expressed in radians.") { double num; - if (!pkGetArgNumber(vm, 1, &num)) return; + if (!pkValidateSlotNumber(vm, 1, &num)) return; if (num < -1 || 1 < num) { pkSetRuntimeError(vm, "Argument should be between -1 and +1"); } - pkReturnNumber(vm, asin(num)); + pkSetSlotNumber(vm, 0, asin(num)); } DEF(stdMathArcCosine, @@ -146,13 +146,13 @@ DEF(stdMathArcCosine, "an angle expressed in radians.") { double num; - if (!pkGetArgNumber(vm, 1, &num)) return; + if (!pkValidateSlotNumber(vm, 1, &num)) return; if (num < -1 || 1 < num) { pkSetRuntimeError(vm, "Argument should be between -1 and +1"); } - pkReturnNumber(vm, acos(num)); + pkSetSlotNumber(vm, 0, acos(num)); } DEF(stdMathArcTangent, @@ -161,8 +161,8 @@ DEF(stdMathArcTangent, "an angle expressed in radians.") { double num; - if (!pkGetArgNumber(vm, 1, &num)) return; - pkReturnNumber(vm, atan(num)); + if (!pkValidateSlotNumber(vm, 1, &num)) return; + pkSetSlotNumber(vm, 0, atan(num)); } DEF(stdMathLog10, @@ -170,8 +170,8 @@ DEF(stdMathLog10, "Return the logarithm to base 10 of argument [value]") { double num; - if (!pkGetArgNumber(vm, 1, &num)) return; - pkReturnNumber(vm, log10(num)); + if (!pkValidateSlotNumber(vm, 1, &num)) return; + pkSetSlotNumber(vm, 0, log10(num)); } DEF(stdMathRound, @@ -179,8 +179,8 @@ DEF(stdMathRound, "Round to nearest integer, away from zero and return the number.") { double num; - if (!pkGetArgNumber(vm, 1, &num)) return; - pkReturnNumber(vm, round(num)); + if (!pkValidateSlotNumber(vm, 1, &num)) return; + pkSetSlotNumber(vm, 0, round(num)); } void registerModuleMath(PKVM* vm) { diff --git a/cli/modules/std_path.c b/cli/modules/std_path.c index 4f62a4c..2fa4111 100644 --- a/cli/modules/std_path.c +++ b/cli/modules/std_path.c @@ -94,7 +94,7 @@ static inline size_t pathAbs(const char* path, char* buff, size_t buff_size) { DEF(_pathSetStyleUnix, "") { bool value; - if (!pkGetArgBool(vm, 1, &value)) return; + if (!pkValidateSlotBool(vm, 1, &value)) return; cwk_path_set_style((value) ? CWK_STYLE_UNIX : CWK_STYLE_WINDOWS); } @@ -103,22 +103,22 @@ DEF(_pathGetCWD, "") { if (get_cwd(cwd, sizeof(cwd)) == NULL) { // TODO: Handle error. } - pkReturnString(vm, cwd); + pkSetSlotString(vm, 0, cwd); } DEF(_pathAbspath, "") { const char* path; - if (!pkGetArgString(vm, 1, &path, NULL)) return; + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; char abspath[FILENAME_MAX]; - size_t len = pathAbs(path, abspath, sizeof(abspath)); - pkReturnStringLength(vm, abspath, len); + uint32_t len = (uint32_t) pathAbs(path, abspath, sizeof(abspath)); + pkSetSlotStringLength(vm, 0, abspath, len); } DEF(_pathRelpath, "") { const char* from, * path; - if (!pkGetArgString(vm, 1, &from, NULL)) return; - if (!pkGetArgString(vm, 2, &path, NULL)) return; + if (!pkValidateSlotString(vm, 1, &from, NULL)) return; + if (!pkValidateSlotString(vm, 2, &path, NULL)) return; char abs_from[FILENAME_MAX]; pathAbs(from, abs_from, sizeof(abs_from)); @@ -127,9 +127,9 @@ DEF(_pathRelpath, "") { pathAbs(path, abs_path, sizeof(abs_path)); char result[FILENAME_MAX]; - size_t len = cwk_path_get_relative(abs_from, abs_path, - result, sizeof(result)); - pkReturnStringLength(vm, result, len); + uint32_t len = (uint32_t) cwk_path_get_relative(abs_from, abs_path, + result, sizeof(result)); + pkSetSlotStringLength(vm, 0, result, len); } DEF(_pathJoin, "") { @@ -143,79 +143,80 @@ DEF(_pathJoin, "") { } for (int i = 0; i < argc; i++) { - pkGetArgString(vm, i + 1, &paths[i], NULL); + pkValidateSlotString(vm, i + 1, &paths[i], NULL); } paths[argc] = NULL; char result[FILENAME_MAX]; - size_t len = cwk_path_join_multiple(paths, result, sizeof(result)); - pkReturnStringLength(vm, result, len); + uint32_t len = (uint32_t) cwk_path_join_multiple(paths, result, + sizeof(result)); + pkSetSlotStringLength(vm, 0, result, len); } DEF(_pathNormalize, "") { const char* path; - if (!pkGetArgString(vm, 1, &path, NULL)) return; + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; char result[FILENAME_MAX]; - size_t len = cwk_path_normalize(path, result, sizeof(result)); - pkReturnStringLength(vm, result, len); + uint32_t len = (uint32_t) cwk_path_normalize(path, result, sizeof(result)); + pkSetSlotStringLength(vm, 0, result, len); } DEF(_pathBaseName, "") { const char* path; - if (!pkGetArgString(vm, 1, &path, NULL)) return; + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; const char* base_name; size_t length; cwk_path_get_basename(path, &base_name, &length); - pkReturnString(vm, base_name); + pkSetSlotStringLength(vm, 0, base_name, (uint32_t)length); } DEF(_pathDirName, "") { const char* path; - if (!pkGetArgString(vm, 1, &path, NULL)) return; + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; size_t length; cwk_path_get_dirname(path, &length); - pkReturnStringLength(vm, path, length); + pkSetSlotStringLength(vm, 0, path, (uint32_t)length); } DEF(_pathIsPathAbs, "") { const char* path; - if (!pkGetArgString(vm, 1, &path, NULL)) return; + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; - pkReturnBool(vm, cwk_path_is_absolute(path)); + pkSetSlotBool(vm, 0, cwk_path_is_absolute(path)); } DEF(_pathGetExtension, "") { const char* path; - if (!pkGetArgString(vm, 1, &path, NULL)) return; + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; const char* ext; size_t length; if (cwk_path_get_extension(path, &ext, &length)) { - pkReturnStringLength(vm, ext, length); + pkSetSlotStringLength(vm, 0, ext, (uint32_t)length); } else { - pkReturnStringLength(vm, NULL, 0); + pkSetSlotStringLength(vm, 0, NULL, 0); } } DEF(_pathExists, "") { const char* path; - if (!pkGetArgString(vm, 1, &path, NULL)) return; - pkReturnBool(vm, pathIsExists(path)); + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; + pkSetSlotBool(vm, 0, pathIsExists(path)); } DEF(_pathIsFile, "") { const char* path; - if (!pkGetArgString(vm, 1, &path, NULL)) return; - pkReturnBool(vm, pathIsFileExists(path)); + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; + pkSetSlotBool(vm, 0, pathIsFileExists(path)); } DEF(_pathIsDir, "") { const char* path; - if (!pkGetArgString(vm, 1, &path, NULL)) return; - pkReturnBool(vm, pathIsDirectoryExists(path)); + if (!pkValidateSlotString(vm, 1, &path, NULL)) return; + pkSetSlotBool(vm, 0, pathIsDirectoryExists(path)); } void registerModulePath(PKVM* vm) { diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index 0b27742..d102584 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.h @@ -69,12 +69,6 @@ typedef struct PKVM PKVM; // till it's released with pkReleaseHandle(). typedef struct PkHandle PkHandle; -// A temproary pointer to the pocketlang variable. This pointer is acquired -// from the pocketlang's current stack frame and the pointer will become -// dangling once the stack frame is popped. If you want to keep the value -// alive use `pkNewHandle()`. -typedef void* PkVar; - typedef enum PkVarType PkVarType; typedef enum PkErrorType PkErrorType; typedef enum PkResult PkResult; @@ -228,12 +222,12 @@ struct PkCompileOptions { // Create a new PkConfiguration with the default values and return it. // Override those default configuration to adopt to another hosting // application. -PK_PUBLIC PkConfiguration pkNewConfiguration(void); +PK_PUBLIC PkConfiguration pkNewConfiguration(); // Create a new pkCompilerOptions with the default values and return it. // Override those default configuration to adopt to another hosting // application. -PK_PUBLIC PkCompileOptions pkNewCompilerOptions(void); +PK_PUBLIC PkCompileOptions pkNewCompilerOptions(); // Allocate, initialize and returns a new VM. PK_PUBLIC PKVM* pkNewVM(PkConfiguration* config); @@ -247,29 +241,17 @@ PK_PUBLIC void pkSetUserData(PKVM* vm, void* user_data); // Returns the associated user data. PK_PUBLIC void* pkGetUserData(const PKVM* vm); -// Create a new handle for the [value]. This is useful to keep the [value] -// alive once it acquired from the stack. **DO NOT** use the [value] once -// creating a new handle for it instead get the value from the handle by -// using pkGetHandleValue() function (otherwise the [value] would become a -// dangling pointer once it's stack frame is popped). -PK_PUBLIC PkHandle* pkNewHandle(PKVM* vm, PkVar value); - -// Return the PkVar pointer in the handle, the returned pointer will be valid -// till the handle is released. -PK_PUBLIC PkVar pkGetHandleValue(const PkHandle* handle); - // Release the handle and allow its value to be garbage collected. Always call // this for every handles before freeing the VM. PK_PUBLIC void pkReleaseHandle(PKVM* vm, PkHandle* handle); -// Add a global [value] to the given module. -PK_PUBLIC void pkModuleAddGlobal(PKVM* vm, PkHandle* module, - const char* name, PkHandle* value); +// Add a new module named [name] to the [vm]. Note that the module shouldn't +// already existed, otherwise an assertion will fail to indicate that. +PK_PUBLIC PkHandle* pkNewModule(PKVM* vm, const char* name); -// Returns the global value with the [name] in the given [module], if the name -// not exists in the globals of the module, it'll return NULL. -PK_PUBLIC PkHandle* pkModuleGetGlobal(PKVM* vm, PkHandle* module, - const char* name); +// Register the module to the PKVM's modules map, once after it can be +// imported in other modules. +PK_PUBLIC void pkRegisterModule(PKVM* vm, PkHandle* module); // Add a native function to the given module. If [arity] is -1 that means // the function has variadic parameters and use pkGetArgc() to get the argc. @@ -283,14 +265,6 @@ PK_PUBLIC void pkModuleAddFunction(PKVM* vm, PkHandle* module, // it's statements are wrapped around an implicit main function. PK_PUBLIC PkHandle* pkModuleGetMainFunction(PKVM* vm, PkHandle* module); -// Add a new module named [name] to the [vm]. Note that the module shouldn't -// already existed, otherwise an assertion will fail to indicate that. -PK_PUBLIC PkHandle* pkNewModule(PKVM* vm, const char* name); - -// Register the module to the PKVM's modules map, once after it can be -// imported in other modules. -PK_PUBLIC void pkRegisterModule(PKVM* vm, PkHandle* module); - // Create a new class on the [module] with the [name] and return it. // If the [base_class] is NULL by default it'll set to "Object" class. PK_PUBLIC PkHandle* pkNewClass(PKVM* vm, const char* name, @@ -304,9 +278,6 @@ PK_PUBLIC void pkClassAddMethod(PKVM* vm, PkHandle* cls, const char* name, pkNativeFn fptr, int arity); -// Create and return a new fiber around the function [fn]. -PK_PUBLIC PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn); - // Compile the [module] with the provided [source]. Set the compiler options // with the the [options] argument or set to NULL for default options. PK_PUBLIC PkResult pkCompileModule(PKVM* vm, PkHandle* module, @@ -322,6 +293,13 @@ PK_PUBLIC PkResult pkInterpretSource(PKVM* vm, PkStringPtr path, const PkCompileOptions* options); +// FIXME: +// Remove pkNewFiber and pkRunFiber functions and add interface to run closure +// with pkRunClosure or pkRunFunction. + +// Create and return a new fiber around the function [fn]. +PK_PUBLIC PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn); + // 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 // result (success or failure) if you need the yielded or returned value use @@ -330,13 +308,8 @@ PK_PUBLIC PkResult pkInterpretSource(PKVM* vm, PK_PUBLIC PkResult pkRunFiber(PKVM* vm, PkHandle* fiber, int argc, PkHandle** argv); -// Resume a yielded fiber with an optional [value]. (could be set to NULL) -// It'll returns it's run status result (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); - /*****************************************************************************/ -/* NATIVE FUNCTION API */ +/* NATIVE / RUNTIME FUNCTION API */ /*****************************************************************************/ // Set a runtime error to VM. @@ -348,10 +321,6 @@ PK_PUBLIC void pkSetRuntimeError(PKVM* vm, const char* message); // Returns native [self] of the current method as a void*. PK_PUBLIC void* pkGetSelf(const PKVM* vm); -// Return the type of the [value] this will help to get the type of the -// variable that was extracted from pkGetArg() earlier. -PK_PUBLIC PkVarType pkGetValueType(const PkVar value); - // Return the current functions argument count. This is needed for functions // registered with -1 argument count (which means variadic arguments). PK_PUBLIC int pkGetArgc(const PKVM* vm); @@ -361,70 +330,72 @@ PK_PUBLIC int pkGetArgc(const PKVM* vm); // that min <= max, and pocketlang won't validate this in release binary. PK_PUBLIC bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max); -// Return the [arg] th argument as a PkVar. This pointer will only be -// valid till the current function ends, because it points to the var at the -// stack and it'll popped when the current call frame ended. Use handles to -// keep the var alive even after that. -PK_PUBLIC PkVar pkGetArg(const PKVM* vm, int arg); +// SLOTS DOCS -// The functions below are used to extract the function arguments from the -// stack as a type. They will first validate the argument's type and set a -// runtime error if it's not and return false. Otherwise it'll set the [value] -// with the extracted value. -// -// NOTE: The arguments are 1 based (to get the first argument use 1 not 0). -// Only use arg index 0 to get the value of attribute setter call. +// Helper function to check if the argument at the [arg] slot is Boolean and +// if not set a runtime error. +PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int arg, bool* value); -PK_PUBLIC bool pkGetArgBool(PKVM* vm, int arg, bool* value); -PK_PUBLIC bool pkGetArgNumber(PKVM* vm, int arg, double* value); -PK_PUBLIC bool pkGetArgString(PKVM* vm, int arg, - const char** value, uint32_t* length); -PK_PUBLIC bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value); +// Helper function to check if the argument at the [arg] slot is Number and +// if not set a runtime error. +PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int arg, double* value); -// The functions follow are used to set the return value of the current native -// function's. Don't use it outside a registered native function. +// Helper function to check if the argument at the [arg] slot is String and +// if not set a runtime error. +PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int arg, + const char** value, uint32_t* length); -PK_PUBLIC void pkReturnNull(PKVM* vm); -PK_PUBLIC void pkReturnBool(PKVM* vm, bool value); -PK_PUBLIC void pkReturnNumber(PKVM* vm, double value); -PK_PUBLIC void pkReturnString(PKVM* vm, const char* value); -PK_PUBLIC void pkReturnStringLength(PKVM* vm, const char* value, size_t len); -PK_PUBLIC void pkReturnValue(PKVM* vm, PkVar value); -PK_PUBLIC void pkReturnHandle(PKVM* vm, PkHandle* handle); +// Make sure the fiber has [count] number of slots to work with (including the +// arguments). +PK_PUBLIC void pkReserveSlots(PKVM* vm, int count); -// 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 available number of slots to work with. It has at least the +// number argument the function is registered plus one for return value. +PK_PUBLIC int pkGetSlotsCount(PKVM* vm); -// 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 the type of the variable at the [index] slot. +PK_PUBLIC PkVarType pkGetSlotType(PKVM* vm, int index); -// Returns true if the fiber is finished it's execution and cannot be resumed -// anymore. -PK_PUBLIC bool pkFiberIsDone(const PkHandle* fiber); +// Returns boolean value at the [index] slot. If the value at the [index] +// is not a boolean it'll be casted (only for booleans). +PK_PUBLIC bool pkGetSlotBool(PKVM* vm, int index); -/*****************************************************************************/ -/* POCKETLANG TYPE FUNCTIONS */ -/*****************************************************************************/ +// Returns number value at the [index] slot. If the value at the [index] +// is not a boolean, an assertion will fail. +PK_PUBLIC double pkGetSlotNumber(PKVM* vm, int index); -// The functions below will allocate a new object and return's it's value -// wrapped around a handler. +// Returns the string at the [index] slot. The returned pointer is only valid +// inside the native function that called this. Afterwards it may garbage +// collected and become demangled. If the [length] is not NULL the length of +// the string will be written. +PK_PUBLIC const char* pkGetSlotString(PKVM* vm, int index, uint32_t* length); -PK_PUBLIC PkHandle* pkNewString(PKVM* vm, const char* value); -PK_PUBLIC PkHandle* pkNewStringLength(PKVM* vm, const char* value, size_t len); -PK_PUBLIC PkHandle* pkNewList(PKVM* vm); -PK_PUBLIC PkHandle* pkNewMap(PKVM* vm); +// Capture the variable at the [index] slot and return its handle. As long as +// the handle is not released with `pkReleaseHandle()` the variable won't be +// garbage collected. +PK_PUBLIC PkHandle* pkGetSlotHandle(PKVM* vm, int index); -// TODO: Create a primitive (non garbage collected) variable buffer (or a -// fixed size array) to store them and make the handle points to the variable -// in that buffer, this will prevent us from invoking an allocation call for -// each time we want to pass a primitive type. +// Set the [index] slot value as pocketlang null. +PK_PUBLIC void pkSetSlotNull(PKVM* vm, int index); -//PK_PUBLIC PkVar pkPushNull(PKVM* vm); -//PK_PUBLIC PkVar pkPushBool(PKVM* vm, bool value); -//PK_PUBLIC PkVar pkPushNumber(PKVM* vm, double value); +// Set the [index] slot boolean value as the given [value]. +PK_PUBLIC void pkSetSlotBool(PKVM* vm, int index, bool value); + +// Set the [index] slot numeric value as the given [value]. +PK_PUBLIC void pkSetSlotNumber(PKVM* vm, int index, double value); + +// Create a new String copying the [value] and set it to [index] slot. +PK_PUBLIC void pkSetSlotString(PKVM* vm, int index, const char* value); + +// Create a new String copying the [value] and set it to [index] slot. Unlike +// the above function it'll copy only the spicified length. +PK_PUBLIC void pkSetSlotStringLength(PKVM* vm, int index, + const char* value, uint32_t length); + +// Set the [index] slot's value as the given [handle]. The function won't +// reclaim the ownership of the handle and you can still use it till +// it's released by yourself. +PK_PUBLIC void PkSetSlotHandle(PKVM* vm, int index, PkHandle* handle); #ifdef __cplusplus } // extern "C" diff --git a/src/pk_compiler.c b/src/pk_compiler.c index 2feca34..11c830d 100644 --- a/src/pk_compiler.c +++ b/src/pk_compiler.c @@ -3386,27 +3386,6 @@ PkResult compile(PKVM* vm, Module* module, const char* source, return PK_RESULT_SUCCESS; } -// FIXME: -// move this to pk_core or create new pk_public.c to implement all -// public functions. -PkResult pkCompileModule(PKVM* vm, PkHandle* module_handle, PkStringPtr source, - const PkCompileOptions* options) { - - ASSERT(source.string != NULL, OOPS); - - // FIXME: - // CHECK_NULL, CHECK_TYPE marcros can be use after this function is moved. - ASSERT(module_handle != NULL, "Argument module was NULL."); - ASSERT(IS_OBJ_TYPE(module_handle->value, OBJ_MODULE), - "Given handle is not a module."); - - Module* module = (Module*)AS_OBJ(module_handle->value); - - PkResult result = compile(vm, module, source.string, options); - if (source.on_done) source.on_done(vm, source); - return result; -} - void compilerMarkObjects(PKVM* vm, Compiler* compiler) { // Mark the module which is currently being compiled. diff --git a/src/pk_compiler.h b/src/pk_compiler.h index 7d483c7..286b991 100644 --- a/src/pk_compiler.h +++ b/src/pk_compiler.h @@ -7,7 +7,6 @@ #ifndef PK_COMPILER_H #define PK_COMPILER_H -#include "pk_internal.h" #include "pk_value.h" typedef enum { diff --git a/src/pk_core.c b/src/pk_core.c index 1ed7228..5f2f6bb 100644 --- a/src/pk_core.c +++ b/src/pk_core.c @@ -25,145 +25,6 @@ static const char* DOCSTRING(fn) = docstring; \ static void fn(PKVM* vm) -// Create a new module with the given [name] and returns as a Module* for -// internal. Which will be wrapped by pkNewModule to return a pkHandle*. -static Module* newModuleInternal(PKVM* vm, const char* name); - -// Adds a function to the module with the give properties and add the function -// to the module's globals variables. -static void moduleAddFunctionInternal(PKVM* vm, Module* module, - const char* name, pkNativeFn fptr, - int arity, const char* docstring); - -/*****************************************************************************/ -/* CORE PUBLIC API */ -/*****************************************************************************/ - -#define CHECK_NULL(name) \ - ASSERT(name != NULL, "Argument " #name " was NULL."); - -#define CHECK_TYPE(handle, type) \ - do { \ - CHECK_NULL(handle); \ - ASSERT(IS_OBJ_TYPE(handle->value, type), \ - "Given handle is not of type " #type "."); \ - } while (false) - -PkHandle* pkNewModule(PKVM* vm, const char* name) { - CHECK_NULL(name); - Module* module = newModuleInternal(vm, name); - return vmNewHandle(vm, VAR_OBJ(module)); -} - -void pkRegisterModule(PKVM* vm, PkHandle* module) { - CHECK_TYPE(module, OBJ_MODULE); - - Module* module_ = (Module*)AS_OBJ(module->value); - vmRegisterModule(vm, module_, module_->name); -} - -PkHandle* pkNewClass(PKVM* vm, const char* name, - PkHandle* base_class, PkHandle* module, - pkNewInstanceFn new_fn, - pkDeleteInstanceFn delete_fn) { - CHECK_NULL(module); - CHECK_NULL(name); - CHECK_TYPE(module, OBJ_MODULE); - - Class* super = vm->builtin_classes[PK_OBJECT]; - if (base_class != NULL) { - CHECK_TYPE(base_class, OBJ_CLASS); - super = (Class*)AS_OBJ(base_class->value); - } - - Class* class_ = newClass(vm, name, (int)strlen(name), - super, (Module*)AS_OBJ(module->value), - NULL, NULL); - class_->new_fn = new_fn; - class_->delete_fn = delete_fn; - - return vmNewHandle(vm, VAR_OBJ(class_)); -} - -void pkClassAddMethod(PKVM* vm, PkHandle* cls, - const char* name, - pkNativeFn fptr, int arity) { - CHECK_NULL(cls); - CHECK_NULL(fptr); - CHECK_TYPE(cls, OBJ_CLASS); - - Class* class_ = (Class*)AS_OBJ(cls->value); - - Function* fn = newFunction(vm, name, (int)strlen(name), - class_->owner, true, NULL, NULL); - - // No need to push the function to temp references of the VM - // since it's written to the constant pool of the module and the module - // won't be garbage collected (class handle has reference to the module). - - Closure* method = newClosure(vm, fn); - - // FIXME: name "_init" is literal everywhere. - if (strcmp(name, "_init") == 0) { - class_->ctor = method; - - } else { - vmPushTempRef(vm, &method->_super); // method. - pkClosureBufferWrite(&class_->methods, vm, method); - vmPopTempRef(vm); // method. - } -} - -void* pkGetSelf(const PKVM* vm) { - ASSERT(IS_OBJ_TYPE(vm->fiber->self, OBJ_INST), OOPS); - Instance* inst = (Instance*)AS_OBJ(vm->fiber->self); - ASSERT(inst->native != NULL, OOPS); - return inst->native; -} - -void pkModuleAddGlobal(PKVM* vm, PkHandle* module, - const char* name, PkHandle* value) { - CHECK_TYPE(module, OBJ_MODULE); - CHECK_NULL(value); - - moduleAddGlobal(vm, (Module*)AS_OBJ(module->value), - name, (uint32_t)strlen(name), value->value); -} - -PkHandle* pkModuleGetGlobal(PKVM* vm, PkHandle* module, const char* name) { - CHECK_TYPE(module, OBJ_MODULE); - CHECK_NULL(name); - - Module* module_ = (Module*)AS_OBJ(module->value); - int index = moduleGetGlobalIndex(module_, name, (uint32_t)strlen(name)); - if (index == -1) return NULL; - return vmNewHandle(vm, module_->globals.data[index]); -} - -void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name, - pkNativeFn fptr, int arity) { - CHECK_TYPE(module, OBJ_MODULE); - CHECK_NULL(fptr); - - moduleAddFunctionInternal(vm, (Module*)AS_OBJ(module->value), - name, fptr, arity, - NULL /*TODO: Public API for function docstring.*/); -} - -PkHandle* pkModuleGetMainFunction(PKVM* vm, PkHandle* module) { - CHECK_TYPE(module, OBJ_MODULE); - - Module* _module = (Module*)AS_OBJ(module->value); - - int main_index = moduleGetGlobalIndex(_module, IMPLICIT_MAIN_NAME, - (uint32_t)strlen(IMPLICIT_MAIN_NAME)); - if (main_index == -1) return NULL; - ASSERT_INDEX(main_index, (int)_module->globals.count); - Var main_fn = _module->globals.data[main_index]; - ASSERT(IS_OBJ_TYPE(main_fn, OBJ_CLOSURE), OOPS); - return vmNewHandle(vm, main_fn); -} - // A convenient macro to get the nth (1 based) argument of the current // function. #define ARG(n) (vm->fiber->ret[n]) @@ -184,172 +45,6 @@ PkHandle* pkModuleGetMainFunction(PKVM* vm, PkHandle* module) { RET(VAR_NULL); \ } while(false) -// Check for errors in before calling the get arg public api function. -#define CHECK_GET_ARG_API_ERRORS() \ - do { \ - ASSERT(vm->fiber != NULL, \ - "This function can only be called at runtime."); \ - if (arg != 0) {/* If Native setter, the value would be at fiber->ret */ \ - ASSERT(arg > 0 && arg <= ARGC, "Invalid argument index."); \ - } \ - ASSERT(value != NULL, "Argument [value] was NULL."); \ - } while (false) - -// Set error for incompatible type provided as an argument. (TODO: got type). -#define ERR_INVALID_ARG_TYPE(m_type) \ -do { \ - if (arg != 0) { /* If Native setter, arg index would be 0. */ \ - char buff[STR_INT_BUFF_SIZE]; \ - sprintf(buff, "%d", arg); \ - VM_SET_ERROR(vm, stringFormat(vm, "Expected a '$' at argument $.", \ - m_type, buff)); \ - } else { \ - VM_SET_ERROR(vm, stringFormat(vm, "Expected a '$'.", m_type)); \ - } \ -} while (false) - -int pkGetArgc(const PKVM* vm) { - ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); - return ARGC; -} - -bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max) { - ASSERT(min <= max, "invalid argc range (min > max)."); - - if (argc < min) { - char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", min); - VM_SET_ERROR(vm, stringFormat(vm, "Expected at least %s argument(s).", - buff)); - return false; - - } else if (argc > max) { - char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", max); - VM_SET_ERROR(vm, stringFormat(vm, "Expected at most %s argument(s).", - buff)); - return false; - } - - return true; -} - -PkVar pkGetArg(const PKVM* vm, int arg) { - ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); - ASSERT(arg > 0 || arg <= ARGC, "Invalid argument index."); - - return &(ARG(arg)); -} - -bool pkGetArgBool(PKVM* vm, int arg, bool* value) { - CHECK_GET_ARG_API_ERRORS(); - - Var val = ARG(arg); - *value = toBool(val); - return true; -} - -bool pkGetArgNumber(PKVM* vm, int arg, double* value) { - CHECK_GET_ARG_API_ERRORS(); - - Var val = ARG(arg); - if (IS_NUM(val)) { - *value = AS_NUM(val); - - } else if (IS_BOOL(val)) { - *value = AS_BOOL(val) ? 1 : 0; - - } else { - ERR_INVALID_ARG_TYPE("number"); - return false; - } - - return true; -} - -bool pkGetArgString(PKVM* vm, int arg, const char** value, uint32_t* length) { - CHECK_GET_ARG_API_ERRORS(); - - Var val = ARG(arg); - if (IS_OBJ_TYPE(val, OBJ_STRING)) { - String* str = (String*)AS_OBJ(val); - *value = str->data; - if (length) *length = str->length; - - } else { - ERR_INVALID_ARG_TYPE("string"); - return false; - } - - return true; -} - -bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) { - CHECK_GET_ARG_API_ERRORS(); - - Var val = ARG(arg); - if (pkGetValueType((PkVar)&val) != type) { - char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", arg); - VM_SET_ERROR(vm, stringFormat(vm, "Expected a $ at argument $.", - getPkVarTypeName(type), buff)); - return false; - } - - *value = (PkVar)&val; - return true; -} - -void pkReturnNull(PKVM* vm) { - RET(VAR_NULL); -} - -void pkReturnBool(PKVM* vm, bool value) { - RET(VAR_BOOL(value)); -} - -void pkReturnNumber(PKVM* vm, double value) { - RET(VAR_NUM(value)); -} - -void pkReturnString(PKVM* vm, const char* value) { - RET(VAR_OBJ(newString(vm, value))); -} - -void pkReturnStringLength(PKVM* vm, const char* value, size_t length) { - RET(VAR_OBJ(newStringLength(vm, value, (uint32_t)length))); -} - -void pkReturnValue(PKVM* vm, PkVar value) { - RET(*(Var*)value); -} - -void pkReturnHandle(PKVM* vm, PkHandle* handle) { - RET(handle->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; -} - -#undef CHECK_NULL -#undef CHECK_TYPE - /*****************************************************************************/ /* VALIDATORS */ /*****************************************************************************/ @@ -833,7 +528,7 @@ static void initializeBuiltinFunctions(PKVM* vm) { /*****************************************************************************/ // Create a module and add it to the vm's core modules, returns the module. -static Module* newModuleInternal(PKVM* vm, const char* name) { +Module* newModuleInternal(PKVM* vm, const char* name) { String* _name = newString(vm, name); vmPushTempRef(vm, &_name->_super); // _name @@ -854,9 +549,9 @@ static Module* newModuleInternal(PKVM* vm, const char* name) { } // An internal function to add a function to the given [module]. -static void moduleAddFunctionInternal(PKVM* vm, Module* module, - const char* name, pkNativeFn fptr, - int arity, const char* docstring) { +void moduleAddFunctionInternal(PKVM* vm, Module* module, + const char* name, pkNativeFn fptr, + int arity, const char* docstring) { Function* fn = newFunction(vm, name, (int)strlen(name), module, true, docstring, NULL); diff --git a/src/pk_core.h b/src/pk_core.h index 6bf294e..76db4df 100644 --- a/src/pk_core.h +++ b/src/pk_core.h @@ -13,6 +13,17 @@ // Initialize core language, builtin function and core libs. void initializeCore(PKVM* vm); +// Create a new module with the given [name] and returns as a Module*. +// This is function is a wrapper around `newModule()` function to create +// native modules for pocket core and public native api. +Module* newModuleInternal(PKVM* vm, const char* name); + +// Adds a function to the module with the give properties and add the function +// to the module's globals variables. +void moduleAddFunctionInternal(PKVM* vm, Module* module, + const char* name, pkNativeFn fptr, + int arity, const char* docstring); + /*****************************************************************************/ /* OPERATORS */ /*****************************************************************************/ diff --git a/src/pk_debug.c b/src/pk_debug.c index 2806081..30b7e3c 100644 --- a/src/pk_debug.c +++ b/src/pk_debug.c @@ -7,8 +7,6 @@ #include "pk_debug.h" #include -#include "pk_core.h" -#include "pk_value.h" #include "pk_vm.h" // FIXME: diff --git a/src/pk_public.c b/src/pk_public.c new file mode 100644 index 0000000..111c61c --- /dev/null +++ b/src/pk_public.c @@ -0,0 +1,553 @@ +/* + * Copyright (c) 2020-2022 Thakee Nathees + * Copyright (c) 2021-2022 Pocketlang Contributors + * Distributed Under The MIT License + */ + +// This file contains all the pocketlang public function implementations. + +#include "include/pocketlang.h" + +#include "pk_core.h" +#include "pk_value.h" +#include "pk_vm.h" + +#define CHECK_ARG_NULL(name) \ + ASSERT((name) != NULL, "Argument " #name " was NULL."); + +#define CHECK_HANDLE_TYPE(handle, type) \ + do { \ + CHECK_ARG_NULL(handle); \ + ASSERT(IS_OBJ_TYPE(handle->value, type), \ + "Given handle is not of type " #type "."); \ + } while (false) + +// The default allocator that will be used to initialize the PKVM's +// configuration if the host doesn't provided any allocators for us. +static void* defaultRealloc(void* memory, size_t new_size, void* _); + +PkConfiguration pkNewConfiguration() { + PkConfiguration config; + config.realloc_fn = defaultRealloc; + + config.stdout_write = NULL; + config.stderr_write = NULL; + config.stdin_read = NULL; + + config.load_script_fn = NULL; + config.resolve_path_fn = NULL; + + config.use_ansi_color = false; + config.user_data = NULL; + + return config; +} + +PkCompileOptions pkNewCompilerOptions() { + PkCompileOptions options; + options.debug = false; + options.repl_mode = false; + return options; +} + +PKVM* pkNewVM(PkConfiguration* config) { + + PkConfiguration default_config = pkNewConfiguration(); + + if (config == NULL) config = &default_config; + + PKVM* vm = (PKVM*)config->realloc_fn(NULL, sizeof(PKVM), config->user_data); + memset(vm, 0, sizeof(PKVM)); + + vm->config = *config; + vm->working_set_count = 0; + vm->working_set_capacity = MIN_CAPACITY; + vm->working_set = (Object**)vm->config.realloc_fn( + NULL, sizeof(Object*) * vm->working_set_capacity, NULL); + vm->next_gc = INITIAL_GC_SIZE; + vm->min_heap_size = MIN_HEAP_SIZE; + vm->heap_fill_percent = HEAP_FILL_PERCENT; + + vm->modules = newMap(vm); + vm->builtins_count = 0; + + // This is necessary to prevent garbage collection skip the entry in this + // array while we're building it. + for (int i = 0; i < PK_INSTANCE; i++) { + vm->builtin_classes[i] = NULL; + } + + initializeCore(vm); + return vm; +} + +void pkFreeVM(PKVM* vm) { + + Object* obj = vm->first; + while (obj != NULL) { + Object* next = obj->next; + freeObject(vm, obj); + obj = next; + } + + vm->working_set = (Object**)vm->config.realloc_fn( + vm->working_set, 0, vm->config.user_data); + + // Tell the host application that it forget to release all of it's handles + // before freeing the VM. + ASSERT(vm->handles == NULL, "Not all handles were released."); + + DEALLOCATE(vm, vm); +} + +void* pkGetUserData(const PKVM* vm) { + return vm->config.user_data; +} + +void pkSetUserData(PKVM* vm, void* user_data) { + vm->config.user_data = user_data; +} + +PkHandle* pkNewModule(PKVM* vm, const char* name) { + CHECK_ARG_NULL(name); + Module* module = newModuleInternal(vm, name); + return vmNewHandle(vm, VAR_OBJ(module)); +} + +void pkRegisterModule(PKVM* vm, PkHandle* module) { + CHECK_HANDLE_TYPE(module, OBJ_MODULE); + + Module* module_ = (Module*)AS_OBJ(module->value); + vmRegisterModule(vm, module_, module_->name); +} + +void pkModuleAddFunction(PKVM* vm, PkHandle* module, const char* name, + pkNativeFn fptr, int arity) { + CHECK_HANDLE_TYPE(module, OBJ_MODULE); + CHECK_ARG_NULL(fptr); + + moduleAddFunctionInternal(vm, (Module*)AS_OBJ(module->value), + name, fptr, arity, + NULL /*TODO: Public API for function docstring.*/); +} + +PkHandle* pkModuleGetMainFunction(PKVM* vm, PkHandle* module) { + CHECK_HANDLE_TYPE(module, OBJ_MODULE); + + Module* _module = (Module*)AS_OBJ(module->value); + + int main_index = moduleGetGlobalIndex(_module, IMPLICIT_MAIN_NAME, + (uint32_t)strlen(IMPLICIT_MAIN_NAME)); + if (main_index == -1) return NULL; + ASSERT_INDEX(main_index, (int)_module->globals.count); + Var main_fn = _module->globals.data[main_index]; + ASSERT(IS_OBJ_TYPE(main_fn, OBJ_CLOSURE), OOPS); + return vmNewHandle(vm, main_fn); +} + +PkHandle* pkNewClass(PKVM* vm, const char* name, + PkHandle* base_class, PkHandle* module, + pkNewInstanceFn new_fn, + pkDeleteInstanceFn delete_fn) { + CHECK_ARG_NULL(module); + CHECK_ARG_NULL(name); + CHECK_HANDLE_TYPE(module, OBJ_MODULE); + + Class* super = vm->builtin_classes[PK_OBJECT]; + if (base_class != NULL) { + CHECK_HANDLE_TYPE(base_class, OBJ_CLASS); + super = (Class*)AS_OBJ(base_class->value); + } + + Class* class_ = newClass(vm, name, (int)strlen(name), + super, (Module*)AS_OBJ(module->value), + NULL, NULL); + class_->new_fn = new_fn; + class_->delete_fn = delete_fn; + + return vmNewHandle(vm, VAR_OBJ(class_)); +} + +void pkClassAddMethod(PKVM* vm, PkHandle* cls, + const char* name, + pkNativeFn fptr, int arity) { + CHECK_ARG_NULL(cls); + CHECK_ARG_NULL(fptr); + CHECK_HANDLE_TYPE(cls, OBJ_CLASS); + + Class* class_ = (Class*)AS_OBJ(cls->value); + + Function* fn = newFunction(vm, name, (int)strlen(name), + class_->owner, true, NULL, NULL); + + // No need to push the function to temp references of the VM + // since it's written to the constant pool of the module and the module + // won't be garbage collected (class handle has reference to the module). + + Closure* method = newClosure(vm, fn); + + // FIXME: name "_init" is literal everywhere. + if (strcmp(name, "_init") == 0) { + class_->ctor = method; + + } else { + vmPushTempRef(vm, &method->_super); // method. + pkClosureBufferWrite(&class_->methods, vm, method); + vmPopTempRef(vm); // method. + } +} + +void pkReleaseHandle(PKVM* vm, PkHandle* handle) { + ASSERT(handle != NULL, "Given handle was NULL."); + + // If the handle is the head of the vm's handle chain set it to the next one. + if (handle == vm->handles) { + vm->handles = handle->next; + } + + // Remove the handle from the chain by connecting the both ends together. + if (handle->next) handle->next->prev = handle->prev; + if (handle->prev) handle->prev->next = handle->next; + + // Free the handle. + DEALLOCATE(vm, handle); +} + +PkResult pkCompileModule(PKVM* vm, PkHandle* module_handle, PkStringPtr source, + const PkCompileOptions* options) { + CHECK_ARG_NULL(source.string); + CHECK_HANDLE_TYPE(module_handle, OBJ_MODULE); + + Module* module = (Module*)AS_OBJ(module_handle->value); + + PkResult result = compile(vm, module, source.string, options); + if (source.on_done) source.on_done(vm, source); + return result; +} + +// This function is responsible to call on_done function if it's done with the +// provided string pointers. +PkResult pkInterpretSource(PKVM* vm, PkStringPtr source, PkStringPtr path, + const PkCompileOptions* options) { + + String* path_ = newString(vm, path.string); + if (path.on_done) path.on_done(vm, path); + vmPushTempRef(vm, &path_->_super); // path_ + + // FIXME: + // Should I clean the module if it already exists before compiling it? + + // Load a new module to the vm's modules cache. + Module* module = vmGetModule(vm, path_); + if (module == NULL) { + module = newModule(vm); + module->path = path_; + vmPushTempRef(vm, &module->_super); // module. + vmRegisterModule(vm, module, path_); + vmPopTempRef(vm); // module. + } + vmPopTempRef(vm); // path_ + + // Compile the source. + PkResult result = compile(vm, module, source.string, options); + if (source.on_done) source.on_done(vm, source); + if (result != PK_RESULT_SUCCESS) return result; + + // Set module initialized to true before the execution ends to prevent cyclic + // inclusion cause a crash. + module->initialized = true; + + return vmRunFiber(vm, newFiber(vm, module->body)); +} + +PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn) { + CHECK_HANDLE_TYPE(fn, OBJ_CLOSURE); + + Fiber* fiber = newFiber(vm, (Closure*)AS_OBJ(fn->value)); + vmPushTempRef(vm, &fiber->_super); // fiber + PkHandle* handle = vmNewHandle(vm, VAR_OBJ(fiber)); + vmPopTempRef(vm); // fiber + return handle; +} + +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 vmRunFiber(vm, _fiber); +} + +// TODO: Get resume argument. +PkResult pkResumeFiber(PKVM* vm, 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); + + if (!vmSwitchFiber(vm, _fiber, NULL /* TODO: argument */)) { + return PK_RESULT_RUNTIME_ERROR; + } + + return vmRunFiber(vm, _fiber); +} + +/*****************************************************************************/ +/* RUNTIME */ +/*****************************************************************************/ + +#define CHECK_RUNTIME() \ + do { \ + ASSERT(vm->fiber != NULL, \ + "This function can only be called at runtime."); \ + } while (false) + +// A convenient macro to get the nth (1 based) argument of the current +// function. +#define ARG(n) (vm->fiber->ret[n]) + +// Nth slot is same as Nth argument, It'll also work if we allocate more +// slots but the caller should ensure the index. +#define SLOT(n) ARG(n) + +// This will work. +#define SET_SLOT(n, val) SLOT(n) = (val); + +// Evaluates to the current function's argument count. +#define ARGC ((int)(vm->fiber->sp - vm->fiber->ret) - 1) + +void pkSetRuntimeError(PKVM* vm, const char* message) { + CHECK_RUNTIME(); + VM_SET_ERROR(vm, newString(vm, message)); +} + +void* pkGetSelf(const PKVM* vm) { + CHECK_RUNTIME(); + ASSERT(IS_OBJ_TYPE(vm->fiber->self, OBJ_INST), OOPS); + Instance* inst = (Instance*)AS_OBJ(vm->fiber->self); + ASSERT(inst->native != NULL, OOPS); + return inst->native; +} + +int pkGetArgc(const PKVM* vm) { + CHECK_RUNTIME(); + return ARGC; +} + +bool pkCheckArgcRange(PKVM* vm, int argc, int min, int max) { + ASSERT(min <= max, "invalid argc range (min > max)."); + + if (argc < min) { + char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", min); + VM_SET_ERROR(vm, stringFormat(vm, "Expected at least %s argument(s).", + buff)); + return false; + + } else if (argc > max) { + char buff[STR_INT_BUFF_SIZE]; sprintf(buff, "%d", max); + VM_SET_ERROR(vm, stringFormat(vm, "Expected at most %s argument(s).", + buff)); + return false; + } + + return true; +} + +#define VALIDATE_SLOT_INDEX(index) \ + do { \ + ASSERT(index >= 0, "Slot index was negative."); \ + ASSERT(index < pkGetSlotsCount(vm), \ + "Slot index is too large. Did you forget to call pkReserveSlots()?."); \ + } while (false) + +// ARGC won't be the real arity if any slots allocated before calling argument +// validation calling this first is the callers responsibility. +#define VALIDATE_ARGC(arg) \ + ASSERT(arg > 0 && arg <= ARGC, "Invalid argument index.") + +// Set error for incompatible type provided as an argument. (TODO: got type). +#define ERR_INVALID_ARG_TYPE(ty_name) \ + do { \ + char buff[STR_INT_BUFF_SIZE]; \ + sprintf(buff, "%d", arg); \ + VM_SET_ERROR(vm, stringFormat(vm, "Expected a '$' at argument $.", \ + ty_name, buff)); \ + } while (false) + +// FIXME: If the user needs just the boolean value of the object, they should +// use pkGetSlotBool(). +PK_PUBLIC bool pkValidateSlotBool(PKVM* vm, int arg, bool* value) { + CHECK_RUNTIME(); + VALIDATE_ARGC(arg); + + Var val = ARG(arg); + if (!IS_BOOL(val)) { + ERR_INVALID_ARG_TYPE("Boolean"); + return false; + } + + if (value) *value = AS_BOOL(val); + return true; +} + +PK_PUBLIC bool pkValidateSlotNumber(PKVM* vm, int arg, double* value) { + CHECK_RUNTIME(); + VALIDATE_ARGC(arg); + + Var val = ARG(arg); + if (!IS_NUM(val)) { + ERR_INVALID_ARG_TYPE("Number"); + return false; + } + + if (value) *value = AS_NUM(val); + return true; +} + +PK_PUBLIC bool pkValidateSlotString(PKVM* vm, int arg, const char** value, + uint32_t* length) { + CHECK_RUNTIME(); + VALIDATE_ARGC(arg); + + Var val = ARG(arg); + if (!IS_OBJ_TYPE(val, OBJ_STRING)) { + ERR_INVALID_ARG_TYPE("String"); + return false; + } + String* str = (String*)AS_OBJ(val); + if (value) *value = str->data; + if (length) *length = str->length; + return true; +} + +void pkReserveSlots(PKVM* vm, int count) { + CHECK_RUNTIME(); + + int needed = (int)(vm->fiber->ret - vm->fiber->stack) + count; + vmEnsureStackSize(vm, needed); +} + +int pkGetSlotsCount(PKVM* vm) { + CHECK_RUNTIME(); + + return (int)(vm->fiber->sp - vm->fiber->ret); +} + +PkVarType pkGetSlotType(PKVM* vm, int index) { + CHECK_RUNTIME(); + VALIDATE_SLOT_INDEX(index); + + return getVarType(SLOT(index)); +} + +bool pkGetSlotBool(PKVM* vm, int index) { + CHECK_RUNTIME(); + VALIDATE_SLOT_INDEX(index); + + Var value = SLOT(index); + return toBool(value); +} + +double pkGetSlotNumber(PKVM* vm, int index) { + CHECK_RUNTIME(); + VALIDATE_SLOT_INDEX(index); + + Var value = SLOT(index); + ASSERT(IS_NUM(value), "Slot value wasn't a Number."); + return AS_NUM(value); +} + +const char* pkGetSlotString(PKVM* vm, int index, uint32_t* length) { + CHECK_RUNTIME(); + VALIDATE_SLOT_INDEX(index); + + Var value = SLOT(index); + ASSERT(IS_OBJ_TYPE(value, OBJ_STRING), "Slot value wasn't a String."); + if (length != NULL) *length = ((String*)AS_OBJ(value))->length; + return ((String*)AS_OBJ(value))->data; +} + +PkHandle* pkGetSlotHandle(PKVM* vm, int index) { + CHECK_RUNTIME(); + VALIDATE_SLOT_INDEX(index); + + return vmNewHandle(vm, SLOT(index)); +} + +void pkSetSlotNull(PKVM* vm, int index) { + CHECK_RUNTIME(); + VALIDATE_SLOT_INDEX(index); + + SET_SLOT(index, VAR_NULL); +} + +void pkSetSlotBool(PKVM* vm, int index, bool value) { + CHECK_RUNTIME(); + VALIDATE_SLOT_INDEX(index); + + SET_SLOT(index, VAR_BOOL(value)); +} + +void pkSetSlotNumber(PKVM* vm, int index, double value) { + CHECK_RUNTIME(); + VALIDATE_SLOT_INDEX(index); + + SET_SLOT(index, VAR_NUM(value)); +} + +void pkSetSlotString(PKVM* vm, int index, const char* value) { + CHECK_RUNTIME(); + VALIDATE_SLOT_INDEX(index); + + SET_SLOT(index, VAR_OBJ(newString(vm, value))); +} + +PK_PUBLIC void pkSetSlotStringLength(PKVM* vm, int index, + const char* value, uint32_t length) { + CHECK_RUNTIME(); + VALIDATE_SLOT_INDEX(index); + + SET_SLOT(index, VAR_OBJ(newStringLength(vm, value, length))); +} + +void PkSetSlotHandle(PKVM* vm, int index, PkHandle* handle) { + CHECK_RUNTIME(); + VALIDATE_SLOT_INDEX(index); + SET_SLOT(index, handle->value); +} + +#undef CHECK_RUNTIME +#undef VALIDATE_ARGC +#undef ERR_INVALID_ARG_TYPE +#undef ARG +#undef SLOT +#undef SET_SLOT +#undef ARGC +#undef CHECK_NULL +#undef CHECK_TYPE + +/*****************************************************************************/ +/* INTERNAL */ +/*****************************************************************************/ + +// The default allocator that will be used to initialize the vm's configuration +// if the host doesn't provided any allocators for us. +static void* defaultRealloc(void* memory, size_t new_size, void* _) { + if (new_size == 0) { + free(memory); + return NULL; + } + return realloc(memory, new_size); +} diff --git a/src/pk_value.c b/src/pk_value.c index 52c0a4c..e00ca38 100644 --- a/src/pk_value.c +++ b/src/pk_value.c @@ -12,73 +12,6 @@ #include "pk_utils.h" #include "pk_vm.h" -/*****************************************************************************/ -/* VAR PUBLIC API */ -/*****************************************************************************/ - -PkVarType pkGetValueType(const PkVar value) { - __ASSERT(value != NULL, "Given value was NULL."); - const Var value_ = *(const Var*)(value); - - if (IS_NULL(value_)) return PK_NULL; - if (IS_BOOL(value_)) return PK_BOOL; - if (IS_NUM(value_)) return PK_NUMBER; - - ASSERT(IS_OBJ(*(const Var*)(value)), - "Invalid var pointer (Might be a dangling pointer)."); - - const Object* obj = AS_OBJ(value_); - return getObjPkVarType(obj->type); - -} - -PkHandle* pkNewString(PKVM* vm, const char* value) { - String* str = newString(vm, value); - vmPushTempRef(vm, &str->_super); // str - PkHandle* handle = vmNewHandle(vm, VAR_OBJ(str)); - vmPopTempRef(vm); // str - return handle; -} - -PkHandle* pkNewStringLength(PKVM* vm, const char* value, size_t len) { - String* str = newStringLength(vm, value, (uint32_t)len); - vmPushTempRef(vm, &str->_super); // str - PkHandle* handle = vmNewHandle(vm, VAR_OBJ(str)); - vmPopTempRef(vm); // str - return handle; -} - -PkHandle* pkNewList(PKVM* vm) { - List* list = newList(vm, MIN_CAPACITY); - vmPushTempRef(vm, &list->_super); // list - PkHandle* handle = vmNewHandle(vm, VAR_OBJ(list)); - vmPopTempRef(vm); // list - return handle; -} - -PkHandle* pkNewMap(PKVM* vm) { - Map* map = newMap(vm); - vmPushTempRef(vm, &map->_super); // map - PkHandle* handle = vmNewHandle(vm, VAR_OBJ(map)); - vmPopTempRef(vm); // map - return handle; -} - -PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn) { - __ASSERT(IS_OBJ_TYPE(fn->value, OBJ_CLOSURE), - "Handle should be of type function."); - - Fiber* fiber = newFiber(vm, (Closure*)AS_OBJ(fn->value)); - vmPushTempRef(vm, &fiber->_super); // fiber - PkHandle* handle = vmNewHandle(vm, VAR_OBJ(fiber)); - vmPopTempRef(vm); // fiber - return handle; -} - -/*****************************************************************************/ -/* VAR INTERNALS */ -/*****************************************************************************/ - // The maximum percentage of the map entries that can be filled before the map // is grown. A lower percentage reduce collision which makes looks up faster // but take more memory. @@ -270,6 +203,8 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) { markObject(vm, &fiber->caller->_super); markObject(vm, &fiber->error->_super); + markValue(vm, fiber->self); + } break; case OBJ_CLASS: diff --git a/src/pk_value.h b/src/pk_value.h index efd4f3f..3cc4c31 100644 --- a/src/pk_value.h +++ b/src/pk_value.h @@ -455,6 +455,11 @@ struct Fiber { // The stack pointer (%rsp) pointing to the stack top. Var* sp; + // Heap allocated array of call frames will grow as needed. + CallFrame* frames; + int frame_capacity; //< Capacity of the frames array. + int frame_count; //< Number of frame entry in frames. + // All the open upvalues will form a linked list in the fiber and the // upvalues are sorted in the same order their locals in the stack. The // bellow pointer is the head of those upvalues near the stack top. @@ -473,11 +478,6 @@ struct Fiber { // and reset to VAR_UNDEFINED. Var self; - // Heap allocated array of call frames will grow as needed. - CallFrame* frames; - int frame_capacity; //< Capacity of the frames array. - int frame_count; //< Number of frame entry in frames. - // Caller of this fiber if it has one, NULL otherwise. Fiber* caller; diff --git a/src/pk_vm.c b/src/pk_vm.c index 0471a89..177d821 100644 --- a/src/pk_vm.c +++ b/src/pk_vm.c @@ -7,205 +7,9 @@ #include "pk_vm.h" #include -#include "pk_core.h" #include "pk_utils.h" #include "pk_debug.h" -/*****************************************************************************/ -/* VM PUBLIC API */ -/*****************************************************************************/ - -// The default allocator that will be used to initialize the vm's configuration -// if the host doesn't provided any allocators for us. -static void* defaultRealloc(void* memory, size_t new_size, void* user_data); - -// Runs the [fiber] if it's at yielded state, this will resume the execution -// till the next yield or return statement, and return result. -static PkResult runFiber(PKVM* vm, Fiber* fiber); - -PkConfiguration pkNewConfiguration(void) { - PkConfiguration config; - config.realloc_fn = defaultRealloc; - - config.stdout_write = NULL; - config.stderr_write = NULL; - config.stdin_read = NULL; - - config.load_script_fn = NULL; - config.resolve_path_fn = NULL; - - config.use_ansi_color = false; - config.user_data = NULL; - - return config; -} - -PkCompileOptions pkNewCompilerOptions(void) { - PkCompileOptions options; - options.debug = false; - options.repl_mode = false; - return options; -} - -PKVM* pkNewVM(PkConfiguration* config) { - - PkConfiguration default_config = pkNewConfiguration(); - - if (config == NULL) config = &default_config; - - PKVM* vm = (PKVM*)config->realloc_fn(NULL, sizeof(PKVM), config->user_data); - memset(vm, 0, sizeof(PKVM)); - - vm->config = *config; - vm->working_set_count = 0; - vm->working_set_capacity = MIN_CAPACITY; - vm->working_set = (Object**)vm->config.realloc_fn( - NULL, sizeof(Object*) * vm->working_set_capacity, NULL); - vm->next_gc = INITIAL_GC_SIZE; - vm->min_heap_size = MIN_HEAP_SIZE; - vm->heap_fill_percent = HEAP_FILL_PERCENT; - - vm->modules = newMap(vm); - vm->builtins_count = 0; - - // This is necessary to prevent garbage collection skip the entry in this - // array while we're building it. - for (int i = 0; i < PK_INSTANCE; i++) { - vm->builtin_classes[i] = NULL; - } - - initializeCore(vm); - return vm; -} - -void pkFreeVM(PKVM* vm) { - - Object* obj = vm->first; - while (obj != NULL) { - Object* next = obj->next; - freeObject(vm, obj); - obj = next; - } - - vm->working_set = (Object**)vm->config.realloc_fn( - vm->working_set, 0, vm->config.user_data); - - // Tell the host application that it forget to release all of it's handles - // before freeing the VM. - __ASSERT(vm->handles == NULL, "Not all handles were released."); - - DEALLOCATE(vm, vm); -} - -void* pkGetUserData(const PKVM* vm) { - return vm->config.user_data; -} - -void pkSetUserData(PKVM* vm, void* user_data) { - vm->config.user_data = user_data; -} - -PkHandle* pkNewHandle(PKVM* vm, PkVar value) { - return vmNewHandle(vm, *((Var*)value)); -} - -PkVar pkGetHandleValue(const PkHandle* handle) { - return (PkVar)&handle->value; -} - -void pkReleaseHandle(PKVM* vm, PkHandle* handle) { - __ASSERT(handle != NULL, "Given handle was NULL."); - - // If the handle is the head of the vm's handle chain set it to the next one. - if (handle == vm->handles) { - vm->handles = handle->next; - } - - // Remove the handle from the chain by connecting the both ends together. - if (handle->next) handle->next->prev = handle->prev; - if (handle->prev) handle->prev->next = handle->next; - - // Free the handle. - DEALLOCATE(vm, handle); -} - -// This function is responsible to call on_done function if it's done with the -// provided string pointers. -PkResult pkInterpretSource(PKVM* vm, PkStringPtr source, PkStringPtr path, - const PkCompileOptions* options) { - - String* path_ = newString(vm, path.string); - if (path.on_done) path.on_done(vm, path); - vmPushTempRef(vm, &path_->_super); // path_ - - // FIXME: - // Should I clean the module if it already exists before compiling it? - - // Load a new module to the vm's modules cache. - Module* module = vmGetModule(vm, path_); - if (module == NULL) { - module = newModule(vm); - module->path = path_; - vmPushTempRef(vm, &module->_super); // module. - vmRegisterModule(vm, module, path_); - vmPopTempRef(vm); // module. - } - vmPopTempRef(vm); // path_ - - // Compile the source. - PkResult result = compile(vm, module, source.string, options); - if (source.on_done) source.on_done(vm, source); - if (result != PK_RESULT_SUCCESS) return result; - - // Set module initialized to true before the execution ends to prevent cyclic - // inclusion cause a crash. - module->initialized = true; - - return runFiber(vm, newFiber(vm, module->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) { - __ASSERT(vm->fiber != NULL, "This function can only be called at runtime."); - VM_SET_ERROR(vm, newString(vm, message)); -} - -/*****************************************************************************/ -/* SHARED FUNCTIONS */ -/*****************************************************************************/ - PkHandle* vmNewHandle(PKVM* vm, Var value) { PkHandle* handle = (PkHandle*)ALLOCATE(vm, PkHandle); handle->value = value; @@ -449,16 +253,6 @@ void vmYieldFiber(PKVM* vm, Var* value) { /* VM INTERNALS */ /*****************************************************************************/ -// The default allocator that will be used to initialize the vm's configuration -// if the host doesn't provided any allocators for us. -static void* defaultRealloc(void* memory, size_t new_size, void* user_data) { - if (new_size == 0) { - free(memory); - return NULL; - } - return realloc(memory, new_size); -} - // FIXME: // We're assuming that the module should be available at the VM's modules cache // which is added by the compilation pahse, but we cannot rely on the @@ -486,9 +280,11 @@ static inline Var importModule(PKVM* vm, String* key) { return VAR_NULL; } -static inline void growStack(PKVM* vm, int size) { +void vmEnsureStackSize(PKVM* vm, int size) { + Fiber* fiber = vm->fiber; - ASSERT(fiber->stack_size <= size, OOPS); + if (fiber->stack_size > size) return; + int new_size = utilPowerOf2Ceil(size); Var* old_rbp = fiber->stack; //< Old stack base pointer. @@ -546,7 +342,7 @@ static inline void pushCallFrame(PKVM* vm, const Closure* closure, Var* rbp) { // Grow the stack if needed. int needed = (closure->fn->fn->stack_size + (int)(vm->fiber->sp - vm->fiber->stack)); - if (vm->fiber->stack_size <= needed) growStack(vm, needed); + vmEnsureStackSize(vm, needed); CallFrame* frame = vm->fiber->frames + vm->fiber->frame_count++; frame->rbp = rbp; @@ -589,7 +385,7 @@ static inline void reuseCallFrame(PKVM* vm, const Closure* closure) { // Grow the stack if needed (least probably). int needed = (closure->fn->fn->stack_size + (int)(vm->fiber->sp - vm->fiber->stack)); - if (vm->fiber->stack_size <= needed) growStack(vm, needed); + vmEnsureStackSize(vm, needed); } // Capture the [local] into an upvalue and return it. If the upvalue already @@ -691,7 +487,7 @@ static void reportError(PKVM* vm) { * RUNTIME * *****************************************************************************/ -static PkResult runFiber(PKVM* vm, Fiber* fiber_) { +PkResult vmRunFiber(PKVM* vm, Fiber* fiber_) { // Set the fiber as the vm's current fiber (another root object) to prevent // it from garbage collection and get the reference from native functions. diff --git a/src/pk_vm.h b/src/pk_vm.h index 8b5bc4f..4ac1330 100644 --- a/src/pk_vm.h +++ b/src/pk_vm.h @@ -8,8 +8,7 @@ #define PK_VM_H #include "pk_compiler.h" -#include "pk_internal.h" -#include "pk_value.h" +#include "pk_core.h" // The maximum number of temporary object reference to protect them from being // garbage collected. @@ -140,6 +139,10 @@ void* vmRealloc(PKVM* vm, void* memory, size_t old_size, size_t new_size); // Create and return a new handle for the [value]. PkHandle* vmNewHandle(PKVM* vm, Var value); +// If the stack size is less than [size], the stack will grow to keep more +// values on it. +void vmEnsureStackSize(PKVM* vm, int size); + // Trigger garbage collection. This is an implementation of mark and sweep // garbage collection (https://en.wikipedia.org/wiki/Tracing_garbage_collection). // @@ -214,4 +217,8 @@ bool vmSwitchFiber(PKVM* vm, Fiber* fiber, Var* value); // yield value. void vmYieldFiber(PKVM* vm, Var* value); +// Runs the [fiber] if it's at yielded state, this will resume the execution +// till the next yield or return statement, and return result. +PkResult vmRunFiber(PKVM* vm, Fiber* fiber); + #endif // PK_VM_H diff --git a/tests/native/example1.c b/tests/native/example1.c index 43c7cb5..e621aa7 100644 --- a/tests/native/example1.c +++ b/tests/native/example1.c @@ -24,12 +24,12 @@ static void cFunction(PKVM* vm) { // Get the parameter from pocket VM. double a; - if (!pkGetArgNumber(vm, 1, &a)) return; + if (!pkValidateSlotNumber(vm, 1, &a)) return; printf("[C] a = %f\n", a); // Return value to the pocket VM. - pkReturnNumber(vm, 3.14); + pkSetSlotNumber(vm, 0, 3.14); } /*****************************************************************************/