native api refactored (#157)

This commit is contained in:
Thakee Nathees 2021-07-01 14:53:16 +05:30
parent 6ad5dfe9f7
commit 3ef213b170
8 changed files with 160 additions and 81 deletions

View File

@ -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;

View File

@ -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) {

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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.

82
tests/native/example1.c Normal file
View File

@ -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 <pocketlang.h>
#include <stdio.h>
// 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;
}

View File

@ -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 <pocketlang.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
// 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);