diff --git a/Makefile b/Makefile index 7814c8df..d4ac491c 100644 --- a/Makefile +++ b/Makefile @@ -46,11 +46,6 @@ test: debug @ $(MAKE) -f script/wren.mk MODE=debug test @ ./script/test.py $(suite) -# Take the contents of the scripts under builtin/ and copy them into their -# respective wren_.c source files. -builtin: - @ ./script/generate_builtins.py - # Generate the Wren site. docs: @ ./script/generate_docs.py diff --git a/builtin/README.md b/builtin/README.md index ccbb79a2..dc9b74b3 100644 --- a/builtin/README.md +++ b/builtin/README.md @@ -1,7 +1,9 @@ -The Wren scripts in this directory get converted to C string literals and then -inserted into their respective .c files so that the interpreter can load them -directly without having to do any file IO. +The Wren scripts in this directory get converted to C string literals into files +with a `.wren.inc` extension. Those are then `#includ`ed into their respective +`.c` files so that the interpreter can load them directly without having to do +any file IO. -The script that does this copying is `script/generate_builtins.py`. +The script that does this translation is `script/wren_to_c_string.py`. -You can invoke using `make builtin`. \ No newline at end of file +When any of the ".wren" files in here are changed, the Makefile automatically +updates the generated C headers. diff --git a/script/generate_builtins.py b/script/generate_builtins.py deleted file mode 100755 index 0edea337..00000000 --- a/script/generate_builtins.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -import glob -import os.path -import re - -PATTERN = re.compile(r'LibSource =\n("(.|[\n])*?);') - -def copy_builtin(filename, out_prefix): - name = os.path.basename(filename) - name = name.split('.')[0] - - with open(filename, "r") as f: - lines = f.readlines() - - wren_source = "" - for line in lines: - line = line.replace('"', "\\\"") - line = line.replace("\n", "\\n\"") - if wren_source: wren_source += "\n" - wren_source += '"' + line - - # re.sub() will unescape escape sequences, but we want them to stay escapes - # in the C string literal. - wren_source = wren_source.replace('\\', '\\\\') - - constant = "LibSource =\n" + wren_source + ";" - - with open(out_prefix + name + ".c", "r") as f: - c_source = f.read() - - c_source = PATTERN.sub(constant, c_source) - - with open(out_prefix + name + ".c", "w") as f: - f.write(c_source) - - print(filename.ljust(25) + " → " + out_prefix + name + ".c") - - -for f in glob.iglob("builtin/*.wren"): - copy_builtin(f, "src/vm/wren_") - -for f in glob.iglob("src/module/*.wren"): - copy_builtin(f, "src/module/") diff --git a/script/wren.mk b/script/wren.mk index 2dc5bb54..1b6fdb95 100644 --- a/script/wren.mk +++ b/script/wren.mk @@ -22,10 +22,10 @@ CLI_HEADERS := $(wildcard src/cli/*.h) CLI_SOURCES := $(wildcard src/cli/*.c) -MODULE_HEADERS := $(wildcard src/module/*.h) +MODULE_HEADERS := $(wildcard src/module/*.h) $(wildcard src/module/*.wren.inc) MODULE_SOURCES := $(wildcard src/module/*.c) -VM_HEADERS := $(wildcard src/vm/*.h) +VM_HEADERS := $(wildcard src/vm/*.h) $(wildcard src/vm/*.wren.inc) VM_SOURCES := $(wildcard src/vm/*.c) TEST_HEADERS := $(wildcard test/api/*.h) @@ -192,4 +192,11 @@ $(LIBUV_DIR)/build/gyp/gyp: script/libuv.py $(LIBUV): $(LIBUV_DIR)/build/gyp/gyp script/libuv.py @ ./script/libuv.py build $(LIBUV_ARCH) +# Wren modules that get compiled into the binary as C strings. +src/vm/wren_%.wren.inc: builtin/%.wren script/wren_to_c_string.py + @ ./script/wren_to_c_string.py $@ $< + +src/module/%.wren.inc: src/module/%.wren script/wren_to_c_string.py + @ ./script/wren_to_c_string.py $@ $< + .PHONY: all cli test vm diff --git a/script/wren_to_c_string.py b/script/wren_to_c_string.py new file mode 100755 index 00000000..445b7be8 --- /dev/null +++ b/script/wren_to_c_string.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# coding: utf-8 + +import argparse +import glob +import os.path +import re + +# The source for the Wren modules that are built into the VM or CLI are turned +# include C string literals. This way they can be compiled directly into the +# code so that file IO is not needed to find and read them. +# +# These string literals are stored in files with a ".wren.inc" extension and +# #included directly by other source files. This generates a ".wren.inc" file +# given a ".wren" module. + +PREAMBLE = """// Generated automatically from {0}. Do not edit. +static const char* {1}ModuleSource = +{2}; +""" + +def wren_to_c_string(input_path, wren_source_lines, module): + wren_source = "" + for line in wren_source_lines: + line = line.replace('"', "\\\"") + line = line.replace("\n", "\\n\"") + if wren_source: wren_source += "\n" + wren_source += '"' + line + + return PREAMBLE.format(input_path, module, wren_source) + + +def main(): + parser = argparse.ArgumentParser( + description="Convert a Wren library to a C string literal.") + parser.add_argument("output", help="The output file to write") + parser.add_argument("input", help="The source .wren file") + + args = parser.parse_args() + + with open(args.input, "r") as f: + wren_source_lines = f.readlines() + + module = os.path.splitext(os.path.basename(args.input))[0] + c_source = wren_to_c_string(args.input, wren_source_lines, module) + + with open(args.output, "w") as f: + f.write(c_source) + + print(" str " + args.input) + + +main() diff --git a/src/module/timer.c b/src/module/timer.c index 0862ac75..9046d5f5 100644 --- a/src/module/timer.c +++ b/src/module/timer.c @@ -7,43 +7,7 @@ #include "wren.h" #include "vm.h" -// This is generated from builtin/module/timer.wren. Do not edit here. -static const char* timerLibSource = -"class Timer {\n" -" static sleep(milliseconds) {\n" -" if (!(milliseconds is Num)) Fiber.abort(\"Milliseconds must be a number.\")\n" -" if (milliseconds < 0) Fiber.abort(\"Milliseconds cannot be negative.\")\n" -" startTimer_(milliseconds, Fiber.current)\n" -"\n" -" runNextScheduled_()\n" -" }\n" -"\n" -" // TODO: Once the CLI modules are more fleshed out, find a better place to\n" -" // put this.\n" -" static schedule(callable) {\n" -" if (__scheduled == null) __scheduled = []\n" -" __scheduled.add(Fiber.new {\n" -" callable.call()\n" -" runNextScheduled_()\n" -" })\n" -" }\n" -"\n" -" foreign static startTimer_(milliseconds, fiber)\n" -"\n" -" // Called by native code.\n" -" static resumeTimer_(fiber) {\n" -" fiber.transfer()\n" -" }\n" -"\n" -" static runNextScheduled_() {\n" -" if (__scheduled == null || __scheduled.isEmpty) {\n" -" Fiber.suspend()\n" -" } else {\n" -" __scheduled.removeAt(0).transfer()\n" -" }\n" -" }\n" -"}\n" -"\n"; +#include "timer.wren.inc" // The Wren method to call when a timer has completed. static WrenValue* resumeTimer; @@ -58,7 +22,7 @@ static void timerCloseCallback(uv_handle_t* handle) static void timerCallback(uv_timer_t* handle) { WrenValue* fiber = (WrenValue*)handle->data; - + // Tell libuv that we don't need the timer anymore. uv_close((uv_handle_t*)handle, timerCloseCallback); @@ -74,24 +38,24 @@ static void startTimer(WrenVM* vm) { resumeTimer = wrenGetMethod(vm, "timer", "Timer", "resumeTimer_(_)"); } - + int milliseconds = (int)wrenGetArgumentDouble(vm, 1); WrenValue* fiber = wrenGetArgumentValue(vm, 2); - + // Store the fiber to resume when the timer completes. uv_timer_t* handle = (uv_timer_t*)malloc(sizeof(uv_timer_t)); handle->data = fiber; - + uv_timer_init(getLoop(), handle); uv_timer_start(handle, timerCallback, milliseconds, 0); } char* timerGetSource() { - size_t length = strlen(timerLibSource); + size_t length = strlen(timerModuleSource); char* copy = (char*)malloc(length + 1); - strncpy(copy, timerLibSource, length + 1); - + strncpy(copy, timerModuleSource, length + 1); + return copy; } @@ -101,7 +65,7 @@ WrenForeignMethodFn timerBindForeign( if (strcmp(className, "Timer") != 0) return NULL; if (isStatic && strcmp(signature, "startTimer_(_,_)") == 0) return startTimer; - + return NULL; } diff --git a/src/module/timer.wren b/src/module/timer.wren index 7dc173f5..d358ebec 100644 --- a/src/module/timer.wren +++ b/src/module/timer.wren @@ -32,4 +32,3 @@ class Timer { } } } - diff --git a/src/module/timer.wren.inc b/src/module/timer.wren.inc new file mode 100644 index 00000000..fbb18daf --- /dev/null +++ b/src/module/timer.wren.inc @@ -0,0 +1,36 @@ +// Generated automatically from src/module/timer.wren. Do not edit. +static const char* timerModuleSource = +"class Timer {\n" +" static sleep(milliseconds) {\n" +" if (!(milliseconds is Num)) Fiber.abort(\"Milliseconds must be a number.\")\n" +" if (milliseconds < 0) Fiber.abort(\"Milliseconds cannot be negative.\")\n" +" startTimer_(milliseconds, Fiber.current)\n" +"\n" +" runNextScheduled_()\n" +" }\n" +"\n" +" // TODO: Once the CLI modules are more fleshed out, find a better place to\n" +" // put this.\n" +" static schedule(callable) {\n" +" if (__scheduled == null) __scheduled = []\n" +" __scheduled.add(Fiber.new {\n" +" callable.call()\n" +" runNextScheduled_()\n" +" })\n" +" }\n" +"\n" +" foreign static startTimer_(milliseconds, fiber)\n" +"\n" +" // Called by native code.\n" +" static resumeTimer_(fiber) {\n" +" fiber.transfer()\n" +" }\n" +"\n" +" static runNextScheduled_() {\n" +" if (__scheduled == null || __scheduled.isEmpty) {\n" +" Fiber.suspend()\n" +" } else {\n" +" __scheduled.removeAt(0).transfer()\n" +" }\n" +" }\n" +"}\n"; diff --git a/src/vm/wren_core.c b/src/vm/wren_core.c index b45494b2..3bd48e24 100644 --- a/src/vm/wren_core.c +++ b/src/vm/wren_core.c @@ -10,223 +10,7 @@ #include "wren_primitive.h" #include "wren_value.h" -// This string literal is generated automatically from core. Do not edit. -static const char* coreLibSource = -"class Bool {}\n" -"class Fiber {}\n" -"class Fn {}\n" -"class Null {}\n" -"class Num {}\n" -"\n" -"class Sequence {\n" -" all(f) {\n" -" var result = true\n" -" for (element in this) {\n" -" result = f.call(element)\n" -" if (!result) return result\n" -" }\n" -" return result\n" -" }\n" -"\n" -" any(f) {\n" -" var result = false\n" -" for (element in this) {\n" -" result = f.call(element)\n" -" if (result) return result\n" -" }\n" -" return result\n" -" }\n" -"\n" -" contains(element) {\n" -" for (item in this) {\n" -" if (element == item) return true\n" -" }\n" -" return false\n" -" }\n" -"\n" -" count {\n" -" var result = 0\n" -" for (element in this) {\n" -" result = result + 1\n" -" }\n" -" return result\n" -" }\n" -"\n" -" count(f) {\n" -" var result = 0\n" -" for (element in this) {\n" -" if (f.call(element)) result = result + 1\n" -" }\n" -" return result\n" -" }\n" -"\n" -" each(f) {\n" -" for (element in this) {\n" -" f.call(element)\n" -" }\n" -" }\n" -"\n" -" isEmpty { iterate(null) ? false : true }\n" -"\n" -" map(transformation) { MapSequence.new(this, transformation) }\n" -"\n" -" where(predicate) { WhereSequence.new(this, predicate) }\n" -"\n" -" reduce(acc, f) {\n" -" for (element in this) {\n" -" acc = f.call(acc, element)\n" -" }\n" -" return acc\n" -" }\n" -"\n" -" reduce(f) {\n" -" var iter = iterate(null)\n" -" if (!iter) Fiber.abort(\"Can't reduce an empty sequence.\")\n" -"\n" -" // Seed with the first element.\n" -" var result = iteratorValue(iter)\n" -" while (iter = iterate(iter)) {\n" -" result = f.call(result, iteratorValue(iter))\n" -" }\n" -"\n" -" return result\n" -" }\n" -"\n" -" join { join(\"\") }\n" -"\n" -" join(sep) {\n" -" var first = true\n" -" var result = \"\"\n" -"\n" -" for (element in this) {\n" -" if (!first) result = result + sep\n" -" first = false\n" -" result = result + element.toString\n" -" }\n" -"\n" -" return result\n" -" }\n" -"\n" -" toList {\n" -" var result = List.new()\n" -" for (element in this) {\n" -" result.add(element)\n" -" }\n" -" return result\n" -" }\n" -"}\n" -"\n" -"class MapSequence is Sequence {\n" -" construct new(sequence, fn) {\n" -" _sequence = sequence\n" -" _fn = fn\n" -" }\n" -"\n" -" iterate(iterator) { _sequence.iterate(iterator) }\n" -" iteratorValue(iterator) { _fn.call(_sequence.iteratorValue(iterator)) }\n" -"}\n" -"\n" -"class WhereSequence is Sequence {\n" -" construct new(sequence, fn) {\n" -" _sequence = sequence\n" -" _fn = fn\n" -" }\n" -"\n" -" iterate(iterator) {\n" -" while (iterator = _sequence.iterate(iterator)) {\n" -" if (_fn.call(_sequence.iteratorValue(iterator))) break\n" -" }\n" -" return iterator\n" -" }\n" -"\n" -" iteratorValue(iterator) { _sequence.iteratorValue(iterator) }\n" -"}\n" -"\n" -"class String is Sequence {\n" -" bytes { StringByteSequence.new(this) }\n" -" codePoints { StringCodePointSequence.new(this) }\n" -"}\n" -"\n" -"class StringByteSequence is Sequence {\n" -" construct new(string) {\n" -" _string = string\n" -" }\n" -"\n" -" [index] { _string.byteAt_(index) }\n" -" iterate(iterator) { _string.iterateByte_(iterator) }\n" -" iteratorValue(iterator) { _string.byteAt_(iterator) }\n" -"\n" -" count { _string.byteCount_ }\n" -"}\n" -"\n" -"class StringCodePointSequence is Sequence {\n" -" construct new(string) {\n" -" _string = string\n" -" }\n" -"\n" -" [index] { _string.codePointAt_(index) }\n" -" iterate(iterator) { _string.iterate(iterator) }\n" -" iteratorValue(iterator) { _string.codePointAt_(iterator) }\n" -"\n" -" count { _string.count }\n" -"}\n" -"\n" -"class List is Sequence {\n" -" addAll(other) {\n" -" for (element in other) {\n" -" add(element)\n" -" }\n" -" return other\n" -" }\n" -"\n" -" toString { \"[\" + join(\", \") + \"]\" }\n" -"\n" -" +(other) {\n" -" var result = this[0..-1]\n" -" for (element in other) {\n" -" result.add(element)\n" -" }\n" -" return result\n" -" }\n" -"}\n" -"\n" -"class Map {\n" -" keys { MapKeySequence.new(this) }\n" -" values { MapValueSequence.new(this) }\n" -"\n" -" toString {\n" -" var first = true\n" -" var result = \"{\"\n" -"\n" -" for (key in keys) {\n" -" if (!first) result = result + \", \"\n" -" first = false\n" -" result = result + key.toString + \": \" + this[key].toString\n" -" }\n" -"\n" -" return result + \"}\"\n" -" }\n" -"}\n" -"\n" -"class MapKeySequence is Sequence {\n" -" construct new(map) {\n" -" _map = map\n" -" }\n" -"\n" -" iterate(n) { _map.iterate_(n) }\n" -" iteratorValue(iterator) { _map.keyIteratorValue_(iterator) }\n" -"}\n" -"\n" -"class MapValueSequence is Sequence {\n" -" construct new(map) {\n" -" _map = map\n" -" }\n" -"\n" -" iterate(n) { _map.iterate_(n) }\n" -" iteratorValue(iterator) { _map.valueIteratorValue_(iterator) }\n" -"}\n" -"\n" -"class Range is Sequence {}\n"; +#include "wren_core.wren.inc" DEF_PRIMITIVE(bool_not) { @@ -304,7 +88,7 @@ static PrimitiveResult runFiber(WrenVM* vm, ObjFiber* fiber, Value* args, if (isCall) { if (fiber->caller != NULL) RETURN_ERROR("Fiber has already been called."); - + // Remember who ran it. fiber->caller = vm->fiber; } @@ -315,21 +99,21 @@ static PrimitiveResult runFiber(WrenVM* vm, ObjFiber* fiber, Value* args, // return below, it would overwrite the fiber being transferred to. if (fiber == vm->fiber) RETURN_VAL(hasValue ? args[1] : NULL_VAL); } - + if (fiber->numFrames == 0) { args[0] = wrenStringFormat(vm, "Cannot $ a finished fiber.", isCall ? "call" : "transfer to"); return PRIM_ERROR; } - + if (fiber->error != NULL) { args[0] = wrenStringFormat(vm, "Cannot $ an aborted fiber.", isCall ? "call" : "transfer to"); return PRIM_ERROR; } - + // When the calling fiber resumes, we'll store the result of the call in its // stack. If the call has two arguments (the fiber and the value), we only // need one slot for the result, so discard the other slot now. @@ -340,7 +124,7 @@ static PrimitiveResult runFiber(WrenVM* vm, ObjFiber* fiber, Value* args, { *(fiber->stackTop - 1) = hasValue ? args[1] : NULL_VAL; } - + return PRIM_RUN_FIBER; } @@ -416,7 +200,7 @@ DEF_PRIMITIVE(fiber_yield) ObjFiber* caller = fiber->caller; fiber->caller = NULL; fiber->callerIsTrying = false; - + // If we don't have any other pending fibers, jump all the way out of the // interpreter. if (caller == NULL) @@ -1274,7 +1058,7 @@ static ObjClass* defineClass(WrenVM* vm, ObjModule* module, const char* name) void wrenInitializeCore(WrenVM* vm) { ObjModule* coreModule = wrenGetCoreModule(vm); - + // Define the root Object class. This has to be done a little specially // because it has no superclass. vm->objectClass = defineClass(vm, coreModule, "Object"); @@ -1329,7 +1113,7 @@ void wrenInitializeCore(WrenVM* vm) // '---------' '-------------------' -' // The rest of the classes can now be defined normally. - wrenInterpret(vm, "", coreLibSource); + wrenInterpret(vm, "", coreModuleSource); vm->boolClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Bool")); PRIMITIVE(vm->boolClass, "toString", bool_toString); diff --git a/src/vm/wren_core.wren.inc b/src/vm/wren_core.wren.inc new file mode 100644 index 00000000..96ff3a16 --- /dev/null +++ b/src/vm/wren_core.wren.inc @@ -0,0 +1,217 @@ +// Generated automatically from builtin/core.wren. Do not edit. +static const char* coreModuleSource = +"class Bool {}\n" +"class Fiber {}\n" +"class Fn {}\n" +"class Null {}\n" +"class Num {}\n" +"\n" +"class Sequence {\n" +" all(f) {\n" +" var result = true\n" +" for (element in this) {\n" +" result = f.call(element)\n" +" if (!result) return result\n" +" }\n" +" return result\n" +" }\n" +"\n" +" any(f) {\n" +" var result = false\n" +" for (element in this) {\n" +" result = f.call(element)\n" +" if (result) return result\n" +" }\n" +" return result\n" +" }\n" +"\n" +" contains(element) {\n" +" for (item in this) {\n" +" if (element == item) return true\n" +" }\n" +" return false\n" +" }\n" +"\n" +" count {\n" +" var result = 0\n" +" for (element in this) {\n" +" result = result + 1\n" +" }\n" +" return result\n" +" }\n" +"\n" +" count(f) {\n" +" var result = 0\n" +" for (element in this) {\n" +" if (f.call(element)) result = result + 1\n" +" }\n" +" return result\n" +" }\n" +"\n" +" each(f) {\n" +" for (element in this) {\n" +" f.call(element)\n" +" }\n" +" }\n" +"\n" +" isEmpty { iterate(null) ? false : true }\n" +"\n" +" map(transformation) { MapSequence.new(this, transformation) }\n" +"\n" +" where(predicate) { WhereSequence.new(this, predicate) }\n" +"\n" +" reduce(acc, f) {\n" +" for (element in this) {\n" +" acc = f.call(acc, element)\n" +" }\n" +" return acc\n" +" }\n" +"\n" +" reduce(f) {\n" +" var iter = iterate(null)\n" +" if (!iter) Fiber.abort(\"Can't reduce an empty sequence.\")\n" +"\n" +" // Seed with the first element.\n" +" var result = iteratorValue(iter)\n" +" while (iter = iterate(iter)) {\n" +" result = f.call(result, iteratorValue(iter))\n" +" }\n" +"\n" +" return result\n" +" }\n" +"\n" +" join { join(\"\") }\n" +"\n" +" join(sep) {\n" +" var first = true\n" +" var result = \"\"\n" +"\n" +" for (element in this) {\n" +" if (!first) result = result + sep\n" +" first = false\n" +" result = result + element.toString\n" +" }\n" +"\n" +" return result\n" +" }\n" +"\n" +" toList {\n" +" var result = List.new()\n" +" for (element in this) {\n" +" result.add(element)\n" +" }\n" +" return result\n" +" }\n" +"}\n" +"\n" +"class MapSequence is Sequence {\n" +" construct new(sequence, fn) {\n" +" _sequence = sequence\n" +" _fn = fn\n" +" }\n" +"\n" +" iterate(iterator) { _sequence.iterate(iterator) }\n" +" iteratorValue(iterator) { _fn.call(_sequence.iteratorValue(iterator)) }\n" +"}\n" +"\n" +"class WhereSequence is Sequence {\n" +" construct new(sequence, fn) {\n" +" _sequence = sequence\n" +" _fn = fn\n" +" }\n" +"\n" +" iterate(iterator) {\n" +" while (iterator = _sequence.iterate(iterator)) {\n" +" if (_fn.call(_sequence.iteratorValue(iterator))) break\n" +" }\n" +" return iterator\n" +" }\n" +"\n" +" iteratorValue(iterator) { _sequence.iteratorValue(iterator) }\n" +"}\n" +"\n" +"class String is Sequence {\n" +" bytes { StringByteSequence.new(this) }\n" +" codePoints { StringCodePointSequence.new(this) }\n" +"}\n" +"\n" +"class StringByteSequence is Sequence {\n" +" construct new(string) {\n" +" _string = string\n" +" }\n" +"\n" +" [index] { _string.byteAt_(index) }\n" +" iterate(iterator) { _string.iterateByte_(iterator) }\n" +" iteratorValue(iterator) { _string.byteAt_(iterator) }\n" +"\n" +" count { _string.byteCount_ }\n" +"}\n" +"\n" +"class StringCodePointSequence is Sequence {\n" +" construct new(string) {\n" +" _string = string\n" +" }\n" +"\n" +" [index] { _string.codePointAt_(index) }\n" +" iterate(iterator) { _string.iterate(iterator) }\n" +" iteratorValue(iterator) { _string.codePointAt_(iterator) }\n" +"\n" +" count { _string.count }\n" +"}\n" +"\n" +"class List is Sequence {\n" +" addAll(other) {\n" +" for (element in other) {\n" +" add(element)\n" +" }\n" +" return other\n" +" }\n" +"\n" +" toString { \"[\" + join(\", \") + \"]\" }\n" +"\n" +" +(other) {\n" +" var result = this[0..-1]\n" +" for (element in other) {\n" +" result.add(element)\n" +" }\n" +" return result\n" +" }\n" +"}\n" +"\n" +"class Map {\n" +" keys { MapKeySequence.new(this) }\n" +" values { MapValueSequence.new(this) }\n" +"\n" +" toString {\n" +" var first = true\n" +" var result = \"{\"\n" +"\n" +" for (key in keys) {\n" +" if (!first) result = result + \", \"\n" +" first = false\n" +" result = result + key.toString + \": \" + this[key].toString\n" +" }\n" +"\n" +" return result + \"}\"\n" +" }\n" +"}\n" +"\n" +"class MapKeySequence is Sequence {\n" +" construct new(map) {\n" +" _map = map\n" +" }\n" +"\n" +" iterate(n) { _map.iterate_(n) }\n" +" iteratorValue(iterator) { _map.keyIteratorValue_(iterator) }\n" +"}\n" +"\n" +"class MapValueSequence is Sequence {\n" +" construct new(map) {\n" +" _map = map\n" +" }\n" +"\n" +" iterate(n) { _map.iterate_(n) }\n" +" iteratorValue(iterator) { _map.valueIteratorValue_(iterator) }\n" +"}\n" +"\n" +"class Range is Sequence {}\n"; diff --git a/src/vm/wren_io.c b/src/vm/wren_io.c index e8813fce..1b0201fd 100644 --- a/src/vm/wren_io.c +++ b/src/vm/wren_io.c @@ -9,109 +9,7 @@ // TODO: This is an arbitrary limit. Do something smarter. #define MAX_READ_LEN 1024 -// This string literal is generated automatically from io.wren. Do not edit. -static const char* ioLibSource = -"class IO {\n" -" static print {\n" -" writeString_(\"\n\")\n" -" }\n" -"\n" -" static print(obj) {\n" -" writeObject_(obj)\n" -" writeString_(\"\n\")\n" -" return obj\n" -" }\n" -"\n" -" static print(a1, a2) {\n" -" printAll([a1, a2])\n" -" }\n" -"\n" -" static print(a1, a2, a3) {\n" -" printAll([a1, a2, a3])\n" -" }\n" -"\n" -" static print(a1, a2, a3, a4) {\n" -" printAll([a1, a2, a3, a4])\n" -" }\n" -"\n" -" static print(a1, a2, a3, a4, a5) {\n" -" printAll([a1, a2, a3, a4, a5])\n" -" }\n" -"\n" -" static print(a1, a2, a3, a4, a5, a6) {\n" -" printAll([a1, a2, a3, a4, a5, a6])\n" -" }\n" -"\n" -" static print(a1, a2, a3, a4, a5, a6, a7) {\n" -" printAll([a1, a2, a3, a4, a5, a6, a7])\n" -" }\n" -"\n" -" static print(a1, a2, a3, a4, a5, a6, a7, a8) {\n" -" printAll([a1, a2, a3, a4, a5, a6, a7, a8])\n" -" }\n" -"\n" -" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9) {\n" -" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9])\n" -" }\n" -"\n" -" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {\n" -" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10])\n" -" }\n" -"\n" -" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) {\n" -" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11])\n" -" }\n" -"\n" -" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) {\n" -" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12])\n" -" }\n" -"\n" -" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) {\n" -" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13])\n" -" }\n" -"\n" -" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) {\n" -" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14])\n" -" }\n" -"\n" -" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) {\n" -" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15])\n" -" }\n" -"\n" -" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) {\n" -" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16])\n" -" }\n" -"\n" -" static printAll(sequence) {\n" -" for (object in sequence) writeObject_(object)\n" -" writeString_(\"\n\")\n" -" }\n" -"\n" -" static write(obj) {\n" -" writeObject_(obj)\n" -" return obj\n" -" }\n" -"\n" -" static read(prompt) {\n" -" if (!(prompt is String)) Fiber.abort(\"Prompt must be a string.\")\n" -" write(prompt)\n" -" return read()\n" -" }\n" -"\n" -" static writeObject_(obj) {\n" -" var string = obj.toString\n" -" if (string is String) {\n" -" writeString_(string)\n" -" } else {\n" -" writeString_(\"[invalid toString]\")\n" -" }\n" -" }\n" -"\n" -" foreign static writeString_(string)\n" -" foreign static clock\n" -" foreign static time\n" -" foreign static read()\n" -"}\n"; +#include "wren_io.wren.inc" static void ioWriteString(WrenVM* vm) { @@ -143,14 +41,14 @@ static void ioTime(WrenVM* vm) void wrenLoadIOLibrary(WrenVM* vm) { - wrenInterpret(vm, "", ioLibSource); + wrenInterpret(vm, "", ioModuleSource); } WrenForeignMethodFn wrenBindIOForeignMethod(WrenVM* vm, const char* className, const char* signature) { if (strcmp(className, "IO") != 0) return NULL; - + if (strcmp(signature, "writeString_(_)") == 0) return ioWriteString; if (strcmp(signature, "clock") == 0) return ioClock; if (strcmp(signature, "time") == 0) return ioTime; diff --git a/src/vm/wren_io.wren.inc b/src/vm/wren_io.wren.inc new file mode 100644 index 00000000..d9ced63d --- /dev/null +++ b/src/vm/wren_io.wren.inc @@ -0,0 +1,103 @@ +// Generated automatically from builtin/io.wren. Do not edit. +static const char* ioModuleSource = +"class IO {\n" +" static print {\n" +" writeString_(\"\n\")\n" +" }\n" +"\n" +" static print(obj) {\n" +" writeObject_(obj)\n" +" writeString_(\"\n\")\n" +" return obj\n" +" }\n" +"\n" +" static print(a1, a2) {\n" +" printAll([a1, a2])\n" +" }\n" +"\n" +" static print(a1, a2, a3) {\n" +" printAll([a1, a2, a3])\n" +" }\n" +"\n" +" static print(a1, a2, a3, a4) {\n" +" printAll([a1, a2, a3, a4])\n" +" }\n" +"\n" +" static print(a1, a2, a3, a4, a5) {\n" +" printAll([a1, a2, a3, a4, a5])\n" +" }\n" +"\n" +" static print(a1, a2, a3, a4, a5, a6) {\n" +" printAll([a1, a2, a3, a4, a5, a6])\n" +" }\n" +"\n" +" static print(a1, a2, a3, a4, a5, a6, a7) {\n" +" printAll([a1, a2, a3, a4, a5, a6, a7])\n" +" }\n" +"\n" +" static print(a1, a2, a3, a4, a5, a6, a7, a8) {\n" +" printAll([a1, a2, a3, a4, a5, a6, a7, a8])\n" +" }\n" +"\n" +" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9) {\n" +" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9])\n" +" }\n" +"\n" +" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) {\n" +" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10])\n" +" }\n" +"\n" +" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) {\n" +" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11])\n" +" }\n" +"\n" +" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) {\n" +" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12])\n" +" }\n" +"\n" +" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) {\n" +" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13])\n" +" }\n" +"\n" +" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) {\n" +" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14])\n" +" }\n" +"\n" +" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) {\n" +" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15])\n" +" }\n" +"\n" +" static print(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) {\n" +" printAll([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16])\n" +" }\n" +"\n" +" static printAll(sequence) {\n" +" for (object in sequence) writeObject_(object)\n" +" writeString_(\"\n\")\n" +" }\n" +"\n" +" static write(obj) {\n" +" writeObject_(obj)\n" +" return obj\n" +" }\n" +"\n" +" static read(prompt) {\n" +" if (!(prompt is String)) Fiber.abort(\"Prompt must be a string.\")\n" +" write(prompt)\n" +" return read()\n" +" }\n" +"\n" +" static writeObject_(obj) {\n" +" var string = obj.toString\n" +" if (string is String) {\n" +" writeString_(string)\n" +" } else {\n" +" writeString_(\"[invalid toString]\")\n" +" }\n" +" }\n" +"\n" +" foreign static writeString_(string)\n" +" foreign static clock\n" +" foreign static time\n" +" foreign static read()\n" +"}\n"; diff --git a/src/vm/wren_meta.c b/src/vm/wren_meta.c index 0c5d30ad..435eb22e 100644 --- a/src/vm/wren_meta.c +++ b/src/vm/wren_meta.c @@ -6,9 +6,7 @@ #include "wren_primitive.h" -// This string literal is generated automatically from meta.wren. Do not edit. -static const char* metaLibSource = -"class Meta {}\n"; +#include "wren_meta.wren.inc" DEF_PRIMITIVE(meta_eval) { @@ -43,10 +41,10 @@ DEF_PRIMITIVE(meta_eval) void wrenLoadMetaLibrary(WrenVM* vm) { - wrenInterpret(vm, "", metaLibSource); + wrenInterpret(vm, "", metaModuleSource); ObjModule* coreModule = wrenGetCoreModule(vm); - + // 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); diff --git a/src/vm/wren_meta.wren.inc b/src/vm/wren_meta.wren.inc new file mode 100644 index 00000000..6e4e5441 --- /dev/null +++ b/src/vm/wren_meta.wren.inc @@ -0,0 +1,3 @@ +// Generated automatically from builtin/meta.wren. Do not edit. +static const char* metaModuleSource = +"class Meta {}\n"; diff --git a/src/vm/wren_vm.c b/src/vm/wren_vm.c index 5d77958e..6aefa3ee 100644 --- a/src/vm/wren_vm.c +++ b/src/vm/wren_vm.c @@ -634,7 +634,7 @@ static void bindForeignClass(WrenVM* vm, ObjClass* classObj, ObjModule* module) // occurred. // // Returns false if the result is an error. -static bool defineClass(WrenVM* vm, ObjFiber* fiber, int numFields, +static bool createClass(WrenVM* vm, ObjFiber* fiber, int numFields, ObjModule* module) { // Pull the name and superclass off the stack. @@ -1195,13 +1195,13 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber) CASE_CODE(CLASS): { - if (!defineClass(vm, fiber, READ_BYTE(), NULL)) RUNTIME_ERROR(PEEK()); + if (!createClass(vm, fiber, READ_BYTE(), NULL)) RUNTIME_ERROR(PEEK()); DISPATCH(); } CASE_CODE(FOREIGN_CLASS): { - if (!defineClass(vm, fiber, -1, fn->module)) RUNTIME_ERROR(PEEK()); + if (!createClass(vm, fiber, -1, fn->module)) RUNTIME_ERROR(PEEK()); DISPATCH(); }