diff --git a/doc/site/classes.markdown b/doc/site/classes.markdown index 317e1416..60ca1631 100644 --- a/doc/site/classes.markdown +++ b/doc/site/classes.markdown @@ -2,8 +2,8 @@ ^category types Every value in Wren is an object, and every object is an instance of a class. -Even `true` and `false` are full-featured objects—instances of the `Bool` -class. +Even `true` and `false` are full-featured objects—instances of the +[`Bool`](core/bool.html) class. Classes contain both *behavior* and *state*. Behavior is defined in *methods* which are stored in the class. State is defined in *fields*, whose values are @@ -24,13 +24,13 @@ To let our unicorn do stuff, we need to give it methods. :::dart class Unicorn { - prance { + prance() { IO.print("The unicorn prances in a fancy manner!") } } -This defines a `prance` method that takes no arguments. To support parameters, -add a parenthesized parameter list after the method's name: +This defines a `prance()` method that takes no arguments. To support +parameters, put their names inside the parentheses: :::dart class Unicorn { @@ -43,12 +43,12 @@ add a parenthesized parameter list after the method's name: Unlike most other dynamically-typed languages, in Wren you can have multiple methods in a class with the same name, as long as they have a different -parameter *signature*. In technical terms, you can *overload by arity*. So this -class is fine: +*signature*. In technical terms, you can *overload by arity*. So this class is +fine: :::dart class Unicorn { - prance { + prance() { IO.print("The unicorn prances in a fancy manner!") } @@ -65,7 +65,7 @@ And you can call each of the methods like so: :::dart var unicorn = Unicorn.new() - unicorn.prance + unicorn.prance() unicorn.prance("Antwerp") unicorn.prance("Brussels", "high noon") @@ -77,8 +77,25 @@ sets of arguments. In other languages, you'd define a single method for the operation and have to check for "undefined" or missing arguments. Wren just treats them as different methods that you can implement separately. -Signature is a bit more than just arity. It also lets you distinguish between a -method that takes an *empty* argument list (`()`) and no argument list at all: +### Getters + +Many methods on a class exist to expose or compute some property of the object. +For example: + + :::dart + IO.print("string".count) // "6". + +These *getters* are just another kind of method—one without a parameter +list. You can define them like so: + + :::dart + class Unicorn { + isFancy { true } // Unicorns are always fancy. + } + +Whether or not a method name has parentheses is also part of its signature. +This lets you distinguish between a method that takes an *empty* argument list +(`()`) and no argument list at all: :::dart class Confusing { @@ -91,30 +108,38 @@ method that takes an *empty* argument list (`()`) and no argument list at all: confusing.method() // "empty argument list". Like the example says, having two methods that differ just by an empty set of -parentheses is pretty confusing. That's not what this is for. It's mainly so -you can define methods that don't take any arguments but look "method-like". +parentheses is pretty confusing. That's not what this is for. Instead, it +ensures that the way you *declare* the method is the way you *call* it. -Methods that don't need arguments and don't modify the underlying object tend -to omit the parentheses. These are "getters" and usually access a property of -an object, or produce a new object from it: - - :::dart - "string".count - (1..3).min - 0.123.sin - -Other methods do change the object, and it's helpful to draw attention to that: - - :::dart - list.clear() - -Since the parentheses are part of the method's signature, the callsite and -definition have to agree. These don't work: +Unlike other languages with "optional parentheses", Wren wants to make sure you +call a getter like a getter and a `()` method like a `()` method. These don't +work: :::dart "string".count() list.clear +Methods that don't need arguments and don't modify the underlying object are +usually getters: + + :::dart + "string".count + (1..10).min + 1.23.sin + [1, 2, 3].isEmpty + +When a method doesn't need any parameters, but does modify the object, it's +helpful to draw attention to that by requiring an empty set of parentheses: + + :::dart + list.clear() + +Also, when a method supports multiple arities, it's typical to include the `()` +in the zero-argument case to be consistent with the other versions: + + Fn.new { "a function" }.call() + Fiber.yield() + ### Operators Operators are just special syntax for a method call on the left hand operand @@ -159,16 +184,9 @@ overloading by arity, it's no problem for a class to define both. ## Constructors -To create a new instance of a class, call a *constructor method* on its class. -By default, if you don't define any constructors yourself, you get a free one -named `new()`: - - :::dart - Unicorn.new() - -However, you almost always want to define some state or do some other -initialization on a new object. For that, you'll want to define your own -constructor, like so: +Unicorns can prance around now, but we don't actually *have* any unicorns to do +it. To create instances of a class, we need a *constructor*. You can define one +like so: :::dart class Unicorn { @@ -177,9 +195,17 @@ constructor, like so: } } -The `construct` before the method name makes it a constructor. The `new` isn't -special. Constructors can have any name you like, which lets you clarify how it -creates the instance: +The `construct` keyword says we're defining a constructor, and `new` is its +name. In Wren, all constructors have names, just like [methods][#methods]. The +word "new" isn't special to Wren, it's just a common constructor name. + +To make a unicorn now, we just call the constructor method on the class itself: + + :::dart + var fred = Unicorn.new("Fred", "palomino") + +Giving constructors names is handy because it means you can have more than one, +and each can clarify how it creates the instance: :::dart class Unicorn { @@ -188,14 +214,23 @@ creates the instance: } } -Constructors can obviously have arguments, and can be overloaded by -[arity](#signature). A constructor *must* be a named method with a (possibly -empty) argument list. Operators, getters, and setters cannot be constructors. + var dave = Unicorn.brown("Dave") + +Note that we have to declare a constructor because, unlike some other +languages, Wren doesn't give you a default one. This is useful because some +classes aren't designed to be constructed. If you have an abstract base class +that just contains methods to be inherited by other classes, it doesn't need +and won't have a constructor. + +Like other methods, constructors can obviously have arguments, and can be +overloaded by [arity](#signature). A constructor *must* be a named method with +a (possibly empty) argument list. Operators, getters, and setters cannot be +constructors. A constructor is actually a pair of methods. You get a method on the class: :::dart - Unicorn.brown("Fred") + Unicorn.brown("Dave") That creates the new instance, then it invokes the *initializer* on that instance. This is where the constructor body you defined gets run. diff --git a/example/import-module/cthulu.wren b/example/import-module/cthulu.wren index 301f1e9e..f125b5b2 100644 --- a/example/import-module/cthulu.wren +++ b/example/import-module/cthulu.wren @@ -1,3 +1,4 @@ class Cthulu { + construct new() {} message { "Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn" } } diff --git a/example/import-module/lovecraft.wren b/example/import-module/lovecraft.wren index 7eebb15e..728c081a 100644 --- a/example/import-module/lovecraft.wren +++ b/example/import-module/lovecraft.wren @@ -1,6 +1,7 @@ import "cthulu" for Cthulu class Lovecraft { + construct new() {} say() { Cthulu.new().message } } diff --git a/src/vm/wren_compiler.c b/src/vm/wren_compiler.c index fef7212f..49c9bd94 100644 --- a/src/vm/wren_compiler.c +++ b/src/vm/wren_compiler.c @@ -2964,7 +2964,7 @@ static void defineMethod(Compiler* compiler, int classSlot, bool isStatic, // Returns `true` if it compiled successfully, or `false` if the method couldn't // be parsed. static bool method(Compiler* compiler, ClassCompiler* classCompiler, - int classSlot, bool* hasConstructor) + int classSlot) { // TODO: What about foreign constructors? bool isForeign = match(compiler, TOKEN_FOREIGN); @@ -3030,31 +3030,11 @@ static bool method(Compiler* compiler, ClassCompiler* classCompiler, createConstructor(compiler, &signature, methodSymbol); defineMethod(compiler, classSlot, true, constructorSymbol); - - // We don't need a default constructor anymore. - *hasConstructor = true; } return true; } -// Defines a default "new()" constructor on the current class. -// -// It just invokes "init new()" on the instance. If a base class defines that, -// it will get invoked. Otherwise, it falls to the default one in Object which -// does nothing. -static void createDefaultConstructor(Compiler* compiler, int classSlot) -{ - Signature signature = { "new", 3, SIG_INITIALIZER, 0 }; - int initializerSymbol = signatureSymbol(compiler, &signature); - - signature.type = SIG_METHOD; - int constructorSymbol = signatureSymbol(compiler, &signature); - - createConstructor(compiler, &signature, initializerSymbol); - defineMethod(compiler, classSlot, true, constructorSymbol); -} - // Compiles a class definition. Assumes the "class" token has already been // consumed (along with a possibly preceding "foreign" token). static void classDefinition(Compiler* compiler, bool isForeign) @@ -3117,11 +3097,9 @@ static void classDefinition(Compiler* compiler, bool isForeign) consume(compiler, TOKEN_LEFT_BRACE, "Expect '{' after class declaration."); matchLine(compiler); - bool hasConstructor = false; - while (!match(compiler, TOKEN_RIGHT_BRACE)) { - if (!method(compiler, &classCompiler, slot, &hasConstructor)) break; + if (!method(compiler, &classCompiler, slot)) break; // Don't require a newline after the last definition. if (match(compiler, TOKEN_RIGHT_BRACE)) break; @@ -3129,12 +3107,6 @@ static void classDefinition(Compiler* compiler, bool isForeign) consumeLine(compiler, "Expect newline after definition in class."); } - // If no constructor was defined, create a default new() one. - if (!hasConstructor) - { - createDefaultConstructor(compiler, slot); - } - // Update the class with the number of fields. if (!isForeign) { diff --git a/src/vm/wren_core.c b/src/vm/wren_core.c index 0a0a8499..fe1b930d 100644 --- a/src/vm/wren_core.c +++ b/src/vm/wren_core.c @@ -213,16 +213,6 @@ static const char* coreLibSource = "\n" "class Range is Sequence {}\n"; -// A simple primitive that just returns "this". Used in a few different places: -// -// * The default new() initializer on Object needs no initialization so just -// uses this. -// * String's toString method obviously can use this. -DEF_PRIMITIVE(return_this) -{ - RETURN_VAL(args[0]); -} - DEF_PRIMITIVE(bool_not) { RETURN_BOOL(!AS_BOOL(args[0])); @@ -1258,6 +1248,11 @@ DEF_PRIMITIVE(string_subscript) RETURN_ERROR("Subscript ranges for strings are not implemented yet."); } +DEF_PRIMITIVE(string_toString) +{ + RETURN_VAL(args[0]); +} + // Creates either the Object or Class class in the core library with [name]. static ObjClass* defineClass(WrenVM* vm, ObjModule* module, const char* name) { @@ -1282,7 +1277,6 @@ void wrenInitializeCore(WrenVM* vm) PRIMITIVE(vm->objectClass, "!", object_not); PRIMITIVE(vm->objectClass, "==(_)", object_eqeq); PRIMITIVE(vm->objectClass, "!=(_)", object_bangeq); - PRIMITIVE(vm->objectClass, "init new()", return_this); PRIMITIVE(vm->objectClass, "is(_)", object_is); PRIMITIVE(vm->objectClass, "toString", object_toString); PRIMITIVE(vm->objectClass, "type", object_type); @@ -1436,7 +1430,7 @@ void wrenInitializeCore(WrenVM* vm) PRIMITIVE(vm->stringClass, "iterateByte_(_)", string_iterateByte); PRIMITIVE(vm->stringClass, "iteratorValue(_)", string_iteratorValue); PRIMITIVE(vm->stringClass, "startsWith(_)", string_startsWith); - PRIMITIVE(vm->stringClass, "toString", return_this); + PRIMITIVE(vm->stringClass, "toString", string_toString); vm->listClass = AS_CLASS(wrenFindVariable(vm, coreModule, "List")); PRIMITIVE(vm->listClass->obj.classObj, "new()", list_new); diff --git a/test/api/foreign_class.wren b/test/api/foreign_class.wren index 7c6e1bdd..03889f4d 100644 --- a/test/api/foreign_class.wren +++ b/test/api/foreign_class.wren @@ -5,6 +5,7 @@ class Api { // Class with a default constructor. foreign class Counter { + construct new() {} foreign increment(amount) foreign value } @@ -53,7 +54,9 @@ var error = Fiber.new { IO.print(error) // expect: Class 'Subclass' cannot inherit from foreign class 'Point'. // Class with a finalizer. -foreign class Resource {} +foreign class Resource { + construct new() {} +} var resources = [ Resource.new(), diff --git a/test/core/bool/no_constructor.wren b/test/core/bool/no_constructor.wren new file mode 100644 index 00000000..acf41c16 --- /dev/null +++ b/test/core/bool/no_constructor.wren @@ -0,0 +1 @@ +Bool.new() // expect runtime error: Bool metaclass does not implement 'new()'. diff --git a/test/core/class/no_constructor.wren b/test/core/class/no_constructor.wren new file mode 100644 index 00000000..e3e25abf --- /dev/null +++ b/test/core/class/no_constructor.wren @@ -0,0 +1 @@ +Class.new() // expect runtime error: Class does not implement 'new()'. diff --git a/test/core/list/join.wren b/test/core/list/join.wren index 02bbe9d4..07f41333 100644 --- a/test/core/list/join.wren +++ b/test/core/list/join.wren @@ -15,6 +15,7 @@ IO.print([1, [2, [3], 4], 5].join(",")) // expect: 1,[2, [3], 4],5 // Calls toString on elements. class Foo { + construct new() {} toString { "Foo.toString" } } diff --git a/test/core/list/to_string.wren b/test/core/list/to_string.wren index 9de95977..8392aa96 100644 --- a/test/core/list/to_string.wren +++ b/test/core/list/to_string.wren @@ -9,6 +9,7 @@ IO.print([1, [2, [3], 4], 5]) // expect: [1, [2, [3], 4], 5] // Calls toString on elements. class Foo { + construct new() {} toString { "Foo.toString" } } diff --git a/test/core/map/to_string.wren b/test/core/map/to_string.wren index 8b51c6a7..66a951b4 100644 --- a/test/core/map/to_string.wren +++ b/test/core/map/to_string.wren @@ -9,6 +9,7 @@ IO.print({1: {2: {}}}) // expect: {1: {2: {}}} // Calls toString on elements. class Foo { + construct new() {} toString { "Foo.toString" } } diff --git a/test/core/null/no_constructor.wren b/test/core/null/no_constructor.wren new file mode 100644 index 00000000..6e334122 --- /dev/null +++ b/test/core/null/no_constructor.wren @@ -0,0 +1 @@ +Null.new() // expect runtime error: Null metaclass does not implement 'new()'. diff --git a/test/core/number/no_constructor.wren b/test/core/number/no_constructor.wren new file mode 100644 index 00000000..aadfbd24 --- /dev/null +++ b/test/core/number/no_constructor.wren @@ -0,0 +1 @@ +Num.new() // expect runtime error: Num metaclass does not implement 'new()'. diff --git a/test/core/object/no_constructor.wren b/test/core/object/no_constructor.wren new file mode 100644 index 00000000..2337cb9d --- /dev/null +++ b/test/core/object/no_constructor.wren @@ -0,0 +1 @@ +Object.new() // expect runtime error: Object metaclass does not implement 'new()'. diff --git a/test/core/object/not.wren b/test/core/object/not.wren index 14da7283..d658d6fb 100644 --- a/test/core/object/not.wren +++ b/test/core/object/not.wren @@ -1,2 +1,4 @@ -class Foo {} +class Foo { + construct new() {} +} IO.print(!Foo.new()) // expect: false diff --git a/test/core/object/same.wren b/test/core/object/same.wren index 95274db4..43b51494 100644 --- a/test/core/object/same.wren +++ b/test/core/object/same.wren @@ -24,7 +24,9 @@ IO.print(Object.same(Bool, Num)) // expect: false IO.print(Object.same(Bool, Bool)) // expect: true // Other types compare by identity. -class Foo {} +class Foo { + construct new() {} +} var foo = Foo.new() IO.print(Object.same(foo, foo)) // expect: true @@ -32,6 +34,7 @@ IO.print(Object.same(foo, Foo.new())) // expect: false // Ignores == operators. class Bar { + construct new() {} ==(other) { true } } diff --git a/test/core/object/to_string.wren b/test/core/object/to_string.wren index b64a8f82..59d44126 100644 --- a/test/core/object/to_string.wren +++ b/test/core/object/to_string.wren @@ -1,2 +1,4 @@ -class Foo {} +class Foo { + construct new() {} +} IO.print(Foo.new().toString == "instance of Foo") // expect: true diff --git a/test/core/range/no_constructor.wren b/test/core/range/no_constructor.wren new file mode 100644 index 00000000..acb65737 --- /dev/null +++ b/test/core/range/no_constructor.wren @@ -0,0 +1 @@ +Range.new() // expect runtime error: Range metaclass does not implement 'new()'. diff --git a/test/core/sequence/count.wren b/test/core/sequence/count.wren index 4d753cc1..959026f8 100644 --- a/test/core/sequence/count.wren +++ b/test/core/sequence/count.wren @@ -1,4 +1,6 @@ class TestSequence is Sequence { + construct new() {} + iterate(iterator) { if (iterator == null) return 1 if (iterator == 10) return false diff --git a/test/core/sequence/is_empty.wren b/test/core/sequence/is_empty.wren index 9bf15213..5134d608 100644 --- a/test/core/sequence/is_empty.wren +++ b/test/core/sequence/is_empty.wren @@ -2,6 +2,7 @@ IO.print([].isEmpty) // expect: true IO.print([1].isEmpty) // expect: false class InfiniteSequence is Sequence { + construct new() {} iterate(iterator) { true } iteratorValue(iterator) { iterator } } diff --git a/test/core/sequence/map.wren b/test/core/sequence/map.wren index e977fe68..cb9833a2 100644 --- a/test/core/sequence/map.wren +++ b/test/core/sequence/map.wren @@ -15,6 +15,8 @@ class FibIterator { } class Fib is Sequence { + construct new() {} + iterate(iterator) { if (iterator == null) return FibIterator.new() iterator.iterate diff --git a/test/core/sequence/no_constructor.wren b/test/core/sequence/no_constructor.wren new file mode 100644 index 00000000..50ac93d8 --- /dev/null +++ b/test/core/sequence/no_constructor.wren @@ -0,0 +1 @@ +Sequence.new() // expect runtime error: Sequence metaclass does not implement 'new()'. diff --git a/test/core/sequence/to_list.wren b/test/core/sequence/to_list.wren index 7594d8dd..cdaf96ff 100644 --- a/test/core/sequence/to_list.wren +++ b/test/core/sequence/to_list.wren @@ -1,4 +1,6 @@ class TestSequence is Sequence { + construct new() {} + iterate(iterator) { if (iterator == null) return 1 if (iterator == 3) return false diff --git a/test/core/sequence/where.wren b/test/core/sequence/where.wren index f59b3917..c40176fd 100644 --- a/test/core/sequence/where.wren +++ b/test/core/sequence/where.wren @@ -15,6 +15,8 @@ class FibIterator { } class Fib is Sequence { + construct new() {} + iterate(iterator) { if (iterator == null) return FibIterator.new() iterator.iterate diff --git a/test/core/string/no_constructor.wren b/test/core/string/no_constructor.wren new file mode 100644 index 00000000..52a049d7 --- /dev/null +++ b/test/core/string/no_constructor.wren @@ -0,0 +1 @@ +String.new() // expect runtime error: String metaclass does not implement 'new()'. diff --git a/test/io/print.wren b/test/io/print.wren index d402ac12..c02647d8 100644 --- a/test/io/print.wren +++ b/test/io/print.wren @@ -1,4 +1,6 @@ class Foo { + construct new() {} + toString { "Foo.toString" } } diff --git a/test/io/print_bad_to_string.wren b/test/io/print_bad_to_string.wren index 9dbd99af..af750175 100644 --- a/test/io/print_bad_to_string.wren +++ b/test/io/print_bad_to_string.wren @@ -1,4 +1,5 @@ class BadToString { + construct new() {} toString { 3 } } diff --git a/test/io/write_bad_to_string.wren b/test/io/write_bad_to_string.wren index c8015573..df9d3b66 100644 --- a/test/io/write_bad_to_string.wren +++ b/test/io/write_bad_to_string.wren @@ -1,4 +1,5 @@ class BadToString { + construct new() {} toString { 3 } } diff --git a/test/language/class/lowercase_name_inside_body.wren b/test/language/class/lowercase_name_inside_body.wren index 015e9eae..6819fd70 100644 --- a/test/language/class/lowercase_name_inside_body.wren +++ b/test/language/class/lowercase_name_inside_body.wren @@ -1,4 +1,6 @@ class foo { + construct new() {} + static callFoo { IO.print(foo) } diff --git a/test/language/class/name_inside_body.wren b/test/language/class/name_inside_body.wren index 871a004d..477777c0 100644 --- a/test/language/class/name_inside_body.wren +++ b/test/language/class/name_inside_body.wren @@ -1,4 +1,6 @@ class Foo { + construct new() {} + static sayName { IO.print(Foo) } diff --git a/test/language/closure/close_over_method_parameter.wren b/test/language/closure/close_over_method_parameter.wren index 93831906..f1aa8080 100644 --- a/test/language/closure/close_over_method_parameter.wren +++ b/test/language/closure/close_over_method_parameter.wren @@ -1,6 +1,8 @@ var F = null class Foo { + construct new() {} + method(param) { F = Fn.new { IO.print(param) diff --git a/test/language/closure/closed_closure_in_method.wren b/test/language/closure/closed_closure_in_method.wren index 78300fa8..44971792 100644 --- a/test/language/closure/closed_closure_in_method.wren +++ b/test/language/closure/closed_closure_in_method.wren @@ -4,6 +4,8 @@ var foo = null { var local = "local" class Foo { + construct new() {} + method { IO.print(local) } diff --git a/test/language/closure/open_closure_in_method.wren b/test/language/closure/open_closure_in_method.wren index f4c577b3..977dbe59 100644 --- a/test/language/closure/open_closure_in_method.wren +++ b/test/language/closure/open_closure_in_method.wren @@ -2,6 +2,8 @@ { var local = "local" class Foo { + construct new() {} + method { IO.print(local) } diff --git a/test/language/conditional/precedence.wren b/test/language/conditional/precedence.wren index 41cd8c45..d24a7fef 100644 --- a/test/language/conditional/precedence.wren +++ b/test/language/conditional/precedence.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} static bar { true } static baz { 1 } } diff --git a/test/language/constructor/default.wren b/test/language/constructor/default.wren deleted file mode 100644 index 77585262..00000000 --- a/test/language/constructor/default.wren +++ /dev/null @@ -1,8 +0,0 @@ -class Foo { - toString { "Foo" } -} - -// Classes get an argument-less "new()" by default. -var foo = Foo.new() -IO.print(foo is Foo) // expect: true -IO.print(foo.toString) // expect: Foo diff --git a/test/language/constructor/default_calls_new.wren b/test/language/constructor/default_calls_new.wren deleted file mode 100644 index 1c00bca0..00000000 --- a/test/language/constructor/default_calls_new.wren +++ /dev/null @@ -1,9 +0,0 @@ -class Foo { - construct new() { - IO.print("Foo.new()") - } -} - -class Bar is Foo {} - -Bar.new() // expect: Foo.new() diff --git a/test/language/constructor/no_default.wren b/test/language/constructor/no_default.wren index 287f1b83..1bb916f7 100644 --- a/test/language/constructor/no_default.wren +++ b/test/language/constructor/no_default.wren @@ -1,6 +1,4 @@ -class Foo { - construct real() {} -} +class Foo {} -// Classes do not get an argument-less "new()" if they define a constructor. +// Classes do not get a constructor by default. var foo = Foo.new() // expect runtime error: Foo metaclass does not implement 'new()'. diff --git a/test/language/constructor/object.wren b/test/language/constructor/object.wren deleted file mode 100644 index b60f99ac..00000000 --- a/test/language/constructor/object.wren +++ /dev/null @@ -1,11 +0,0 @@ -// Tests that Object implements new(). The only way to call that is through a -// super() call in a subclass, so this does that. - -class Foo { - construct new() { - super() // Should not cause a no method error. - IO.print("ok") - } -} - -Foo.new() // expect: ok diff --git a/test/language/field/default_to_null.wren b/test/language/field/default_to_null.wren index 7b9e2fc4..5b834f71 100644 --- a/test/language/field/default_to_null.wren +++ b/test/language/field/default_to_null.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} write { IO.print(_field) } } diff --git a/test/language/field/multiple.wren b/test/language/field/multiple.wren index dcf7536d..4c6f7e45 100644 --- a/test/language/field/multiple.wren +++ b/test/language/field/multiple.wren @@ -1,4 +1,6 @@ class Foo { + construct new() {} + set(a, b, c, d, e) { _a = a _b = b diff --git a/test/language/field/nested_class.wren b/test/language/field/nested_class.wren index d9f53d6e..f23fed32 100644 --- a/test/language/field/nested_class.wren +++ b/test/language/field/nested_class.wren @@ -1,9 +1,13 @@ class Outer { + construct new() {} + method { _field = "outer" IO.print(_field) // expect: outer class Inner { + construct new() {} + method { _field = "inner" IO.print(_field) // expect: inner diff --git a/test/language/field/use_before_set.wren b/test/language/field/use_before_set.wren index 62e5d83d..d5a56f65 100644 --- a/test/language/field/use_before_set.wren +++ b/test/language/field/use_before_set.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} write { IO.print(_field) } // Compile a use of the field... init { _field = "value" } // ...before an assignment to it. } diff --git a/test/language/implicit_receiver/inherited_methods.wren b/test/language/implicit_receiver/inherited_methods.wren index 89dae1e6..12888cc7 100644 --- a/test/language/implicit_receiver/inherited_methods.wren +++ b/test/language/implicit_receiver/inherited_methods.wren @@ -1,4 +1,6 @@ class Foo { + construct new() {} + getter { IO.print("getter") } @@ -13,6 +15,8 @@ class Foo { } class Bar is Foo { + construct new() {} + test { getter // expect: getter setter = "value" // expect: setter diff --git a/test/language/implicit_receiver/instance_methods.wren b/test/language/implicit_receiver/instance_methods.wren index a3db0f42..a3e5ab13 100644 --- a/test/language/implicit_receiver/instance_methods.wren +++ b/test/language/implicit_receiver/instance_methods.wren @@ -1,4 +1,6 @@ class Foo { + construct new() {} + getter { IO.print("getter") } diff --git a/test/language/implicit_receiver/locals_shadow_getter.wren b/test/language/implicit_receiver/locals_shadow_getter.wren index 90ef6a58..f04ef590 100644 --- a/test/language/implicit_receiver/locals_shadow_getter.wren +++ b/test/language/implicit_receiver/locals_shadow_getter.wren @@ -1,4 +1,6 @@ class Foo { + construct new() {} + bar { "getter" } test { diff --git a/test/language/implicit_receiver/locals_shadow_setter.wren b/test/language/implicit_receiver/locals_shadow_setter.wren index 09de076c..56bea7c7 100644 --- a/test/language/implicit_receiver/locals_shadow_setter.wren +++ b/test/language/implicit_receiver/locals_shadow_setter.wren @@ -1,4 +1,6 @@ class Foo { + construct new() {} + bar=(value) { IO.print("setter") return value diff --git a/test/language/implicit_receiver/nested_class.wren b/test/language/implicit_receiver/nested_class.wren index 5e9fc18c..738447b9 100644 --- a/test/language/implicit_receiver/nested_class.wren +++ b/test/language/implicit_receiver/nested_class.wren @@ -1,4 +1,6 @@ class Outer { + construct new() {} + getter { IO.print("outer getter") } @@ -17,6 +19,8 @@ class Outer { method("arg") // expect: outer method class Inner { + construct new() {} + getter { IO.print("inner getter") } diff --git a/test/language/inheritance/inherit_fields.wren b/test/language/inheritance/inherit_fields.wren index d9532870..d25e1e38 100644 --- a/test/language/inheritance/inherit_fields.wren +++ b/test/language/inheritance/inherit_fields.wren @@ -1,4 +1,6 @@ class Foo { + construct new() {} + foo(a, b) { _field1 = a _field2 = b @@ -11,6 +13,8 @@ class Foo { } class Bar is Foo { + construct new() {} + bar(a, b) { _field1 = a _field2 = b diff --git a/test/language/inheritance/inherit_methods.wren b/test/language/inheritance/inherit_methods.wren index 164c8668..d71b696b 100644 --- a/test/language/inheritance/inherit_methods.wren +++ b/test/language/inheritance/inherit_methods.wren @@ -6,6 +6,7 @@ class Foo { } class Bar is Foo { + construct new() {} methodOnBar { IO.print("bar") } method(a, b) { IO.print("bar") } method(a, b, c, d) { IO.print("bar") } diff --git a/test/language/inheritance/is.wren b/test/language/inheritance/is.wren index 893e21a1..d07d776a 100644 --- a/test/language/inheritance/is.wren +++ b/test/language/inheritance/is.wren @@ -1,6 +1,12 @@ -class A {} -class B is A {} -class C is B {} +class A { + construct new() {} +} +class B is A { + construct new() {} +} +class C is B { + construct new() {} +} var a = A.new() var b = B.new() var c = C.new() diff --git a/test/language/method/arity.wren b/test/language/method/arity.wren index 91efd35d..987de59f 100644 --- a/test/language/method/arity.wren +++ b/test/language/method/arity.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} method { "getter" } method() { "no args" } method(a) { a } diff --git a/test/language/method/empty_block.wren b/test/language/method/empty_block.wren index fcd0a2d8..70dba5ba 100644 --- a/test/language/method/empty_block.wren +++ b/test/language/method/empty_block.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} bar {} } diff --git a/test/language/method/long_name.wren b/test/language/method/long_name.wren index 871f7221..83cb6e7f 100644 --- a/test/language/method/long_name.wren +++ b/test/language/method/long_name.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} thisHasAMethodNameThatIsExactly64CharactersLongWhichIsTheMaximum { return "result" } diff --git a/test/language/method/many_methods.wren b/test/language/method/many_methods.wren index d24d34b4..adc69b0b 100644 --- a/test/language/method/many_methods.wren +++ b/test/language/method/many_methods.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} method000 { 1 } method001 { 1 } method002 { 1 } diff --git a/test/language/method/newlines.wren b/test/language/method/newlines.wren index 87cf45e6..0f9225e9 100644 --- a/test/language/method/newlines.wren +++ b/test/language/method/newlines.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} method(a, b) { "method " + a + " " + b } [a, b] { "subscript " + a + " " + b } } diff --git a/test/language/method/not_found.wren b/test/language/method/not_found.wren index 68e0dbc9..fc678905 100644 --- a/test/language/method/not_found.wren +++ b/test/language/method/not_found.wren @@ -1,3 +1,5 @@ -class Foo {} +class Foo { + construct new() {} +} Foo.new().someUnknownMethod // expect runtime error: Foo does not implement 'someUnknownMethod'. diff --git a/test/language/method/not_found_eleven_arguments.wren b/test/language/method/not_found_eleven_arguments.wren index fd946fe8..b909cbe0 100644 --- a/test/language/method/not_found_eleven_arguments.wren +++ b/test/language/method/not_found_eleven_arguments.wren @@ -1,3 +1,5 @@ -class Foo {} +class Foo { + construct new() {} +} Foo.new().someUnknownMethod(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) // expect runtime error: Foo does not implement 'someUnknownMethod(_,_,_,_,_,_,_,_,_,_,_)'. \ No newline at end of file diff --git a/test/language/method/not_found_multiple_arguments.wren b/test/language/method/not_found_multiple_arguments.wren index 75436201..76bf64f2 100644 --- a/test/language/method/not_found_multiple_arguments.wren +++ b/test/language/method/not_found_multiple_arguments.wren @@ -1,3 +1,5 @@ -class Foo {} +class Foo { + construct new() {} +} Foo.new().someUnknownMethod(1, 2) // expect runtime error: Foo does not implement 'someUnknownMethod(_,_)'. \ No newline at end of file diff --git a/test/language/method/not_found_one_argument.wren b/test/language/method/not_found_one_argument.wren index d86a083d..59b52b67 100644 --- a/test/language/method/not_found_one_argument.wren +++ b/test/language/method/not_found_one_argument.wren @@ -1,3 +1,5 @@ -class Foo {} +class Foo { + construct new() {} +} Foo.new().someUnknownMethod(1) // expect runtime error: Foo does not implement 'someUnknownMethod(_)'. \ No newline at end of file diff --git a/test/language/method/operators.wren b/test/language/method/operators.wren index c181c788..53934f51 100644 --- a/test/language/method/operators.wren +++ b/test/language/method/operators.wren @@ -1,4 +1,6 @@ class Foo { + construct new() {} + +(other) { "infix + " + other } -(other) { "infix - " + other } *(other) { "infix * " + other } diff --git a/test/language/method/static.wren b/test/language/method/static.wren index bbab05ef..bdf4984c 100644 --- a/test/language/method/static.wren +++ b/test/language/method/static.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} bar { "on instance" } static bar { "on metaclass" } diff --git a/test/language/method/subscript_operators.wren b/test/language/method/subscript_operators.wren index 4daca244..57b6d4df 100644 --- a/test/language/method/subscript_operators.wren +++ b/test/language/method/subscript_operators.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} [a] { "1-subscript " + a } [a, b] { "2-subscript " + a + " " + b } [a, b, c] { "3-subscript " + a + " " + b + " " + c } diff --git a/test/language/nonlocal/mutual_recursion.wren b/test/language/nonlocal/mutual_recursion.wren index f88169f9..a0c9e65e 100644 --- a/test/language/nonlocal/mutual_recursion.wren +++ b/test/language/nonlocal/mutual_recursion.wren @@ -1,8 +1,10 @@ class Foo { + construct new() {} static bar { Bar.new() } } class Bar { + construct new() {} static foo { Foo.new() } } diff --git a/test/language/nonlocal/use_in_method.wren b/test/language/nonlocal/use_in_method.wren index bef96b5f..91945fa9 100644 --- a/test/language/nonlocal/use_in_method.wren +++ b/test/language/nonlocal/use_in_method.wren @@ -1,6 +1,8 @@ var Global = "global" class Foo { + construct new() {} + method { IO.print(Global) } diff --git a/test/language/nonlocal/use_in_method_before_definition.wren b/test/language/nonlocal/use_in_method_before_definition.wren index 8c2974c7..81f27b38 100644 --- a/test/language/nonlocal/use_in_method_before_definition.wren +++ b/test/language/nonlocal/use_in_method_before_definition.wren @@ -1,4 +1,6 @@ class Foo { + construct new() {} + method { IO.print(Global) } diff --git a/test/language/return/in_method.wren b/test/language/return/in_method.wren index 7f0cbbc2..eeee1f38 100644 --- a/test/language/return/in_method.wren +++ b/test/language/return/in_method.wren @@ -1,4 +1,6 @@ class Foo { + construct new() {} + method { return "ok" IO.print("bad") diff --git a/test/language/setter/instance.wren b/test/language/setter/instance.wren index b9f5c1a8..05362131 100644 --- a/test/language/setter/instance.wren +++ b/test/language/setter/instance.wren @@ -1,4 +1,6 @@ class Foo { + construct new() {} + bar=(value) { IO.print(value) } diff --git a/test/language/setter/result.wren b/test/language/setter/result.wren index 55d78346..dbc315c4 100644 --- a/test/language/setter/result.wren +++ b/test/language/setter/result.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} bar=(value) { "result" } } diff --git a/test/language/setter/same_name_as_method.wren b/test/language/setter/same_name_as_method.wren index 4e05c388..c6b34cc4 100644 --- a/test/language/setter/same_name_as_method.wren +++ b/test/language/setter/same_name_as_method.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} bar=(value) { IO.print("set") } bar { IO.print("get") } } diff --git a/test/language/static_field/in_instance_method.wren b/test/language/static_field/in_instance_method.wren index 0e0f8060..bb00412e 100644 --- a/test/language/static_field/in_instance_method.wren +++ b/test/language/static_field/in_instance_method.wren @@ -1,4 +1,6 @@ class Foo { + construct new() {} + set(a, b, c, d, e) { __a = a __b = b diff --git a/test/language/static_field/nested_class.wren b/test/language/static_field/nested_class.wren index 3517da77..8e1ce892 100644 --- a/test/language/static_field/nested_class.wren +++ b/test/language/static_field/nested_class.wren @@ -1,9 +1,13 @@ class Outer { + construct new() {} + static staticMethod { __field = "outer" IO.print(__field) // expect: outer class Inner { + construct new() {} + static staticMethod { __field = "inner" IO.print(__field) // expect: inner @@ -19,6 +23,8 @@ class Outer { IO.print(__field) // expect: outer class Inner { + construct new() {} + instanceMethod { __field = "inner" IO.print(__field) // expect: inner diff --git a/test/language/super/call_different_arity.wren b/test/language/super/call_different_arity.wren index 0666149a..03edb94f 100644 --- a/test/language/super/call_different_arity.wren +++ b/test/language/super/call_different_arity.wren @@ -5,6 +5,8 @@ class Base { } class Derived is Base { + construct new() {} + foo(a) { IO.print("Derived.bar(a)") super diff --git a/test/language/super/call_other_method.wren b/test/language/super/call_other_method.wren index ca1ac678..e048ac5b 100644 --- a/test/language/super/call_other_method.wren +++ b/test/language/super/call_other_method.wren @@ -5,6 +5,8 @@ class Base { } class Derived is Base { + construct new() {} + bar { IO.print("Derived.bar") super.foo diff --git a/test/language/super/call_same_method.wren b/test/language/super/call_same_method.wren index cf765e94..a70d60bf 100644 --- a/test/language/super/call_same_method.wren +++ b/test/language/super/call_same_method.wren @@ -5,6 +5,8 @@ class Base { } class Derived is Base { + construct new() {} + foo { IO.print("Derived.foo") super.foo diff --git a/test/language/super/closure.wren b/test/language/super/closure.wren index 8a6c2b84..b1302467 100644 --- a/test/language/super/closure.wren +++ b/test/language/super/closure.wren @@ -3,6 +3,7 @@ class Base { } class Derived is Base { + construct new() {} getClosure { Fn.new { super.toString } } toString { "Derived" } } diff --git a/test/language/super/implicit_name.wren b/test/language/super/implicit_name.wren index c9ed109c..75100560 100644 --- a/test/language/super/implicit_name.wren +++ b/test/language/super/implicit_name.wren @@ -5,6 +5,8 @@ class Base { } class Derived is Base { + construct new() {} + foo { IO.print("Derived.foo") super diff --git a/test/language/super/indirectly_inherited.wren b/test/language/super/indirectly_inherited.wren index b9d7706c..765459b8 100644 --- a/test/language/super/indirectly_inherited.wren +++ b/test/language/super/indirectly_inherited.wren @@ -7,6 +7,8 @@ class A { class B is A {} class C is B { + construct new() {} + foo { IO.print("C.foo") super.foo diff --git a/test/language/super/no_superclass_method.wren b/test/language/super/no_superclass_method.wren index e85c5afa..1e177dcd 100644 --- a/test/language/super/no_superclass_method.wren +++ b/test/language/super/no_superclass_method.wren @@ -1,6 +1,7 @@ class Base {} class Derived is Base { + construct new() {} foo { super.doesNotExist(1) } // expect runtime error: Base does not implement 'doesNotExist(_)'. } diff --git a/test/language/super/super_in_closure_in_inherited_method.wren b/test/language/super/super_in_closure_in_inherited_method.wren index c39c83d2..ef97d602 100644 --- a/test/language/super/super_in_closure_in_inherited_method.wren +++ b/test/language/super/super_in_closure_in_inherited_method.wren @@ -6,6 +6,8 @@ class A { toString { "A.toString" } } -class B is A {} +class B is A { + construct new() {} +} IO.print(B.new().callSuperToString) // expect: instance of B diff --git a/test/language/super/super_in_inherited_method.wren b/test/language/super/super_in_inherited_method.wren index 229fddc8..1a7e914d 100644 --- a/test/language/super/super_in_inherited_method.wren +++ b/test/language/super/super_in_inherited_method.wren @@ -4,6 +4,8 @@ class A { toString { "A.toString" } } -class B is A {} +class B is A { + construct new() {} +} IO.print(B.new().callSuperToString) // expect: instance of B diff --git a/test/language/this/closure.wren b/test/language/this/closure.wren index 6c220984..d254b371 100644 --- a/test/language/this/closure.wren +++ b/test/language/this/closure.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} getClosure { Fn.new { toString } } toString { "Foo" } } diff --git a/test/language/this/nested_class.wren b/test/language/this/nested_class.wren index 8efdc1e9..f7c5351f 100644 --- a/test/language/this/nested_class.wren +++ b/test/language/this/nested_class.wren @@ -1,4 +1,6 @@ class Outer { + construct new() {} + method { IO.print(this) // expect: Outer @@ -6,6 +8,8 @@ class Outer { IO.print(this) // expect: Outer class Inner { + construct new() {} + method { IO.print(this) // expect: Inner } diff --git a/test/language/this/nested_closure.wren b/test/language/this/nested_closure.wren index 9fb5793a..ccb5f146 100644 --- a/test/language/this/nested_closure.wren +++ b/test/language/this/nested_closure.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} getClosure { Fn.new { Fn.new { Fn.new { toString } } } } toString { "Foo" } } diff --git a/test/language/this/this_in_method.wren b/test/language/this/this_in_method.wren index af5fddba..ba212a56 100644 --- a/test/language/this/this_in_method.wren +++ b/test/language/this/this_in_method.wren @@ -1,4 +1,5 @@ class Foo { + construct new() {} bar { this } baz { "baz" } } diff --git a/test/language/variable/local_in_middle_of_block.wren b/test/language/variable/local_in_middle_of_block.wren index a0f1c819..47266af1 100644 --- a/test/language/variable/local_in_middle_of_block.wren +++ b/test/language/variable/local_in_middle_of_block.wren @@ -1,4 +1,6 @@ class Foo { + construct new() {} + bar { var a = "a" IO.print(a) // expect: a diff --git a/test/language/variable/outside_method.wren b/test/language/variable/outside_method.wren index 19a77919..b2a5d59d 100644 --- a/test/language/variable/outside_method.wren +++ b/test/language/variable/outside_method.wren @@ -1,6 +1,8 @@ var foo = "variable" class Foo { + construct new() {} + foo { "method" } method {