mirror of
https://github.com/zekexiao/pocketlang.git
synced 2025-02-05 20:26:53 +08:00
instance and class type cleaned
this is a part of the class implementation. In this commit classes and instances type were completely cleaned and prepared for a new implementation. Native class registering mechanism were fully refactored and made it much simpler.
This commit is contained in:
parent
29be68fc86
commit
4d46930d1b
@ -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"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
@ -14,73 +14,26 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/*****************************************************************************/
|
||||
/* 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 */
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
||||
|
@ -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.");
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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.
|
||||
|
243
src/pk_value.c
243
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;
|
||||
}
|
||||
|
@ -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 */
|
||||
|
31
src/pk_vm.c
31
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):
|
||||
|
Loading…
Reference in New Issue
Block a user