#include #include #include #include #include #ifdef _WIN32 #include #else #include #endif #include "wren.h" // It's common practice to include the C source directly for small-to-medium // modules like this to simplify the build process. #include "requests_backend.c" #include "socket_backend.c" #include "string_backend.c" #include "sqlite3_backend.c" #include "io_backend.c" #include "crypto_backend.c" #include "gtk_backend.c" // --- Global flag to control the main loop --- static volatile bool g_mainFiberIsDone = false; // --- Foreign function for Wren to signal the host to exit --- void hostSignalDone(WrenVM* vm) { (void)vm; g_mainFiberIsDone = true; } // --- File/VM Setup --- static char* readFile(const char* path) { FILE* file = fopen(path, "rb"); if (file == NULL) return NULL; fseek(file, 0L, SEEK_END); size_t fileSize = ftell(file); rewind(file); char* buffer = (char*)malloc(fileSize + 1); if (!buffer) { fclose(file); return NULL; } size_t bytesRead = fread(buffer, sizeof(char), fileSize, file); if (bytesRead < fileSize) { free(buffer); fclose(file); return NULL; } buffer[bytesRead] = '\0'; fclose(file); return buffer; } static void writeFn(WrenVM* vm, const char* text) { (void)vm; printf("%s", text); } static void errorFn(WrenVM* vm, WrenErrorType type, const char* module, int line, const char* message) { (void)vm; switch (type) { case WREN_ERROR_COMPILE: fprintf(stderr, "[%s line %d] [Error] %s\n", module, line, message); break; case WREN_ERROR_RUNTIME: fprintf(stderr, "[Runtime Error] %s\n", message); g_mainFiberIsDone = true; // Stop on runtime errors break; case WREN_ERROR_STACK_TRACE: fprintf(stderr, "[%s line %d] in %s\n", module, line, message); break; } } static void onModuleComplete(WrenVM* vm, const char* name, WrenLoadModuleResult result) { (void)vm; (void)name; if (result.source) free((void*)result.source); } static WrenLoadModuleResult loadModule(WrenVM* vm, const char* name) { (void)vm; WrenLoadModuleResult result = {0}; char path[256]; snprintf(path, sizeof(path), "%s.wren", name); char* source = readFile(path); if (strcmp(name, "main") == 0) { // Load your main module with the String extensions const char* source = "import string\n" "class String {\n" " foreign endsWith(suffix)\n" " foreign startsWith(prefix)\n" " foreign replace(from, to)\n" " foreign split(delimiter)\n" " foreign toUpper()\n" " foreign toLower()\n" " foreign trim()\n" " foreign trimStart()\n" " foreign trimEnd()\n" " foreign count(substring)\n" " foreign indexOf(substring)\n" " foreign lastIndexOf(substring)\n" " foreign contains(substring)\n" " foreign toInt()\n" " foreign toNum()\n" " foreign reverse()\n" " foreign repeat(count)\n" " foreign padStart(length, padString)\n" " foreign padEnd(length, padString)\n" " foreign charAt(index)\n" " foreign charCodeAt(index)\n" " foreign isNumeric()\n" " foreign isAlpha()\n" " foreign isAlphaNumeric()\n" " foreign isEmpty()\n" " foreign isWhitespace()\n" " foreign capitalize()\n" " foreign toCamelCase()\n" " foreign toSnakeCase()\n" " foreign toKebabCase()\n" " foreign toPascalCase()\n" " foreign swapCase()\n" " foreign substring(start, end)\n" " foreign slice(start)\n" " foreign slice(start, end)\n" " foreign toBytes()\n" " static foreign length()\n" " \n" " static foreign join(list, separator)\n" " static foreign fromCharCode(code)\n" "}\n"; result.source = source; } if (source != NULL) { result.source = source; result.onComplete = onModuleComplete; } return result; } // --- Combined Foreign Function Binders --- WrenForeignMethodFn combinedBindForeignMethod(WrenVM* vm, const char* module, const char* className, bool isStatic, const char* signature) { if (strcmp(module, "socket") == 0) { return bindSocketForeignMethod(vm, module, className, isStatic, signature); } if (strcmp(module, "requests") == 0) { return bindForeignMethod(vm, module, className, isStatic, signature); } if (strcmp(module, "sqlite") == 0) { return bindSqliteForeignMethod(vm, module, className, isStatic, signature); } if (strcmp(module, "io") == 0) { return bindIoForeignMethod(vm, module, className, isStatic, signature); } if (strcmp(className, "String") == 0) { return bindStringForeignMethod(vm, module, className, isStatic, signature); } if (!strcmp(module,"gtk")){ return bindGtkForeignMethod(vm,module,className,isStatic,signature); } if (strcmp(module, "crypto") == 0) return bindCryptoForeignMethod(vm,module,className,isStatic,signature); // ← NEW if (strcmp(module, "main") == 0 && strcmp(className, "Host") == 0 && isStatic) { if (strcmp(signature, "signalDone()") == 0) return hostSignalDone; } return NULL; } WrenForeignClassMethods combinedBindForeignClass(WrenVM* vm, const char* module, const char* className) { if (strcmp(module, "socket") == 0) { return bindSocketForeignClass(vm, module, className); } if (strcmp(module, "requests") == 0) { return bindForeignClass(vm, module, className); } if (strcmp(module, "sqlite") == 0) { return bindSqliteForeignClass(vm, module, className); } if (strcmp(module, "io") == 0) { WrenForeignClassMethods methods = {0, 0}; return methods; } if (strcmp(module, "gtk") == 0){ return bindForeignClass(vm, module, className); } WrenForeignClassMethods methods = {0, 0}; return methods; } // --- Main Application Entry Point --- int main(int argc, char* argv[]) { if (argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } curl_global_init(CURL_GLOBAL_ALL); WrenConfiguration config; wrenInitConfiguration(&config); config.writeFn = writeFn; config.errorFn = errorFn; config.bindForeignMethodFn = combinedBindForeignMethod; config.bindForeignClassFn = combinedBindForeignClass; config.loadModuleFn = loadModule; WrenVM* vm = wrenNewVM(&config); // ** Initialize ALL managers ** socketManager_create(vm); httpManager_create(vm); dbManager_create(vm); char* mainSource = readFile(argv[1]); if (!mainSource) { fprintf(stderr, "Could not open script: %s\n", argv[1]); socketManager_destroy(); httpManager_destroy(); dbManager_destroy(); wrenFreeVM(vm); curl_global_cleanup(); return 1; } wrenInterpret(vm, "main", mainSource); free(mainSource); if (g_mainFiberIsDone) { socketManager_destroy(); httpManager_destroy(); dbManager_destroy(); wrenFreeVM(vm); curl_global_cleanup(); return 1; } wrenEnsureSlots(vm, 1); wrenGetVariable(vm, "main", "mainFiber", 0); WrenHandle* mainFiberHandle = wrenGetSlotHandle(vm, 0); WrenHandle* callHandle = wrenMakeCallHandle(vm, "call()"); // === Main Event Loop === while (!g_mainFiberIsDone) { // ** Process completions for ALL managers ** bool had_work = true; socketManager_processCompletions(); httpManager_processCompletions(); dbManager_processCompletions(); // Resume the main Wren fiber wrenEnsureSlots(vm, 1); wrenSetSlotHandle(vm, 0, mainFiberHandle); WrenInterpretResult result = wrenCall(vm, callHandle); if (result == WREN_RESULT_RUNTIME_ERROR) { g_mainFiberIsDone = true; } //socketManager_processCompletions(); //httpManager_processCompletions(); //dbManager_processCompletions(); if(!had_work) { // Prevent 100% CPU usage #ifdef _WIN32 Sleep(1); #else usleep(1); // 1ms #endif } } // Process any final completions before shutting down wrenReleaseHandle(vm, mainFiberHandle); wrenReleaseHandle(vm, callHandle); // ** Destroy ALL managers ** socketManager_destroy(); httpManager_destroy(); dbManager_destroy(); wrenFreeVM(vm); curl_global_cleanup(); printf("\nHost application finished.\n"); return 0; }