diff --git a/cli/all.c b/cli/all.c index 22e0d50..1b8cae7 100644 --- a/cli/all.c +++ b/cli/all.c @@ -13,9 +13,7 @@ /* STD MODULE SOURCES */ /*****************************************************************************/ -#include "modules/modules.c" - -#include "modules/std_file.c" +#include "modules/std_io.c" #include "modules/std_path.c" /*****************************************************************************/ diff --git a/cli/main.c b/cli/main.c index 1c69f74..3c317eb 100644 --- a/cli/main.c +++ b/cli/main.c @@ -129,11 +129,6 @@ static PKVM* intializePocketVM() { config.write_fn = writeFunction; config.read_fn = readFunction; - config.inst_free_fn = freeObj; - config.inst_name_fn = getObjName; - config.inst_get_attrib_fn = objGetAttrib; - config.inst_set_attrib_fn = objSetAttrib; - config.load_script_fn = loadScript; config.resolve_path_fn = resolvePath; @@ -193,7 +188,8 @@ int main(int argc, const char** argv) { user_data.repl_mode = false; pkSetUserData(vm, &user_data); - registerModules(vm); + registerModulePath(vm); + //REGISTER_ALL_MODULES(vm); PkCompileOptions options = pkNewCompilerOptions(); options.debug = debug; diff --git a/cli/modules/modules.c b/cli/modules/modules.c deleted file mode 100644 index 9ff605b..0000000 --- a/cli/modules/modules.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2020-2022 Thakee Nathees - * Copyright (c) 2021-2022 Pocketlang Contributors - * Distributed Under The MIT License - */ - -#include "modules.h" - -// Note: Everything here is for testing the native API, and will have to -// refactor everything. - -// Allocate a new module object of type [Ty]. -#define NEW_OBJ(Ty) (Ty*)malloc(sizeof(Ty)) - -// Dellocate module object, allocated by NEW_OBJ(). Called by the freeObj -// callback. -#define FREE_OBJ(ptr) free(ptr) - -/*****************************************************************************/ -/* MODULE FUNCTIONS DECLARATION */ -/*****************************************************************************/ - -void fileGetAttrib(PKVM* vm, File* file, const char* attrib); -bool fileSetAttrib(PKVM* vm, File* file, const char* attrib); -void fileClean(PKVM* vm, File* file); - -void registerModuleFile(PKVM* vm); -void registerModulePath(PKVM* vm); - -/*****************************************************************************/ -/* MODULE PUBLIC FUNCTIONS */ -/*****************************************************************************/ - -void initObj(Obj* obj, ObjType type) { - obj->type = type; -} - -void objGetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib) { - Obj* obj = (Obj*)instance; - ASSERT(obj->type == (ObjType)id, OOPS); - - switch (obj->type) { - case OBJ_FILE: - fileGetAttrib(vm, (File*)obj, attrib.string); - return; - } - STATIC_ASSERT(_OBJ_MAX_ == 2); -} - -bool objSetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib) { - Obj* obj = (Obj*)instance; - ASSERT(obj->type == (ObjType)id, OOPS); - - switch (obj->type) { - case OBJ_FILE: - return fileSetAttrib(vm, (File*)obj, attrib.string); - } - STATIC_ASSERT(_OBJ_MAX_ == 2); - - return false; -} - -void freeObj(PKVM* vm, void* instance, uint32_t id) { - Obj* obj = (Obj*)instance; - ASSERT(obj->type == (ObjType)id, OOPS); - - switch (obj->type) { - case OBJ_FILE: - fileClean(vm, (File*)obj); - } - STATIC_ASSERT(_OBJ_MAX_ == 2); - - FREE_OBJ(obj); -} - -const char* getObjName(uint32_t id) { - switch ((ObjType)id) { - case OBJ_FILE: return "File"; - } - STATIC_ASSERT(_OBJ_MAX_ == 2); - return NULL; -} - -/*****************************************************************************/ -/* REGISTER MODULES */ -/*****************************************************************************/ - -void registerModules(PKVM* vm) { - registerModuleFile(vm); - registerModulePath(vm); -} diff --git a/cli/modules/modules.h b/cli/modules/modules.h index 842feb2..d81854b 100644 --- a/cli/modules/modules.h +++ b/cli/modules/modules.h @@ -14,73 +14,26 @@ #include #include -/*****************************************************************************/ -/* MODULE OBJECTS */ -/*****************************************************************************/ - -// Str | If already exists | If does not exist | -// -----+-------------------+-------------------| -// 'r' | read from start | failure to open | -// 'w' | destroy contents | create new | -// 'a' | write to end | create new | -// 'r+' | read from start | error | -// 'w+' | destroy contents | create new | -// 'a+' | write to end | create new | -typedef enum { - FMODE_READ = (1 << 0), - FMODE_WRITE = (1 << 1), - FMODE_APPEND = (1 << 2), - _FMODE_EXT = (1 << 3), - FMODE_READ_EXT = (_FMODE_EXT | FMODE_READ), - FMODE_WRITE_EXT = (_FMODE_EXT | FMODE_WRITE), - FMODE_APPEND_EXT = (_FMODE_EXT | FMODE_APPEND), -} FileAccessMode; - -typedef enum { - OBJ_FILE = 1, - - _OBJ_MAX_ -} ObjType; - -typedef struct { - ObjType type; -} Obj; - -typedef struct { - Obj _super; - FILE* fp; // C file poinnter. - FileAccessMode mode; // Access mode of the file. - bool closed; // True if the file isn't closed yet. -} File; - -/*****************************************************************************/ -/* MODULE PUBLIC FUNCTIONS */ -/*****************************************************************************/ - -// Initialize the native module object with it's default values. -void initObj(Obj* obj, ObjType type); - -// A function callback called by pocket VM to get attribute of a native -// instance. The value of the attributes will be returned with pkReturn...() -// functions and if the attribute doesn't exists on the instance we're -// shouldn't return anything, PKVM will know it and set error (or use some -// common attributes like "as_string", "as_repr", etc). -void objGetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib); - -// A function callback called by pocket VM to set attribute of a native -// instance. -bool objSetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib); - -// The free callback of the object, that'll called by pocketlang when a -// pocketlang native instance garbage collected. -void freeObj(PKVM* vm, void* instance, uint32_t id); - -// The native instance get_name callback used to get the name of a native -// instance from pocketlang. Here the id we're using is the ObjType enum. -const char* getObjName(uint32_t id); +void registerModuleIO(PKVM* vm); +void registerModulePath(PKVM* vm); // Registers all the cli modules. -void registerModules(PKVM* vm); +#define REGISTER_ALL_MODULES(vm) \ + do { \ + registerModuleIO(vm); \ + registerModulePath(vm); \ + } while (false) + +/*****************************************************************************/ +/* MODULES INTERNAL */ +/*****************************************************************************/ + +// Allocate a new module object of type [Ty]. +#define NEW_OBJ(Ty) (Ty*)malloc(sizeof(Ty)) + +// Dellocate module object, allocated by NEW_OBJ(). Called by the freeObj +// callback. +#define FREE_OBJ(ptr) free(ptr) /*****************************************************************************/ /* SHARED FUNCTIONS */ diff --git a/cli/modules/std_file.c b/cli/modules/std_io.c similarity index 59% rename from cli/modules/std_file.c rename to cli/modules/std_io.c index 7401177..aa944a9 100644 --- a/cli/modules/std_file.c +++ b/cli/modules/std_io.c @@ -6,6 +6,31 @@ #include "modules.h" + // Str | If already exists | If does not exist | + // -----+-------------------+-------------------| + // 'r' | read from start | failure to open | + // 'w' | destroy contents | create new | + // 'a' | write to end | create new | + // 'r+' | read from start | error | + // 'w+' | destroy contents | create new | + // 'a+' | write to end | create new | +typedef enum { + FMODE_NONE = 0, + FMODE_READ = (1 << 0), + FMODE_WRITE = (1 << 1), + FMODE_APPEND = (1 << 2), + _FMODE_EXT = (1 << 3), + FMODE_READ_EXT = (_FMODE_EXT | FMODE_READ), + FMODE_WRITE_EXT = (_FMODE_EXT | FMODE_WRITE), + FMODE_APPEND_EXT = (_FMODE_EXT | FMODE_APPEND), +} FileAccessMode; + +typedef struct { + FILE* fp; // C file poinnter. + FileAccessMode mode; // Access mode of the file. + bool closed; // True if the file isn't closed yet. +} File; + /*****************************************************************************/ /* FILE OBJECT OPERATORS */ /*****************************************************************************/ @@ -62,25 +87,29 @@ static void _fileOpen(PKVM* vm) { } while (false); } + // This TODO is just a blockade from running the bellow code, complete the + // native interface and test before removing it. + TODO; + FILE* fp = fopen(path, mode_str); if (fp != NULL) { - File* file = NEW_OBJ(File); - initObj(&file->_super, OBJ_FILE); - file->fp = fp; - file->mode = mode; - file->closed = false; - - pkReturnInstNative(vm, (void*)file, OBJ_FILE); + File* self = (File*)pkGetSelf(vm); + self->fp = fp; + self->mode = mode; + self->closed = false; } else { - pkReturnNull(vm); + pkSetRuntimeError(vm, "Error opening the file."); } } static void _fileRead(PKVM* vm) { - File* file; - if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return; + // This TODO is just a blockade from running the bellow code, complete the + // native interface and test before removing it. + TODO; + + File* file = (File*)pkGetSelf(vm); if (file->closed) { pkSetRuntimeError(vm, "Cannot read from a closed file."); @@ -99,10 +128,13 @@ static void _fileRead(PKVM* vm) { } static void _fileWrite(PKVM* vm) { - File* file; + // This TODO is just a blockade from running the bellow code, complete the + // native interface and test before removing it. + TODO; + + File* file = (File*)pkGetSelf(vm); const char* text; uint32_t length; - if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return; - if (!pkGetArgString(vm, 2, &text, &length)) return; + if (!pkGetArgString(vm, 1, &text, &length)) return; if (file->closed) { pkSetRuntimeError(vm, "Cannot write to a closed file."); @@ -118,8 +150,11 @@ static void _fileWrite(PKVM* vm) { } static void _fileClose(PKVM* vm) { - File* file; - if (!pkGetArgInst(vm, 1, OBJ_FILE, (void**)&file)) return; + // This TODO is just a blockade from running the bellow code, complete the + // native interface and test before removing it. + TODO; + + File* file = (File*)pkGetSelf(vm); if (file->closed) { pkSetRuntimeError(vm, "File already closed."); @@ -133,14 +168,16 @@ static void _fileClose(PKVM* vm) { file->closed = true; } -void registerModuleFile(PKVM* vm) { - PkHandle* file = pkNewModule(vm, "File"); +void registerModuleIO(PKVM* vm) { + PkHandle* io = pkNewModule(vm, "io"); - pkModuleAddFunction(vm, file, "open", _fileOpen, -1); - pkModuleAddFunction(vm, file, "read", _fileRead, 1); - pkModuleAddFunction(vm, file, "write", _fileWrite, 2); - pkModuleAddFunction(vm, file, "close", _fileClose, 1); + PkHandle* cls_file = pkNewClass(vm, io, "File"); + pkClassAddMethod(vm, cls_file, "open", _fileOpen, -1); + pkClassAddMethod(vm, cls_file, "read", _fileRead, 0); + pkClassAddMethod(vm, cls_file, "write", _fileWrite, 1); + pkClassAddMethod(vm, cls_file, "close", _fileClose, 0); + pkReleaseHandle(vm, cls_file); - pkRegisterModule(vm, file); - pkReleaseHandle(vm, file); + pkRegisterModule(vm, io); + pkReleaseHandle(vm, io); } diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index da92a03..58b9f46 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.h @@ -55,8 +55,9 @@ extern "C" { #define PK_PUBLIC #endif + /*****************************************************************************/ -/* POCKETLANG TYPES */ +/* POCKETLANG TYPEDEFS & CALLBACKS */ /*****************************************************************************/ // PocketLang Virtual Machine. It'll contain the state of the execution, stack, @@ -74,57 +75,13 @@ typedef struct PkHandle PkHandle; // alive use `pkNewHandle()`. typedef void* PkVar; -// Type enum of the pocketlang's first class types. Note that Object isn't -// instanciable (as of now) but they're considered first calss. -typedef enum { - PK_OBJECT = 0, - - PK_NULL, - PK_BOOL, - PK_NUMBER, - PK_STRING, - PK_LIST, - PK_MAP, - PK_RANGE, - PK_MODULE, - PK_CLOSURE, - PK_FIBER, - PK_CLASS, - PK_INSTANCE, -} PkVarType; - +typedef enum PkVarType PkVarType; +typedef enum PkErrorType PkErrorType; +typedef enum PkResult PkResult; typedef struct PkStringPtr PkStringPtr; typedef struct PkConfiguration PkConfiguration; typedef struct PkCompileOptions PkCompileOptions; -// Type of the error message that pocketlang will provide with the pkErrorFn -// callback. -typedef enum { - PK_ERROR_COMPILE = 0, // Compile time errors. - PK_ERROR_RUNTIME, // Runtime error message. - PK_ERROR_STACKTRACE, // One entry of a runtime error stack. -} PkErrorType; - -// Result that pocketlang will return after a compilation or running a script -// or a function or evaluating an expression. -typedef enum { - PK_RESULT_SUCCESS = 0, // Successfully finished the execution. - - // Unexpected EOF while compiling the source. This is another compile time - // error that will ONLY be returned if we're compiling with the REPL mode set - // in the compile options. We need this specific error to indicate the host - // application to add another line to the last input. If REPL is not enabled, - // this will be PK_RESULT_COMPILE_ERROR. - PK_RESULT_UNEXPECTED_EOF, - - PK_RESULT_COMPILE_ERROR, // Compilation failed. - PK_RESULT_RUNTIME_ERROR, // An error occurred at runtime. -} PkResult; - -/*****************************************************************************/ -/* POCKETLANG FUNCTION POINTERS & CALLBACKS */ -/*****************************************************************************/ - // C function pointer which is callable from pocketLang by native module // functions. typedef void (*pkNativeFn)(PKVM* vm); @@ -155,35 +112,6 @@ typedef void (*pkWriteFn) (PKVM* vm, const char* text); // contain a line ending (\n or \r\n). typedef PkStringPtr (*pkReadFn) (PKVM* vm); -// A function callback, that'll be called when a native instance (wrapper) is -// freed by by the garbage collector, to indicate that pocketlang is done with -// the native instance. -typedef void (*pkInstFreeFn) (PKVM* vm, void* instance, uint32_t id); - -// A function callback to get the type name of the native instance from -// pocketlang, using it's [id]. The returned string won't be copied by -// pocketlang so it's expected to be alived since the instance is alive and -// recomended to return a C literal string. -typedef const char* (*pkInstNameFn) (uint32_t id); - -// A get arribute callback, called by pocket VM when trying to get an attribute -// from a native type. to return the value of the attribute use 'pkReturn...()' -// functions. DON'T set an error to the VM if the attribute not exists. Example -// if the '.as_string' attribute doesn't exists, pocket VM will use a default -// to string value. -typedef void (*pkInstGetAttribFn) (PKVM* vm, void* instance, uint32_t id, - PkStringPtr attrib); - -// Use pkGetArg...(vm, 0, ptr) function to get the value of the attribute -// and use 0 as the argument index, using any other arg index value cause UB. -// -// If the attribute dones't exists DON'T set an error, instead return false. -// Pocket VM will handle it, On success update the native instance and return -// true. And DON'T ever use 'pkReturn...()' in the attribute setter It's is a -// void return function. -typedef bool (*pkInstSetAttribFn) (PKVM* vm, void* instance, uint32_t id, - PkStringPtr attrib); - // A function callback symbol for clean/free the pkStringResult. typedef void (*pkResultDoneFn) (PKVM* vm, PkStringPtr result); @@ -199,6 +127,96 @@ typedef PkStringPtr (*pkResolvePathFn) (PKVM* vm, const char* from, // to indicate if it's failed to load the script. typedef PkStringPtr (*pkLoadScriptFn) (PKVM* vm, const char* path); +/*****************************************************************************/ +/* POCKETLANG TYPES */ +/*****************************************************************************/ + +// Type enum of the pocketlang's first class types. Note that Object isn't +// instanciable (as of now) but they're considered first calss. +enum PkVarType { + PK_OBJECT = 0, + + PK_NULL, + PK_BOOL, + PK_NUMBER, + PK_STRING, + PK_LIST, + PK_MAP, + PK_RANGE, + PK_MODULE, + PK_CLOSURE, + PK_FIBER, + PK_CLASS, + PK_INSTANCE, +}; + +// Type of the error message that pocketlang will provide with the pkErrorFn +// callback. +enum PkErrorType { + PK_ERROR_COMPILE = 0, // Compile time errors. + PK_ERROR_RUNTIME, // Runtime error message. + PK_ERROR_STACKTRACE, // One entry of a runtime error stack. +}; + +// Result that pocketlang will return after a compilation or running a script +// or a function or evaluating an expression. +enum PkResult { + PK_RESULT_SUCCESS = 0, // Successfully finished the execution. + + // Unexpected EOF while compiling the source. This is another compile time + // error that will ONLY be returned if we're compiling with the REPL mode set + // in the compile options. We need this specific error to indicate the host + // application to add another line to the last input. If REPL is not enabled, + // this will be PK_RESULT_COMPILE_ERROR. + PK_RESULT_UNEXPECTED_EOF, + + PK_RESULT_COMPILE_ERROR, // Compilation failed. + PK_RESULT_RUNTIME_ERROR, // An error occurred at runtime. +}; + +// A string pointer wrapper to pass c string between host application and +// pocket VM. With a on_done() callback to clean it when the pocket VM is done +// with the string. +struct PkStringPtr { + const char* string; //< The string result. + pkResultDoneFn on_done; //< Called once vm done with the string. + void* user_data; //< User related data. + + // These values are provided by the pocket VM to the host application, you're + // not expected to set this when provideing string to the pocket VM. + uint32_t length; //< Length of the string. + uint32_t hash; //< Its 32 bit FNV-1a hash. +}; + +struct PkConfiguration { + + // The callback used to allocate, reallocate, and free. If the function + // pointer is NULL it defaults to the VM's realloc(), free() wrappers. + pkReallocFn realloc_fn; + + pkErrorFn error_fn; + pkWriteFn write_fn; + pkReadFn read_fn; + + pkResolvePathFn resolve_path_fn; + pkLoadScriptFn load_script_fn; + + // User defined data associated with VM. + void* user_data; +}; + +// The options to configure the compilation provided by the command line +// arguments (or other ways the host application provides). +struct PkCompileOptions { + + // Compile debug version of the source. + bool debug; + + // Set to true if compiling in REPL mode, This will print repr version of + // each evaluated non-null values. + bool repl_mode; +}; + /*****************************************************************************/ /* POCKETLANG PUBLIC API */ /*****************************************************************************/ @@ -250,7 +268,7 @@ PK_PUBLIC PkHandle* pkModuleGetGlobal(PKVM* vm, PkHandle* module, const char* name); // 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. +// the function has variadic parameters and use pkGetArgc() to get the argc. // Note that the function will be added as a global variable of the module, // to retrieve the function use pkModuleGetGlobal(). PK_PUBLIC void pkModuleAddFunction(PKVM* vm, PkHandle* module, @@ -261,6 +279,26 @@ 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. +PK_PUBLIC PkHandle* pkNewClass(PKVM* vm, PkHandle* module, const char* name); + +// Add a native method to the given class. If the [arity] is -1 that means +// the method has variadic parameters and use pkGetArgc() to get the argc. +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, @@ -289,59 +327,6 @@ PK_PUBLIC PkResult pkRunFiber(PKVM* vm, PkHandle* fiber, // yielded or returned value use the pkFiberGetReturnValue() function. PK_PUBLIC PkResult pkResumeFiber(PKVM* vm, PkHandle* fiber, PkVar value); -/*****************************************************************************/ -/* POCKETLANG PUBLIC TYPE DEFINES */ -/*****************************************************************************/ - -// A string pointer wrapper to pass c string between host application and -// pocket VM. With a on_done() callback to clean it when the pocket VM is done -// with the string. -struct PkStringPtr { - const char* string; //< The string result. - pkResultDoneFn on_done; //< Called once vm done with the string. - void* user_data; //< User related data. - - // These values are provided by the pocket VM to the host application, you're - // not expected to set this when provideing string to the pocket VM. - uint32_t length; //< Length of the string. - uint32_t hash; //< Its 32 bit FNV-1a hash. -}; - -struct PkConfiguration { - - // The callback used to allocate, reallocate, and free. If the function - // pointer is NULL it defaults to the VM's realloc(), free() wrappers. - pkReallocFn realloc_fn; - - pkErrorFn error_fn; - pkWriteFn write_fn; - pkReadFn read_fn; - - pkInstFreeFn inst_free_fn; - pkInstNameFn inst_name_fn; - pkInstGetAttribFn inst_get_attrib_fn; - pkInstSetAttribFn inst_set_attrib_fn; - - pkResolvePathFn resolve_path_fn; - pkLoadScriptFn load_script_fn; - - // User defined data associated with VM. - void* user_data; -}; - -// The options to configure the compilation provided by the command line -// arguments (or other ways the host application provides). -struct PkCompileOptions { - - // Compile debug version of the source. - bool debug; - - // Set to true if compiling in REPL mode, This will print repr version of - // each evaluated non-null values. - bool repl_mode; - -}; - /*****************************************************************************/ /* NATIVE FUNCTION API */ /*****************************************************************************/ @@ -352,6 +337,9 @@ PK_PUBLIC void pkSetRuntimeError(PKVM* vm, const char* message); // TODO: Set a runtime error to VM, with the formated string. //PK_PUBLIC void pkSetRuntimeErrorFmt(PKVM* vm, const char* fmt, ...); +// 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); @@ -383,7 +371,6 @@ 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 pkGetArgInst(PKVM* vm, int arg, uint32_t id, void** value); PK_PUBLIC bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value); // The functions follow are used to set the return value of the current native @@ -397,8 +384,6 @@ 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); -PK_PUBLIC void pkReturnInstNative(PKVM* vm, void* data, uint32_t id); - // 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); @@ -424,23 +409,6 @@ PK_PUBLIC PkHandle* pkNewStringLength(PKVM* vm, const char* value, size_t len); PK_PUBLIC PkHandle* pkNewList(PKVM* vm); PK_PUBLIC PkHandle* pkNewMap(PKVM* vm); -// 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 and return a new fiber around the function [fn]. -PK_PUBLIC PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn); - -// Create and return a native instance around the [data]. The [id] is the -// unique id of the instance, this would be used to check if two instances are -// equal and used to get the name of the instance using NativeTypeNameFn -// callback. -PK_PUBLIC PkHandle* pkNewInstNative(PKVM* vm, void* data, uint32_t id); - // 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 diff --git a/src/pk_core.c b/src/pk_core.c index 82b07ad..5d9ee61 100644 --- a/src/pk_core.c +++ b/src/pk_core.c @@ -69,6 +69,34 @@ void pkRegisterModule(PKVM* vm, PkHandle* module) { vmRegisterModule(vm, module_, module_->name); } +PkHandle* pkNewClass(PKVM* vm, PkHandle* module, const char* name) { + CHECK_NULL(module); + CHECK_NULL(name); + CHECK_TYPE(module, OBJ_MODULE); + + Class* class_ = newClass(vm, name, (int)strlen(name), + (Module*)AS_OBJ(module->value), NULL, NULL); + 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_MODULE); + + TODO; +} + +void* pkGetSelf(const PKVM* vm) { + Var self = vm->fiber->frames[vm->fiber->frame_count - 1].self; + ASSERT(IS_OBJ_TYPE(self, OBJ_INST), OOPS); + Instance* inst = (Instance*)AS_OBJ(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); @@ -230,33 +258,6 @@ bool pkGetArgString(PKVM* vm, int arg, const char** value, uint32_t* length) { return true; } -bool pkGetArgInst(PKVM* vm, int arg, uint32_t id, void** value) { - CHECK_GET_ARG_API_ERRORS(); - - Var val = ARG(arg); - bool is_native_instance = false; - - if (IS_OBJ_TYPE(val, OBJ_INST)) { - Instance* inst = ((Instance*)AS_OBJ(val)); - if (inst->is_native && inst->native_id == id) { - *value = inst->native; - is_native_instance = true; - } - } - - if (!is_native_instance) { - const char* ty_name = "$(?)"; - if (vm->config.inst_name_fn != NULL) { - ty_name = vm->config.inst_name_fn(id); - } - - ERR_INVALID_ARG_TYPE(ty_name); - return false; - } - - return true; -} - bool pkGetArgValue(PKVM* vm, int arg, PkVarType type, PkVar* value) { CHECK_GET_ARG_API_ERRORS(); @@ -300,10 +301,6 @@ void pkReturnHandle(PKVM* vm, PkHandle* handle) { RET(handle->value); } -void pkReturnInstNative(PKVM* vm, void* data, uint32_t id) { - RET(VAR_OBJ(newInstanceNative(vm, data, id))); -} - 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."); diff --git a/src/pk_debug.c b/src/pk_debug.c index f907227..a86aff6 100644 --- a/src/pk_debug.c +++ b/src/pk_debug.c @@ -126,26 +126,14 @@ void dumpFunctionCode(PKVM* vm, Function* func) { NO_ARGS(); break; - case OP_PUSH_LIST: SHORT_ARG(); break; - case OP_PUSH_INSTANCE: - { - int cls_index = READ_SHORT(); - ASSERT_INDEX((uint32_t)cls_index, func->owner->constants.count); - Var constant = func->owner->constants.data[cls_index]; - ASSERT(IS_OBJ_TYPE(constant, OBJ_CLASS), OOPS); - - // Prints: %5d [Class:%s]\n - PRINT_INT(cls_index); - PRINT(" [Class:"); - PRINT(func->owner->name->data); - PRINT("]\n"); + case OP_PUSH_LIST: + SHORT_ARG(); break; - } + case OP_PUSH_MAP: case OP_PUSH_SELF: case OP_LIST_APPEND: case OP_MAP_INSERT: - case OP_INST_APPEND: NO_ARGS(); break; diff --git a/src/pk_opcodes.h b/src/pk_opcodes.h index b1cae8a..1a7a561 100644 --- a/src/pk_opcodes.h +++ b/src/pk_opcodes.h @@ -43,10 +43,6 @@ OPCODE(PUSH_MAP, 0, 1) // Push the self of the current method on the stack. OPCODE(PUSH_SELF, 0, 1) -// Push a new instance to the stack. -// param: 1 byte index. -OPCODE(PUSH_INSTANCE, 1, 1) - // Pop the value on the stack the next stack top would be a list. Append the // value to the list. Used in literal array construction. OPCODE(LIST_APPEND, 0, -1) @@ -55,10 +51,6 @@ OPCODE(LIST_APPEND, 0, -1) // Insert the key value pairs to the map. Used in literal map construction. OPCODE(MAP_INSERT, 0, -2) -// Pop the value on the stack, the next stack top would be an instance. Append -// the value to the instance. Used in instance construction. -OPCODE(INST_APPEND, 0, -1) - // Push stack local on top of the stack. Locals at 0 to 8 marked explicitly // since it's performance critical. // params: PUSH_LOCAL_N -> 1 byte count value. diff --git a/src/pk_value.c b/src/pk_value.c index de892ec..07bcfad 100644 --- a/src/pk_value.c +++ b/src/pk_value.c @@ -75,14 +75,6 @@ PkHandle* pkNewFiber(PKVM* vm, PkHandle* fn) { return handle; } -PkHandle* pkNewInstNative(PKVM* vm, void* data, uint32_t id) { - Instance* inst = newInstanceNative(vm, data, id); - vmPushTempRef(vm, &inst->_super); // inst - PkHandle* handle = vmNewHandle(vm, VAR_OBJ(inst)); - vmPopTempRef(vm); // inst - return handle; -} - /*****************************************************************************/ /* VAR INTERNALS */ /*****************************************************************************/ @@ -279,17 +271,14 @@ static void popMarkedObjectsInternal(Object* obj, PKVM* vm) { markObject(vm, &cls->owner->_super); markObject(vm, &cls->ctor->_super); markObject(vm, &cls->name->_super); - vm->bytes_allocated += sizeof(uint32_t) * cls->field_names.capacity; } break; case OBJ_INST: { Instance* inst = (Instance*)obj; - if (!inst->is_native) { - Inst* ins = inst->ins; - vm->bytes_allocated += sizeof(Inst); - vm->bytes_allocated += sizeof(Var*) * ins->fields.capacity; - } + markObject(vm, &inst->attribs._super); + markObject(vm, &inst->cls->_super); + vm->bytes_allocated += sizeof(Instance); } break; } } @@ -529,43 +518,11 @@ Class* newClass(PKVM* vm, const char* name, int length, return cls; } -Instance* newInstance(PKVM* vm, Class* cls, bool initialize) { - +Instance* newInstance(PKVM* vm, Class* cls) { Instance* inst = ALLOCATE(vm, Instance); varInitObject(&inst->_super, vm, OBJ_INST); - - vmPushTempRef(vm, &inst->_super); // inst. - - inst->ty_name = cls->name->data; - inst->is_native = false; - - Inst* ins = ALLOCATE(vm, Inst); - inst->ins = ins; - ins->type = cls; - pkVarBufferInit(&ins->fields); - - if (initialize && cls->field_names.count != 0) { - pkVarBufferFill(&ins->fields, vm, VAR_NULL, cls->field_names.count); - } - - vmPopTempRef(vm); // inst. - - return inst; -} - -Instance* newInstanceNative(PKVM* vm, void* data, uint32_t id) { - Instance* inst = ALLOCATE(vm, Instance); - varInitObject(&inst->_super, vm, OBJ_INST); - inst->is_native = true; - inst->native_id = id; - - if (vm->config.inst_name_fn != NULL) { - inst->ty_name = vm->config.inst_name_fn(id); - } else { - inst->ty_name = "$(?)"; - } - - inst->native = data; + inst->cls = cls; + inst->native = NULL; return inst; } @@ -1058,27 +1015,14 @@ void freeObject(PKVM* vm, Object* self) { case OBJ_CLASS: { Class* cls = (Class*)self; - pkUintBufferClear(&cls->field_names, vm); } break; - case OBJ_INST: - { + case OBJ_INST: { Instance* inst = (Instance*)self; - - if (inst->is_native) { - if (vm->config.inst_free_fn != NULL) { - // TODO: Allow user to set error when freeing the object. - vm->config.inst_free_fn(vm, inst->native, inst->native_id); - } - - } else { - Inst* ins = inst->ins; - pkVarBufferClear(&ins->fields, vm); - DEALLOCATE(vm, ins); + if (inst->native) { + TODO; // Call native clean function. } - - break; - } + } break; } DEALLOCATE(vm, self); @@ -1185,129 +1129,35 @@ void moduleAddMain(PKVM* vm, Module* module) { } bool instGetAttrib(PKVM* vm, Instance* inst, String* attrib, Var* value) { - ASSERT(inst != NULL, OOPS); - ASSERT(attrib != NULL, OOPS); - ASSERT(value != NULL, OOPS); + ASSERT((inst != NULL) && (attrib != NULL) && (value != NULL), OOPS); - // This function should only be called at runtime. - ASSERT(vm->fiber != NULL, OOPS); - - if (inst->is_native) { - - if (vm->config.inst_get_attrib_fn) { - // Temproarly change the fiber's "return address" to points to the - // below var 'val' so that the users can use 'pkReturn...()' function - // to return the attribute as well. - Var* temp = vm->fiber->ret; - Var val = VAR_UNDEFINED; - - vm->fiber->ret = &val; - PkStringPtr attr = { attrib->data, NULL, NULL, - attrib->length, attrib->hash }; - vm->config.inst_get_attrib_fn(vm, inst->native, inst->native_id, attr); - vm->fiber->ret = temp; - - if (IS_UNDEF(val)) { - - // FIXME: add a list of attribute overrides. - if ((CHECK_HASH("as_string", 0xbdef4147) == attrib->hash) && - IS_CSTR_EQ(attrib, "as_string", 9)) { - *value = VAR_OBJ(toRepr(vm, VAR_OBJ(inst))); - return true; - } - - // If we reached here, the native instance don't have the attribute - // and no overriden attributes found, return false to indicate that the - // attribute doesn't exists. - return false; - } - - // Attribute [val] updated by the hosting application. - *value = val; - return true; - } - - // If the hosting application doesn't provided a getter function, we treat - // it as if the instance don't has the attribute. - return false; - - } else { - - // TODO: Optimize this with binary search. - Class* cls = inst->ins->type; - for (uint32_t i = 0; i < cls->field_names.count; i++) { - ASSERT_INDEX(i, cls->field_names.count); - String* f_name = moduleGetStringAt(cls->owner, cls->field_names.data[i]); - ASSERT(f_name != NULL, OOPS); - if (IS_STR_EQ(f_name, attrib)) { - *value = inst->ins->fields.data[i]; - return true; - } - } - - // Couldn't find the attribute in it's type class, return false. - return false; + if (inst->native != NULL) { } + TODO; - UNREACHABLE(); - return false; + Var value_ = mapGet(&inst->attribs, VAR_OBJ(attrib)); + if (IS_UNDEF(value_)) return false; + + *value = value_; + return true; } bool instSetAttrib(PKVM* vm, Instance* inst, String* attrib, Var value) { + ASSERT((inst != NULL) && (attrib != NULL), OOPS); - if (inst->is_native) { + if (inst->native != NULL) { + // Try setting the attribute from the native interface, and if success, we + // should return. otherwise the code will "fall through" and set on it's + // dynamic attributes map. + TODO; - if (vm->config.inst_set_attrib_fn) { - // Temproarly change the fiber's "return address" to points to the - // below var 'attrib_ptr' so that the users can use 'pkGetArg...()' - // function to validate and get the attribute (users should use 0 as the - // index of the argument since it's at the return address and we cannot - // ensure fiber->ret[1] will be in bounds). - Var* temp = vm->fiber->ret; - Var attrib_ptr = value; - - vm->fiber->ret = &attrib_ptr; - PkStringPtr attr = { attrib->data, NULL, NULL, - attrib->length, attrib->hash }; - bool exists = vm->config.inst_set_attrib_fn(vm, inst->native, - inst->native_id, attr); - vm->fiber->ret = temp; - - // If the type is incompatible there'll be an error by now, return false - // and the user of this function has to check VM_HAS_ERROR() as well. - if (VM_HAS_ERROR(vm)) return false; - - // If the attribute exists on the native type, the host application would - // returned true by now, return it. - return exists; - } - - // If the host application doesn't provided a setter we treat it as it - // doesn't has the attribute. - return false; - - } else { - - // TODO: Optimize this with binary search. - Class* ty = inst->ins->type; - for (uint32_t i = 0; i < ty->field_names.count; i++) { - ASSERT_INDEX(i, ty->field_names.count); - String* f_name = moduleGetStringAt(ty->owner, ty->field_names.data[i]); - ASSERT(f_name != NULL, OOPS); - if (f_name->hash == attrib->hash && - f_name->length == attrib->length && - memcmp(f_name->data, attrib->data, attrib->length) == 0) { - inst->ins->fields.data[i] = value; - return true; - } - } - - // Couldn't find the attribute in it's type class, return false. - return false; + // FIXME: + // Only return true if attribute have been set. + return true; } - UNREACHABLE(); - return false; + mapSet(vm, &inst->attribs, VAR_OBJ(attrib), value); + return true; } /*****************************************************************************/ @@ -1693,35 +1543,16 @@ static void _toStringInternal(PKVM* vm, const Var v, pkByteBuffer* buff, { const Instance* inst = (const Instance*)obj; pkByteBufferWrite(buff, vm, '['); - pkByteBufferAddString(buff, vm, inst->ty_name, - (uint32_t)strlen(inst->ty_name)); + pkByteBufferAddString(buff, vm, inst->cls->name->data, + inst->cls->name->length); pkByteBufferWrite(buff, vm, ':'); - if (!inst->is_native) { - const Class* cls = inst->ins->type; - const Inst* ins = inst->ins; - ASSERT(ins->fields.count == cls->field_names.count, OOPS); - - for (uint32_t i = 0; i < cls->field_names.count; i++) { - if (i != 0) pkByteBufferWrite(buff, vm, ','); - - pkByteBufferWrite(buff, vm, ' '); - String* f_name = moduleGetStringAt(cls->owner, - cls->field_names.data[i]); - pkByteBufferAddString(buff, vm, f_name->data, f_name->length); - pkByteBufferWrite(buff, vm, '='); - _toStringInternal(vm, ins->fields.data[i], buff, outer, repr); - } - } else { - - char buff_addr[STR_HEX_BUFF_SIZE]; - char* ptr = (char*)buff_addr; - (*ptr++) = '0'; (*ptr++) = 'x'; - const int len = snprintf(ptr, sizeof(buff_addr) - 2, - "%08x", (unsigned int)(uintptr_t)inst->native); - pkByteBufferAddString(buff, vm, buff_addr, (uint32_t)len); - } - + char buff_addr[STR_HEX_BUFF_SIZE]; + char* ptr = (char*)buff_addr; + (*ptr++) = '0'; (*ptr++) = 'x'; + const int len = snprintf(ptr, sizeof(buff_addr) - 2, + "%08x", (unsigned int)(uintptr_t)inst); + pkByteBufferAddString(buff, vm, buff_addr, (uint32_t)len); pkByteBufferWrite(buff, vm, ']'); return; } diff --git a/src/pk_value.h b/src/pk_value.h index a316962..b8e80c2 100644 --- a/src/pk_value.h +++ b/src/pk_value.h @@ -491,8 +491,6 @@ struct Class { const char* docstring; Closure* ctor; //< The constructor function. - pkUintBuffer field_names; //< Buffer of field names. - // TODO: ordered names buffer for binary search. }; typedef struct { @@ -503,15 +501,17 @@ typedef struct { struct Instance { Object _super; - const char* ty_name; //< Name of the type it belongs to. + Class* cls; //< Class of the instance. - bool is_native; //< True if it's a native type instance. - uint32_t native_id; //< Unique ID of this native instance. + // If the instance is native, the [native] pointer points to the user data + // (generally a heap allocated struct of that type) that contains it's + // attributes. We'll use it to access an attribute first with setters and + // getters and if the attribute not exists we'll continue search in the + // bellow attribs map. + void* native; - union { - void* native; //< C struct pointer. // TODO: - Inst* ins; //< Module instance pointer. - }; + // Dynamic attributes of an instance. + Map attribs; }; /*****************************************************************************/ @@ -569,16 +569,8 @@ Class* newClass(PKVM* vm, const char* name, int length, Module* module, const char* docstring, int* cls_index); -// Allocate new instance with of the base [type]. Note that if [initialize] is -// false, the field value buffer of the instance would be un initialized (ie. -// the buffer count = 0). Otherwise they'll be set to VAR_NULL. -Instance* newInstance(PKVM* vm, Class* cls, bool initialize); - -// Allocate new native instance and with [data] as the native type handle and -// return Instance*. The [id] is the unique id of the instance, this would be -// used to check if two instances are equal and used to get the name of the -// instance using NativeTypeNameFn callback. -Instance* newInstanceNative(PKVM* vm, void* data, uint32_t id); +// Allocate new instance with of the base [type]. +Instance* newInstance(PKVM* vm, Class* cls); /*****************************************************************************/ /* METHODS */ diff --git a/src/pk_vm.c b/src/pk_vm.c index bd5fd7e..4de2829 100644 --- a/src/pk_vm.c +++ b/src/pk_vm.c @@ -31,11 +31,6 @@ PkConfiguration pkNewConfiguration(void) { config.write_fn = NULL; config.read_fn = NULL; - config.inst_free_fn = NULL; - config.inst_name_fn = NULL; - config.inst_get_attrib_fn = NULL; - config.inst_set_attrib_fn = NULL; - config.load_script_fn = NULL; config.resolve_path_fn = NULL; config.user_data = NULL; @@ -857,17 +852,6 @@ L_vm_main_loop: DISPATCH(); } - OPCODE(PUSH_INSTANCE): - { - uint8_t index = READ_SHORT(); - ASSERT_INDEX(index, module->constants.count); - ASSERT(IS_OBJ_TYPE(module->constants.data[index], OBJ_CLASS), OOPS); - Instance* inst = newInstance(vm, - (Class*)AS_OBJ(module->constants.data[index]), false); - PUSH(VAR_OBJ(inst)); - DISPATCH(); - } - OPCODE(LIST_APPEND): { Var elem = PEEK(-1); // Don't pop yet, we need the reference for gc. @@ -898,21 +882,6 @@ L_vm_main_loop: DISPATCH(); } - OPCODE(INST_APPEND): - { - Var value = PEEK(-1); // Don't pop yet, we need the reference for gc. - Var inst = PEEK(-2); - ASSERT(IS_OBJ_TYPE(inst, OBJ_INST), OOPS); - - Instance* inst_p = (Instance*)AS_OBJ(inst); - ASSERT(!inst_p->is_native, OOPS); - Inst* ins = inst_p->ins; - pkVarBufferWrite(&ins->fields, vm, value); - DROP(); // value - - DISPATCH(); - } - OPCODE(PUSH_LOCAL_0): OPCODE(PUSH_LOCAL_1): OPCODE(PUSH_LOCAL_2):