diff --git a/Makefile b/Makefile index 4155985f..9ba2c480 100644 --- a/Makefile +++ b/Makefile @@ -53,4 +53,9 @@ watchdocs: gh-pages: docs @ cp -r build/docs/. build/gh-pages +# Build amalgamation of all Wren library files. +wren.c: src/include/wren.h src/vm/*.h src/vm/*.c + ./script/generate_amalgamation.py > $@ + +.DELETE_ON_ERROR: wren.c .PHONY: debug release all clean test builtin docs watchdocs gh-pages diff --git a/script/generate_amalgamation.py b/script/generate_amalgamation.py new file mode 100755 index 00000000..6f862e90 --- /dev/null +++ b/script/generate_amalgamation.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +import sys +from os.path import basename, dirname, join, realpath +from glob import iglob +import re + +INCLUDE_PATTERN = re.compile(r'^\s*#include "([\w.]+)"') +GUARD_PATTERN = re.compile(r'^#ifndef wren(_\w+)?_h$') +WREN_DIR = dirname(dirname(realpath(__file__))) + +seen_files = set() +out = sys.stdout + +# Prints a plain text file, adding comment markers. +def add_comment_file(filename): + with open(filename, 'r') as f: + for line in f: + out.write('// ') + out.write(line) + +# Prints the given C source file, recursively resolving local #includes. +def add_file(filename): + bname = basename(filename) + # Only include each file at most once. + if bname in seen_files: + return + once = False + path = dirname(filename) + + out.write('// Begin file "{0}"\n'.format(filename)) + with open(filename, 'r') as f: + for line in f: + m = INCLUDE_PATTERN.match(line) + if m: + add_file(join(path, m.group(1))) + else: + out.write(line) + if GUARD_PATTERN.match(line): + once = True + out.write('// End file "{0}"\n'.format(filename)) + + # Only skip header files which use #ifndef guards. + # This is necessary because of the X Macro technique. + if once: + seen_files.add(bname) + +# Print license on top. +add_comment_file(join(WREN_DIR, 'LICENSE')) +out.write('\n') +# Source files. +add_file(join(WREN_DIR, 'src', 'include', 'wren.h')) +# Must be included here because of conditional compilation. +add_file(join(WREN_DIR, 'src', 'vm', 'wren_debug.h')) +for f in iglob(join(WREN_DIR, 'src', 'vm', '*.c')): + add_file(f) diff --git a/script/generate_builtins.py b/script/generate_builtins.py index 56d6e1bb..99c8bfe1 100755 --- a/script/generate_builtins.py +++ b/script/generate_builtins.py @@ -4,7 +4,7 @@ import glob import os.path import re -PATTERN = re.compile(r'libSource =\n("(.|[\n])*?);') +PATTERN = re.compile(r'LibSource =\n("(.|[\n])*?);') def copy_builtin(filename): name = os.path.basename(filename) @@ -24,7 +24,7 @@ def copy_builtin(filename): # in the C string literal. wren_source = wren_source.replace('\\', '\\\\') - constant = "libSource =\n" + wren_source + ";" + constant = "LibSource =\n" + wren_source + ";" with open("src/vm/wren_" + name + ".c", "r") as f: c_source = f.read() diff --git a/src/vm/wren_core.c b/src/vm/wren_core.c index 9fce8d68..5d0e2b28 100644 --- a/src/vm/wren_core.c +++ b/src/vm/wren_core.c @@ -11,7 +11,7 @@ #include "wren_value.h" // This string literal is generated automatically from core. Do not edit. -static const char* libSource = +static const char* coreLibSource = "class Bool {}\n" "class Fiber {}\n" "class Fn {}\n" @@ -485,7 +485,7 @@ DEF_PRIMITIVE(fn_arity) RETURN_NUM(AS_FN(args[0])->arity); } -static PrimitiveResult callFunction(WrenVM* vm, Value* args, int numArgs) +static PrimitiveResult callFn(WrenVM* vm, Value* args, int numArgs) { ObjFn* fn; if (IS_CLOSURE(args[0])) @@ -502,23 +502,23 @@ static PrimitiveResult callFunction(WrenVM* vm, Value* args, int numArgs) return PRIM_CALL; } -DEF_PRIMITIVE(fn_call0) { return callFunction(vm, args, 0); } -DEF_PRIMITIVE(fn_call1) { return callFunction(vm, args, 1); } -DEF_PRIMITIVE(fn_call2) { return callFunction(vm, args, 2); } -DEF_PRIMITIVE(fn_call3) { return callFunction(vm, args, 3); } -DEF_PRIMITIVE(fn_call4) { return callFunction(vm, args, 4); } -DEF_PRIMITIVE(fn_call5) { return callFunction(vm, args, 5); } -DEF_PRIMITIVE(fn_call6) { return callFunction(vm, args, 6); } -DEF_PRIMITIVE(fn_call7) { return callFunction(vm, args, 7); } -DEF_PRIMITIVE(fn_call8) { return callFunction(vm, args, 8); } -DEF_PRIMITIVE(fn_call9) { return callFunction(vm, args, 9); } -DEF_PRIMITIVE(fn_call10) { return callFunction(vm, args, 10); } -DEF_PRIMITIVE(fn_call11) { return callFunction(vm, args, 11); } -DEF_PRIMITIVE(fn_call12) { return callFunction(vm, args, 12); } -DEF_PRIMITIVE(fn_call13) { return callFunction(vm, args, 13); } -DEF_PRIMITIVE(fn_call14) { return callFunction(vm, args, 14); } -DEF_PRIMITIVE(fn_call15) { return callFunction(vm, args, 15); } -DEF_PRIMITIVE(fn_call16) { return callFunction(vm, args, 16); } +DEF_PRIMITIVE(fn_call0) { return callFn(vm, args, 0); } +DEF_PRIMITIVE(fn_call1) { return callFn(vm, args, 1); } +DEF_PRIMITIVE(fn_call2) { return callFn(vm, args, 2); } +DEF_PRIMITIVE(fn_call3) { return callFn(vm, args, 3); } +DEF_PRIMITIVE(fn_call4) { return callFn(vm, args, 4); } +DEF_PRIMITIVE(fn_call5) { return callFn(vm, args, 5); } +DEF_PRIMITIVE(fn_call6) { return callFn(vm, args, 6); } +DEF_PRIMITIVE(fn_call7) { return callFn(vm, args, 7); } +DEF_PRIMITIVE(fn_call8) { return callFn(vm, args, 8); } +DEF_PRIMITIVE(fn_call9) { return callFn(vm, args, 9); } +DEF_PRIMITIVE(fn_call10) { return callFn(vm, args, 10); } +DEF_PRIMITIVE(fn_call11) { return callFn(vm, args, 11); } +DEF_PRIMITIVE(fn_call12) { return callFn(vm, args, 12); } +DEF_PRIMITIVE(fn_call13) { return callFn(vm, args, 13); } +DEF_PRIMITIVE(fn_call14) { return callFn(vm, args, 14); } +DEF_PRIMITIVE(fn_call15) { return callFn(vm, args, 15); } +DEF_PRIMITIVE(fn_call16) { return callFn(vm, args, 16); } DEF_PRIMITIVE(fn_toString) { @@ -1331,7 +1331,7 @@ void wrenInitializeCore(WrenVM* vm) // '---------' '--------------' -' // The rest of the classes can now be defined normally. - wrenInterpret(vm, "", libSource); + wrenInterpret(vm, "", coreLibSource); vm->boolClass = AS_CLASS(wrenFindVariable(vm, "Bool")); PRIMITIVE(vm->boolClass, "toString", bool_toString); diff --git a/src/vm/wren_debug.c b/src/vm/wren_debug.c index 7e243157..cf50f7d6 100644 --- a/src/vm/wren_debug.c +++ b/src/vm/wren_debug.c @@ -333,6 +333,9 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine) // Return how many bytes this instruction takes, or -1 if it's an END. if (code == CODE_END) return -1; return i - start; + + #undef READ_BYTE + #undef READ_SHORT } int wrenDumpInstruction(WrenVM* vm, ObjFn* fn, int i) diff --git a/src/vm/wren_io.c b/src/vm/wren_io.c index ff079f02..8463ca8d 100644 --- a/src/vm/wren_io.c +++ b/src/vm/wren_io.c @@ -10,7 +10,7 @@ #define MAX_READ_LEN 1024 // This string literal is generated automatically from io.wren. Do not edit. -static const char* libSource = +static const char* ioLibSource = "class IO {\n" " static print {\n" " IO.writeString_(\"\n\")\n" @@ -142,7 +142,7 @@ static void ioTime(WrenVM* vm) void wrenLoadIOLibrary(WrenVM* vm) { - wrenInterpret(vm, "", libSource); + wrenInterpret(vm, "", ioLibSource); } WrenForeignMethodFn wrenBindIOForeignMethod(WrenVM* vm, const char* className, diff --git a/src/vm/wren_meta.c b/src/vm/wren_meta.c index ff8cd10a..6f20f6fd 100644 --- a/src/vm/wren_meta.c +++ b/src/vm/wren_meta.c @@ -7,7 +7,7 @@ #include "wren_primitive.h" // This string literal is generated automatically from meta.wren. Do not edit. -static const char* libSource = +static const char* metaLibSource = "class Meta {}\n"; DEF_PRIMITIVE(meta_eval) @@ -43,7 +43,7 @@ DEF_PRIMITIVE(meta_eval) void wrenLoadMetaLibrary(WrenVM* vm) { - wrenInterpret(vm, "", libSource); + wrenInterpret(vm, "", metaLibSource); // The methods on "Meta" are static, so get the metaclass for the Meta class. ObjClass* meta = AS_CLASS(wrenFindVariable(vm, "Meta"))->obj.classObj; diff --git a/src/vm/wren_vm.c b/src/vm/wren_vm.c index 26f424b7..d4c81a1b 100644 --- a/src/vm/wren_vm.c +++ b/src/vm/wren_vm.c @@ -1219,6 +1219,8 @@ static bool runInterpreter(WrenVM* vm) UNREACHABLE(); return false; } +#undef READ_BYTE +#undef READ_SHORT // Creates an [ObjFn] that invokes a method with [signature] when called. static ObjFn* makeCallStub(WrenVM* vm, ObjModule* module, const char* signature)