diff --git a/.gitignore b/.gitignore index 49c129e0..fa18aa19 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ -# Output directory. +# Intermediate files directory. build/ +# Built applications. +wren +wrend + # XCode user-specific stuff. xcuserdata/ *.xccheckout diff --git a/Makefile b/Makefile index 79615b54..118f2f65 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,53 @@ +# Compiler flags. +CFLAGS = -std=c99 -Wall -Werror +# TODO: Add -Wextra. +DEBUG_CFLAGS = -O0 -DDEBUG +RELEASE_CFLAGS = -Os + +# Files. +SOURCES = $(wildcard src/*.c) +HEADERS = $(wildcard src/*.h) +OBJECTS = $(SOURCES:.c=.o) + +DEBUG_OBJECTS = $(addprefix build/debug/, $(notdir $(OBJECTS))) +RELEASE_OBJECTS = $(addprefix build/release/, $(notdir $(OBJECTS))) + .PHONY: all clean test docs corelib -all: - gcc -Iinclude src/*.c -owren - mkdir -p build/Release - mv wren build/Release +all: release + +# Debug build. +debug: prep wrend + +wrend: $(DEBUG_OBJECTS) + $(CC) $(CFLAGS) $(DEBUG_CFLAGS) -Iinclude -o wrend $^ + +build/debug/%.o: src/%.c include/wren.h $(HEADERS) + $(CC) -c $(CFLAGS) $(DEBUG_CFLAGS) -Iinclude -o $@ $< + +# Release build. +release: prep wren + +wren: $(RELEASE_OBJECTS) + $(CC) $(CFLAGS) $(RELEASE_CFLAGS) -Iinclude -o wren $^ + +build/release/%.o: src/%.c include/wren.h $(HEADERS) + $(CC) -c $(CFLAGS) $(RELEASE_CFLAGS) -Iinclude -o $@ $< clean: - rm -rf build + rm -rf build wren wrend -test: - @./runtests +prep: + mkdir -p build/debug build/release +# Run the tests against the debug build of Wren. +test: wrend + @./script/test.py + +# Generate the Wren site. docs: - @./make_docs + @./script/generate_docs.py --watch +# Take the contents of corelib.wren and copy them into src/wren_core.c. corelib: - @./make_corelib + @./script/generate_corelib.py diff --git a/benchmark/fannkuch.lua b/benchmark/fannkuch.lua new file mode 100644 index 00000000..072b9c05 --- /dev/null +++ b/benchmark/fannkuch.lua @@ -0,0 +1,48 @@ +-- The Computer Language Benchmarks Game +-- http://benchmarksgame.alioth.debian.org/ +-- contributed by Mike Pall + +local function fannkuch(n) + local p, q, s, sign, maxflips, sum = {}, {}, {}, 1, 0, 0 + for i=1,n do p[i] = i; q[i] = i; s[i] = i end + repeat + -- Copy and flip. + local q1 = p[1] -- Cache 1st element. + if q1 ~= 1 then + for i=2,n do q[i] = p[i] end -- Work on a copy. + local flips = 1 + repeat + local qq = q[q1] + if qq == 1 then -- ... until 1st element is 1. + sum = sum + sign*flips + if flips > maxflips then maxflips = flips end -- New maximum? + break + end + q[q1] = q1 + if q1 >= 4 then + local i, j = 2, q1 - 1 + repeat q[i], q[j] = q[j], q[i]; i = i + 1; j = j - 1; until i >= j + end + q1 = qq; flips = flips + 1 + until false + end + -- Permute. + if sign == 1 then + p[2], p[1] = p[1], p[2]; sign = -1 -- Rotate 1<-2. + else + p[2], p[3] = p[3], p[2]; sign = 1 -- Rotate 1<-2 and 1<-2<-3. + for i=3,n do + local sx = s[i] + if sx ~= 1 then s[i] = sx-1; break end + if i == n then return sum, maxflips end -- Out of permutations. + s[i] = i + -- Rotate 1<-...<-i+1. + local t = p[1]; for j=1,i do p[j] = p[j+1] end; p[i+1] = t + end + end + until false +end + +local n = 9 +local sum, flips = fannkuch(n) +io.write(sum, "\nPfannkuchen(", n, ") = ", flips, "\n") \ No newline at end of file diff --git a/benchmark/fannkuch.py b/benchmark/fannkuch.py new file mode 100644 index 00000000..53e41106 --- /dev/null +++ b/benchmark/fannkuch.py @@ -0,0 +1,56 @@ +# The Computer Language Benchmarks Game +# http://benchmarksgame.alioth.debian.org/ + +# contributed by Isaac Gouy +# converted to Java by Oleg Mazurov +# converted to Python by Buck Golemon +# modified by Justin Peel + +def fannkuch(n): + maxFlipsCount = 0 + permSign = True + checksum = 0 + + perm1 = list(range(n)) + count = perm1[:] + rxrange = range(2, n - 1) + nm = n - 1 + while 1: + k = perm1[0] + if k: + perm = perm1[:] + flipsCount = 1 + kk = perm[k] + while kk: + perm[:k+1] = perm[k::-1] + flipsCount += 1 + k = kk + kk = perm[kk] + if maxFlipsCount < flipsCount: + maxFlipsCount = flipsCount + checksum += flipsCount if permSign else -flipsCount + + # Use incremental change to generate another permutation + if permSign: + perm1[0],perm1[1] = perm1[1],perm1[0] + permSign = False + else: + perm1[1],perm1[2] = perm1[2],perm1[1] + permSign = True + for r in rxrange: + if count[r]: + break + count[r] = r + perm0 = perm1[0] + perm1[:r+1] = perm1[1:r+2] + perm1[r+1] = perm0 + else: + r = nm + if not count[r]: + print( checksum ) + return maxFlipsCount + count[r] -= 1 + +n = 9 + +print(( "Pfannkuchen(%i) = %i" % (n, fannkuch(n)) )) \ No newline at end of file diff --git a/benchmark/fannkuch.rb b/benchmark/fannkuch.rb new file mode 100644 index 00000000..14aeed10 --- /dev/null +++ b/benchmark/fannkuch.rb @@ -0,0 +1,60 @@ +def fannkuch(n) + p = (0..n).to_a + s = p.dup + q = p.dup + sign = 1 + sum = maxflips = 0 + while(true) + # flip. + + if (q1 = p[1]) != 1 + q[0..-1] = p + flips = 1 + until (qq = q[q1]) == 1 + q[q1] = q1 + if q1 >= 4 + i, j = 2, q1 - 1 + while i < j + q[i], q[j] = q[j], q[i] + i += 1 + j -= 1 + end + end + q1 = qq + flips += 1 + end + sum += sign * flips + maxflips = flips if flips > maxflips # New maximum? + + end + # Permute. + + if sign == 1 + # Rotate 1<-2. + + p[1], p[2] = p[2], p[1] + sign = -1 + else + # Rotate 1<-2 and 1<-2<-3. + + p[2], p[3] = p[3], p[2] + sign = 1 + i = 3 + while i <= n && s[i] == 1 + return [sum, maxflips] if i == n # Out of permutations. + + s[i] = i + # Rotate 1<-...<-i+1. + + t = p.delete_at(1) + i += 1 + p.insert(i, t) + end + s[i] -= 1 if i <= n + end + end +end + +n = 9 +sum, flips = fannkuch(n) +printf "%d\nPfannkuchen(%d) = %d\n", sum, n, flips \ No newline at end of file diff --git a/benchmark/run_bench b/benchmark/run_bench index 73fe2f06..bcd38a3d 100755 --- a/benchmark/run_bench +++ b/benchmark/run_bench @@ -3,10 +3,15 @@ import argparse import math import os +import os.path import re import subprocess import sys +# Runs the tests. +WREN_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) +BENCHMARK_DIR = os.path.join(WREN_DIR, 'benchmark') + # How many times to run a given benchmark. NUM_TRIALS = 10 @@ -36,9 +41,8 @@ BENCHMARK("method_call", r"""true false""") LANGUAGES = [ - ("wren", ["../build/Release/wren"], ".wren"), + ("wren", ["../wren"], ".wren"), ("lua", ["lua"], ".lua"), -# ("luajit", ["luajit"], ".lua"), ("luajit (-joff)", ["luajit", "-joff"], ".lua"), ("python", ["python"], ".py"), ("ruby", ["ruby"], ".rb") @@ -75,7 +79,7 @@ def run_trial(benchmark, language): """Runs one benchmark one time for one language.""" args = [] args.extend(language[1]) - args.append(benchmark[0] + language[2]) + args.append(os.path.join(BENCHMARK_DIR, benchmark[0] + language[2])) out = subprocess.check_output(args, universal_newlines=True) match = benchmark[1].match(out) if match: @@ -91,7 +95,8 @@ def run_benchmark_language(benchmark, language): name = "{0} - {1}".format(benchmark[0], language[0]) print "{0:30s}".format(name), - if not os.path.exists(benchmark[0] + language[2]): + if not os.path.exists(os.path.join( + BENCHMARK_DIR, benchmark[0] + language[2])): print "No implementation for this language" return diff --git a/example/sokoban.wren b/example/sokoban.wren new file mode 100644 index 00000000..0bb440c3 --- /dev/null +++ b/example/sokoban.wren @@ -0,0 +1 @@ +IO.print("Hello, world!") diff --git a/make_corelib b/script/generate_corelib.py similarity index 100% rename from make_corelib rename to script/generate_corelib.py diff --git a/make_docs b/script/generate_docs.py similarity index 100% rename from make_docs rename to script/generate_docs.py diff --git a/runtests b/script/test.py similarity index 89% rename from runtests rename to script/test.py index d5310e62..83574cc2 100755 --- a/runtests +++ b/script/test.py @@ -8,29 +8,9 @@ from subprocess import Popen, PIPE import sys # Runs the tests. -WREN_DIR = dirname(realpath(__file__)) +WREN_DIR = dirname(dirname(realpath(__file__))) TEST_DIR = join(WREN_DIR, 'test') - -if sys.platform == 'win32': - WREN_APP = join(WREN_DIR, 'Debug', 'wren.exe') - if not isfile(WREN_DIR): - WREN_APP = join(WREN_DIR, 'Release', 'wren.exe') - if not isfile(WREN_APP): - sys.exit('Cannot find wren.exe!') -elif sys.platform.startswith('linux'): - WREN_APP = join(WREN_DIR, '1', 'out', 'Debug', 'wren') - if not isfile(WREN_APP): - WREN_APP = join(WREN_DIR, '1', 'out', 'Release', 'wren') - if not isfile(WREN_APP): - sys.exit('Cannot find wren!') -elif sys.platform.startswith('darwin'): - WREN_APP = join(WREN_DIR, 'build', 'Debug', 'wren') - if not isfile(WREN_APP): - WREN_APP = join(WREN_DIR, 'build', 'Release', 'wren') - if not isfile(WREN_APP): - sys.exit('Cannot find wren!') -else: - sys.exit('System not supported!') +WREN_APP = join(WREN_DIR, 'wrend') EXPECT_PATTERN = re.compile(r'// expect: (.*)') EXPECT_ERROR_PATTERN = re.compile(r'// expect error') diff --git a/src/main.c b/src/main.c index 478b55e3..5d10ce16 100644 --- a/src/main.c +++ b/src/main.c @@ -9,9 +9,6 @@ // This is the source file for the standalone command line interpreter. It is // not needed if you are embedding Wren in an application. -// TODO: Don't hardcode this. -#define MAX_LINE 1024 - static void failIf(bool condition, int exitCode, const char* format, ...) { if (!condition) return; @@ -66,8 +63,10 @@ static int runRepl(WrenVM* vm) for (;;) { printf("> "); - char line[MAX_LINE]; - fgets(line, MAX_LINE, stdin); + + char* line = NULL; + size_t size = 0; + getline(&line, &size, stdin); // If stdin was closed (usually meaning the used entered Ctrl-D), exit. if (feof(stdin)) @@ -77,7 +76,10 @@ static int runRepl(WrenVM* vm) } // TODO: Handle failure. - wrenInterpret(vm, "(repl)", line); + wrenInterpret(vm, "Prompt", line); + + free(line); + // TODO: Figure out how this should work with wren API. /* ObjFn* fn = compile(vm, line); diff --git a/src/wren_vm.c b/src/wren_vm.c index 1d9c5f67..3a11c23f 100644 --- a/src/wren_vm.c +++ b/src/wren_vm.c @@ -478,7 +478,8 @@ static bool runInterpreter(WrenVM* vm) &&code_CLASS, &&code_SUBCLASS, &&code_METHOD_INSTANCE, - &&code_METHOD_STATIC + &&code_METHOD_STATIC, + &&code_END }; #define INTERPRET_LOOP DISPATCH(); @@ -1022,7 +1023,8 @@ static bool runInterpreter(WrenVM* vm) CASE_CODE(END): // A CODE_END should always be preceded by a CODE_RETURN. If we get here, // the compiler generated wrong code. - ASSERT(0, "Should not execute past end of bytecode."); + UNREACHABLE(); + DISPATCH(); } ASSERT(0, "Should not reach end of interpret.");