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
@ ./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.
docs:
@ ./script/generate_docs.py

View File

@ -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`.
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_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

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

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_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);

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.
#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;

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

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.
//
// 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();
}