1
0
forked from Mirror/wren

A vertical slice of real libuv integration.

This adds a "timer" module to the CLI that provides a Timer class with
a static sleep() method. Not the most exciting functionality in the
world, but it requires the full hunk of libuv integration:

- The CLI sets up libuv and runs the event loop.
- Added a new directory src/module for CLI modules.
- Updated all the make scripts to handle it.
- Reorganized some other CLI code.
This commit is contained in:
Bob Nystrom
2015-08-07 08:10:55 -07:00
parent 254729d067
commit d4acbe8a70
13 changed files with 456 additions and 70 deletions

View File

@ -4,15 +4,15 @@
# Executables are built to bin/. Libraries are built to lib/.
LIB_UV := build/libuv/build/Release/libuv.a
LIBUV := build/libuv/build/Release/libuv.a
# A normal, optimized release build for the current CPU architecture.
release: $(LIB_UV)
release: $(LIBUV)
@ $(MAKE) -f script/wren.mk
@ cp bin/wren wren # For convenience, copy the interpreter to the top level.
# A debug build for the current architecture.
debug: $(LIB_UV)
debug: $(LIBUV)
@ $(MAKE) -f script/wren.mk MODE=debug
# A release build of just the VM. Does not require libuv.
@ -33,7 +33,7 @@ all: debug release
@ $(MAKE) -f script/wren.mk MODE=debug LANG=cpp ARCH=64
# Download and build libuv to a static library.
$(LIB_UV): script/libuv.py
$(LIBUV): script/libuv.py
@ ./script/libuv.py
# Remove all build outputs and intermediate files.

View File

