Automatically update the string constants when a builtin module changes.

This commit is contained in:
Bob Nystrom
2015-09-13 10:03:02 -07:00
parent e1cf8e22c3
commit f36a795534
15 changed files with 455 additions and 441 deletions

View File

@ -46,11 +46,6 @@ test: debug
@ $(MAKE) -f script/wren.mk MODE=debug test @ $(MAKE) -f script/wren.mk MODE=debug test
@ ./script/test.py $(suite) @ ./script/test.py $(suite)
# Take the contents of the scripts under builtin/ and copy them into their
# respective wren_<name>.c source files.
builtin:
@ ./script/generate_builtins.py
# Generate the Wren site. # Generate the Wren site.
docs: docs:
@ ./script/generate_docs.py @ ./script/generate_docs.py

View File

@ -1,7 +1,9 @@
The Wren scripts in this directory get converted to C string literals and then The Wren scripts in this directory get converted to C string literals into files
inserted into their respective .c files so that the interpreter can load them with a `.wren.inc` extension. Those are then `#includ`ed into their respective
directly without having to do any file IO. `.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`. When any of the ".wren" files in here are changed, the Makefile automatically
updates the generated C headers.

View File

@ -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/")

View File

@ -22,10 +22,10 @@
CLI_HEADERS := $(wildcard src/cli/*.h) CLI_HEADERS := $(wildcard src/cli/*.h)
CLI_SOURCES := $(wildcard src/cli/*.c) 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) 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) VM_SOURCES := $(wildcard src/vm/*.c)
TEST_HEADERS := $(wildcard test/api/*.h) 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 $(LIBUV): $(LIBUV_DIR)/build/gyp/gyp script/libuv.py
@ ./script/libuv.py build $(LIBUV_ARCH) @ ./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 .PHONY: all cli test vm

53
script/wren_to_c_string.py Executable file
View File

@ -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()

View File

@ -7,43 +7,7 @@
#include "wren.h" #include "wren.h"
#include "vm.h" #include "vm.h"
// This is generated from builtin/module/timer.wren. Do not edit here. #include "timer.wren.inc"
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";
// The Wren method to call when a timer has completed. // The Wren method to call when a timer has completed.
static WrenValue* resumeTimer; static WrenValue* resumeTimer;
@ -58,7 +22,7 @@ static void timerCloseCallback(uv_handle_t* handle)
static void timerCallback(uv_timer_t* handle) static void timerCallback(uv_timer_t* handle)
{ {
WrenValue* fiber = (WrenValue*)handle->data; WrenValue* fiber = (WrenValue*)handle->data;
// Tell libuv that we don't need the timer anymore. // Tell libuv that we don't need the timer anymore.
uv_close((uv_handle_t*)handle, timerCloseCallback); uv_close((uv_handle_t*)handle, timerCloseCallback);
@ -74,24 +38,24 @@ static void startTimer(WrenVM* vm)
{ {
resumeTimer = wrenGetMethod(vm, "timer", "Timer", "resumeTimer_(_)"); resumeTimer = wrenGetMethod(vm, "timer", "Timer", "resumeTimer_(_)");
} }
int milliseconds = (int)wrenGetArgumentDouble(vm, 1); int milliseconds = (int)wrenGetArgumentDouble(vm, 1);
WrenValue* fiber = wrenGetArgumentValue(vm, 2); WrenValue* fiber = wrenGetArgumentValue(vm, 2);
// Store the fiber to resume when the timer completes. // Store the fiber to resume when the timer completes.
uv_timer_t* handle = (uv_timer_t*)malloc(sizeof(uv_timer_t)); uv_timer_t* handle = (uv_timer_t*)malloc(sizeof(uv_timer_t));
handle->data = fiber; handle->data = fiber;
uv_timer_init(getLoop(), handle); uv_timer_init(getLoop(), handle);
uv_timer_start(handle, timerCallback, milliseconds, 0); uv_timer_start(handle, timerCallback, milliseconds, 0);
} }
char* timerGetSource() char* timerGetSource()
{ {
size_t length = strlen(timerLibSource); size_t length = strlen(timerModuleSource);
char* copy = (char*)malloc(length + 1); char* copy = (char*)malloc(length + 1);
strncpy(copy, timerLibSource, length + 1); strncpy(copy, timerModuleSource, length + 1);
return copy; return copy;
} }
@ -101,7 +65,7 @@ WrenForeignMethodFn timerBindForeign(
if (strcmp(className, "Timer") != 0) return NULL; if (strcmp(className, "Timer") != 0) return NULL;
if (isStatic && strcmp(signature, "startTimer_(_,_)") == 0) return startTimer; if (isStatic && strcmp(signature, "startTimer_(_,_)") == 0) return startTimer;
return NULL; return NULL;
} }

View File

@ -32,4 +32,3 @@ class Timer {
} }
} }
} }

36
src/module/timer.wren.inc Normal file
View File

@ -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";

View File

@ -10,223 +10,7 @@
#include "wren_primitive.h" #include "wren_primitive.h"
#include "wren_value.h" #include "wren_value.h"
// This string literal is generated automatically from core. Do not edit. #include "wren_core.wren.inc"
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";
DEF_PRIMITIVE(bool_not) DEF_PRIMITIVE(bool_not)
{ {
@ -304,7 +88,7 @@ static PrimitiveResult runFiber(WrenVM* vm, ObjFiber* fiber, Value* args,
if (isCall) if (isCall)
{ {
if (fiber->caller != NULL) RETURN_ERROR("Fiber has already been called."); if (fiber->caller != NULL) RETURN_ERROR("Fiber has already been called.");
// Remember who ran it. // Remember who ran it.
fiber->caller = vm->fiber; 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. // return below, it would overwrite the fiber being transferred to.
if (fiber == vm->fiber) RETURN_VAL(hasValue ? args[1] : NULL_VAL); if (fiber == vm->fiber) RETURN_VAL(hasValue ? args[1] : NULL_VAL);
} }
if (fiber->numFrames == 0) if (fiber->numFrames == 0)
{ {
args[0] = wrenStringFormat(vm, "Cannot $ a finished fiber.", args[0] = wrenStringFormat(vm, "Cannot $ a finished fiber.",
isCall ? "call" : "transfer to"); isCall ? "call" : "transfer to");
return PRIM_ERROR; return PRIM_ERROR;
} }
if (fiber->error != NULL) if (fiber->error != NULL)
{ {
args[0] = wrenStringFormat(vm, "Cannot $ an aborted fiber.", args[0] = wrenStringFormat(vm, "Cannot $ an aborted fiber.",
isCall ? "call" : "transfer to"); isCall ? "call" : "transfer to");
return PRIM_ERROR; return PRIM_ERROR;
} }
// When the calling fiber resumes, we'll store the result of the call in its // 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 // 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. // 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; *(fiber->stackTop - 1) = hasValue ? args[1] : NULL_VAL;
} }
return PRIM_RUN_FIBER; return PRIM_RUN_FIBER;
} }
@ -416,7 +200,7 @@ DEF_PRIMITIVE(fiber_yield)
ObjFiber* caller = fiber->caller; ObjFiber* caller = fiber->caller;
fiber->caller = NULL; fiber->caller = NULL;
fiber->callerIsTrying = false; fiber->callerIsTrying = false;
// If we don't have any other pending fibers, jump all the way out of the // If we don't have any other pending fibers, jump all the way out of the
// interpreter. // interpreter.
if (caller == NULL) if (caller == NULL)
@ -1274,7 +1058,7 @@ static ObjClass* defineClass(WrenVM* vm, ObjModule* module, const char* name)
void wrenInitializeCore(WrenVM* vm) void wrenInitializeCore(WrenVM* vm)
{ {
ObjModule* coreModule = wrenGetCoreModule(vm); ObjModule* coreModule = wrenGetCoreModule(vm);
// Define the root Object class. This has to be done a little specially // Define the root Object class. This has to be done a little specially
// because it has no superclass. // because it has no superclass.
vm->objectClass = defineClass(vm, coreModule, "Object"); vm->objectClass = defineClass(vm, coreModule, "Object");
@ -1329,7 +1113,7 @@ void wrenInitializeCore(WrenVM* vm)
// '---------' '-------------------' -' // '---------' '-------------------' -'
// The rest of the classes can now be defined normally. // The rest of the classes can now be defined normally.
wrenInterpret(vm, "", coreLibSource); wrenInterpret(vm, "", coreModuleSource);
vm->boolClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Bool")); vm->boolClass = AS_CLASS(wrenFindVariable(vm, coreModule, "Bool"));
PRIMITIVE(vm->boolClass, "toString", bool_toString); PRIMITIVE(vm->boolClass, "toString", bool_toString);

217
src/vm/wren_core.wren.inc Normal file
View File

@ -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";

View File

@ -9,109 +9,7 @@
// TODO: This is an arbitrary limit. Do something smarter. // TODO: This is an arbitrary limit. Do something smarter.
#define MAX_READ_LEN 1024 #define MAX_READ_LEN 1024
// This string literal is generated automatically from io.wren. Do not edit. #include "wren_io.wren.inc"
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";
static void ioWriteString(WrenVM* vm) static void ioWriteString(WrenVM* vm)
{ {
@ -143,14 +41,14 @@ static void ioTime(WrenVM* vm)
void wrenLoadIOLibrary(WrenVM* vm) void wrenLoadIOLibrary(WrenVM* vm)
{ {
wrenInterpret(vm, "", ioLibSource); wrenInterpret(vm, "", ioModuleSource);
} }
WrenForeignMethodFn wrenBindIOForeignMethod(WrenVM* vm, const char* className, WrenForeignMethodFn wrenBindIOForeignMethod(WrenVM* vm, const char* className,
const char* signature) const char* signature)
{ {
if (strcmp(className, "IO") != 0) return NULL; if (strcmp(className, "IO") != 0) return NULL;
if (strcmp(signature, "writeString_(_)") == 0) return ioWriteString; if (strcmp(signature, "writeString_(_)") == 0) return ioWriteString;
if (strcmp(signature, "clock") == 0) return ioClock; if (strcmp(signature, "clock") == 0) return ioClock;
if (strcmp(signature, "time") == 0) return ioTime; if (strcmp(signature, "time") == 0) return ioTime;

103
src/vm/wren_io.wren.inc Normal file
View File

@ -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";

View File

@ -6,9 +6,7 @@
#include "wren_primitive.h" #include "wren_primitive.h"
// This string literal is generated automatically from meta.wren. Do not edit. #include "wren_meta.wren.inc"
static const char* metaLibSource =
"class Meta {}\n";
DEF_PRIMITIVE(meta_eval) DEF_PRIMITIVE(meta_eval)
{ {
@ -43,10 +41,10 @@ DEF_PRIMITIVE(meta_eval)
void wrenLoadMetaLibrary(WrenVM* vm) void wrenLoadMetaLibrary(WrenVM* vm)
{ {
wrenInterpret(vm, "", metaLibSource); wrenInterpret(vm, "", metaModuleSource);
ObjModule* coreModule = wrenGetCoreModule(vm); ObjModule* coreModule = wrenGetCoreModule(vm);
// The methods on "Meta" are static, so get the metaclass for the Meta class. // The methods on "Meta" are static, so get the metaclass for the Meta class.
ObjClass* meta = AS_CLASS(wrenFindVariable(vm, coreModule, "Meta")); ObjClass* meta = AS_CLASS(wrenFindVariable(vm, coreModule, "Meta"));
PRIMITIVE(meta->obj.classObj, "eval(_)", meta_eval); PRIMITIVE(meta->obj.classObj, "eval(_)", meta_eval);

View File

@ -0,0 +1,3 @@
// Generated automatically from builtin/meta.wren. Do not edit.
static const char* metaModuleSource =
"class Meta {}\n";

View File

@ -634,7 +634,7 @@ static void bindForeignClass(WrenVM* vm, ObjClass* classObj, ObjModule* module)
// occurred. // occurred.
// //
// Returns false if the result is an error. // 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) ObjModule* module)
{ {
// Pull the name and superclass off the stack. // Pull the name and superclass off the stack.
@ -1195,13 +1195,13 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
CASE_CODE(CLASS): CASE_CODE(CLASS):
{ {
if (!defineClass(vm, fiber, READ_BYTE(), NULL)) RUNTIME_ERROR(PEEK()); if (!createClass(vm, fiber, READ_BYTE(), NULL)) RUNTIME_ERROR(PEEK());
DISPATCH(); DISPATCH();
} }
CASE_CODE(FOREIGN_CLASS): 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(); DISPATCH();
} }