From 3ef213b17015bf89f5b55c0e2988a10b84fa8863 Mon Sep 17 00:00:00 2001 From: Thakee Nathees Date: Thu, 1 Jul 2021 14:53:16 +0530 Subject: [PATCH] native api refactored (#157) --- cli/main.c | 1 + cli/modules.c | 15 ++-- cli/modules.h | 8 +- src/include/pocketlang.h | 6 +- src/pk_var.c | 7 +- tests/native/README.md | 17 ++-- tests/native/example1.c | 82 +++++++++++++++++++ tests/native/{example.c => example2.c} | 105 ++++++++++++------------- 8 files changed, 160 insertions(+), 81 deletions(-) create mode 100644 tests/native/example1.c rename tests/native/{example.c => example2.c} (67%) diff --git a/cli/main.c b/cli/main.c index c8e9883..67e7a6b 100644 --- a/cli/main.c +++ b/cli/main.c @@ -132,6 +132,7 @@ static PKVM* intializePocketVM() { 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; diff --git a/cli/modules.c b/cli/modules.c index 56ca770..2e9b6fb 100644 --- a/cli/modules.c +++ b/cli/modules.c @@ -19,10 +19,9 @@ void initObj(Obj* obj, ObjType type) { obj->type = type; } -void objGetAttrib(PKVM* vm, void* instance, PkStringPtr attrib) { - +void objGetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib) { Obj* obj = (Obj*)instance; - // TODO: assert obj type is valid. + ASSERT(obj->type == (ObjType)id, OOPS); if (obj->type == OBJ_FILE) { File* file = (File*)obj; @@ -30,15 +29,14 @@ void objGetAttrib(PKVM* vm, void* instance, PkStringPtr attrib) { pkReturnBool(vm, file->closed); return; } - } return; // Attribute not found. } -bool objSetAttrib(PKVM* vm, void* instance, PkStringPtr attrib) { +bool objSetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib) { Obj* obj = (Obj*)instance; - // TODO: assert obj type is valid. + ASSERT(obj->type == (ObjType)id, OOPS); if (obj->type == OBJ_FILE) { File* file = (File*)obj; @@ -48,10 +46,9 @@ bool objSetAttrib(PKVM* vm, void* instance, PkStringPtr attrib) { return false; } -void freeObj(PKVM* vm, void* instance) { - +void freeObj(PKVM* vm, void* instance, uint32_t id) { Obj* obj = (Obj*)instance; - // TODO: assert obj type is valid. + ASSERT(obj->type == (ObjType)id, OOPS); // If the file isn't closed, close it to flush it's buffer. if (obj->type == OBJ_FILE) { diff --git a/cli/modules.h b/cli/modules.h index 404e1bf..403208e 100644 --- a/cli/modules.h +++ b/cli/modules.h @@ -59,11 +59,15 @@ void initObj(Obj* obj, ObjType type); // A function callback called by pocket VM to get attribute of a native // instance. -void objGetAttrib(PKVM* vm, void* instance, PkStringPtr attrib); +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); +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. diff --git a/src/include/pocketlang.h b/src/include/pocketlang.h index 6fdc97e..eacbc18 100644 --- a/src/include/pocketlang.h +++ b/src/include/pocketlang.h @@ -161,7 +161,7 @@ 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); +typedef void (*pkInstFreeFn) (PKVM* vm, void* instance, uint32_t id); // A function callback to get the name of the native instance from pocketlang, // using it's [id]. The returned string won't be copied by pocketlang so it's @@ -174,7 +174,7 @@ typedef const char* (*pkInstNameFn) (uint32_t id); // 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, +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 @@ -184,7 +184,7 @@ typedef void (*pkInstGetAttribFn) (PKVM* vm, void* instance, // 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, +typedef bool (*pkInstSetAttribFn) (PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib); // A function callback symbol for clean/free the pkStringResult. diff --git a/src/pk_var.c b/src/pk_var.c index 9ab4db8..486edaa 100644 --- a/src/pk_var.c +++ b/src/pk_var.c @@ -1052,7 +1052,7 @@ void freeObject(PKVM* vm, Object* 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); + vm->config.inst_free_fn(vm, inst->native, inst->native_id); } } else { @@ -1163,7 +1163,7 @@ bool instGetAttrib(PKVM* vm, Instance* inst, String* attrib, Var* value) { vm->fiber->ret = &val; PkStringPtr attr = { attrib->data, NULL, NULL, attrib->length, attrib->hash }; - vm->config.inst_get_attrib_fn(vm, inst->native, attr); + vm->config.inst_get_attrib_fn(vm, inst->native, inst->native_id, attr); vm->fiber->ret = temp; if (IS_UNDEF(val)) { @@ -1225,7 +1225,8 @@ bool instSetAttrib(PKVM* vm, Instance* inst, String* attrib, Var 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, attr); + 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 diff --git a/tests/native/README.md b/tests/native/README.md index 5e4376c..140b7e5 100644 --- a/tests/native/README.md +++ b/tests/native/README.md @@ -1,9 +1,8 @@ ## Example on how to integrate pocket VM with in your application. -### This readme is incomplete (WIP) -- Including this example this repository contains 3 examples on how to integrate +- Including this example this repository contains several examples on how to integrate pocket VM with your application - - This example + - These examples (currently 2 examples) - The `cli/` application - The `docs/try/main.c` web assembly version of pocketlang @@ -16,11 +15,13 @@ pocket VM with your application ---- -Compile the `example.c` file which contains examples on how to write your custom module -to pocketlang, using the below command. - +#### `example1.c` - Contains how to pass values between pocket VM and C ``` -gcc example.c -o example ../../src/*.c -I../../src/include -lm +gcc example1.c -o example1 ../../src/*.c -I../../src/include -lm +``` + +#### `example2.c` - Contains how to create your own custom native type in C +``` +gcc example2.c -o example2 ../../src/*.c -I../../src/include -lm ``` -Note that only calling C from pocket is completed and calling pocket from C is WIP. diff --git a/tests/native/example1.c b/tests/native/example1.c new file mode 100644 index 0000000..c490613 --- /dev/null +++ b/tests/native/example1.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020-2021 Thakee Nathees + * Distributed Under The MIT License + */ + +// This is a minimal example on how to pass values between pocket VM and C. + +#include +#include + +// The pocket script we're using to test. +static const char* code = + " from YourModule import variableToC \n" + " a = 42 \n" + " b = variableToC(a) \n" + " print('[pocket] b =', b) \n" + ; + +/*****************************************************************************/ +/* MODULE FUNCTION */ +/*****************************************************************************/ + +static void variableToC(PKVM* vm) { + + // Get the parameter from pocket VM. + double a; + if (!pkGetArgNumber(vm, 1, &a)) return; + + printf("[C] a = %f\n", a); + + // Return value to the pocket VM. + pkReturnNumber(vm, 3.14); +} + +/*****************************************************************************/ +/* POCKET VM CALLBACKS */ +/*****************************************************************************/ + +// Error report callback. +static void reportError(PKVM* vm, PkErrorType type, + const char* file, int line, + const char* message) { + fprintf(stderr, "Error: %s\n", message); +} + +// print() callback to write stdout. +static void stdoutWrite(PKVM* vm, const char* text) { + fprintf(stdout, "%s", text); +} + +/*****************************************************************************/ +/* MAIN */ +/*****************************************************************************/ + +int main(int argc, char** argv) { + + // Pocket VM configuration. + PkConfiguration config = pkNewConfiguration(); + config.error_fn = reportError; + config.write_fn = stdoutWrite; + //config.read_fn = stdinRead; + + // Create a new pocket VM. + PKVM* vm = pkNewVM(&config); + + // Register your module. + PkHandle* your_module = pkNewModule(vm, "YourModule"); + pkModuleAddFunction(vm, your_module, "variableToC", variableToC, 1); + pkReleaseHandle(vm, your_module); + + // The path and the source code. + PkStringPtr source = { code, NULL, NULL, 0, 0 }; + PkStringPtr path = { "./some/path/", NULL, NULL, 0, 0 }; + + // Run the code. + PkResult result = pkInterpretSource(vm, source, path, NULL/*options*/); + + // Free the VM. + pkFreeVM(vm); + + return (int)result; +} diff --git a/tests/native/example.c b/tests/native/example2.c similarity index 67% rename from tests/native/example.c rename to tests/native/example2.c index dd41538..7a9e2ae 100644 --- a/tests/native/example.c +++ b/tests/native/example2.c @@ -1,40 +1,46 @@ +/* + * Copyright (c) 2020-2021 Thakee Nathees + * Distributed Under The MIT License + */ + +// This is an example on how to write your own custom type (Vector here) and +// bind it with with the pocket VM. #include #include -#include #include #include // The script we're using to test the native Vector type. -static const char* code = " \n\ - import Vector # The native module. \n\ - print('Module =', Vector) \n\ - \n\ - vec1 = Vector.new(1, 2) # Calling native method. \n\ - print('vec1 =', 'Vector.new(1, 2)') \n\ - print() \n\ - \n\ - # Using the native getter. \n\ - print('vec1.x =', vec1.x) \n\ - print('vec1.y =', vec1.y) \n\ - print('vec1.length =', vec1.length) \n\ - print() \n\ - \n\ - # Using the native setter. \n\ - vec1.x = 3; vec1.y = 4; \n\ - print('vec1.x =', vec1.x) \n\ - print('vec1.y =', vec1.y) \n\ - print('vec1.length =', vec1.length) \n\ - print() \n\ - \n\ - vec2 = Vector.new(5, 6) \n\ - vec3 = Vector.add(vec1, vec2) \n\ - print('vec3 =', 'Vector.add(vec1, vec2)') \n\ - print('vec3.x =', vec3.x) \n\ - print('vec3.y =', vec3.y) \n\ - \n\ -"; +static const char* code = + " import Vector # The native module. \n" + " print('Module =', Vector) \n" + " \n" + " vec1 = Vector.new(1, 2) # Calling native method. \n" + " print('vec1 =', 'Vector.new(1, 2)') \n" + " print() \n" + " \n" + " # Using the native getter. \n" + " print('vec1.x =', vec1.x) \n" + " print('vec1.y =', vec1.y) \n" + " print('vec1.length =', vec1.length) \n" + " print() \n" + " \n" + " # Using the native setter. \n" + " vec1.x = 3; vec1.y = 4; \n" + " print('vec1.x =', vec1.x) \n" + " print('vec1.y =', vec1.y) \n" + " print('vec1.length =', vec1.length) \n" + " print() \n" + " \n" + " vec2 = Vector.new(5, 6) \n" + " vec3 = Vector.add(vec1, vec2) \n" + " print('vec3 =', 'Vector.add(vec1, vec2)') \n" + " print('vec3.x =', vec3.x) \n" + " print('vec3.y =', vec3.y) \n" + " \n" + ; /*****************************************************************************/ /* NATIVE TYPE DEFINES & CALLBACKS */ @@ -46,11 +52,6 @@ typedef enum { } ObjType; typedef struct { - ObjType type; -} Obj; - -typedef struct { - Obj base; // "Inherits" objects. double x, y; // Vector variables. } Vector; @@ -66,13 +67,12 @@ const char* getObjName(uint32_t id) { // Instance getter callback to get a value from the native instance. // The hash value and the length of the string are provided with the // argument [attrib]. -void objGetAttrib(PKVM* vm, void* instance, PkStringPtr attrib) { +void objGetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib) { - Obj* obj = (Obj*)instance; - - switch (obj->type) { + switch ((ObjType)id) { case OBJ_VECTOR: { - Vector* vector = ((Vector*)obj); + Vector* vector = ((Vector*)instance); + if (strcmp(attrib.string, "x") == 0) { pkReturnNumber(vm, vector->x); return; @@ -99,22 +99,22 @@ void objGetAttrib(PKVM* vm, void* instance, PkStringPtr attrib) { // Instance setter callback to set the value to the native instance. // The hash value and the length of the string are provided with the // argument [attrib]. -bool objSetAttrib(PKVM* vm, void* instance, PkStringPtr attrib) { +bool objSetAttrib(PKVM* vm, void* instance, uint32_t id, PkStringPtr attrib) { - Obj* obj = (Obj*)instance; - - switch (obj->type) { + switch ((ObjType)id) { case OBJ_VECTOR: { + Vector* vector = ((Vector*)instance); + if (strcmp(attrib.string, "x") == 0) { double x; // Get the number x. if (!pkGetArgNumber(vm, 0, &x)) return false; - ((Vector*)obj)->x = x; + vector->x = x; return true; } else if (strcmp(attrib.string, "y") == 0) { double y; // Get the number x. if (!pkGetArgNumber(vm, 0, &y)) return false; - ((Vector*)obj)->y = y; + vector->y = y; return true; } @@ -128,10 +128,8 @@ bool objSetAttrib(PKVM* vm, void* instance, PkStringPtr attrib) { // The free object callback, called just before the native instance, garbage // collect. -void freeObj(PKVM* vm, void* instance) { - Obj* obj = (Obj*)instance; - // Your cleanups. - free((void*)obj); +void freeObj(PKVM* vm, void* instance, uint32_t id) { + free((void*)instance); // Your cleanups. } /*****************************************************************************/ @@ -148,9 +146,7 @@ void _vecNew(PKVM* vm) { // Create a new vector. Vector* vec = (Vector*)malloc(sizeof(Vector)); - vec->base.type = OBJ_VECTOR; vec->x = x, vec->y = y; - pkReturnInstNative(vm, (void*)vec, OBJ_VECTOR); } @@ -162,7 +158,6 @@ void _vecAdd(PKVM* vm) { // Create a new vector. Vector* v3 = (Vector*)malloc(sizeof(Vector)); - v3->base.type = OBJ_VECTOR; v3->x = v1->x + v2->x; v3->y = v1->y + v2->y; @@ -206,14 +201,12 @@ int main(int argc, char** argv) { 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; PKVM* vm = pkNewVM(&config); registerVector(vm); - PkStringPtr source = { code, NULL, NULL }; - PkStringPtr path = { "./some/path/", NULL, NULL }; + PkStringPtr source = { code, NULL, NULL, 0, 0 }; + PkStringPtr path = { "./some/path/", NULL, NULL, 0, 0 }; PkResult result = pkInterpretSource(vm, source, path, NULL/*options*/); pkFreeVM(vm);