Skip to content

Commit

Permalink
Simplify bind module example.
Browse files Browse the repository at this point in the history
  • Loading branch information
fubark committed May 14, 2024
1 parent a49bf64 commit 6d3dca8
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 60 deletions.
94 changes: 38 additions & 56 deletions examples/c-embedded/bind_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,15 @@ CLValue add(CLVM* vm, const CLValue* args, uint8_t nargs) {
}

// Forward declaration.
CLValue myCollectionNew(CLVM* vm, const CLValue* args, uint8_t nargs);
CLValue myCollectionAsList(CLVM* vm, const CLValue* args, uint8_t nargs);
CLValue myNodeNew(CLVM* vm, const CLValue* args, uint8_t nargs);
CLValue myNodeAsList(CLVM* vm, const CLValue* args, uint8_t nargs);

struct { char* n; CLFuncFn fn; } funcs[] = {
{"add", add},
{"asList", myCollectionAsList},
{"MyCollection.new", myCollectionNew},
CLHostFuncEntry funcs[] = {
CL_FUNC("add", add),
CL_FUNC("MyNode.asList", myNodeAsList),
CL_FUNC("MyNode.new", myNodeNew),
};

bool funcLoader(CLVM* vm, CLFuncInfo info, CLFuncResult* out) {
// Check that the name matches before setting the function pointer.
if (strncmp(funcs[info.idx].n, info.name.ptr, info.name.len) == 0) {
out->ptr = funcs[info.idx].fn;
return true;
} else {
return false;
}
}

// C has limited static initializers (and objects need a vm instance) so initialize them in `main`.
typedef struct { char* n; CLValue v; } NameValue;
NameValue vars[2];
Expand All @@ -54,20 +44,20 @@ bool varLoader(CLVM* vm, CLVarInfo info, CLValue* out) {

// Binding a C struct with it's own children and finalizer.
// This struct retains 2 VM values and has 2 arbitrary data values unrelated to the VM.
typedef struct MyCollection {
typedef struct MyNode {
CLValue val1;
CLValue val2;
int a;
double b;
} MyCollection;
} MyNode;

CLTypeId myCollectionId;
CLTypeId myNodeId;

// Implement the `new` function in MyCollection.
CLValue myCollectionNew(CLVM* vm, const CLValue* args, uint8_t nargs) {
// Implement the `new` function in MyNode.
CLValue myNodeNew(CLVM* vm, const CLValue* args, uint8_t nargs) {
// Instantiate our object.
CLValue new = clNewHostObject(vm, myCollectionId, sizeof(MyCollection));
MyCollection* my = (MyCollection*)clAsHostObject(new);
CLValue new = clNewHostObject(vm, myNodeId, sizeof(MyNode));
MyNode* my = (MyNode*)clAsHostObject(new);

// Assign the constructor args passed in and retain them since the new object now references them.
clRetain(vm, args[0]);
Expand All @@ -81,53 +71,45 @@ CLValue myCollectionNew(CLVM* vm, const CLValue* args, uint8_t nargs) {
return new;
}

// Implement the `asList` method in MyCollection.
CLValue myCollectionAsList(CLVM* vm, const CLValue* args, uint8_t nargs) {
// Implement the `asList` method in MyNode.
CLValue myNodeAsList(CLVM* vm, const CLValue* args, uint8_t nargs) {
// First argument is `self`.
MyCollection* my = (MyCollection*)clAsHostObject(args[0]);
MyNode* my = (MyNode*)clAsHostObject(args[0]);

CLValue vals[4] = {my->val1, my->val2, clInteger(my->a), clFloat(my->b)};
return clNewList(vm, &vals[0], 4);
return clNewListDyn(vm, &vals[0], 4);
}

CLValueSlice myCollectionGetChildren(CLVM* vm, void* obj) {
MyCollection* my = (MyCollection*)obj;
CLValueSlice myNodeGetChildren(CLVM* vm, void* obj) {
MyNode* my = (MyNode*)obj;
return (CLValueSlice){ .ptr = &my->val1, .len = 2 };
}

void myCollectionFinalizer(CLVM* vm, void* obj) {
printf("MyCollection finalizer was called.\n");
void myNodeFinalizer(CLVM* vm, void* obj) {
printf("MyNode finalizer was called.\n");
}

bool typeLoader(CLVM* vm, CLTypeInfo info, CLTypeResult* out) {
if (strncmp("MyCollection", info.name.ptr, info.name.len) == 0) {
out->type = CL_BIND_TYPE_CUSTOM;
out->data.custom.out_type_id = &myCollectionId;
out->data.custom.get_children = myCollectionGetChildren;
out->data.custom.finalizer = myCollectionFinalizer;
return true;
} else {
return false;
}
}
CLHostTypeEntry types[] = {
CL_CUSTOM_TYPE("MyNode", &myNodeId, myNodeGetChildren, myNodeFinalizer),
};

// This module loader provides the source code and callbacks to load @host funcs, vars, and types.
bool modLoader(CLVM* vm, CLStr spec, CLModuleLoaderResult* out) {
if (strncmp("my_mod", spec.ptr, spec.len) == 0) {
out->src =
"@host func add(a float, b float) float\n"
"@host var .MyConstant float\n"
"@host var .MyList List\n"
"@host var .MyList List[dyn]\n"
"\n"
"@host\n"
"type MyCollection:\n"
"type MyNode _:\n"
" @host func asList() any"
"\n"
"@host func MyCollection.new(a any, b any) MyCollection\n";
"@host func MyNode.new(a any, b any) MyNode\n";
out->srcLen = strlen(out->src);
out->funcLoader = funcLoader;
out->funcs = (CLSlice){ .ptr = funcs, .len = 3 };
out->types = (CLSlice){ .ptr = types, .len = 1 };
out->varLoader = varLoader;
out->typeLoader = typeLoader;
return true;
} else {
// Fallback to the default module loader to load `builtins`.
Expand All @@ -152,21 +134,21 @@ int main() {
// Initialize var array for loader.
vars[0] = (NameValue){"MyConstant", clFloat(1.23)};
CLValue myInt = clInteger(123);
vars[1] = (NameValue){"MyList", clNewList(vm, &myInt, 1)};
vars[1] = (NameValue){"MyList", clNewListDyn(vm, &myInt, 1)};

CLStr main = STR(
"use mod 'my_mod'\n"
"use m 'my_mod'\n"
"\n"
"var a = 1.0\n"
"print mod.add(a, 2)\n"
"print(-mod.MyConstant)\n"
"print m.add(a, 2)\n"
"print(-m.MyConstant)\n"
"\n"
"mod.MyList.append(3)\n"
"print mod.MyList.len()\n"
"m.MyList.append(3)\n"
"print m.MyList.len()\n"
"\n"
"-- Instantiate a new MyCollection.\n"
"var myc = mod.MyCollection.new(1, 2)\n"
"dump myc.asList()"
"-- Instantiate a new MyNode.\n"
"var n = m.MyNode.new(1, 2)\n"
"dump n.asList()"
);
CLValue resv;
CLResultCode res = clEval(vm, main, &resv);
Expand Down
11 changes: 7 additions & 4 deletions src/include/cyber.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>

#ifndef CYBER_H
#define CYBER_H
Expand Down Expand Up @@ -234,10 +235,12 @@ typedef struct CLHostType {
uint8_t type;
} CLHostType;

#define CL_CORE_TYPE(t) ((CLHostType){ .data = { .core_custom = { .type_id = t, .get_children = NULL, .finalizer = NULL }}, .type = CL_BIND_TYPE_CORE_CUSTOM })
#define CL_CORE_TYPE_EXT(t, gc, f) ((CLHostType){ .data = { .core_custom = { .type_id = t, .get_children = gc, .finalizer = f }}, .type = CL_BIND_TYPE_CORE_CUSTOM })
#define CL_CORE_TYPE_DECL(t) ((CLHostType){ .data = { .core_decl = { .type_id = t }}, .type = CL_BIND_TYPE_CORE_DECL })
#define CL_CUSTOM_TYPE(ot, gc, f) ((CLHostType){ .data = { .custom = { .out_type_id = ot, .get_children = gc, .finalizer = f }}, .type = CL_BIND_TYPE_CUSTOM })
#define CL_STR(str) ((CLStr){ .ptr = str, .len = strlen(str) })
#define CL_FUNC(name, fn) ((CLHostFuncEntry){ CL_STR(name), fn })
// #define CL_CORE_TYPE(t) ((CLHostType){ .data = { .core_custom = { .type_id = t, .get_children = NULL, .finalizer = NULL }}, .type = CL_BIND_TYPE_CORE_CUSTOM })
// #define CL_CORE_TYPE_EXT(t, gc, f) ((CLHostType){ .data = { .core_custom = { .type_id = t, .get_children = gc, .finalizer = f }}, .type = CL_BIND_TYPE_CORE_CUSTOM })
// #define CL_CORE_TYPE_DECL(t) ((CLHostType){ .data = { .core_decl = { .type_id = t }}, .type = CL_BIND_TYPE_CORE_DECL })
#define CL_CUSTOM_TYPE(name, ot, gc, f) ((CLHostTypeEntry){ CL_STR(name), (CLHostType){ .data = { .custom = { .out_type_id = ot, .get_children = gc, .finalizer = f }}, .type = CL_BIND_TYPE_CUSTOM }})

// A mapping from a matching symbol string to a CLHostType.
typedef struct CLHostTypeEntry {
Expand Down

0 comments on commit 6d3dca8

Please sign in to comment.