@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
2901D7641B74F4050083A2C8 /* timer.c in Sources */ = {isa = PBXBuildFile; fileRef = 2901D7621B74F4050083A2C8 /* timer.c */; };
2901D7651B74F4050083A2C8 /* timer.c in Sources */ = {isa = PBXBuildFile; fileRef = 2901D7621B74F4050083A2C8 /* timer.c */; };
29205C8F1AB4E5C90073018D /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C8E1AB4E5C90073018D /* main.c */; };
29205C991AB4E6430073018D /* wren_compiler.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C921AB4E6430073018D /* wren_compiler.c */; };
29205C9A1AB4E6430073018D /* wren_core.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C931AB4E6430073018D /* wren_core.c */; };
@ -19,6 +21,22 @@
29C0888F1B6E7F0100B00CFD /* libuv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 29C0888E1B6E7F0100B00CFD /* libuv.a */; };
29C8A92F1AB71C1C00DEC81D /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 29C8A92E1AB71C1C00DEC81D /* io.c */; };
29C8A9331AB71FFF00DEC81D /* vm.c in Sources */ = {isa = PBXBuildFile; fileRef = 29C8A9311AB71FFF00DEC81D /* vm.c */; };
29CE20181B7257720057FBA3 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 29CE20171B7257720057FBA3 /* main.c */; };
29CE201B1B72577C0057FBA3 /* return_bool.c in Sources */ = {isa = PBXBuildFile; fileRef = 29CE20191B72577C0057FBA3 /* return_bool.c */; };
29CE201E1B7257840057FBA3 /* return_double.c in Sources */ = {isa = PBXBuildFile; fileRef = 29CE201C1B7257840057FBA3 /* return_double.c */; };
29CE20211B72578C0057FBA3 /* return_null.c in Sources */ = {isa = PBXBuildFile; fileRef = 29CE201F1B72578C0057FBA3 /* return_null.c */; };
29CE20221B72586B0057FBA3 /* io.c in Sources */ = {isa = PBXBuildFile; fileRef = 29C8A92E1AB71C1C00DEC81D /* io.c */; };
29CE20231B72586B0057FBA3 /* vm.c in Sources */ = {isa = PBXBuildFile; fileRef = 29C8A9311AB71FFF00DEC81D /* vm.c */; };
29CE20241B72586B0057FBA3 /* wren_compiler.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C921AB4E6430073018D /* wren_compiler.c */; };
29CE20251B72586B0057FBA3 /* wren_core.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C931AB4E6430073018D /* wren_core.c */; };
29CE20261B72586B0057FBA3 /* wren_debug.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C941AB4E6430073018D /* wren_debug.c */; };
29CE20271B72586B0057FBA3 /* wren_io.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C951AB4E6430073018D /* wren_io.c */; };
29CE20281B72586B0057FBA3 /* wren_meta.c in Sources */ = {isa = PBXBuildFile; fileRef = 29DE39511AC3A50A00987D41 /* wren_meta.c */; };
29CE20291B72586B0057FBA3 /* wren_primitive.c in Sources */ = {isa = PBXBuildFile; fileRef = 2986F6D51ACF93BA00BCE26C /* wren_primitive.c */; };
29CE202A1B72586B0057FBA3 /* wren_utils.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C961AB4E6430073018D /* wren_utils.c */; };
29CE202B1B72586B0057FBA3 /* wren_value.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C971AB4E6430073018D /* wren_value.c */; };
29CE202C1B72586B0057FBA3 /* wren_vm.c in Sources */ = {isa = PBXBuildFile; fileRef = 29205C981AB4E6430073018D /* wren_vm.c */; };
29CE202D1B7258E20057FBA3 /* libuv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 29C0888E1B6E7F0100B00CFD /* libuv.a */; };
29DE39531AC3A50A00987D41 /* wren_meta.c in Sources */ = {isa = PBXBuildFile; fileRef = 29DE39511AC3A50A00987D41 /* wren_meta.c */; };
/* End PBXBuildFile section */
@ -32,9 +50,20 @@
);
runOnlyForDeploymentPostprocessing = 1;
};
29CE20081B7256660057FBA3 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = /usr/share/man/man1/;
dstSubfolderSpec = 0;
files = (
);
runOnlyForDeploymentPostprocessing = 1;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
2901D7621B74F4050083A2C8 /* timer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = timer.c; path = ../../src/module/timer.c; sourceTree = "<group>"; };
2901D7631B74F4050083A2C8 /* timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = timer.h; path = ../../src/module/timer.h; sourceTree = "<group>"; };
29205C8E1AB4E5C90073018D /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = main.c; path = ../../src/cli/main.c; sourceTree = "<group>"; };
29205C901AB4E62B0073018D /* wren.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = wren.h; path = ../../src/include/wren.h; sourceTree = "<group>"; };
29205C921AB4E6430073018D /* wren_compiler.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = wren_compiler.c; path = ../../src/vm/wren_compiler.c; sourceTree = "<group>"; };
@ -61,6 +90,14 @@
29C8A9301AB71C3300DEC81D /* io.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = io.h; path = ../../src/cli/io.h; sourceTree = "<group>"; };
29C8A9311AB71FFF00DEC81D /* vm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = vm.c; path = ../../src/cli/vm.c; sourceTree = "<group>"; };
29C8A9321AB71FFF00DEC81D /* vm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vm.h; path = ../../src/cli/vm.h; sourceTree = "<group>"; };
29CE200A1B7256660057FBA3 /* api_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = api_test; sourceTree = BUILT_PRODUCTS_DIR; };
29CE20171B7257720057FBA3 /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = main.c; path = ../../test/api/main.c; sourceTree = "<group>"; };
29CE20191B72577C0057FBA3 /* return_bool.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = return_bool.c; path = ../../test/api/return_bool/return_bool.c; sourceTree = "<group>"; };
29CE201A1B72577C0057FBA3 /* return_bool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = return_bool.h; path = ../../test/api/return_bool/return_bool.h; sourceTree = "<group>"; };
29CE201C1B7257840057FBA3 /* return_double.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = return_double.c; path = ../../test/api/return_double/return_double.c; sourceTree = "<group>"; };
29CE201D1B7257840057FBA3 /* return_double.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = return_double.h; path = ../../test/api/return_double/return_double.h; sourceTree = "<group>"; };
29CE201F1B72578C0057FBA3 /* return_null.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = return_null.c; path = ../../test/api/return_null/return_null.c; sourceTree = "<group>"; };
29CE20201B72578C0057FBA3 /* return_null.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = return_null.h; path = ../../test/api/return_null/return_null.h; sourceTree = "<group>"; };
29DE39511AC3A50A00987D41 /* wren_meta.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wren_meta.c; path = ../../src/vm/wren_meta.c; sourceTree = "<group>"; };
29DE39521AC3A50A00987D41 /* wren_meta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = wren_meta.h; path = ../../src/vm/wren_meta.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -74,9 +111,26 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
29CE20071B7256660057FBA3 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
29CE202D1B7258E20057FBA3 /* libuv.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2901D7611B74F3E20083A2C8 /* module */ = {
isa = PBXGroup;
children = (
2901D7631B74F4050083A2C8 /* timer.h */,
2901D7621B74F4050083A2C8 /* timer.c */,
);
name = module;
sourceTree = "<group>";
};
29205CA01AB4E6470073018D /* vm */ = {
isa = PBXGroup;
children = (
@ -130,7 +184,9 @@
29C0888E1B6E7F0100B00CFD /* libuv.a */,
29205CA91AB4E67B0073018D /* cli */,
29205CAA1AB4E6840073018D /* include */,
2901D7611B74F3E20083A2C8 /* module */,
29205CA01AB4E6470073018D /* vm */,
29CE20161B7257680057FBA3 /* api */,
29AB1F071816E3AD004B501E /* Products */,
);
sourceTree = "<group>";
@ -139,10 +195,25 @@
isa = PBXGroup;
children = (
29AB1F061816E3AD004B501E /* wren */,
29CE200A1B7256660057FBA3 /* api_test */,
);
name = Products;
sourceTree = "<group>";
};
29CE20161B7257680057FBA3 /* api */ = {
isa = PBXGroup;
children = (
29CE20171B7257720057FBA3 /* main.c */,
29CE20191B72577C0057FBA3 /* return_bool.c */,
29CE201A1B72577C0057FBA3 /* return_bool.h */,
29CE201C1B7257840057FBA3 /* return_double.c */,
29CE201D1B7257840057FBA3 /* return_double.h */,
29CE201F1B72578C0057FBA3 /* return_null.c */,
29CE20201B72578C0057FBA3 /* return_null.h */,
);
name = api;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -163,6 +234,23 @@
productReference = 29AB1F061816E3AD004B501E /* wren */;
productType = "com.apple.product-type.tool";
};
29CE20091B7256660057FBA3 /* api_test */ = {
isa = PBXNativeTarget;
buildConfigurationList = 29CE20101B7256660057FBA3 /* Build configuration list for PBXNativeTarget "api_test" */;
buildPhases = (
29CE20061B7256660057FBA3 /* Sources */,
29CE20071B7256660057FBA3 /* Frameworks */,
29CE20081B7256660057FBA3 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = api_test;
productName = api_test;
productReference = 29CE200A1B7256660057FBA3 /* api_test */;
productType = "com.apple.product-type.tool";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@ -171,6 +259,11 @@
attributes = {
LastUpgradeCheck = 0610;
ORGANIZATIONNAME = "Bob Nystrom";
TargetAttributes = {
29CE20091B7256660057FBA3 = {
CreatedOnToolsVersion = 6.3.2;
};
};
};
buildConfigurationList = 29AB1F011816E3AD004B501E /* Build configuration list for PBXProject "wren" */;
compatibilityVersion = "Xcode 3.2";
@ -185,6 +278,7 @@
projectRoot = "";
targets = (
29AB1F051816E3AD004B501E /* wren */,
29CE20091B7256660057FBA3 /* api_test */,
);
};
/* End PBXProject section */
@ -197,6 +291,7 @@
29205C991AB4E6430073018D /* wren_compiler.c in Sources */,
2986F6D71ACF93BA00BCE26C /* wren_primitive.c in Sources */,
29205C9A1AB4E6430073018D /* wren_core.c in Sources */,
2901D7641B74F4050083A2C8 /* timer.c in Sources */,
29C8A9331AB71FFF00DEC81D /* vm.c in Sources */,
29205C9B1AB4E6430073018D /* wren_debug.c in Sources */,
29205C9C1AB4E6430073018D /* wren_io.c in Sources */,
@ -209,6 +304,29 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
29CE20061B7256660057FBA3 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
29CE20221B72586B0057FBA3 /* io.c in Sources */,
29CE20231B72586B0057FBA3 /* vm.c in Sources */,
29CE20241B72586B0057FBA3 /* wren_compiler.c in Sources */,
29CE20251B72586B0057FBA3 /* wren_core.c in Sources */,
29CE20261B72586B0057FBA3 /* wren_debug.c in Sources */,
29CE20271B72586B0057FBA3 /* wren_io.c in Sources */,
29CE20281B72586B0057FBA3 /* wren_meta.c in Sources */,
29CE20291B72586B0057FBA3 /* wren_primitive.c in Sources */,
29CE202A1B72586B0057FBA3 /* wren_utils.c in Sources */,
2901D7651B74F4050083A2C8 /* timer.c in Sources */,
29CE202B1B72586B0057FBA3 /* wren_value.c in Sources */,
29CE202C1B72586B0057FBA3 /* wren_vm.c in Sources */,
29CE20181B7257720057FBA3 /* main.c in Sources */,
29CE201B1B72577C0057FBA3 /* return_bool.c in Sources */,
29CE201E1B7257840057FBA3 /* return_double.c in Sources */,
29CE20211B72578C0057FBA3 /* return_null.c in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
@ -310,6 +428,48 @@
};
name = Release;
};
29CE200E1B7256660057FBA3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = c99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_PEDANTIC = NO;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
LIBRARY_SEARCH_PATHS = ../../build/libuv/build/Release;
MACOSX_DEPLOYMENT_TARGET = 10.10;
MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_NAME = "$(TARGET_NAME)";
USER_HEADER_SEARCH_PATHS = ../../build/libuv/include;
};
name = Debug;
};
29CE200F1B7256660057FBA3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = c99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_PEDANTIC = NO;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
LIBRARY_SEARCH_PATHS = ../../build/libuv/build/Release;
MACOSX_DEPLOYMENT_TARGET = 10.10;
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_NAME = "$(TARGET_NAME)";
USER_HEADER_SEARCH_PATHS = ../../build/libuv/include;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@ -331,6 +491,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
29CE20101B7256660057FBA3 /* Build configuration list for PBXNativeTarget "api_test" */ = {
isa = XCConfigurationList;
buildConfigurations = (
29CE200E1B7256660057FBA3 /* Debug */,
29CE200F1B7256660057FBA3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 29AB1EFE1816E3AD004B501E /* Project object */;

View File

@ -1,4 +1,5 @@
#!/usr/bin/env python
# coding: utf-8
import glob
import os.path
@ -6,7 +7,7 @@ import re
PATTERN = re.compile(r'LibSource =\n("(.|[\n])*?);')
def copy_builtin(filename):
def copy_builtin(filename, out_prefix):
name = os.path.basename(filename)
name = name.split('.')[0]
@ -26,16 +27,19 @@ def copy_builtin(filename):
constant = "LibSource =\n" + wren_source + ";"
with open("src/vm/wren_" + name + ".c", "r") as f:
with open(out_prefix + name + ".c", "r") as f:
c_source = f.read()
c_source = PATTERN.sub(constant, c_source)
with open("src/vm/wren_" + name + ".c", "w") as f:
with open(out_prefix + name + ".c", "w") as f:
f.write(c_source)
print(name)
print(filename.ljust(25) + "" + out_prefix + name + ".c")
for f in glob.iglob("builtin/*.wren"):
copy_builtin(f)
copy_builtin(f, "src/vm/wren_")
for f in glob.iglob("src/module/*.wren"):
copy_builtin(f, "src/module/")

View File

@ -20,9 +20,14 @@
# Files.
CLI_HEADERS := $(wildcard src/cli/*.h)
VM_HEADERS := $(wildcard src/vm/*.h)
CLI_SOURCES := $(wildcard src/cli/*.c)
MODULE_HEADERS := $(wildcard src/module/*.h)
MODULE_SOURCES := $(wildcard src/module/*.c)
VM_HEADERS := $(wildcard src/vm/*.h)
VM_SOURCES := $(wildcard src/vm/*.c)
TEST_SOURCES := $(shell find test/api -name '*.c')
BUILD_DIR := build
@ -94,11 +99,13 @@ endif
CFLAGS := $(C_OPTIONS) $(C_WARNINGS)
CLI_OBJECTS := $(addprefix $(BUILD_DIR)/cli/, $(notdir $(CLI_SOURCES:.c=.o)))
VM_OBJECTS := $(addprefix $(BUILD_DIR)/vm/, $(notdir $(VM_SOURCES:.c=.o)))
TEST_OBJECTS := $(patsubst test/api/%.c, $(BUILD_DIR)/test/%.o, $(TEST_SOURCES))
CLI_OBJECTS := $(addprefix $(BUILD_DIR)/cli/, $(notdir $(CLI_SOURCES:.c=.o)))
MODULE_OBJECTS := $(addprefix $(BUILD_DIR)/module/, $(notdir $(MODULE_SOURCES:.c=.o)))
VM_OBJECTS := $(addprefix $(BUILD_DIR)/vm/, $(notdir $(VM_SOURCES:.c=.o)))
TEST_OBJECTS := $(patsubst test/api/%.c, $(BUILD_DIR)/test/%.o, $(TEST_SOURCES))
LIB_UV_DIR := build/libuv
LIBUV_DIR := build/libuv
LIBUV := $(LIBUV_DIR)/build/Release/libuv.a
# Targets ---------------------------------------------------------------------
@ -115,10 +122,10 @@ cli: bin/$(WREN)
test: $(BUILD_DIR)/test/$(WREN)
# Command-line interpreter.
bin/$(WREN): $(CLI_OBJECTS) $(VM_OBJECTS) $(LIB_UV_DIR)/build/Release/libuv.a
bin/$(WREN): $(CLI_OBJECTS) $(MODULE_OBJECTS) $(VM_OBJECTS) $(LIBUV)
@printf "%10s %-30s %s\n" $(CC) $@ "$(C_OPTIONS)"
@mkdir -p bin
@$(CC) $(CFLAGS) -L$(LIB_UV_DIR)/build/Release -l uv -o $@ $^ -lm
@$(CC) $(CFLAGS) -L$(LIBUV_DIR)/build/Release -l uv -o $@ $^ -lm
# Static library.
lib/lib$(WREN).a: $(VM_OBJECTS)
@ -133,16 +140,23 @@ lib/lib$(WREN).$(SHARED_EXT): $(VM_OBJECTS)
@$(CC) $(CFLAGS) -shared $(SHARED_LIB_FLAGS) -o $@ $^
# Test executable.
$(BUILD_DIR)/test/$(WREN): $(TEST_OBJECTS) $(VM_OBJECTS) $(BUILD_DIR)/cli/io.o $(BUILD_DIR)/cli/vm.o
$(BUILD_DIR)/test/$(WREN): $(TEST_OBJECTS) $(MODULE_OBJECTS) $(VM_OBJECTS) \
$(BUILD_DIR)/cli/io.o $(BUILD_DIR)/cli/vm.o $(LIBUV)
@printf "%10s %-30s %s\n" $(CC) $@ "$(C_OPTIONS)"
@mkdir -p $(BUILD_DIR)/test
@$(CC) $(CFLAGS) -o $@ $^ -lm
@$(CC) $(CFLAGS) -L$(LIBUV_DIR)/build/Release -l uv -o $@ $^ -lm
# CLI object files.
$(BUILD_DIR)/cli/%.o: src/cli/%.c $(CLI_HEADERS) $(VM_HEADERS)
$(BUILD_DIR)/cli/%.o: src/cli/%.c $(CLI_HEADERS) $(MODULE_HEADERS) $(VM_HEADERS)
@printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)"
@mkdir -p $(BUILD_DIR)/cli
@$(CC) -c $(CFLAGS) -Isrc/include -I$(LIB_UV_DIR)/include -o $@ $(FILE_FLAG) $<
@$(CC) -c $(CFLAGS) -Isrc/include -Isrc/module -I$(LIBUV_DIR)/include -o $@ $(FILE_FLAG) $<
# Module object files.
$(BUILD_DIR)/module/%.o: src/module/%.c $(CLI_HEADERS) $(MODULE_HEADERS) $(VM_HEADERS)
@printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)"
@mkdir -p $(BUILD_DIR)/module
@$(CC) -c $(CFLAGS) -Isrc/include -Isrc/cli -I$(LIBUV_DIR)/include -o $@ $(FILE_FLAG) $<
# VM object files.
$(BUILD_DIR)/vm/%.o: src/vm/%.c $(VM_HEADERS)
@ -151,9 +165,9 @@ $(BUILD_DIR)/vm/%.o: src/vm/%.c $(VM_HEADERS)
@$(CC) -c $(CFLAGS) -Isrc/include -o $@ $(FILE_FLAG) $<
# Test object files.
$(BUILD_DIR)/test/%.o: test/api/%.c $(VM_HEADERS)
$(BUILD_DIR)/test/%.o: test/api/%.c $(MODULE_HEADERS) $(VM_HEADERS)
@printf "%10s %-30s %s\n" $(CC) $< "$(C_OPTIONS)"
@mkdir -p $(dir $@)
@$(CC) -c $(CFLAGS) -Isrc/include -Isrc/cli -o $@ $(FILE_FLAG) $<
@$(CC) -c $(CFLAGS) -Isrc/include -I$(LIBUV_DIR)/include -Isrc/cli -o $@ $(FILE_FLAG) $<
.PHONY: all cli test vm

19
src/README.md Normal file
View File

@ -0,0 +1,19 @@
This contains the Wren source code. It is organized like so:
* `cli`: the source code for the command line interface. This is a custom
exectuable that embeds the VM in itself. Code here handles reading
command-line, running the REPL, loading modules from disc, etc.
* `include`: the public header directory for the VM. If you are embedding the
VM in your own application, you will add this to your include path.
* `module`: the source code for the built-in modules that come with the CLI.
These modules are written in a mixture of C and Wren and generally use
[libuv][] to implement their underlying functionality.
* `vm`: the source code for the Wren VM itself. Unlike code in `cli` and
`module`, this has no dependencies on libuv. If you are embedding the VM in
your own application from source, you will compile the files here into your
app.
[libuv]: http://libuv.org/

View File

@ -4,6 +4,8 @@
#include "io.h"
#include "timer.h"
char const* rootDirectory = NULL;
// Reads the contents of the file at [path] and returns it as a heap allocated
@ -71,6 +73,8 @@ char* wrenFilePath(const char* name)
char* readModule(WrenVM* vm, const char* module)
{
if (strcmp(module, "timer") == 0) return timerGetSource();
// First try to load the module with a ".wren" extension.
char* modulePath = wrenFilePath(module);
char* moduleContents = readFile(modulePath);

View File

@ -5,37 +5,6 @@
#include "vm.h"
#include "wren.h"
#define MAX_LINE_LENGTH 1024 // TODO: Something less arbitrary.
static int runRepl()
{
WrenVM* vm = createVM(NULL);
printf("\\\\/\"-\n");
printf(" \\_/ wren v0.0.0\n");
char line[MAX_LINE_LENGTH];
for (;;)
{
printf("> ");
if (!fgets(line, MAX_LINE_LENGTH, stdin))
{
printf("\n");
break;
}
// TODO: Handle failure.
wrenInterpret(vm, "Prompt", line);
// TODO: Automatically print the result of expressions.
}
wrenFreeVM(vm);
return 0;
}
int main(int argc, const char* argv[])
{
if (argc < 1 || argc > 2)
@ -43,14 +12,14 @@ int main(int argc, const char* argv[])
fprintf(stderr, "Usage: wren [file]\n");
return 64; // EX_USAGE.
}
if (argc == 1)
{
runRepl();
}
else if (argc == 2)
{
runFile(NULL, argv[1]);
runFile(argv[1], NULL);
}
return 0;

View File

@ -3,12 +3,41 @@
#include "io.h"
#include "vm.h"
#include "timer.h"
WrenVM* createVM(WrenBindForeignMethodFn bindForeign)
#define MAX_LINE_LENGTH 1024 // TODO: Something less arbitrary.
// The single VM instance that the CLI uses.
WrenVM* vm;
WrenBindForeignMethodFn externalBindForeign;
uv_loop_t* loop;
static WrenForeignMethodFn bindForeignMethod(WrenVM* vm,
const char* module,
const char* className,
bool isStatic,
const char* signature)
{
if (strcmp(module, "timer") == 0)
{
return timerBindForeign(vm, className, isStatic, signature);
}
if (externalBindForeign != NULL)
{
return externalBindForeign(vm, module, className, isStatic, signature);
}
return NULL;
}
static void initVM()
{
WrenConfiguration config;
config.bindForeignMethodFn = bindForeign;
config.bindForeignMethodFn = bindForeignMethod;
config.loadModuleFn = readModule;
// Since we're running in a standalone process, be generous with memory.
@ -18,12 +47,28 @@ WrenVM* createVM(WrenBindForeignMethodFn bindForeign)
config.reallocateFn = NULL;
config.minHeapSize = 0;
config.heapGrowthPercent = 0;
return wrenNewVM(&config);
vm = wrenNewVM(&config);
// Initialize the event loop.
loop = (uv_loop_t*)malloc(sizeof(uv_loop_t));
uv_loop_init(loop);
}
void runFile(WrenBindForeignMethodFn bindForeign, const char* path)
static void freeVM()
{
timerReleaseMethods();
uv_loop_close(loop);
free(loop);
wrenFreeVM(vm);
}
void runFile(const char* path, WrenBindForeignMethodFn bindForeign)
{
externalBindForeign = bindForeign;
// Use the directory where the file is as the root to resolve imports
// relative to.
char* root = NULL;
@ -42,12 +87,18 @@ void runFile(WrenBindForeignMethodFn bindForeign, const char* path)
fprintf(stderr, "Could not find file \"%s\".\n", path);
exit(66);
}
WrenVM* vm = createVM(bindForeign);
initVM();
WrenInterpretResult result = wrenInterpret(vm, path, source);
if (result == WREN_RESULT_SUCCESS)
{
uv_run(loop, UV_RUN_DEFAULT);
}
wrenFreeVM(vm);
freeVM();
free(source);
free(root);
@ -55,3 +106,43 @@ void runFile(WrenBindForeignMethodFn bindForeign, const char* path)
if (result == WREN_RESULT_COMPILE_ERROR) exit(65); // EX_DATAERR.
if (result == WREN_RESULT_RUNTIME_ERROR) exit(70); // EX_SOFTWARE.
}
int runRepl()
{
initVM();
printf("\\\\/\"-\n");
printf(" \\_/ wren v0.0.0\n");
char line[MAX_LINE_LENGTH];
for (;;)
{
printf("> ");
if (!fgets(line, MAX_LINE_LENGTH, stdin))
{
printf("\n");
break;
}
// TODO: Handle failure.
wrenInterpret(vm, "Prompt", line);
// TODO: Automatically print the result of expressions.
}
freeVM();
return 0;
}
WrenVM* getVM()
{
return vm;
}
uv_loop_t* getLoop()
{
return loop;
}

View File

@ -1,14 +1,22 @@
#ifndef vm_h
#define vm_h
#include "uv.h"
#include "wren.h"
// Creates a new Wren VM with the CLI's module loader and other configuration.
WrenVM* createVM(WrenBindForeignMethodFn bindForeign);
// Executes the Wren script at [path] in a new VM.
//
// Exits if the script failed or could not be loaded.
void runFile(WrenBindForeignMethodFn bindForeign, const char* path);
// If [bindForeign] is not `NULL`, it will be called to register any foreign
// methods that the CLI itself doesn't handle.
void runFile(const char* path, WrenBindForeignMethodFn bindForeign);
// Runs the Wren interactive REPL.
int runRepl();
// Gets the currently running VM.
WrenVM* getVM();
// Gets the event loop the VM is using.
uv_loop_t* getLoop();
#endif

81
src/module/timer.c Normal file
View File

@ -0,0 +1,81 @@
#include <stdlib.h>
#include <string.h>
#include "uv.h"
#include "timer.h"
#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"
" startTimer_(milliseconds, Fiber.current)\n"
" Fiber.yield()\n"
" }\n"
"\n"
" foreign static startTimer_(milliseconds, fiber)\n"
"\n"
" // Called by native code.\n"
" static resumeTimer_(fiber) {\n"
" fiber.run()\n"
" }\n"
"}\n";
// The Wren method to call when a timer has completed.
static WrenMethod* resumeTimer;
// Called by libuv when the timer has completed.
static void timerCallback(uv_timer_t* handle)
{
WrenValue* fiber = (WrenValue*)handle->data;
free(handle);
// Run the fiber that was sleeping.
wrenCall(getVM(), resumeTimer, "v", fiber);
wrenReleaseValue(getVM(), fiber);
}
static void startTimer(WrenVM* vm)
{
// If we haven't looked up the resume method yet, grab it now.
if (resumeTimer == NULL)
{
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);
char* copy = malloc(length + 1);
strncpy(copy, timerLibSource, length);
return copy;
}
WrenForeignMethodFn timerBindForeign(
WrenVM* vm, const char* className, bool isStatic, const char* signature)
{
if (strcmp(className, "Timer") != 0) return NULL;
if (isStatic && strcmp(signature, "startTimer_(_,_)") == 0) return startTimer;
return NULL;
}
void timerReleaseMethods()
{
if (resumeTimer != NULL) wrenReleaseMethod(getVM(), resumeTimer);
}

14
src/module/timer.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef timer_h
#define timer_h
#include "wren.h"
// TODO: Coherent naming scheme.
char* timerGetSource();
WrenForeignMethodFn timerBindForeign(
WrenVM* vm, const char* className, bool isStatic, const char* signature);
void timerReleaseMethods();
#endif

13
src/module/timer.wren Normal file
View File

@ -0,0 +1,13 @@
class Timer {
static sleep(milliseconds) {
startTimer_(milliseconds, Fiber.current)
Fiber.yield()
}
foreign static startTimer_(milliseconds, fiber)
// Called by native code.
static resumeTimer_(fiber) {
fiber.run()
}
}

View File

@ -60,6 +60,6 @@ int main(int argc, const char* argv[])
strcat(testPath, testName);
strcat(testPath, ".wren");
runFile(bindForeign, testPath);
runFile(testPath, bindForeign);
return 0;
}