components

This commit is contained in:
IonutParau 2026-02-03 15:56:17 +01:00
parent c0ce5bad26
commit d0450c067b
3 changed files with 423 additions and 28 deletions

View File

@ -3,6 +3,22 @@
// it is simply just to test stuff and showcase the API. // it is simply just to test stuff and showcase the API.
#include "neonucleus.h" #include "neonucleus.h"
#include <stdio.h>
static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
switch(req->action) {
case NN_COMP_INIT:
return NN_OK;
case NN_COMP_DEINIT:
return NN_OK;
case NN_COMP_CALL:
return NN_OK;
case NN_COMP_ENABLED:
req->methodEnabled = true; // all methods always enabled
return NN_OK;
}
return NN_OK;
}
int main() { int main() {
nn_Context ctx; nn_Context ctx;
@ -15,13 +31,19 @@ int main() {
{"log", "log(msg: string) - Log to stdout", true}, {"log", "log(msg: string) - Log to stdout", true},
NULL, NULL,
}; };
nn_ComponentType *ctype = nn_createComponentType(u, "sandbox", NULL, sandboxMethods, NULL); nn_ComponentType *ctype = nn_createComponentType(u, "sandbox", NULL, sandboxMethods, sandbox_handler);
nn_Computer *c = nn_createComputer(u, NULL, "computer0", 8 * NN_MiB, 256, 256); nn_Computer *c = nn_createComputer(u, NULL, "computer0", 8 * NN_MiB, 256, 256);
nn_addComponent(c, ctype, "sandbox", -1, NULL);
printf("Component added: %s\n", nn_hasComponent(c, "sandbox") ? "TRUE" : "FALSE");
printf("Method active: %s\n", nn_hasMethod(c, "sandbox", "log") ? "TRUE" : "FALSE");
printf("Incorrect method active: %s\n", nn_hasMethod(c, "sandbox", "notlog") ? "TRUE" : "FALSE");
cleanup:; cleanup:;
nn_destroyComponentType(ctype);
nn_destroyComputer(c); nn_destroyComputer(c);
nn_destroyComponentType(ctype);
// rip the universe // rip the universe
nn_destroyUniverse(u); nn_destroyUniverse(u);
return 0; return 0;

View File

@ -13,27 +13,9 @@
// Based off https://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor // Based off https://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
//define something for Windows (32-bit and 64-bit, this part is common) //define something for Windows (32-bit and 64-bit, this part is common)
#ifdef _WIN64 #define NN_WINDOWS
#define NN_WINDOWS
#else
#error "Windows 32-bit is not supported"
#endif
#elif __APPLE__ #elif __APPLE__
#include <TargetConditionals.h> #define NN_MACOS
#if TARGET_IPHONE_SIMULATOR
#error "iPhone Emulators are not supported"
#elif TARGET_OS_MACCATALYST
// I guess?
#define NN_MACOS
#elif TARGET_OS_IPHONE
#error "iPhone are not supported"
#elif TARGET_OS_MAC
#define NN_MACOS
#else
#error "Unknown Apple platform"
#endif
#elif __ANDROID__
#error "Android is not supported"
#elif __linux__ #elif __linux__
#define NN_LINUX #define NN_LINUX
#endif #endif
@ -168,7 +150,7 @@ void *nn_aralloc(nn_Arena *arena, size_t size) {
} }
} }
while(arena->nextCap < size) arena->nextCap *= 2; while(arena->nextCap <= size) arena->nextCap *= 2;
nn_ArenaBlock *newBlock = nn_arallocblock(&arena->ctx, arena->nextCap); nn_ArenaBlock *newBlock = nn_arallocblock(&arena->ctx, arena->nextCap);
if(newBlock == NULL) { if(newBlock == NULL) {
@ -397,8 +379,10 @@ typedef enum nn_BuiltinComponent {
typedef struct nn_ComponentType { typedef struct nn_ComponentType {
nn_Universe *universe; nn_Universe *universe;
void *userdata;
nn_Arena arena; nn_Arena arena;
const char *name; const char *name;
nn_ComponentHandler *handler;
// NULL-terminated // NULL-terminated
nn_ComponentMethod *methods; nn_ComponentMethod *methods;
size_t methodCount; size_t methodCount;
@ -412,8 +396,9 @@ typedef struct nn_Universe {
typedef struct nn_Component { typedef struct nn_Component {
char *address; char *address;
nn_ComponentType *ctype; nn_ComponentType *ctype;
size_t slot; int slot;
void *userdata; void *userdata;
void *state;
} nn_Component; } nn_Component;
// the values // the values
@ -496,6 +481,8 @@ nn_ComponentType *nn_createComponentType(nn_Universe *universe, const char *name
nn_ComponentType *ctype = nn_alloc(ctx, sizeof(nn_ComponentType)); nn_ComponentType *ctype = nn_alloc(ctx, sizeof(nn_ComponentType));
if(ctype == NULL) return NULL; if(ctype == NULL) return NULL;
ctype->universe = universe; ctype->universe = universe;
ctype->userdata = userdata;
ctype->handler = handler;
nn_Arena *arena = &ctype->arena; nn_Arena *arena = &ctype->arena;
nn_arinit(arena, ctx); nn_arinit(arena, ctx);
@ -586,10 +573,13 @@ nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char
return c; return c;
} }
static void nn_dropValue(nn_Value val);
static void nn_dropComponent(nn_Computer *computer, nn_Component c);
void nn_destroyComputer(nn_Computer *computer) { void nn_destroyComputer(nn_Computer *computer) {
nn_Context *ctx = &computer->universe->ctx; nn_Context *ctx = &computer->universe->ctx;
if(computer->arch.name != NULL) { if(computer->arch.name != NULL && computer->archState != NULL) {
nn_ArchitectureRequest req; nn_ArchitectureRequest req;
req.computer = computer; req.computer = computer;
req.globalState = computer->arch.state; req.globalState = computer->arch.state;
@ -598,6 +588,13 @@ void nn_destroyComputer(nn_Computer *computer) {
computer->arch.handler(&req); computer->arch.handler(&req);
} }
for(size_t i = 0; i < computer->stackSize; i++) {
nn_dropValue(computer->callstack[i]);
}
for(size_t i = 0; i < computer->componentLen; i++) {
nn_dropComponent(computer, computer->components[i]);
}
nn_free(ctx, computer->components, sizeof(nn_Component) * computer->componentCap); nn_free(ctx, computer->components, sizeof(nn_Component) * computer->componentCap);
nn_free(ctx, computer->deviceInfo, sizeof(nn_DeviceInfo) * computer->deviceInfoCap); nn_free(ctx, computer->deviceInfo, sizeof(nn_DeviceInfo) * computer->deviceInfoCap);
nn_strfree(ctx, computer->address); nn_strfree(ctx, computer->address);
@ -779,3 +776,374 @@ nn_Exit nn_tick(nn_Computer *computer) {
} }
return NN_OK; return NN_OK;
} }
nn_Exit nn_addComponent(nn_Computer *computer, nn_ComponentType *ctype, const char *address, int slot, void *userdata) {
if(computer->componentLen == computer->componentCap) return NN_ELIMIT;
nn_Component c;
c.address = nn_strdup(&computer->universe->ctx, address);
if(c.address == NULL) return NN_ENOMEM;
c.ctype = ctype;
c.slot = slot;
c.userdata = userdata;
c.state = NULL;
nn_ComponentRequest req;
req.typeUserdata = ctype->userdata;
req.compUserdata = userdata;
req.state = NULL;
req.computer = computer;
req.compAddress = address;
req.action = NN_COMP_INIT;
req.methodCalled = NULL;
nn_Exit err = ctype->handler(&req);
if(err != NN_OK) {
nn_strfree(&computer->universe->ctx, c.address);
return err;
}
// get the state back!
c.state = req.state;
computer->components[computer->componentLen++] = c;
// TODO: send the signal
return NN_OK;
}
bool nn_hasComponent(nn_Computer *computer, const char *address) {
for(size_t i = 0; i < computer->componentLen; i++) {
nn_Component *c = &computer->components[i];
if(nn_strcmp(c->address, address) == 0) {
return true;
}
}
return false;
}
bool nn_hasMethod(nn_Computer *computer, const char *address, const char *method) {
for(size_t i = 0; i < computer->componentLen; i++) {
nn_Component *c = &computer->components[i];
if(nn_strcmp(c->address, address) != 0) continue;
bool found = false;
for(size_t j = 0; j < c->ctype->methodCount; j++) {
if(nn_strcmp(c->ctype->methods[j].name, method) != 0) continue;
found = true;
break;
}
if(!found) return false;
nn_ComponentRequest req;
req.typeUserdata = c->ctype->userdata;
req.compUserdata = c->userdata;
req.state = c->state;
req.computer = computer;
req.compAddress = address;
req.action = NN_COMP_ENABLED;
req.methodCalled = method;
// default response in case it is not implemented
req.methodEnabled = true;
// should never error
c->ctype->handler(&req);
return req.methodEnabled;
}
return false;
}
static void nn_dropComponent(nn_Computer *computer, nn_Component c) {
nn_ComponentRequest req;
req.typeUserdata = c.ctype->userdata;
req.compUserdata = c.userdata;
req.state = c.state;
req.computer = computer;
req.compAddress = c.address;
req.action = NN_COMP_DEINIT;
req.methodCalled = NULL;
c.ctype->handler(&req);
nn_strfree(&computer->universe->ctx, c.address);
}
nn_Exit nn_removeComponent(nn_Computer *computer, const char *address) {
size_t j = 0;
nn_Component c;
c.address = NULL;
for(size_t i = 0; i < computer->componentLen; i++) {
if(nn_strcmp(computer->components[i].address, address) == 0) {
c = computer->components[i];
} else {
computer->components[j++] = computer->components[i];
}
}
computer->componentLen = j;
// already removed!
if(c.address == NULL) return NN_EBADSTATE;
nn_dropComponent(computer, c);
// TODO: send the signal
return NN_OK;
}
const char *nn_getComponentType(nn_Computer *computer, const char *address) {
for(size_t i = 0; i < computer->componentLen; i++) {
nn_Component *c = &computer->components[i];
if(nn_strcmp(c->address, address) == 0) {
return c->ctype->name;
}
}
return NULL;
}
int nn_getComponentSlot(nn_Computer *computer, const char *address) {
for(size_t i = 0; i < computer->componentLen; i++) {
nn_Component *c = &computer->components[i];
if(nn_strcmp(c->address, address) == 0) {
return c->slot;
}
}
return 0;
}
const nn_ComponentMethod *nn_getComponentMethods(nn_Computer *computer, const char *address, size_t *len) {
for(size_t i = 0; i < computer->componentLen; i++) {
nn_Component *c = &computer->components[i];
if(nn_strcmp(c->address, address) == 0) {
if(len != NULL) *len = c->ctype->methodCount;
return c->ctype->methods;
}
}
if(len != NULL) *len = 0;
return NULL;
}
static void nn_retainValue(nn_Value val) {
switch(val.type) {
case NN_VAL_NULL:
case NN_VAL_BOOL:
case NN_VAL_NUM:
case NN_VAL_USERDATA:
return;
case NN_VAL_STR:
val.string->refc++;
return;
case NN_VAL_TABLE:
val.table->refc++;
return;
}
}
static void nn_dropValue(nn_Value val) {
nn_Context ctx;
size_t size;
switch(val.type) {
case NN_VAL_NULL:
case NN_VAL_BOOL:
case NN_VAL_NUM:
case NN_VAL_USERDATA:
return;
case NN_VAL_STR:
val.string->refc--;
if(val.string->refc != 0) return;
ctx = val.string->ctx;
size = val.string->len + 1;
nn_free(&ctx, val.string, sizeof(nn_String) + sizeof(char) * size);
return;
case NN_VAL_TABLE:
val.table->refc--;
if(val.table->refc != 0) return;
ctx = val.table->ctx;
size = val.table->len;
nn_free(&ctx, val.table, sizeof(nn_Table) + sizeof(nn_Value) * size * 2);
return;
}
}
nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method);
bool nn_checkstack(nn_Computer *computer, size_t amount) {
return computer->stackSize + amount <= NN_MAX_STACK;
}
static nn_Exit nn_pushvalue(nn_Computer *computer, nn_Value val) {
if(!nn_checkstack(computer, 1)) {
nn_dropValue(val);
return NN_ENOSTACK;
}
computer->callstack[computer->stackSize++] = val;
return NN_OK;
}
nn_Exit nn_pushnull(nn_Computer *computer) {
return nn_pushvalue(computer, (nn_Value) {.type = NN_VAL_NULL});
}
nn_Exit nn_pushbool(nn_Computer *computer, bool truthy) {
return nn_pushvalue(computer, (nn_Value) {.type = NN_VAL_BOOL, .boolean = truthy});
}
nn_Exit nn_pushnumber(nn_Computer *computer, double num) {
return nn_pushvalue(computer, (nn_Value) {.type = NN_VAL_NUM, .number = num});
}
nn_Exit nn_pushstring(nn_Computer *computer, const char *str) {
return nn_pushlstring(computer, str, nn_strlen(str));
}
nn_Exit nn_pushlstring(nn_Computer *computer, const char *str, size_t len) {
nn_Context ctx = computer->universe->ctx;
nn_String *s = nn_alloc(&ctx, sizeof(nn_String) + sizeof(char) * (len + 1));
if(s == NULL) return NN_ENOMEM;
s->ctx = ctx;
s->refc = 1;
s->len = len;
nn_memcpy(s->data, str, sizeof(char) * len);
s->data[len] = '\0';
return nn_pushvalue(computer, (nn_Value) {.type = NN_VAL_STR, .string = s});
}
nn_Exit nn_pushuserdata(nn_Computer *computer, size_t userdataIdx) {
return nn_pushvalue(computer, (nn_Value) {.type = NN_VAL_USERDATA, .userdataIdx = userdataIdx});
}
nn_Exit nn_pusharraytable(nn_Computer *computer, size_t len) {
if(computer->stackSize < len) return NN_EBELOWSTACK;
nn_Context ctx = computer->universe->ctx;
nn_Table *t = nn_alloc(&ctx, sizeof(nn_Table) + sizeof(nn_Value) * len * 2);
if(t == NULL) return NN_ENOMEM;
t->ctx = ctx;
t->refc = 1;
t->len = len;
for(size_t i = 0; i < len; i++) {
t->vals[i*2].type = NN_VAL_NUM;
t->vals[i*2].number = (double)i;
t->vals[i*2+1] = computer->callstack[computer->stackSize - len + i];
}
computer->stackSize -= len;
return nn_pushvalue(computer, (nn_Value) {.type = NN_VAL_TABLE, .table = t});
}
nn_Exit nn_pushtable(nn_Computer *computer, size_t len) {
size_t size = len * 2;
if(computer->stackSize < size) return NN_EBELOWSTACK;
nn_Context ctx = computer->universe->ctx;
nn_Table *t = nn_alloc(&ctx, sizeof(nn_Table) + sizeof(nn_Value) * size);
if(t == NULL) return NN_ENOMEM;
t->ctx = ctx;
t->refc = 1;
t->len = len;
for(size_t i = 0; i < len*2; i++) {
t->vals[i] = computer->callstack[computer->stackSize - size + i];
}
computer->stackSize -= size;
return nn_pushvalue(computer, (nn_Value) {.type = NN_VAL_TABLE, .table = t});
}
nn_Exit nn_pop(nn_Computer *computer) {
return nn_popn(computer, 1);
}
nn_Exit nn_popn(nn_Computer *computer, size_t n) {
if(computer->stackSize < n) return NN_EBELOWSTACK;
for(size_t i = computer->stackSize - n; i < computer->stackSize; i++) {
nn_dropValue(computer->callstack[i]);
}
computer->stackSize -= n;
return NN_OK;
}
nn_Exit nn_dupe(nn_Computer *computer) {
return nn_dupen(computer, 1);
}
nn_Exit nn_dupen(nn_Computer *computer, size_t n) {
if(computer->stackSize < n) return NN_EBELOWSTACK;
if(!nn_checkstack(computer, n)) return NN_ENOSTACK;
for(size_t i = 0; i < n; i++) {
nn_Value v = computer->callstack[computer->stackSize - n + i];
nn_retainValue(v);
computer->callstack[computer->stackSize + i] = v;
}
computer->stackSize += n;
return NN_OK;
}
size_t nn_getstacksize(nn_Computer *computer) {
return computer->stackSize;
}
void nn_clearstack(nn_Computer *computer) {
nn_popn(computer, nn_getstacksize(computer));
}
bool nn_isnull(nn_Computer *computer, size_t idx) {
if(idx < computer->stackSize) return false;
return computer->callstack[idx].type == NN_VAL_NULL;
}
bool nn_isboolean(nn_Computer *computer, size_t idx) {
if(idx < computer->stackSize) return false;
return computer->callstack[idx].type == NN_VAL_BOOL;
}
bool nn_isnumber(nn_Computer *computer, size_t idx) {
if(idx < computer->stackSize) return false;
return computer->callstack[idx].type == NN_VAL_NUM;
}
bool nn_isstring(nn_Computer *computer, size_t idx) {
if(idx < computer->stackSize) return false;
return computer->callstack[idx].type == NN_VAL_STR;
}
bool nn_isuserdata(nn_Computer *computer, size_t idx) {
if(idx < computer->stackSize) return false;
return computer->callstack[idx].type == NN_VAL_USERDATA;
}
bool nn_istable(nn_Computer *computer, size_t idx) {
if(idx < computer->stackSize) return false;
return computer->callstack[idx].type == NN_VAL_TABLE;
}
bool nn_toboolean(nn_Computer *computer, size_t idx) {
return computer->callstack[idx].boolean;
}
double nn_tonumber(nn_Computer *computer, size_t idx) {
return computer->callstack[idx].number;
}
const char *nn_tostring(nn_Computer *computer, size_t idx) {
return nn_tolstring(computer, idx, NULL);
}
const char *nn_tolstring(nn_Computer *computer, size_t idx, size_t *len) {
nn_String *s = computer->callstack[idx].string;
if(len != NULL) *len = s->len;
return s->data;
}
size_t nn_touserdata(nn_Computer *computer, size_t idx) {
return computer->callstack[idx].userdataIdx;
}
nn_Exit nn_dumptable(nn_Computer *computer, size_t idx, size_t *len) {
nn_Table *t = computer->callstack[idx].table;
if(!nn_checkstack(computer, t->len * 2)) return NN_ENOSTACK;
if(len != NULL) *len = t->len;
for(size_t i = 0; i < t->len * 2; i++) {
computer->callstack[computer->stackSize + i] = t->vals[i];
}
computer->stackSize += t->len * 2;
return NN_OK;
}
// todo: everything
nn_Exit nn_initComponentsLibrary(nn_Universe *universe);

View File

@ -341,12 +341,14 @@ typedef nn_Exit nn_ComponentHandler(nn_ComponentRequest *req);
// Creates a new component type. It is safe to free name and methods afterwards. // Creates a new component type. It is safe to free name and methods afterwards.
nn_ComponentType *nn_createComponentType(nn_Universe *universe, const char *name, void *userdata, const nn_ComponentMethod methods[], nn_ComponentHandler *handler); nn_ComponentType *nn_createComponentType(nn_Universe *universe, const char *name, void *userdata, const nn_ComponentMethod methods[], nn_ComponentHandler *handler);
// NOTE: do not destroy this before destroying any components using it, or any computers with components using it.
// The component type is still used one last time for the destructor of the components.
void nn_destroyComponentType(nn_ComponentType *ctype); void nn_destroyComponentType(nn_ComponentType *ctype);
// adds a component. Outside of the initialization state (aka after the first tick), it also emits the signal for component added. // adds a component. Outside of the initialization state (aka after the first tick), it also emits the signal for component added.
// You MUST NOT destroy the component type while a component using that type still exists. // You MUST NOT destroy the component type while a component using that type still exists.
// You can free the address after the call just fine. // You can free the address after the call just fine.
nn_Exit nn_addComponent(nn_Computer *computer, nn_ComponentType *ctype, const char *address, size_t slot, void *userdata); nn_Exit nn_addComponent(nn_Computer *computer, nn_ComponentType *ctype, const char *address, int slot, void *userdata);
// Checks if a component of that address exists. // Checks if a component of that address exists.
bool nn_hasComponent(nn_Computer *computer, const char *address); bool nn_hasComponent(nn_Computer *computer, const char *address);
// Checks if the component has that method. // Checks if the component has that method.
@ -358,7 +360,7 @@ nn_Exit nn_removeComponent(nn_Computer *computer, const char *address);
// Gets the name of a type of a component. // Gets the name of a type of a component.
const char *nn_getComponentType(nn_Computer *computer, const char *address); const char *nn_getComponentType(nn_Computer *computer, const char *address);
// Gets the slot of a component. // Gets the slot of a component.
size_t nn_getComponentSlot(nn_Computer *computer, const char *address); int nn_getComponentSlot(nn_Computer *computer, const char *address);
// Returns the array of component methods. This can be used for doc strings or just listing methods. // Returns the array of component methods. This can be used for doc strings or just listing methods.
const nn_ComponentMethod *nn_getComponentMethods(nn_Computer *computer, const char *address, size_t *len); const nn_ComponentMethod *nn_getComponentMethods(nn_Computer *computer, const char *address, size_t *len);
@ -373,6 +375,9 @@ nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method);
// Internally, reference counting is used to manage the memory automatically. The API is designed such that strong reference cycles // Internally, reference counting is used to manage the memory automatically. The API is designed such that strong reference cycles
// cannot occur. // cannot occur.
// returns if there is enough space for [amount] values
bool nn_checkstack(nn_Computer *computer, size_t amount);
// pushes a null on the call stack // pushes a null on the call stack
nn_Exit nn_pushnull(nn_Computer *computer); nn_Exit nn_pushnull(nn_Computer *computer);
// pushes a boolean on the call stack // pushes a boolean on the call stack
@ -432,7 +437,7 @@ bool nn_isuserdata(nn_Computer *computer, size_t idx);
// [idx] starts at 0. // [idx] starts at 0.
bool nn_istable(nn_Computer *computer, size_t idx); bool nn_istable(nn_Computer *computer, size_t idx);
// NOTE: behavior of the nn_to*() functions when the types mismatch is undefined. // NOTE: behavior of the nn_to*() functions and nn_dumptable() when the values have the wrong types or at out of bounds indexes is undefined.
// Returns the boolean value at [idx]. // Returns the boolean value at [idx].
bool nn_toboolean(nn_Computer *computer, size_t idx); bool nn_toboolean(nn_Computer *computer, size_t idx);