207 lines
5.9 KiB
C
207 lines
5.9 KiB
C
|
#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;
|
||
|
}
|