diff --git a/corelib.wren b/corelib.wren new file mode 100644 index 00000000..17e83eb7 --- /dev/null +++ b/corelib.wren @@ -0,0 +1,23 @@ +// Note: This is converted to a C string literal and inserted into +// src/wren_core.c using make_corelib. +class IO { + static write(obj) { + IO.write__native__(obj.toString) + return obj + } +} + +class List { + toString { + var result = "[" + var i = 0 + // TODO: Use for loop. + while (i < this.count) { + if (i > 0) result = result + ", " + result = result + this[i].toString + i = i + 1 + } + result = result + "]" + return result + } +} diff --git a/make_corelib b/make_corelib new file mode 100755 index 00000000..fe5098ce --- /dev/null +++ b/make_corelib @@ -0,0 +1,31 @@ +#!/usr/bin/python +import re + +with open("corelib.wren", "r") as f: + lines = f.readlines() + +# Remove the comment from the top. +lines.pop(0) +lines.pop(0) + +corelib = "" +for line in lines: + line = line.replace('"', "\\\"") + line = line.replace("\n", "\\n\"") + if corelib: corelib += "\n" + corelib += '"' + line + +with open("src/wren_core.c", "r") as f: + wren_core = f.read() + +# re.sub() will unescape escape sequences, but we want them to stay as escapes +# in the C string literal. +corelib = corelib.replace('\\', '\\\\') + +corelib = "coreLibSource =\n" + corelib + ";" + +PATTERN = re.compile(r'coreLibSource =\n("(.|[\n])*?);') +wren_core = PATTERN.sub(corelib, wren_core) + +with open("src/wren_core.c", "w") as f: + f.write(wren_core) diff --git a/src/wren_core.c b/src/wren_core.c index e8d62fb2..b31f4285 100644 --- a/src/wren_core.c +++ b/src/wren_core.c @@ -47,6 +47,31 @@ // to a list has O(1) amortized complexity. #define LIST_GROW_FACTOR (2) +// This string literal is generated automatically from corelib.wren using +// make_corelib. Do not edit here. +const char* coreLibSource = +"class IO {\n" +" static write(obj) {\n" +" IO.write__native__(obj.toString)\n" +" return obj\n" +" }\n" +"}\n" +"\n" +"class List {\n" +" toString {\n" +" var result = \"[\"\n" +" var i = 0\n" +" // TODO: Use for loop.\n" +" while (i < this.count) {\n" +" if (i > 0) result = result + \", \"\n" +" result = result + this[i].toString\n" +" i = i + 1\n" +" }\n" +" result = result + \"]\"\n" +" return result\n" +" }\n" +"}\n"; + DEF_NATIVE(bool_not) { return BOOL_VAL(!AS_BOOL(args[0])); @@ -340,6 +365,11 @@ DEF_NATIVE(object_new) return args[0]; } +DEF_NATIVE(object_toString) +{ + return wrenNewString(vm, "", 8); +} + DEF_NATIVE(object_type) { return OBJ_VAL(wrenGetClass(vm, args[0])); @@ -467,6 +497,7 @@ void wrenInitializeCore(WrenVM* vm) NATIVE(vm->objectClass, "== ", object_eqeq); NATIVE(vm->objectClass, "!= ", object_bangeq); NATIVE(vm->objectClass, "new", object_new); + NATIVE(vm->objectClass, "toString", object_toString); NATIVE(vm->objectClass, "type", object_type); // Now we can define Class, which is a subclass of Object, but Object's @@ -524,15 +555,6 @@ void wrenInitializeCore(WrenVM* vm) FIBER_NATIVE(vm->fnClass, "call ", fn_call15); FIBER_NATIVE(vm->fnClass, "call ", fn_call16); - vm->listClass = defineClass(vm, "List"); - NATIVE(vm->listClass, "add ", list_add); - NATIVE(vm->listClass, "clear", list_clear); - NATIVE(vm->listClass, "count", list_count); - NATIVE(vm->listClass, "insert ", list_insert); - NATIVE(vm->listClass, "removeAt ", list_removeAt); - NATIVE(vm->listClass, "[ ]", list_subscript); - NATIVE(vm->listClass, "[ ]=", list_subscriptSetter); - vm->nullClass = defineClass(vm, "Null"); NATIVE(vm->nullClass, "toString", null_toString); @@ -565,13 +587,24 @@ void wrenInitializeCore(WrenVM* vm) NATIVE(vm->stringClass, "!= ", string_bangeq); NATIVE(vm->stringClass, "[ ]", string_subscript); - ObjClass* ioClass = defineClass(vm, "IO"); - NATIVE(ioClass->metaclass, "write ", io_write); - ObjClass* osClass = defineClass(vm, "OS"); NATIVE(osClass->metaclass, "clock", os_clock); // TODO: Make this a distinct object type. ObjClass* unsupportedClass = wrenNewClass(vm, vm->objectClass, 0); vm->unsupported = (Value)wrenNewInstance(vm, unsupportedClass); + + wrenInterpret(vm, coreLibSource); + + vm->listClass = AS_CLASS(findGlobal(vm, "List")); + NATIVE(vm->listClass, "add ", list_add); + NATIVE(vm->listClass, "clear", list_clear); + NATIVE(vm->listClass, "count", list_count); + NATIVE(vm->listClass, "insert ", list_insert); + NATIVE(vm->listClass, "removeAt ", list_removeAt); + NATIVE(vm->listClass, "[ ]", list_subscript); + NATIVE(vm->listClass, "[ ]=", list_subscriptSetter); + + ObjClass* ioClass = AS_CLASS(findGlobal(vm, "IO")); + NATIVE(ioClass->metaclass, "write__native__ ", io_write); } diff --git a/test/io/write.wren b/test/io/write.wren new file mode 100644 index 00000000..10e2b867 --- /dev/null +++ b/test/io/write.wren @@ -0,0 +1,12 @@ +class Foo { + toString { return "Foo.toString" } +} + +// Calls toString on argument. +IO.write(new Foo) // expect: Foo.toString + +// Returns argument. +var result = IO.write(123) // expect: 123 +IO.write(result == 123) // expect: true + +// TODO: What if toString on argument doesn't return a string? diff --git a/test/list/to_string.wren b/test/list/to_string.wren new file mode 100644 index 00000000..1af1282f --- /dev/null +++ b/test/list/to_string.wren @@ -0,0 +1,17 @@ +// Handle empty list. +IO.write([].toString) // expect: [] + +// Does not quote strings. +IO.write([1, "2", true].toString) // expect: [1, 2, true] + +// Nested lists. +IO.write([1, [2, [3], 4], 5]) // expect: [1, [2, [3], 4], 5] + +// Calls toString on elements. +class Foo { + toString { return "Foo.toString" } +} + +IO.write([1, new Foo, 2]) // expect: [1, Foo.toString, 2] + +// TODO: Handle lists that contain themselves. diff --git a/test/object/to_string.wren b/test/object/to_string.wren new file mode 100644 index 00000000..7b31acc5 --- /dev/null +++ b/test/object/to_string.wren @@ -0,0 +1,2 @@ +class Foo {} +IO.write((new Foo).toString) // expect: