feat: add libuv event loop integration with timer module and CLI restructuring

Add a vertical slice of real libuv integration by introducing a "timer" CLI module with a Timer class and static sleep() method. This required significant infrastructure changes: setting up libuv event loop in the CLI, creating a new src/module directory for CLI modules, updating all build scripts (Makefile, wren.mk, Xcode project) to handle the new module compilation, and reorganizing CLI code by moving REPL logic from main.c to vm.c. The generate_builtins.py script was also updated to support copying Wren sources into the module directory.
This commit is contained in:
Bob Nystrom
2015-08-07 15:10:55 +00:00
parent e7bdca2cfa
commit 810da1f0fc
13 changed files with 456 additions and 70 deletions
+4
View File
@@ -4,6 +4,8 @@
#include "io.h"
#include "timer.h"
char const* rootDirectory = NULL;
// Reads the contents of the file at [path] and returns it as a heap allocated
@@ -71,6 +73,8 @@ char* wrenFilePath(const char* name)
char* readModule(WrenVM* vm, const char* module)
{
if (strcmp(module, "timer") == 0) return timerGetSource();
// First try to load the module with a ".wren" extension.
char* modulePath = wrenFilePath(module);
char* moduleContents = readFile(modulePath);
+2 -33
View File
@@ -5,37 +5,6 @@
#include "vm.h"
#include "wren.h"
#define MAX_LINE_LENGTH 1024 // TODO: Something less arbitrary.
static int runRepl()
{
WrenVM* vm = createVM(NULL);
printf("\\\\/\"-\n");
printf(" \\_/ wren v0.0.0\n");
char line[MAX_LINE_LENGTH];
for (;;)
{
printf("> ");
if (!fgets(line, MAX_LINE_LENGTH, stdin))
{
printf("\n");
break;
}
// TODO: Handle failure.
wrenInterpret(vm, "Prompt", line);
// TODO: Automatically print the result of expressions.
}
wrenFreeVM(vm);
return 0;
}
int main(int argc, const char* argv[])
{
if (argc < 1 || argc > 2)
@@ -43,14 +12,14 @@ int main(int argc, const char* argv[])
fprintf(stderr, "Usage: wren [file]\n");
return 64; // EX_USAGE.
}
if (argc == 1)
{
runRepl();
}
else if (argc == 2)
{
runFile(NULL, argv[1]);
runFile(argv[1], NULL);
}
return 0;
+100 -9
View File
@@ -3,12 +3,41 @@
#include "io.h"
#include "vm.h"
#include "timer.h"
WrenVM* createVM(WrenBindForeignMethodFn bindForeign)
#define MAX_LINE_LENGTH 1024 // TODO: Something less arbitrary.
// The single VM instance that the CLI uses.
WrenVM* vm;
WrenBindForeignMethodFn externalBindForeign;
uv_loop_t* loop;
static WrenForeignMethodFn bindForeignMethod(WrenVM* vm,
const char* module,
const char* className,
bool isStatic,
const char* signature)
{
if (strcmp(module, "timer") == 0)
{
return timerBindForeign(vm, className, isStatic, signature);
}
if (externalBindForeign != NULL)
{
return externalBindForeign(vm, module, className, isStatic, signature);
}
return NULL;
}
static void initVM()
{
WrenConfiguration config;
config.bindForeignMethodFn = bindForeign;
config.bindForeignMethodFn = bindForeignMethod;
config.loadModuleFn = readModule;
// Since we're running in a standalone process, be generous with memory.
@@ -18,12 +47,28 @@ WrenVM* createVM(WrenBindForeignMethodFn bindForeign)
config.reallocateFn = NULL;
config.minHeapSize = 0;
config.heapGrowthPercent = 0;
return wrenNewVM(&config);
vm = wrenNewVM(&config);
// Initialize the event loop.
loop = (uv_loop_t*)malloc(sizeof(uv_loop_t));
uv_loop_init(loop);
}
void runFile(WrenBindForeignMethodFn bindForeign, const char* path)
static void freeVM()
{
timerReleaseMethods();
uv_loop_close(loop);
free(loop);
wrenFreeVM(vm);
}
void runFile(const char* path, WrenBindForeignMethodFn bindForeign)
{
externalBindForeign = bindForeign;
// Use the directory where the file is as the root to resolve imports
// relative to.
char* root = NULL;
@@ -42,12 +87,18 @@ void runFile(WrenBindForeignMethodFn bindForeign, const char* path)
fprintf(stderr, "Could not find file \"%s\".\n", path);
exit(66);
}
WrenVM* vm = createVM(bindForeign);
initVM();
WrenInterpretResult result = wrenInterpret(vm, path, source);
if (result == WREN_RESULT_SUCCESS)
{
uv_run(loop, UV_RUN_DEFAULT);
}
wrenFreeVM(vm);
freeVM();
free(source);
free(root);
@@ -55,3 +106,43 @@ void runFile(WrenBindForeignMethodFn bindForeign, const char* path)
if (result == WREN_RESULT_COMPILE_ERROR) exit(65); // EX_DATAERR.
if (result == WREN_RESULT_RUNTIME_ERROR) exit(70); // EX_SOFTWARE.
}
int runRepl()
{
initVM();
printf("\\\\/\"-\n");
printf(" \\_/ wren v0.0.0\n");
char line[MAX_LINE_LENGTH];
for (;;)
{
printf("> ");
if (!fgets(line, MAX_LINE_LENGTH, stdin))
{
printf("\n");
break;
}
// TODO: Handle failure.
wrenInterpret(vm, "Prompt", line);
// TODO: Automatically print the result of expressions.
}
freeVM();
return 0;
}
WrenVM* getVM()
{
return vm;
}
uv_loop_t* getLoop()
{
return loop;
}
+13 -5
View File
@@ -1,14 +1,22 @@
#ifndef vm_h
#define vm_h
#include "uv.h"
#include "wren.h"
// Creates a new Wren VM with the CLI's module loader and other configuration.
WrenVM* createVM(WrenBindForeignMethodFn bindForeign);
// Executes the Wren script at [path] in a new VM.
//
// Exits if the script failed or could not be loaded.
void runFile(WrenBindForeignMethodFn bindForeign, const char* path);
// If [bindForeign] is not `NULL`, it will be called to register any foreign
// methods that the CLI itself doesn't handle.
void runFile(const char* path, WrenBindForeignMethodFn bindForeign);
// Runs the Wren interactive REPL.
int runRepl();
// Gets the currently running VM.
WrenVM* getVM();
// Gets the event loop the VM is using.
uv_loop_t* getLoop();
#endif