1
0
forked from Mirror/wren

Add IO class.

With io.write(), can start writing tests now.
This commit is contained in:
Bob Nystrom
2013-10-27 22:45:40 -07:00
parent ca445412b8
commit 95fb0863d9
7 changed files with 228 additions and 45 deletions

View File

@ -6,3 +6,4 @@ class Foo { // line comment
var a = Foo.new
a.bar
"something".contains("meth")
io.write("hey there")

187
runtests Executable file
View File

@ -0,0 +1,187 @@
#!/usr/bin/python
from collections import defaultdict
from os import listdir
from os.path import abspath, dirname, isdir, isfile, join, realpath, relpath, splitext
import re
from subprocess import Popen, PIPE
import sys
# Runs the tests.
WREN_DIR = 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!')
EXPECT_PATTERN = re.compile(r'// expect: (.*)')
SKIP_PATTERN = re.compile(r'// skip: (.*)')
NONTEST_PATTERN = re.compile(r'// nontest')
if sys.platform == 'win32':
class color:
GREEN = ''
RED = ''
DEFAULT = ''
PINK = ''
YELLOW = ''
else:
class color:
GREEN = '\033[32m'
RED = '\033[31m'
DEFAULT = '\033[0m'
PINK = '\033[91m'
YELLOW = '\033[33m'
passed = 0
failed = 0
skipped = defaultdict(int)
num_skipped = 0;
def walk(dir, callback):
""" Walks [dir], and executes [callback] on each file. """
dir = abspath(dir)
for file in [file for file in listdir(dir) if not file in [".",".."]]:
nfile = join(dir, file)
if isdir(nfile):
walk(nfile, callback)
else:
callback(nfile)
def print_line(line=None):
# Erase the line.
print '\033[2K',
# Move the cursor to the beginning.
print '\r',
if line:
print line,
sys.stdout.flush()
def run_test(path):
global passed
global failed
global skipped
global num_skipped
if (splitext(path)[1] != '.wren'):
return
# Check if we are just running a subset of the tests.
if len(sys.argv) == 2:
this_test = relpath(path, join(WREN_DIR, 'test'))
if not this_test.startswith(sys.argv[1]):
return
# Make a nice short path relative to the working directory.
path = relpath(path)
# Read the test and parse out the expectations.
expect_output = []
expect_error = []
expect_return = 0
print_line('Passed: ' + color.GREEN + str(passed) + color.DEFAULT +
' Failed: ' + color.RED + str(failed) + color.DEFAULT +
' Skipped: ' + color.YELLOW + str(num_skipped) + color.DEFAULT)
line_num = 1
with open(path, 'r') as file:
for line in file:
match = EXPECT_PATTERN.search(line)
if match:
expect_output.append((match.group(1), line_num))
match = SKIP_PATTERN.search(line)
if match:
num_skipped += 1
skipped[match.group(1)] += 1
return
match = NONTEST_PATTERN.search(line)
if match:
# Not a test file at all, so ignore it.
return
line_num += 1
# Invoke wren and run the test.
proc = Popen([WREN_APP, path], stdout=PIPE, stderr=PIPE)
(out, err) = proc.communicate()
(out, err) = out.replace('\r\n', '\n'), err.replace('\r\n', '\n')
fails = []
# Validate the exit code.
if proc.returncode != 0:
fails.append('Expected return code 0 and got {0}.'.
format(proc.returncode))
else:
# Validate the output.
expect_index = 0
# Remove the trailing last empty line.
out_lines = out.split('\n')
if out_lines[-1] == '':
del out_lines[-1]
for line in out_lines:
if expect_index >= len(expect_output):
fails.append('Got output "{0}" when none was expected.'.
format(line))
elif expect_output[expect_index][0] != line:
fails.append('Expected output "{0}" on line {1} and got "{2}".'.
format(expect_output[expect_index][0],
expect_output[expect_index][1], line))
expect_index += 1
while expect_index < len(expect_output):
fails.append('Missing expected output "{0}" on line {1}.'.
format(expect_output[expect_index][0],
expect_output[expect_index][1]))
expect_index += 1
# Display the results.
if len(fails) == 0:
passed += 1
print color.GREEN + 'PASS' + color.DEFAULT + ': ' + path
else:
failed += 1
print_line(color.RED + 'FAIL' + color.DEFAULT + ': ' + path)
print
for fail in fails:
print ' ', color.PINK + fail + color.DEFAULT
print
walk(TEST_DIR, run_test)
print_line()
if failed == 0:
print 'All ' + color.GREEN + str(passed) + color.DEFAULT + ' tests passed.'
else:
print (color.GREEN + str(passed) + color.DEFAULT + ' tests passed. ' +
color.RED + str(failed) + color.DEFAULT + ' tests failed.')
for key in sorted(skipped.keys()):
print ('Skipped ' + color.YELLOW + str(skipped[key]) + color.DEFAULT +
' tests: ' + key)

View File

@ -268,8 +268,8 @@ int defineName(Compiler* compiler)
}
int symbol = addSymbol(symbols,
compiler->parser->source + compiler->parser->previous.start,
compiler->parser->previous.end - compiler->parser->previous.start);
compiler->parser->source + compiler->parser->previous.start,
compiler->parser->previous.end - compiler->parser->previous.start);
if (symbol == -1)
{

View File

@ -30,9 +30,7 @@ int main(int argc, const char * argv[])
if (block)
{
Value value = interpret(vm, block);
printValue(value);
printf("\n");
interpret(vm, block);
}
freeVM(vm);

View File

@ -1,3 +1,4 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -6,14 +7,21 @@
#define PRIMITIVE(cls, name, prim) \
{ \
int symbol = ensureSymbol(&vm->symbols, name, strlen(name)); \
vm->cls##Class->methods[symbol].type = METHOD_PRIMITIVE; \
vm->cls##Class->methods[symbol].primitive = primitive_##cls##_##prim; \
cls->methods[symbol].type = METHOD_PRIMITIVE; \
cls->methods[symbol].primitive = primitive_##prim; \
}
#define DEF_PRIMITIVE(cls, prim) \
static Value primitive_##cls##_##prim(Value* args, int numArgs)
#define DEF_PRIMITIVE(prim) \
static Value primitive_##prim(Value* args, int numArgs)
DEF_PRIMITIVE(num, abs)
#define GLOBAL(cls, name) \
{ \
ObjInstance* obj = makeInstance(cls); \
int symbol = addSymbol(&vm->globalSymbols, name, strlen(name)); \
vm->globals[symbol] = (Value)obj; \
}
DEF_PRIMITIVE(num_abs)
{
double value = ((ObjNum*)args[0])->value;
if (value < 0) value = -value;
@ -21,7 +29,7 @@ DEF_PRIMITIVE(num, abs)
return (Value)makeNum(value);
}
DEF_PRIMITIVE(string, contains)
DEF_PRIMITIVE(string_contains)
{
const char* string = ((ObjString*)args[0])->value;
// TODO(bob): Check type of arg first!
@ -31,16 +39,27 @@ DEF_PRIMITIVE(string, contains)
return (Value)makeNum(strstr(string, search) != NULL);
}
DEF_PRIMITIVE(string, count)
DEF_PRIMITIVE(string_count)
{
double count = strlen(((ObjString*)args[0])->value);
return (Value)makeNum(count);
}
DEF_PRIMITIVE(io_write)
{
printValue(args[1]);
printf("\n");
return args[1];
}
void registerPrimitives(VM* vm)
{
PRIMITIVE(num, "abs", abs);
PRIMITIVE(string, "contains ", contains);
PRIMITIVE(string, "count", count);
PRIMITIVE(vm->numClass, "abs", num_abs);
PRIMITIVE(vm->stringClass, "contains ", string_contains);
PRIMITIVE(vm->stringClass, "count", string_count);
ObjClass* ioClass = makeClass();
PRIMITIVE(ioClass, "write ", io_write);
GLOBAL(ioClass, "io");
}

View File

@ -200,9 +200,6 @@ Value interpret(VM* vm, ObjBlock* block)
int constant = frame->block->bytecode[frame->ip++];
Value value = frame->block->constants[constant];
fiber.stack[fiber.stackSize++] = value;
printf("load constant ");
printValue(value);
printf(" to %d\n", fiber.stackSize - 1);
break;
}
@ -213,13 +210,11 @@ Value interpret(VM* vm, ObjBlock* block)
// Define a "new" method on the metaclass.
// TODO(bob): Can this be inherited?
int newSymbol = ensureSymbol(&vm->symbols, "new", strlen("new"));
printf("define new %d\n", newSymbol);
classObj->metaclass->methods[newSymbol].type = METHOD_PRIMITIVE;
classObj->metaclass->methods[newSymbol].primitive =
primitive_metaclass_new;
push(&fiber, (Value)classObj);
printf("push class at %d\n", fiber.stackSize - 1);
break;
}
@ -232,10 +227,6 @@ Value interpret(VM* vm, ObjBlock* block)
ObjBlock* body = (ObjBlock*)frame->block->constants[constant];
classObj->methods[symbol].type = METHOD_BLOCK;
classObj->methods[symbol].block = body;
printf("define method %d using constant %d on ", symbol, constant);
printValue((Value)classObj);
printf("\n");
break;
}
@ -243,14 +234,12 @@ Value interpret(VM* vm, ObjBlock* block)
{
int local = frame->block->bytecode[frame->ip++];
push(&fiber, fiber.stack[frame->locals + local]);
printf("load local %d to %d\n", local, fiber.stackSize - 1);
break;
}
case CODE_STORE_LOCAL:
{
int local = frame->block->bytecode[frame->ip++];
printf("store local %d from %d\n", local, fiber.stackSize - 1);
fiber.stack[frame->locals + local] = fiber.stack[fiber.stackSize - 1];
break;
}
@ -259,25 +248,21 @@ Value interpret(VM* vm, ObjBlock* block)
{
int global = frame->block->bytecode[frame->ip++];
push(&fiber, vm->globals[global]);
printf("load global %d to %d\n", global, fiber.stackSize - 1);
break;
}
case CODE_STORE_GLOBAL:
{
int global = frame->block->bytecode[frame->ip++];
printf("store global %d from %d\n", global, fiber.stackSize - 1);
vm->globals[global] = fiber.stack[fiber.stackSize - 1];
break;
}
case CODE_DUP:
push(&fiber, fiber.stack[fiber.stackSize - 1]);
printf("dup %d\n", fiber.stackSize - 1);
break;
case CODE_POP:
printf("pop %d\n", fiber.stackSize - 1);
pop(&fiber);
break;
@ -323,10 +308,6 @@ Value interpret(VM* vm, ObjBlock* block)
break;
}
printf("call %d on ", symbol);
printValue(receiver);
printf("\n");
Method* method = &classObj->methods[symbol];
switch (method->type)
{
@ -363,19 +344,10 @@ Value interpret(VM* vm, ObjBlock* block)
fiber.numFrames--;
// If we are returning from the top-level block, just return the value.
if (fiber.numFrames == 0)
{
printf("done with result ");
printValue(result);
printf("\n");
return result;
}
if (fiber.numFrames == 0) return result;
// Store the result of the block in the first slot, which is where the
// caller expects it.
printf("return and store result ");
printValue(result);
printf(" in %d\n", frame->locals);
fiber.stack[frame->locals] = result;
// Discard the stack slots for the locals.
@ -391,7 +363,7 @@ void printValue(Value value)
switch (value->type)
{
case OBJ_NUM:
printf("%f", ((ObjNum*)value)->value);
printf("%g", ((ObjNum*)value)->value);
break;
case OBJ_STRING:

View File

@ -0,0 +1,6 @@
io.write(123) // expect: 123
io.write(987654) // expect: 987654
io.write(0) // expect: 0
io.write(-0) // expect: 0
// TODO(bob): Floating point numbers.