#include #include #include #include #include #ifdef _WIN32 #include #else #include #endif #include "wren.h" #include "requests_backend.c" #include "socket_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 (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) { // Delegate to the socket backend's binder if (strcmp(module, "socket") == 0) { return bindSocketForeignMethod(vm, module, className, isStatic, signature); } // Delegate to the requests backend's binder if (strcmp(module, "requests") == 0) { return bindForeignMethod(vm, module, className, isStatic, signature); } // Handle host-specific methods 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) { // Delegate to the socket backend's class binder if (strcmp(module, "socket") == 0) { return bindSocketForeignClass(vm, module, className); } // Delegate to the requests backend's class binder if (strcmp(module, "requests") == 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; } // Initialize libcurl for the requests module 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 BOTH managers ** socketManager_create(vm); httpManager_create(vm); char* mainSource = readFile(argv[1]); if (!mainSource) { fprintf(stderr, "Could not open script: %s\n", argv[1]); socketManager_destroy(); httpManager_destroy(); wrenFreeVM(vm); curl_global_cleanup(); return 1; } wrenInterpret(vm, "main", mainSource); free(mainSource); if (g_mainFiberIsDone) { socketManager_destroy(); httpManager_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 BOTH managers ** socketManager_processCompletions(); httpManager_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; } // Prevent 100% CPU usage #ifdef _WIN32 Sleep(1); #else usleep(1000); // 1ms #endif } // Process any final completions before shutting down socketManager_processCompletions(); httpManager_processCompletions(); wrenReleaseHandle(vm, mainFiberHandle); wrenReleaseHandle(vm, callHandle); // ** Destroy BOTH managers ** socketManager_destroy(); httpManager_destroy(); wrenFreeVM(vm); curl_global_cleanup(); printf("\nHost application finished.\n"); return 0; }