#include <stdio.h>
#include <string.h>
#include "io.h"
#include "modules.h"
#include "scheduler.h"
#include "vm.h"
static WrenVM* vm;
static WrenBindForeignMethodFn bindMethodFn = NULL;
static WrenBindForeignClassFn bindClassFn = NULL;
static WrenForeignMethodFn afterLoadFn = NULL;
static uv_loop_t* loop;
static char const* rootDirectory = NULL;
int defaultExitCode = 0;
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 == NULL)
{
fprintf(stderr, "Could not read file \"%s\".\n", path);
exit(74);
}
size_t bytesRead = fread(buffer, sizeof(char), fileSize, file);
if (bytesRead < fileSize)
{
fprintf(stderr, "Could not read file \"%s\".\n", path);
exit(74);
}
buffer[bytesRead] = '\0';
fclose(file);
return buffer;
}
static char* wrenFilePath(const char* name)
{
size_t rootLength = rootDirectory == NULL ? 0 : strlen(rootDirectory);
size_t nameLength = strlen(name);
size_t pathLength = rootLength + nameLength + 5;
char* path = (char*)malloc(pathLength + 1);
if (rootDirectory != NULL)
{
memcpy(path, rootDirectory, rootLength);
}
memcpy(path + rootLength, name, nameLength);
memcpy(path + rootLength + nameLength, ".wren", 5);
path[pathLength] = '\0';
return path;
}
static char* readModule(WrenVM* vm, const char* module)
{
char* source = readBuiltInModule(module);
if (source != NULL) return source;
char* modulePath = wrenFilePath(module);
char* moduleContents = readFile(modulePath);
free(modulePath);
if (moduleContents != NULL) return moduleContents;
size_t moduleLength = strlen(module);
size_t moduleDirLength = moduleLength + 7;
char* moduleDir = (char*)malloc(moduleDirLength + 1);
memcpy(moduleDir, module, moduleLength);
memcpy(moduleDir + moduleLength, "/module", 7);
moduleDir[moduleDirLength] = '\0';
char* moduleDirPath = wrenFilePath(moduleDir);
free(moduleDir);
moduleContents = readFile(moduleDirPath);
free(moduleDirPath);
return moduleContents;
}
static WrenForeignMethodFn bindForeignMethod(WrenVM* vm, const char* module,
const char* className, bool isStatic, const char* signature)
{
WrenForeignMethodFn method = bindBuiltInForeignMethod(vm, module, className,
isStatic, signature);
if (method != NULL) return method;
if (bindMethodFn != NULL)
{
return bindMethodFn(vm, module, className, isStatic, signature);
}
return NULL;
}
static WrenForeignClassMethods bindForeignClass(
WrenVM* vm, const char* module, const char* className)
{
WrenForeignClassMethods methods = bindBuiltInForeignClass(vm, module,
className);
if (methods.allocate != NULL) return methods;
if (bindClassFn != NULL)
{
return bindClassFn(vm, module, className);
}
return methods;
}
static void write(WrenVM* vm, const char* text)
{
printf("%s", text);
fflush(stdout);
}
static void reportError(WrenVM* vm, WrenErrorType type,
const char* module, int line, const char* message)
{
switch (type)
{
case WREN_ERROR_COMPILE:
fprintf(stderr, "[%s line %d] %s\n", module, line, message);
break;
case WREN_ERROR_RUNTIME:
fprintf(stderr, "%s\n", message);
break;
case WREN_ERROR_STACK_TRACE:
fprintf(stderr, "[%s line %d] in %s\n", module, line, message);
break;
}
}
static void initVM()
{
WrenConfiguration config;
wrenInitConfiguration(&config);
config.bindForeignMethodFn = bindForeignMethod;
config.bindForeignClassFn = bindForeignClass;
config.loadModuleFn = readModule;
config.writeFn = write;
config.errorFn = reportError;
config.initialHeapSize = 1024 * 1024 * 100;
vm = wrenNewVM(&config);
loop = (uv_loop_t*)malloc(sizeof(uv_loop_t));
uv_loop_init(loop);
}
static void freeVM()
{
ioShutdown();
schedulerShutdown();
uv_loop_close(loop);
free(loop);
wrenFreeVM(vm);
uv_tty_reset_mode();
}
void runFile(const char* path)
{
char* root = NULL;
const char* lastSlash = strrchr(path, '/');
if (lastSlash != NULL)
{
root = (char*)malloc(lastSlash - path + 2);
memcpy(root, path, lastSlash - path + 1);
root[lastSlash - path + 1] = '\0';
rootDirectory = root;
}
char* source = readFile(path);
if (source == NULL)
{
fprintf(stderr, "Could not find file \"%s\".\n", path);
exit(66);
}
initVM();
WrenInterpretResult result = wrenInterpret(vm, source);
if (afterLoadFn != NULL) afterLoadFn(vm);
if (result == WREN_RESULT_SUCCESS)
{
uv_run(loop, UV_RUN_DEFAULT);
}
freeVM();
free(source);
free(root);
if (result == WREN_RESULT_COMPILE_ERROR) exit(65); if (result == WREN_RESULT_RUNTIME_ERROR) exit(70);
if (defaultExitCode != 0) exit(defaultExitCode);
}
int runRepl()
{
initVM();
printf("\\\\/\"-\n");
printf(" \\_/ wren v%s\n", WREN_VERSION_STRING);
wrenInterpret(vm, "import \"repl\"\n");
uv_run(loop, UV_RUN_DEFAULT);
freeVM();
return 0;
}
WrenVM* getVM()
{
return vm;
}
uv_loop_t* getLoop()
{
return loop;
}
void setExitCode(int exitCode)
{
defaultExitCode = exitCode;
}
void setTestCallbacks(WrenBindForeignMethodFn bindMethod,
WrenBindForeignClassFn bindClass,
WrenForeignMethodFn afterLoad)
{
bindMethodFn = bindMethod;
bindClassFn = bindClass;
afterLoadFn = afterLoad;
}