diff --git a/builtin/meta.wren b/builtin/meta.wren index 3db22bbf..3773b0d0 100644 --- a/builtin/meta.wren +++ b/builtin/meta.wren @@ -1 +1,13 @@ -class Meta {} +class Meta { + static eval(source) { + if (!(source is String)) Fiber.abort("Source code must be a string.") + + var fn = compile_(source) + // TODO: Include compile errors. + if (fn == null) Fiber.abort("Could not compile source code.") + + Fiber.new(fn).call() + } + + foreign static compile_(source) +} diff --git a/src/vm/wren_common.h b/src/vm/wren_common.h index 491a8ad1..009a3856 100644 --- a/src/vm/wren_common.h +++ b/src/vm/wren_common.h @@ -44,11 +44,11 @@ #endif #endif -// If true, loads the "Meta" class in the standard library. +// If true, loads the "meta" built in module. // // Defaults to on. -#ifndef WREN_USE_LIB_META - #define WREN_USE_LIB_META 1 +#ifndef WREN_USE_META_MODULE + #define WREN_USE_META_MODULE 1 #endif // These flags are useful for debugging and hacking on Wren itself. They are not diff --git a/src/vm/wren_core.c b/src/vm/wren_core.c index e9745702..3cab7978 100644 --- a/src/vm/wren_core.c +++ b/src/vm/wren_core.c @@ -1047,7 +1047,16 @@ static void fnCall(WrenVM* vm, const char* signature) void wrenInitializeCore(WrenVM* vm) { - ObjModule* coreModule = wrenGetCoreModule(vm); + ObjString* name = AS_STRING(CONST_STRING(vm, "core")); + wrenPushRoot(vm, (Obj*)name); + + ObjModule* coreModule = wrenNewModule(vm, name, NULL); + wrenPopRoot(vm); // name. + wrenPushRoot(vm, (Obj*)coreModule); + + // The core module's key is null in the module map. + wrenMapSet(vm, vm->modules, NULL_VAL, OBJ_VAL(coreModule)); + wrenPopRoot(vm); // coreModule. // Define the root Object class. This has to be done a little specially // because it has no superclass. @@ -1103,7 +1112,7 @@ void wrenInitializeCore(WrenVM* vm) // '---------' '-------------------' -' // The rest of the classes can now be defined normally. - wrenInterpret(vm, "", coreModuleSource); + wrenInterpretInModule(vm, NULL, "", coreModuleSource); vm->boolClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Bool")); PRIMITIVE(vm->boolClass, "toString", bool_toString); diff --git a/src/vm/wren_meta.c b/src/vm/wren_meta.c index 8d9e2955..21a4685b 100644 --- a/src/vm/wren_meta.c +++ b/src/vm/wren_meta.c @@ -1,6 +1,6 @@ #include "wren_meta.h" -#if WREN_USE_LIB_META +#if WREN_USE_META_MODULE #include @@ -8,46 +8,49 @@ #include "wren_meta.wren.inc" -DEF_PRIMITIVE(meta_eval) +void metaCompile(WrenVM* vm) { - if (!validateString(vm, args[1], "Source code")) return false; - - // Eval the code in the module where the calling function was defined. - Value callingFn = OBJ_VAL(vm->fiber->frames[vm->fiber->numFrames - 1].fn); + // Evaluate the code in the module where the calling function was defined. + // That's one stack frame back from the top since the top-most frame is the + // helper eval() method in Meta itself. + Value callingFn = OBJ_VAL(vm->fiber->frames[vm->fiber->numFrames - 2].fn); ObjModule* module = IS_FN(callingFn) ? AS_FN(callingFn)->module : AS_CLOSURE(callingFn)->fn->module; - - // Compile it. - ObjFn* fn = wrenCompile(vm, module, AS_CSTRING(args[1]), false); - if (fn == NULL) RETURN_ERROR("Could not compile source code."); - - // TODO: Include the compile errors in the runtime error message. - - wrenPushRoot(vm, (Obj*)fn); - - // Create a fiber to run the code in. - ObjFiber* evalFiber = wrenNewFiber(vm, (Obj*)fn); - - // Remember what fiber to return to. - evalFiber->caller = vm->fiber; - - // Switch to the fiber. - vm->fiber = evalFiber; - wrenPopRoot(vm); - return false; + // Compile it. + ObjFn* fn = wrenCompile(vm, module, wrenGetArgumentString(vm, 1), false); + if (fn == NULL) return; + + // Return the result. We can't use the public API for this since we have a + // bare ObjFn. + *vm->foreignCallSlot = OBJ_VAL(fn); + vm->foreignCallSlot = NULL; } -void wrenLoadMetaLibrary(WrenVM* vm) +static WrenForeignMethodFn bindMetaForeignMethods(WrenVM* vm, + const char* module, + const char* className, + bool isStatic, + const char* signature) { - wrenInterpret(vm, "", metaModuleSource); + // There is only one foreign method in the meta module. + ASSERT(strcmp(module, "meta") == 0, "Should be in meta module."); + ASSERT(strcmp(className, "Meta") == 0, "Should be in Meta class."); + ASSERT(isStatic, "Should be static."); + ASSERT(strcmp(signature, "compile_(_)") == 0, "Should be compile method."); + + return metaCompile; +} - ObjModule* coreModule = wrenGetCoreModule(vm); +void wrenLoadMetaModule(WrenVM* vm) +{ + WrenBindForeignMethodFn previousBindFn = vm->config.bindForeignMethodFn; + vm->config.bindForeignMethodFn = bindMetaForeignMethods; + + wrenInterpretInModule(vm, "meta", "meta", metaModuleSource); - // The methods on "Meta" are static, so get the metaclass for the Meta class. - ObjClass* meta = AS_CLASS(wrenFindVariable(vm, coreModule, "Meta")); - PRIMITIVE(meta->obj.classObj, "eval(_)", meta_eval); + vm->config.bindForeignMethodFn = previousBindFn; } #endif diff --git a/src/vm/wren_meta.h b/src/vm/wren_meta.h index 8592c946..a79bf99b 100644 --- a/src/vm/wren_meta.h +++ b/src/vm/wren_meta.h @@ -6,9 +6,9 @@ #include "wren.h" // This module defines the Meta class and its associated methods. -#if WREN_USE_LIB_META +#if WREN_USE_META_MODULE -void wrenLoadMetaLibrary(WrenVM* vm); +void wrenLoadMetaModule(WrenVM* vm); #endif diff --git a/src/vm/wren_meta.wren.inc b/src/vm/wren_meta.wren.inc index 6e4e5441..843d120d 100644 --- a/src/vm/wren_meta.wren.inc +++ b/src/vm/wren_meta.wren.inc @@ -1,3 +1,15 @@ // Generated automatically from builtin/meta.wren. Do not edit. static const char* metaModuleSource = -"class Meta {}\n"; +"class Meta {\n" +" static eval(source) {\n" +" if (!(source is String)) Fiber.abort(\"Source code must be a string.\")\n" +"\n" +" var fn = compile_(source)\n" +" // TODO: Include compile errors.\n" +" if (fn == null) Fiber.abort(\"Could not compile source code.\")\n" +"\n" +" Fiber.new(fn).call()\n" +" }\n" +"\n" +" foreign static compile_(source)\n" +"}\n"; diff --git a/src/vm/wren_vm.c b/src/vm/wren_vm.c index 667452dd..a00913c7 100644 --- a/src/vm/wren_vm.c +++ b/src/vm/wren_vm.c @@ -8,11 +8,7 @@ #include "wren_debug.h" #include "wren_vm.h" -#if WREN_USE_LIB_IO - #include "wren_io.h" -#endif - -#if WREN_USE_LIB_META +#if WREN_USE_META_MODULE #include "wren_meta.h" #endif @@ -57,22 +53,12 @@ WrenVM* wrenNewVM(WrenConfiguration* config) wrenSymbolTableInit(&vm->methodNames); - ObjString* name = AS_STRING(CONST_STRING(vm, "core")); - wrenPushRoot(vm, (Obj*)name); - - // Implicitly create a "core" module for the built in libraries. - ObjModule* coreModule = wrenNewModule(vm, name, NULL); - wrenPushRoot(vm, (Obj*)coreModule); - vm->modules = wrenNewMap(vm); - wrenMapSet(vm, vm->modules, NULL_VAL, OBJ_VAL(coreModule)); - - wrenPopRoot(vm); // mainModule. - wrenPopRoot(vm); // name. wrenInitializeCore(vm); - #if WREN_USE_LIB_META - wrenLoadMetaLibrary(vm); + + #if WREN_USE_META_MODULE + wrenLoadMetaModule(vm); #endif return vm; @@ -463,14 +449,17 @@ static ObjFiber* loadModule(WrenVM* vm, Value name, const char* source) // multiple times. wrenMapSet(vm, vm->modules, name, OBJ_VAL(module)); - // Implicitly import the core module. - ObjModule* coreModule = wrenGetCoreModule(vm); - for (int i = 0; i < coreModule->variables.count; i++) + // Implicitly import the core module (unless we *are* core). + if (!IS_NULL(name)) { - wrenDefineVariable(vm, module, - coreModule->variableNames.data[i].buffer, - coreModule->variableNames.data[i].length, - coreModule->variables.data[i]); + ObjModule* coreModule = getModule(vm, NULL_VAL); + for (int i = 0; i < coreModule->variables.count; i++) + { + wrenDefineVariable(vm, module, + coreModule->variableNames.data[i].buffer, + coreModule->variableNames.data[i].length, + coreModule->variables.data[i]); + } } } @@ -1457,49 +1446,36 @@ void* wrenAllocateForeign(WrenVM* vm, size_t size) return (void*)foreign->data; } -// Execute [source] in the context of the core module. -static WrenInterpretResult loadIntoCore(WrenVM* vm, const char* source) -{ - ObjModule* coreModule = wrenGetCoreModule(vm); - - ObjFn* fn = wrenCompile(vm, coreModule, source, true); - if (fn == NULL) return WREN_RESULT_COMPILE_ERROR; - - wrenPushRoot(vm, (Obj*)fn); - ObjFiber* fiber = wrenNewFiber(vm, (Obj*)fn); - wrenPopRoot(vm); - - return runInterpreter(vm, fiber); -} - WrenInterpretResult wrenInterpret(WrenVM* vm, const char* sourcePath, const char* source) { - if (strlen(sourcePath) == 0) return loadIntoCore(vm, source); + return wrenInterpretInModule(vm, "main", sourcePath, source); +} - // TODO: Better module name. - Value name = CONST_STRING(vm, "main"); - wrenPushRoot(vm, AS_OBJ(name)); - - ObjFiber* fiber = loadModule(vm, name, source); +WrenInterpretResult wrenInterpretInModule(WrenVM* vm, const char* module, + const char* sourcePath, + const char* source) +{ + Value nameValue = NULL_VAL; + if (module != NULL) + { + nameValue = wrenStringFormat(vm, "$", module); + wrenPushRoot(vm, AS_OBJ(nameValue)); + } + + ObjFiber* fiber = loadModule(vm, nameValue, source); if (fiber == NULL) { wrenPopRoot(vm); return WREN_RESULT_COMPILE_ERROR; } - wrenPopRoot(vm); // name. + if (module != NULL) + { + wrenPopRoot(vm); // nameValue. + } - WrenInterpretResult result = runInterpreter(vm, fiber); - - return result; -} - -ObjModule* wrenGetCoreModule(WrenVM* vm) -{ - ObjModule* module = getModule(vm, NULL_VAL); - ASSERT(module != NULL, "Could not find core module."); - return module; + return runInterpreter(vm, fiber); } Value wrenImportModule(WrenVM* vm, const char* name) diff --git a/src/vm/wren_vm.h b/src/vm/wren_vm.h index 56bf5173..b0492bbf 100644 --- a/src/vm/wren_vm.h +++ b/src/vm/wren_vm.h @@ -131,8 +131,10 @@ void wrenFinalizeForeign(WrenVM* vm, ObjForeign* foreign); // Creates a new [WrenValue] for [value]. WrenValue* wrenCaptureValue(WrenVM* vm, Value value); -// Looks up the core module in the module map. -ObjModule* wrenGetCoreModule(WrenVM* vm); +// Executes [source] in the context of [module]. +WrenInterpretResult wrenInterpretInModule(WrenVM* vm, const char* module, + const char* sourcePath, + const char* source); // Imports the module with [name]. // diff --git a/test/meta/eval_compile_error.wren b/test/meta/eval_compile_error.wren index 8e25314a..bee114f1 100644 --- a/test/meta/eval_compile_error.wren +++ b/test/meta/eval_compile_error.wren @@ -1 +1,3 @@ +import "meta" for Meta + Meta.eval("!!") // expect runtime error: Could not compile source code. diff --git a/test/meta/eval_existing_scoped_variable.wren b/test/meta/eval_existing_scoped_variable.wren index 82b80466..8dccccfd 100644 --- a/test/meta/eval_existing_scoped_variable.wren +++ b/test/meta/eval_existing_scoped_variable.wren @@ -1,3 +1,5 @@ +import "meta" for Meta + var y Meta.eval("y = 2") diff --git a/test/meta/eval_not_string.wren b/test/meta/eval_not_string.wren index b1996760..18595b37 100644 --- a/test/meta/eval_not_string.wren +++ b/test/meta/eval_not_string.wren @@ -1 +1,3 @@ +import "meta" for Meta + Meta.eval(123) // expect runtime error: Source code must be a string.