From af6b73208cbd85c1d99a84280925d8a5269b0a2d Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Mon, 25 Nov 2013 07:47:02 -0800 Subject: [PATCH] Start defining actual API for embedding Wren. --- include/wren.h | 39 ++++++++++++++++++++++ src/compiler.c | 4 +-- src/compiler.h | 2 +- src/main.c | 55 +++++++++++++++--------------- src/primitives.c | 8 ++--- src/primitives.h | 2 +- src/value.h | 7 ++-- src/vm.c | 61 +++++++++++++++++++++------------- src/vm.h | 23 ++++++------- wren.xcodeproj/project.pbxproj | 10 ++++++ 10 files changed, 137 insertions(+), 74 deletions(-) create mode 100644 include/wren.h diff --git a/include/wren.h b/include/wren.h new file mode 100644 index 00000000..0da9bad1 --- /dev/null +++ b/include/wren.h @@ -0,0 +1,39 @@ +#ifndef wren_h +#define wren_h + +#include + +typedef struct WrenVM WrenVM; + +// A generic allocation that handles all explicit memory management used by +// Wren. It's used like so: +// +// - To allocate new memory, [memory] is NULL and [oldSize] is zero. +// +// - To attempt to grow an existing allocation, [memory] is the memory, +// [oldSize] is its previous size, and [newSize] is the desired size. +// It returns [memory] if it was able to grow it in place, or a new pointer +// if it had to move it. +// +// - To shrink memory, [memory], [oldSize], and [newSize] are the same as above +// but it will always return [memory]. If [newSize] is zero, the memory will +// be freed and `NULL` will be returned. +// +// - To free memory, [newSize] will be zero. +typedef void* (*WrenReallocateFn)(void* memory, size_t oldSize, size_t newSize); + +// Creates a new Wren virtual machine. It allocates memory for the VM itself +// using [reallocateFn] and then uses that throughout its lifetime to manage +// memory. +WrenVM* wrenNewVM(WrenReallocateFn reallocateFn); + +// Disposes of all resources is use by [vm], which was previously created by a +// call to [wrenNewVM]. +void wrenFreeVM(WrenVM* vm); + +// Runs [source], a string of Wren source code in a new fiber in [vm]. Returns +// zero if successful. +// TODO(bob): Define error codes. +int wrenInterpret(WrenVM* vm, const char* source); + +#endif diff --git a/src/compiler.c b/src/compiler.c index 67a0e937..61b68424 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -87,7 +87,7 @@ typedef struct Token_s typedef struct { - VM* vm; + WrenVM* vm; const char* source; @@ -1510,7 +1510,7 @@ void definition(Compiler* compiler) // Parses [source] to a "function" (a chunk of top-level code) for execution by // [vm]. -ObjFn* compile(VM* vm, const char* source) +ObjFn* wrenCompile(WrenVM* vm, const char* source) { Parser parser; parser.vm = vm; diff --git a/src/compiler.h b/src/compiler.h index 2f79b0cf..446cea06 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -3,6 +3,6 @@ #include "vm.h" -ObjFn* compile(VM* vm, const char* source); +ObjFn* wrenCompile(WrenVM* vm, const char* source); #endif diff --git a/src/main.c b/src/main.c index 94b5a4d5..696f005a 100644 --- a/src/main.c +++ b/src/main.c @@ -3,13 +3,15 @@ #include #include -#include "compiler.h" -#include "vm.h" +#include "wren.h" + +// This is the source file for the standalone command line interpreter. It is +// not needed if you are embedding Wren in an application. // TODO(bob): Don't hardcode this. #define MAX_LINE 1024 -void failIf(int condition, int exitCode, const char* format, ...) +static void failIf(int condition, int exitCode, const char* format, ...) { if (!condition) return; @@ -21,7 +23,7 @@ void failIf(int condition, int exitCode, const char* format, ...) exit(exitCode); } -char* readFile(const char* path) +static char* readFile(const char* path) { FILE* file = fopen(path, "r"); failIf(file == NULL, 66, "Could not open file \"%s\".\n", path); @@ -46,38 +48,38 @@ char* readFile(const char* path) return buffer; } -int runFile(const char* path) +static void* reallocate(void* memory, size_t oldSize, size_t newSize) { - char* source = readFile(path); - VM* vm = newVM(); - ObjFn* fn = compile(vm, source); - - int exitCode = 0; - if (fn != NULL) - { - interpret(vm, fn); - } - else - { - exitCode = 1; - } - - freeVM(vm); - free(source); - - return exitCode; + return realloc(memory, newSize); } -int runRepl() +static int runFile(const char* path) { - VM* vm = newVM(); + char* source = readFile(path); + WrenVM* vm = wrenNewVM(reallocate); + + int result = wrenInterpret(vm, source); + + wrenFreeVM(vm); + free(source); + + return result; +} + +static int runRepl() +{ + WrenVM* vm = wrenNewVM(reallocate); for (;;) { printf("> "); char line[MAX_LINE]; fgets(line, MAX_LINE, stdin); + // TODO(bob): Handle failure. + wrenInterpret(vm, line); + // TODO(bob): Figure out how this should work with wren API. + /* ObjFn* fn = compile(vm, line); if (fn != NULL) @@ -87,9 +89,10 @@ int runRepl() printValue(result); printf("\n"); } + */ } - freeVM(vm); + wrenFreeVM(vm); return 0; } diff --git a/src/primitives.c b/src/primitives.c index ae60a628..7107e396 100644 --- a/src/primitives.c +++ b/src/primitives.c @@ -23,10 +23,10 @@ } #define DEF_PRIMITIVE(prim) \ - static Value primitive_##prim(VM* vm, Value* args) + static Value primitive_##prim(WrenVM* vm, Value* args) #define DEF_FIBER_PRIMITIVE(prim) \ - static void primitive_##prim(VM* vm, Fiber* fiber, Value* args) + static void primitive_##prim(WrenVM* vm, Fiber* fiber, Value* args) DEF_PRIMITIVE(bool_not) { @@ -313,9 +313,9 @@ static const char* CORE_LIB = "var io = IO.new\n" "class OS {}\n"; -void loadCore(VM* vm) +void wrenLoadCore(WrenVM* vm) { - ObjFn* core = compile(vm, CORE_LIB); + ObjFn* core = wrenCompile(vm, CORE_LIB); interpret(vm, core); vm->boolClass = AS_CLASS(findGlobal(vm, "Bool")); diff --git a/src/primitives.h b/src/primitives.h index c21645e6..9f6021a7 100644 --- a/src/primitives.h +++ b/src/primitives.h @@ -3,6 +3,6 @@ #include "vm.h" -void loadCore(VM* vm); +void wrenLoadCore(WrenVM* vm); #endif diff --git a/src/value.h b/src/value.h index fd92158f..ed9991e6 100644 --- a/src/value.h +++ b/src/value.h @@ -4,6 +4,7 @@ #include #include "common.h" +#include "wren.h" // TODO(bob): This should be in VM. (Or, really, we shouldn't hardcode this at // all and have growable symbol tables.) @@ -60,11 +61,10 @@ typedef struct #endif -typedef struct sVM VM; typedef struct sFiber Fiber; -typedef Value (*Primitive)(VM* vm, Value* args); -typedef void (*FiberPrimitive)(VM* vm, Fiber* fiber, Value* args); +typedef Value (*Primitive)(WrenVM* vm, Value* args); +typedef void (*FiberPrimitive)(WrenVM* vm, Fiber* fiber, Value* args); typedef struct { @@ -176,7 +176,6 @@ typedef struct #ifdef NAN_TAGGING - // An IEEE 754 double-precision float is a 64-bit value with bits laid out like: // // 1 Sign bit diff --git a/src/vm.c b/src/vm.c index f43f97a2..03049e28 100644 --- a/src/vm.c +++ b/src/vm.c @@ -3,13 +3,15 @@ #include #include "common.h" +#include "compiler.h" #include "primitives.h" #include "vm.h" +#include "wren.h" -VM* newVM() +WrenVM* wrenNewVM(WrenReallocateFn reallocateFn) { // TODO(bob): Get rid of explicit malloc() here. - VM* vm = malloc(sizeof(VM)); + WrenVM* vm = reallocateFn(NULL, 0, sizeof(WrenVM)); initSymbolTable(&vm->methods); initSymbolTable(&vm->globalSymbols); @@ -29,19 +31,29 @@ VM* newVM() vm->globals[i] = NULL_VAL; } - loadCore(vm); + wrenLoadCore(vm); return vm; } -void freeVM(VM* vm) +void wrenFreeVM(WrenVM* vm) { clearSymbolTable(&vm->methods); clearSymbolTable(&vm->globalSymbols); free(vm); } -static void collectGarbage(VM* vm); +int wrenInterpret(WrenVM* vm, const char* source) +{ + ObjFn* fn = wrenCompile(vm, source); + if (fn == NULL) return 1; + + // TODO(bob): Return error code on runtime errors. + interpret(vm, fn); + return 0; +} + +static void collectGarbage(WrenVM* vm); // A generic allocation that handles all memory changes, like so: // @@ -57,7 +69,8 @@ static void collectGarbage(VM* vm); // be freed and `NULL` will be returned. // // - To free memory, [newSize] will be zero. -void* reallocate(VM* vm, void* memory, size_t oldSize, size_t newSize) +static void* reallocate(WrenVM* vm, void* memory, + size_t oldSize, size_t newSize) { ASSERT(memory == NULL || oldSize > 0, "Cannot take unsized previous memory."); @@ -95,12 +108,12 @@ void* reallocate(VM* vm, void* memory, size_t oldSize, size_t newSize) return realloc(memory, newSize); } -static void* allocate(VM* vm, size_t size) +static void* allocate(WrenVM* vm, size_t size) { return reallocate(vm, NULL, 0, size); } -static void* deallocate(VM* vm, void* memory, size_t oldSize) +static void* deallocate(WrenVM* vm, void* memory, size_t oldSize) { return reallocate(vm, memory, oldSize, 0); } @@ -201,7 +214,7 @@ void markValue(Value value) markObj(AS_OBJ(value)); } -void freeObj(VM* vm, Obj* obj) +static void freeObj(WrenVM* vm, Obj* obj) { #ifdef TRACE_MEMORY printf("free "); @@ -261,7 +274,7 @@ void freeObj(VM* vm, Obj* obj) deallocate(vm, obj, size); } -void collectGarbage(VM* vm) +static void collectGarbage(WrenVM* vm) { // Mark all reachable objects. #ifdef TRACE_MEMORY @@ -315,7 +328,7 @@ void collectGarbage(VM* vm) } } -void initObj(VM* vm, Obj* obj, ObjType type) +static void initObj(WrenVM* vm, Obj* obj, ObjType type) { obj->type = type; obj->flags = 0; @@ -323,7 +336,7 @@ void initObj(VM* vm, Obj* obj, ObjType type) vm->first = obj; } -static ObjClass* newSingleClass(VM* vm, ObjClass* metaclass, +static ObjClass* newSingleClass(WrenVM* vm, ObjClass* metaclass, ObjClass* superclass, int numFields) { ObjClass* obj = allocate(vm, sizeof(ObjClass)); @@ -340,7 +353,7 @@ static ObjClass* newSingleClass(VM* vm, ObjClass* metaclass, return obj; } -ObjClass* newClass(VM* vm, ObjClass* superclass, int numFields) +ObjClass* newClass(WrenVM* vm, ObjClass* superclass, int numFields) { // Make the metaclass. // TODO(bob): What is the metaclass's metaclass and superclass? @@ -369,7 +382,7 @@ ObjClass* newClass(VM* vm, ObjClass* superclass, int numFields) return classObj; } -ObjFn* newFunction(VM* vm) +ObjFn* newFunction(WrenVM* vm) { // Allocate these before the function in case they trigger a GC which would // free the function. @@ -386,7 +399,7 @@ ObjFn* newFunction(VM* vm) return fn; } -Value newInstance(VM* vm, ObjClass* classObj) +Value newInstance(WrenVM* vm, ObjClass* classObj) { ObjInstance* instance = allocate(vm, sizeof(ObjInstance) + classObj->numFields * sizeof(Value)); @@ -402,7 +415,7 @@ Value newInstance(VM* vm, ObjClass* classObj) return OBJ_VAL(instance); } -ObjList* newList(VM* vm, int numElements) +ObjList* newList(WrenVM* vm, int numElements) { // Allocate this before the list object in case it triggers a GC which would // free the list. @@ -419,7 +432,7 @@ ObjList* newList(VM* vm, int numElements) return list; } -Value newString(VM* vm, const char* text, size_t length) +Value newString(WrenVM* vm, const char* text, size_t length) { // Allocate before the string object in case this triggers a GC which would // free the string object. @@ -508,14 +521,14 @@ const char* getSymbolName(SymbolTable* symbols, int symbol) return symbols->names[symbol]; } -Value findGlobal(VM* vm, const char* name) +Value findGlobal(WrenVM* vm, const char* name) { int symbol = findSymbol(&vm->globalSymbols, name, strlen(name)); // TODO(bob): Handle failure. return vm->globals[symbol]; } -int dumpInstruction(VM* vm, ObjFn* fn, int i) +int dumpInstruction(WrenVM* vm, ObjFn* fn, int i) { int start = i; unsigned char* bytecode = fn->bytecode; @@ -739,7 +752,7 @@ int dumpInstruction(VM* vm, ObjFn* fn, int i) } // TODO(bob): For debugging. Move to separate module. -void dumpCode(VM* vm, ObjFn* fn) +void dumpCode(WrenVM* vm, ObjFn* fn) { int i = 0; for (;;) @@ -751,7 +764,7 @@ void dumpCode(VM* vm, ObjFn* fn) } // Returns the class of [object]. -static ObjClass* getClass(VM* vm, Value value) +static ObjClass* getClass(WrenVM* vm, Value value) { // TODO(bob): Unify these. #ifdef NAN_TAGGING if (IS_NUM(value)) return vm->numClass; @@ -810,7 +823,7 @@ void dumpStack(Fiber* fiber) printf("\n"); } -Value interpret(VM* vm, ObjFn* fn) +Value interpret(WrenVM* vm, ObjFn* fn) { Fiber* fiber = vm->fiber; callFunction(fiber, fn, 0); @@ -1290,13 +1303,13 @@ void printValue(Value value) #endif } -void pinObj(VM* vm, Obj* obj) +void pinObj(WrenVM* vm, Obj* obj) { ASSERT(vm->numPinned < MAX_PINNED - 1, "Too many pinned objects."); vm->pinned[vm->numPinned++] = obj; } -void unpinObj(VM* vm, Obj* obj) +void unpinObj(WrenVM* vm, Obj* obj) { ASSERT(vm->pinned[vm->numPinned - 1] == obj, "Unpinning out of stack order."); vm->numPinned--; diff --git a/src/vm.h b/src/vm.h index aecd4769..69e0a2d3 100644 --- a/src/vm.h +++ b/src/vm.h @@ -117,7 +117,7 @@ typedef struct int count; } SymbolTable; -struct sVM +struct WrenVM { SymbolTable methods; @@ -184,25 +184,24 @@ struct sFiber int numFrames; }; -VM* newVM(); -void freeVM(VM* vm); +// TODO(bob): Make these static or prefix their names. // Creates a new function object. Assumes the compiler will fill it in with // bytecode, constants, etc. -ObjFn* newFunction(VM* vm); +ObjFn* newFunction(WrenVM* vm); // Creates a new class object. -ObjClass* newClass(VM* vm, ObjClass* superclass, int numFields); +ObjClass* newClass(WrenVM* vm, ObjClass* superclass, int numFields); // Creates a new instance of the given [classObj]. -Value newInstance(VM* vm, ObjClass* classObj); +Value newInstance(WrenVM* vm, ObjClass* classObj); // Creates a new list with [numElements] elements (which are left // uninitialized.) -ObjList* newList(VM* vm, int numElements); +ObjList* newList(WrenVM* vm, int numElements); // Creates a new string object and copies [text] into it. -Value newString(VM* vm, const char* text, size_t length); +Value newString(WrenVM* vm, const char* text, size_t length); // Initializes the symbol table. void initSymbolTable(SymbolTable* symbols); @@ -229,9 +228,9 @@ int findSymbol(SymbolTable* symbols, const char* name, size_t length); const char* getSymbolName(SymbolTable* symbols, int symbol); // Returns the global variable named [name]. -Value findGlobal(VM* vm, const char* name); +Value findGlobal(WrenVM* vm, const char* name); -Value interpret(VM* vm, ObjFn* fn); +Value interpret(WrenVM* vm, ObjFn* fn); // Push [fn] onto [fiber]'s callstack and invoke it. Expects [numArgs] // arguments (including the receiver) to be on the top of the stack already. @@ -240,9 +239,9 @@ void callFunction(Fiber* fiber, ObjFn* fn, int numArgs); void printValue(Value value); // Mark [obj] as a GC root so that it doesn't get collected. -void pinObj(VM* vm, Obj* obj); +void pinObj(WrenVM* vm, Obj* obj); // Unmark [obj] as a GC root so that it doesn't get collected. -void unpinObj(VM* vm, Obj* obj); +void unpinObj(WrenVM* vm, Obj* obj); #endif diff --git a/wren.xcodeproj/project.pbxproj b/wren.xcodeproj/project.pbxproj index 66a3db41..f0d29b3c 100644 --- a/wren.xcodeproj/project.pbxproj +++ b/wren.xcodeproj/project.pbxproj @@ -30,6 +30,7 @@ 292A45D31838566400C34813 /* value.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = value.h; path = src/value.h; sourceTree = SOURCE_ROOT; }; 292A45D41838566F00C34813 /* value.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = value.c; path = src/value.c; sourceTree = SOURCE_ROOT; }; 296681E2183283A500C1407C /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = common.h; path = src/common.h; sourceTree = SOURCE_ROOT; }; + 2969FFC71843A16C0007B143 /* wren.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = wren.h; path = include/wren.h; sourceTree = ""; }; 29AB1F061816E3AD004B501E /* wren */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = wren; sourceTree = BUILT_PRODUCTS_DIR; }; 29AB1F201816E49C004B501E /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = main.c; path = src/main.c; sourceTree = SOURCE_ROOT; }; 29AB1F2D1816FA5B004B501E /* vm.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = vm.h; path = src/vm.h; sourceTree = SOURCE_ROOT; }; @@ -51,9 +52,18 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 2969FFC61843A1430007B143 /* include */ = { + isa = PBXGroup; + children = ( + 2969FFC71843A16C0007B143 /* wren.h */, + ); + name = include; + sourceTree = ""; + }; 29AB1EFD1816E3AD004B501E = { isa = PBXGroup; children = ( + 2969FFC61843A1430007B143 /* include */, 29AB1F081816E3AD004B501E /* src */, 29AB1F071816E3AD004B501E /* Products */, );