207 lines
5.9 KiB
C
Raw Normal View History

2025-07-29 14:35:38 +02:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <curl/curl.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <unistd.h>
#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 <script.wren>\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;
}