mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-11 06:08:41 +01:00
Make constructors just methods.
* Eliminate "new" reserved word. * Allow "this" before a method definition to define a constructor. * Only create a default constructor for classes that don't define one.
This commit is contained in:
@ -54,9 +54,9 @@ class Sequence {
|
||||
|
||||
isEmpty { iterate(null) ? false : true }
|
||||
|
||||
map(transformation) { new MapSequence(this, transformation) }
|
||||
map(transformation) { MapSequence.new(this, transformation) }
|
||||
|
||||
where(predicate) { new WhereSequence(this, predicate) }
|
||||
where(predicate) { WhereSequence.new(this, predicate) }
|
||||
|
||||
reduce(acc, f) {
|
||||
for (element in this) {
|
||||
@ -94,7 +94,7 @@ class Sequence {
|
||||
}
|
||||
|
||||
toList {
|
||||
var result = new List
|
||||
var result = List.new()
|
||||
for (element in this) {
|
||||
result.add(element)
|
||||
}
|
||||
@ -103,7 +103,7 @@ class Sequence {
|
||||
}
|
||||
|
||||
class MapSequence is Sequence {
|
||||
new(sequence, fn) {
|
||||
this new(sequence, fn) {
|
||||
_sequence = sequence
|
||||
_fn = fn
|
||||
}
|
||||
@ -113,7 +113,7 @@ class MapSequence is Sequence {
|
||||
}
|
||||
|
||||
class WhereSequence is Sequence {
|
||||
new(sequence, fn) {
|
||||
this new(sequence, fn) {
|
||||
_sequence = sequence
|
||||
_fn = fn
|
||||
}
|
||||
@ -129,11 +129,11 @@ class WhereSequence is Sequence {
|
||||
}
|
||||
|
||||
class String is Sequence {
|
||||
bytes { new StringByteSequence(this) }
|
||||
bytes { StringByteSequence.new(this) }
|
||||
}
|
||||
|
||||
class StringByteSequence is Sequence {
|
||||
new(string) {
|
||||
this new(string) {
|
||||
_string = string
|
||||
}
|
||||
|
||||
@ -162,8 +162,8 @@ class List is Sequence {
|
||||
}
|
||||
|
||||
class Map {
|
||||
keys { new MapKeySequence(this) }
|
||||
values { new MapValueSequence(this) }
|
||||
keys { MapKeySequence.new(this) }
|
||||
values { MapValueSequence.new(this) }
|
||||
|
||||
toString {
|
||||
var first = true
|
||||
@ -180,7 +180,7 @@ class Map {
|
||||
}
|
||||
|
||||
class MapKeySequence is Sequence {
|
||||
new(map) {
|
||||
this new(map) {
|
||||
_map = map
|
||||
}
|
||||
|
||||
@ -189,7 +189,7 @@ class MapKeySequence is Sequence {
|
||||
}
|
||||
|
||||
class MapValueSequence is Sequence {
|
||||
new(map) {
|
||||
this new(map) {
|
||||
_map = map
|
||||
}
|
||||
|
||||
|
||||
@ -64,7 +64,7 @@ class is fine:
|
||||
And you can call each of the methods like so:
|
||||
|
||||
:::dart
|
||||
var unicorn = new Unicorn
|
||||
var unicorn = Unicorn.new()
|
||||
unicorn.prance
|
||||
unicorn.prance("Antwerp")
|
||||
unicorn.prance("Brussels", "high noon")
|
||||
@ -86,7 +86,7 @@ method that takes an *empty* argument list (`()`) and no argument list at all:
|
||||
method() { "empty argument list" }
|
||||
}
|
||||
|
||||
var confusing = new Confusing
|
||||
var confusing = Confusing.new()
|
||||
confusing.method // "no argument list".
|
||||
confusing.method() // "empty argument list".
|
||||
|
||||
@ -159,39 +159,50 @@ overloading by arity, it's no problem for a class to define both.
|
||||
|
||||
## Constructors
|
||||
|
||||
To create a new instance of a class, you use the `new` keyword. We can make a
|
||||
unicorn like so:
|
||||
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
|
||||
new Unicorn
|
||||
Unicorn.new()
|
||||
|
||||
You almost always want to define some state or do some other initialization on
|
||||
a new object. For that, you'll want to define a constructor, like so:
|
||||
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:
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
new {
|
||||
IO.print("I am a constructor!")
|
||||
}
|
||||
}
|
||||
|
||||
When you create an instance with `new`, its constructor will be invoked. It's
|
||||
just a method with a special name. Like methods, you can pass arguments to the
|
||||
constructor by adding a parenthesized parameter list after `new`:
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
new(name, color) {
|
||||
this new(name, color) {
|
||||
IO.print("My name is " + name + " and I am " + color + ".")
|
||||
}
|
||||
}
|
||||
|
||||
Values are passed to the constructor like so:
|
||||
The `this` 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:
|
||||
|
||||
:::dart
|
||||
new Unicorn("Flicker", "purple")
|
||||
class Unicorn {
|
||||
this brown(name) {
|
||||
IO.print("My name is " + name + " and I am brown.")
|
||||
}
|
||||
}
|
||||
|
||||
Like other methods, you can overload constructors by [arity](#signature).
|
||||
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")
|
||||
|
||||
That creates the new instance, then it invokes the *initializer* on that
|
||||
instance. This is where the constructor body you defined gets run.
|
||||
|
||||
This distinction is important because it means inside the body of the
|
||||
constructor, you can access `this`, assign [fields](#fields), call superclass
|
||||
constructors, etc.
|
||||
|
||||
## Fields
|
||||
|
||||
@ -292,8 +303,8 @@ And also instance methods. When you do so, there is still only one static field
|
||||
shared among all instances of the class:
|
||||
|
||||
:::dart
|
||||
var foo1 = new Foo
|
||||
var foo2 = new Foo
|
||||
var foo1 = Foo.new()
|
||||
var foo2 = Foo.new()
|
||||
|
||||
foo1.setFromInstance("updated")
|
||||
IO.print(foo2.baz) // updated.
|
||||
@ -330,17 +341,36 @@ are not inherited.
|
||||
|
||||
Pegasus.canFly // ERROR: Static methods are not inherited.
|
||||
|
||||
Constructors, however, initialize the instance *after* it has been created.
|
||||
They are defined as instance methods on the class and not on the metaclass.
|
||||
That means that constructors *are* inherited.
|
||||
This also means constructors are not inherited:
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
new(name) {
|
||||
this new(name) {
|
||||
IO.print("My name is " + name + ".")
|
||||
}
|
||||
}
|
||||
|
||||
class Pegasus is Unicorn {}
|
||||
|
||||
new Pegasus("Fred") // Prints "My name is Fred.".
|
||||
Pegasus.new("Fred") // Error!
|
||||
|
||||
Each class gets to control how it may be constructed independently of its base
|
||||
classes. However, constructor *initializers* are inherited since those are
|
||||
instance methods on the new object.
|
||||
|
||||
This means you can do `super` calls inside a constructor:
|
||||
|
||||
:::dart
|
||||
class Unicorn {
|
||||
this new(name) {
|
||||
IO.print("My name is " + name + ".")
|
||||
}
|
||||
}
|
||||
|
||||
class Pegasus is Unicorn {
|
||||
this new(name) {
|
||||
super(name)
|
||||
}
|
||||
}
|
||||
|
||||
Pegasus.new("Fred") // Prints "My name is Fred.".
|
||||
|
||||
@ -3,13 +3,13 @@
|
||||
|
||||
A lightweight coroutine. [Here](../fibers.html) is a gentle introduction.
|
||||
|
||||
### new **Fiber**(function)
|
||||
### Fiber.**new**(function)
|
||||
|
||||
Creates a new fiber that executes `function` in a separate coroutine when the
|
||||
fiber is run. Does not immediately start running the fiber.
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("I won't get printed")
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ Pauses the current fiber and transfers control to the parent fiber. "Parent"
|
||||
here means the last fiber that was started using `call` and not `run`.
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("Before yield")
|
||||
Fiber.yield()
|
||||
IO.print("After yield")
|
||||
@ -41,7 +41,7 @@ If a yielded fiber is resumed by calling `call()` or `run()` with an argument,
|
||||
`yield()` returns that value.
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print(Fiber.yield()) // "value"
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@ Similar to `Fiber.yield` but provides a value to return to the parent fiber's
|
||||
`call`.
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
Fiber.yield("value")
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ Similar to `Fiber.yield` but provides a value to return to the parent fiber's
|
||||
Starts or resumes the fiber if it is in a paused state.
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("Fiber called")
|
||||
Fiber.yield()
|
||||
IO.print("Fiber called again")
|
||||
@ -94,7 +94,7 @@ If the called fiber is resuming from a yield, the `yield()` method returns
|
||||
`null` in the called fiber.
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print(Fiber.yield())
|
||||
}
|
||||
|
||||
@ -107,7 +107,7 @@ Invokes the fiber or resumes the fiber if it is in a paused state and sets
|
||||
`value` as the returned value of the fiber's call to `yield`.
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print(Fiber.yield())
|
||||
}
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
A first class function—an object that wraps an executable chunk of code.
|
||||
[Here](../functions.html) is a friendly introduction.
|
||||
|
||||
### new **Fn**(function)
|
||||
### Fn.**new**(function)
|
||||
|
||||
Creates a new function from... `function`. Of course, `function` is already a
|
||||
function, so this really just returns the argument. It exists mainly to let you
|
||||
@ -12,7 +12,7 @@ create a "bare" function when you don't want to immediately pass it as a [block
|
||||
argument](../functions.html#block-arguments) to some other method.
|
||||
|
||||
:::dart
|
||||
var fn = new Fn {
|
||||
var fn = Fn.new {
|
||||
IO.print("The body")
|
||||
}
|
||||
|
||||
@ -25,15 +25,15 @@ It is a runtime error if `function` is not a function.
|
||||
The number of arguments the function requires.
|
||||
|
||||
:::dart
|
||||
IO.print(new Fn {}.arity) // 0.
|
||||
IO.print(new Fn {|a, b, c| a }.arity) // 3.
|
||||
IO.print(Fn.new {}.arity) // 0.
|
||||
IO.print(Fn.new {|a, b, c| a }.arity) // 3.
|
||||
|
||||
### **call**(args...)
|
||||
|
||||
Invokes the function with the given arguments.
|
||||
|
||||
:::dart
|
||||
var fn = new Fn { |arg|
|
||||
var fn = Fn.new { |arg|
|
||||
IO.print(arg)
|
||||
}
|
||||
|
||||
|
||||
@ -109,11 +109,11 @@ To force eager evaluation, just call `.toList` on the result.
|
||||
|
||||
### **reduce**(function)
|
||||
|
||||
Reduces the sequence down to a single value. `function` is a function that takes
|
||||
two arguments, the accumulator and sequence item and returns the new accumulator
|
||||
value. The accumulator is initialized from the first item in the sequence. Then,
|
||||
the function is invoked on each remaining item in the sequence, iteratively
|
||||
updating the accumulator.
|
||||
Reduces the sequence down to a single value. `function` is a function that
|
||||
takes two arguments, the accumulator and sequence item and returns the new
|
||||
accumulator value. The accumulator is initialized from the first item in the
|
||||
sequence. Then, the function is invoked on each remaining item in the sequence,
|
||||
iteratively updating the accumulator.
|
||||
|
||||
It is a runtime error to call this on an empty sequence.
|
||||
|
||||
|
||||
@ -52,7 +52,7 @@ superclasses) don't define that method, there's nothing Wren can do:
|
||||
:::dart
|
||||
class Foo {}
|
||||
|
||||
var foo = new Foo
|
||||
var foo = Foo.new()
|
||||
foo.someRandomMethod
|
||||
|
||||
If you run this, Wren will print:
|
||||
@ -103,7 +103,7 @@ error message as a string.
|
||||
For example, if you run this program:
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
123.badMethod
|
||||
}
|
||||
|
||||
|
||||
@ -113,13 +113,13 @@ base class constructor:
|
||||
|
||||
:::dart
|
||||
class Base {
|
||||
new(arg) {
|
||||
this new(arg) {
|
||||
IO.print("base constructor got ", arg)
|
||||
}
|
||||
}
|
||||
|
||||
class Derived is Base {
|
||||
new {
|
||||
this new() {
|
||||
super("value") // Prints "base constructor got value".
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ script, a main fiber is created for you automatically. You can spawn new fibers
|
||||
using the `Fiber` class's constructor:
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("This runs in a separate fiber.")
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@ until it passes control to another fiber. If it reaches the end of its body,
|
||||
it's considered *done*:
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber { IO.print("Hi") }
|
||||
var fiber = Fiber.new { IO.print("Hi") }
|
||||
fiber.isDone // false
|
||||
fiber.call()
|
||||
fiber.isDone // true
|
||||
@ -70,7 +70,7 @@ fiber is called, it picks up right where it left off and keeps going.
|
||||
You can make a fiber yield by calling the static `yield()` method on `Fiber`:
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("fiber 1")
|
||||
Fiber.yield()
|
||||
IO.print("fiber 2")
|
||||
@ -103,7 +103,7 @@ fiber has yielded and is waiting to resume, the value becomes the return value
|
||||
of the `yield()` call:
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
var result = Fiber.yield()
|
||||
IO.print(result)
|
||||
}
|
||||
@ -120,7 +120,7 @@ Fibers can also pass values *back* when they yield. If you pass an argument to
|
||||
invoke the fiber:
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
Fiber.yield("sent")
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ Wren's fibers can do that, but they can do much more. Like Lua, they are full
|
||||
example:
|
||||
|
||||
:::dart
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
(1..10).map {|i|
|
||||
Fiber.yield(i)
|
||||
}
|
||||
|
||||
@ -61,7 +61,7 @@ but *don't* need to pass it to a method. For that, you can call the `Fn`
|
||||
class's constructor:
|
||||
|
||||
:::dart
|
||||
var someFn = new Fn {
|
||||
var someFn = Fn.new {
|
||||
IO.print("Hi!")
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ method is dynamically-dispatched like any other, so you can define your own
|
||||
}
|
||||
}
|
||||
|
||||
blondie.callMe(new FakeFn)
|
||||
blondie.callMe(FakeFn.new())
|
||||
|
||||
## Function parameters
|
||||
|
||||
@ -130,9 +130,9 @@ value using a `return` statement. In other words, these two functions do the
|
||||
same thing:
|
||||
|
||||
:::dart
|
||||
new Fn { "return value" }
|
||||
Fn.new { "return value" }
|
||||
|
||||
new Fn {
|
||||
Fn.new {
|
||||
return "return value"
|
||||
}
|
||||
|
||||
@ -146,7 +146,7 @@ leaving the scope where the function is defined:
|
||||
class Counter {
|
||||
static create {
|
||||
var i = 0
|
||||
return new Fn { i = i + 1 }
|
||||
return Fn.new { i = i + 1 }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ a familiar, modern [syntax][].
|
||||
}
|
||||
}
|
||||
|
||||
var adjectives = new Fiber {
|
||||
var adjectives = Fiber.new {
|
||||
["small", "clean", "fast"].each {|word| Fiber.yield(word) }
|
||||
}
|
||||
|
||||
|
||||
@ -56,12 +56,12 @@ Here's the same example in Wren:
|
||||
|
||||
:::dart
|
||||
class Account {
|
||||
new(balance) { _balance = balance }
|
||||
this new(balance) { _balance = balance }
|
||||
withdraw(amount) { _balance = _balance - amount }
|
||||
}
|
||||
|
||||
// create and use an Account
|
||||
var acc = new Account(1000)
|
||||
var acc = Account.new(1000)
|
||||
acc.withdraw(100)
|
||||
|
||||
Classes have a reputation for complexity because most of the widely used
|
||||
|
||||
@ -28,8 +28,8 @@ Some people like to see all of the reserved words in a programming language in
|
||||
one lump. If you're one of those folks, here you go:
|
||||
|
||||
:::dart
|
||||
break class else false for foreign if import in is
|
||||
new null return static super this true var while
|
||||
break class else false for foreign if import in
|
||||
is null return static super this true var while
|
||||
|
||||
## Identifiers
|
||||
|
||||
@ -102,7 +102,7 @@ compact notation:
|
||||
:::dart
|
||||
{ "single expression" }
|
||||
|
||||
If there is no newline after the `{` (or after the parameter list in a of
|
||||
If there is no newline after the `{` (or after the parameter list in a
|
||||
[function](functions.html)), then the block may only contain a single
|
||||
expression, and it automatically returns the result of it. It's exactly the
|
||||
same as doing:
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import "cthulu" for Cthulu
|
||||
|
||||
class Lovecraft {
|
||||
say { (new Cthulu).message }
|
||||
say() { Cthulu.new().message }
|
||||
}
|
||||
|
||||
IO.print((new Lovecraft).say)
|
||||
IO.print(Lovecraft.new().say())
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
// Class definition with a toplevel name.
|
||||
class SyntaxExample {
|
||||
// Constructor
|
||||
new {
|
||||
this new() {
|
||||
// Top-level name `IO`
|
||||
IO.print("I am a constructor!")
|
||||
|
||||
@ -26,7 +26,7 @@ class SyntaxExample {
|
||||
}
|
||||
|
||||
// Constructor with arguments
|
||||
new(a, b) {
|
||||
this constructor(a, b) {
|
||||
print(a, b)
|
||||
field = a
|
||||
}
|
||||
@ -82,14 +82,11 @@ class SyntaxExample {
|
||||
|
||||
// `class`, `is`
|
||||
class ReservedWords is SyntaxExample {
|
||||
// `new`
|
||||
new {
|
||||
reserved {
|
||||
// `super`, `true`, `false`
|
||||
super(true, false)
|
||||
// `this`
|
||||
this.foo
|
||||
// `new`
|
||||
new SyntaxExample
|
||||
}
|
||||
|
||||
foo {
|
||||
|
||||
@ -148,14 +148,15 @@ class Test:
|
||||
|
||||
|
||||
def validate_runtime_error(self, error_lines):
|
||||
if not error_lines:
|
||||
if len(error_lines) < 2:
|
||||
self.fail('Expected runtime error "{0} and got none.',
|
||||
runtime_error_message)
|
||||
self.runtime_error_message)
|
||||
return
|
||||
|
||||
# Make sure we got the right error.
|
||||
if error_lines[0] != self.runtime_error_message:
|
||||
self.fail('Expected runtime error "{0}" and got:', runtime_error_message)
|
||||
self.fail('Expected runtime error "{0}" and got:',
|
||||
self.runtime_error_message)
|
||||
self.fail(error_lines[0])
|
||||
|
||||
# Make sure the stack trace has the right line.
|
||||
|
||||
@ -99,10 +99,11 @@
|
||||
// foo() // No-argument method.
|
||||
// foo(_) // One-argument method.
|
||||
// foo(_,_) // Two-argument method.
|
||||
// this foo() // Constructor initializer.
|
||||
//
|
||||
// The maximum signature length takes into account the longest method name, the
|
||||
// maximum number of parameters with separators between them, and "()".
|
||||
#define MAX_METHOD_SIGNATURE (MAX_METHOD_NAME + (MAX_PARAMETERS * 2) + 1)
|
||||
// maximum number of parameters with separators between them, "this ", and "()".
|
||||
#define MAX_METHOD_SIGNATURE (MAX_METHOD_NAME + (MAX_PARAMETERS * 2) + 6)
|
||||
|
||||
// The maximum length of an identifier. The only real reason for this limitation
|
||||
// is so that error messages mentioning variables can be stack allocated.
|
||||
|
||||
@ -81,7 +81,6 @@ typedef enum
|
||||
TOKEN_IMPORT,
|
||||
TOKEN_IN,
|
||||
TOKEN_IS,
|
||||
TOKEN_NEW,
|
||||
TOKEN_NULL,
|
||||
TOKEN_RETURN,
|
||||
TOKEN_STATIC,
|
||||
@ -213,6 +212,39 @@ typedef struct sLoop
|
||||
struct sLoop* enclosing;
|
||||
} Loop;
|
||||
|
||||
// The different signature syntaxes for different kinds of methods.
|
||||
typedef enum
|
||||
{
|
||||
// A name followed by a (possibly empty) parenthesized parameter list. Also
|
||||
// used for binary operators.
|
||||
SIG_METHOD,
|
||||
|
||||
// Just a name. Also used for unary operators.
|
||||
SIG_GETTER,
|
||||
|
||||
// A name followed by "=".
|
||||
SIG_SETTER,
|
||||
|
||||
// A square bracketed parameter list.
|
||||
SIG_SUBSCRIPT,
|
||||
|
||||
// A square bracketed parameter list followed by "=".
|
||||
SIG_SUBSCRIPT_SETTER,
|
||||
|
||||
// A constructor initializer function. This has a distinct signature to
|
||||
// prevent it from being invoked directly outside of the constructor on the
|
||||
// metaclass.
|
||||
SIG_INITIALIZER
|
||||
} SignatureType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
int length;
|
||||
SignatureType type;
|
||||
int arity;
|
||||
} Signature;
|
||||
|
||||
// Bookkeeping information for compiling a class definition.
|
||||
typedef struct
|
||||
{
|
||||
@ -220,14 +252,10 @@ typedef struct
|
||||
SymbolTable* fields;
|
||||
|
||||
// True if the current method being compiled is static.
|
||||
bool isStaticMethod;
|
||||
bool inStatic;
|
||||
|
||||
// The name of the method being compiled. Note that this is just the bare
|
||||
// method name, and not its full signature.
|
||||
const char* methodName;
|
||||
|
||||
// The length of the method name being compiled.
|
||||
int methodLength;
|
||||
// The signature of the method being compiled.
|
||||
Signature* signature;
|
||||
} ClassCompiler;
|
||||
|
||||
struct sCompiler
|
||||
@ -608,7 +636,6 @@ static void readName(Parser* parser, TokenType type)
|
||||
else if (isKeyword(parser, "import")) type = TOKEN_IMPORT;
|
||||
else if (isKeyword(parser, "in")) type = TOKEN_IN;
|
||||
else if (isKeyword(parser, "is")) type = TOKEN_IS;
|
||||
else if (isKeyword(parser, "new")) type = TOKEN_NEW;
|
||||
else if (isKeyword(parser, "null")) type = TOKEN_NULL;
|
||||
else if (isKeyword(parser, "return")) type = TOKEN_RETURN;
|
||||
else if (isKeyword(parser, "static")) type = TOKEN_STATIC;
|
||||
@ -1386,34 +1413,6 @@ typedef enum
|
||||
|
||||
typedef void (*GrammarFn)(Compiler*, bool allowAssignment);
|
||||
|
||||
// The different signature syntaxes for different kinds of methods.
|
||||
typedef enum
|
||||
{
|
||||
// A name followed by a (possibly empty) parenthesized parameter list. Also
|
||||
// used for binary operators.
|
||||
SIG_METHOD,
|
||||
|
||||
// Just a name. Also used for unary operators.
|
||||
SIG_GETTER,
|
||||
|
||||
// A name followed by "=".
|
||||
SIG_SETTER,
|
||||
|
||||
// A square bracketed parameter list.
|
||||
SIG_SUBSCRIPT,
|
||||
|
||||
// A square bracketed parameter list followed by "=".
|
||||
SIG_SUBSCRIPT_SETTER
|
||||
} SignatureType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
int length;
|
||||
SignatureType type;
|
||||
int arity;
|
||||
} Signature;
|
||||
|
||||
typedef void (*SignatureFn)(Compiler* compiler, Signature* signature);
|
||||
|
||||
typedef struct
|
||||
@ -1484,13 +1483,16 @@ static bool finishBlock(Compiler* compiler)
|
||||
}
|
||||
|
||||
// Parses a method or function body, after the initial "{" has been consumed.
|
||||
static void finishBody(Compiler* compiler, bool isConstructor)
|
||||
//
|
||||
// It [isInitializer] is `true`, this is the body of a constructor initializer.
|
||||
// In that case, this adds the code to ensure it returns `this`.
|
||||
static void finishBody(Compiler* compiler, bool isInitializer)
|
||||
{
|
||||
bool isExpressionBody = finishBlock(compiler);
|
||||
|
||||
if (isConstructor)
|
||||
if (isInitializer)
|
||||
{
|
||||
// If the constructor body evaluates to a value, discard it.
|
||||
// If the initializer body evaluates to a value, discard it.
|
||||
if (isExpressionBody) emit(compiler, CODE_POP);
|
||||
|
||||
// The receiver is always stored in the first local slot.
|
||||
@ -1559,9 +1561,11 @@ static void signatureParameterList(char name[MAX_METHOD_SIGNATURE], int* length,
|
||||
static void signatureToString(Signature* signature,
|
||||
char name[MAX_METHOD_SIGNATURE], int* length)
|
||||
{
|
||||
*length = 0;
|
||||
|
||||
// Build the full name from the signature.
|
||||
*length = signature->length;
|
||||
memcpy(name, signature->name, *length);
|
||||
memcpy(name + *length, signature->name, signature->length);
|
||||
*length += signature->length;
|
||||
|
||||
switch (signature->type)
|
||||
{
|
||||
@ -1587,6 +1591,13 @@ static void signatureToString(Signature* signature,
|
||||
name[(*length)++] = '=';
|
||||
signatureParameterList(name, length, 1, '(', ')');
|
||||
break;
|
||||
|
||||
case SIG_INITIALIZER:
|
||||
memcpy(name, "this ", 5);
|
||||
memcpy(name + 5, signature->name, signature->length);
|
||||
*length = 5 + signature->length;
|
||||
signatureParameterList(name, length, signature->arity, '(', ')');
|
||||
break;
|
||||
}
|
||||
|
||||
name[*length] = '\0';
|
||||
@ -1608,7 +1619,6 @@ static void signatureFromToken(Compiler* compiler, Signature* signature)
|
||||
{
|
||||
// Get the token for the method name.
|
||||
Token* token = &compiler->parser->previous;
|
||||
signature->type = SIG_GETTER;
|
||||
signature->arity = 0;
|
||||
signature->name = token->start;
|
||||
signature->length = token->length;
|
||||
@ -1667,15 +1677,18 @@ static void callMethod(Compiler* compiler, int numArgs, const char* name,
|
||||
emitShortArg(compiler, (Code)(CODE_CALL_0 + numArgs), symbol);
|
||||
}
|
||||
|
||||
// Compiles an (optional) argument list and then calls it.
|
||||
// Compiles an (optional) argument list for a method call with [methodSignature]
|
||||
// and then calls it.
|
||||
static void methodCall(Compiler* compiler, Code instruction,
|
||||
const char* name, int length)
|
||||
Signature* methodSignature)
|
||||
{
|
||||
// Make a new signature that contains the updated arity and type based on
|
||||
// the arguments we find.
|
||||
Signature signature;
|
||||
signature.type = SIG_GETTER;
|
||||
signature.arity = 0;
|
||||
signature.name = name;
|
||||
signature.length = length;
|
||||
signature.name = methodSignature->name;
|
||||
signature.length = methodSignature->length;
|
||||
|
||||
// Parse the argument list, if any.
|
||||
if (match(compiler, TOKEN_LEFT_PAREN))
|
||||
@ -1726,6 +1739,18 @@ static void methodCall(Compiler* compiler, Code instruction,
|
||||
|
||||
// TODO: Allow Grace-style mixfix methods?
|
||||
|
||||
// If this is a super() call for an initializer, make sure we got an actual
|
||||
// argument list.
|
||||
if (methodSignature->type == SIG_INITIALIZER)
|
||||
{
|
||||
if (signature.type != SIG_METHOD)
|
||||
{
|
||||
error(compiler, "A superclass constructor must have an argument list.");
|
||||
}
|
||||
|
||||
signature.type = SIG_INITIALIZER;
|
||||
}
|
||||
|
||||
callSignature(compiler, instruction, &signature);
|
||||
}
|
||||
|
||||
@ -1754,7 +1779,7 @@ static void namedCall(Compiler* compiler, bool allowAssignment,
|
||||
}
|
||||
else
|
||||
{
|
||||
methodCall(compiler, instruction, signature.name, signature.length);
|
||||
methodCall(compiler, instruction, &signature);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1791,7 +1816,7 @@ static void list(Compiler* compiler, bool allowAssignment)
|
||||
emitShortArg(compiler, CODE_LOAD_MODULE_VAR, listClassSymbol);
|
||||
|
||||
// Instantiate a new list.
|
||||
callMethod(compiler, 0, "<instantiate>", 13);
|
||||
callMethod(compiler, 0, "new()", 5);
|
||||
|
||||
// Compile the list elements. Each one compiles to a ".add()" call.
|
||||
if (peek(compiler) != TOKEN_RIGHT_BRACKET)
|
||||
@ -1827,7 +1852,7 @@ static void map(Compiler* compiler, bool allowAssignment)
|
||||
emitShortArg(compiler, CODE_LOAD_MODULE_VAR, mapClassSymbol);
|
||||
|
||||
// Instantiate a new map.
|
||||
callMethod(compiler, 0, "<instantiate>", 13);
|
||||
callMethod(compiler, 0, "new()", 5);
|
||||
|
||||
// Compile the map elements. Each one is compiled to just invoke the
|
||||
// subscript setter on the map.
|
||||
@ -1912,7 +1937,7 @@ static void field(Compiler* compiler, bool allowAssignment)
|
||||
{
|
||||
error(compiler, "Cannot reference a field outside of a class definition.");
|
||||
}
|
||||
else if (enclosingClass->isStaticMethod)
|
||||
else if (enclosingClass->inStatic)
|
||||
{
|
||||
error(compiler, "Cannot use an instance field in a static method.");
|
||||
}
|
||||
@ -2138,14 +2163,17 @@ static void super_(Compiler* compiler, bool allowAssignment)
|
||||
{
|
||||
error(compiler, "Cannot use 'super' outside of a method.");
|
||||
}
|
||||
else if (enclosingClass->isStaticMethod)
|
||||
else if (enclosingClass->inStatic)
|
||||
{
|
||||
// TODO: Why not?
|
||||
error(compiler, "Cannot use 'super' in a static method.");
|
||||
}
|
||||
|
||||
loadThis(compiler);
|
||||
|
||||
// TODO: Super operator calls.
|
||||
// TODO: There's no syntax for invoking a superclass constructor with a
|
||||
// different name from the enclosing one. Figure that out.
|
||||
|
||||
// See if it's a named super call, or an unnamed one.
|
||||
if (match(compiler, TOKEN_DOT))
|
||||
@ -2159,8 +2187,7 @@ static void super_(Compiler* compiler, bool allowAssignment)
|
||||
// No explicit name, so use the name of the enclosing method. Make sure we
|
||||
// check that enclosingClass isn't NULL first. We've already reported the
|
||||
// error, but we don't want to crash here.
|
||||
methodCall(compiler, CODE_SUPER_0, enclosingClass->methodName,
|
||||
enclosingClass->methodLength);
|
||||
methodCall(compiler, CODE_SUPER_0, enclosingClass->signature);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2209,23 +2236,6 @@ static void call(Compiler* compiler, bool allowAssignment)
|
||||
namedCall(compiler, allowAssignment, CODE_CALL_0);
|
||||
}
|
||||
|
||||
static void new_(Compiler* compiler, bool allowAssignment)
|
||||
{
|
||||
// Allow a dotted name after 'new'.
|
||||
consume(compiler, TOKEN_NAME, "Expect name after 'new'.");
|
||||
name(compiler, false);
|
||||
while (match(compiler, TOKEN_DOT))
|
||||
{
|
||||
call(compiler, false);
|
||||
}
|
||||
|
||||
// The angle brackets in the name are to ensure users can't call it directly.
|
||||
callMethod(compiler, 0, "<instantiate>", 13);
|
||||
|
||||
// Invoke the constructor on the new instance.
|
||||
methodCall(compiler, CODE_CALL_0, "new", 3);
|
||||
}
|
||||
|
||||
static void and_(Compiler* compiler, bool allowAssignment)
|
||||
{
|
||||
ignoreNewlines(compiler);
|
||||
@ -2309,7 +2319,6 @@ void infixSignature(Compiler* compiler, Signature* signature)
|
||||
void unarySignature(Compiler* compiler, Signature* signature)
|
||||
{
|
||||
// Do nothing. The name is already complete.
|
||||
signature->type = SIG_GETTER;
|
||||
}
|
||||
|
||||
// Compiles a method signature for an operator that can either be unary or
|
||||
@ -2406,10 +2415,29 @@ void namedSignature(Compiler* compiler, Signature* signature)
|
||||
// Compiles a method signature for a constructor.
|
||||
void constructorSignature(Compiler* compiler, Signature* signature)
|
||||
{
|
||||
signature->type = SIG_GETTER;
|
||||
signature->type = SIG_INITIALIZER;
|
||||
|
||||
// Add the parameters, if there are any.
|
||||
parameterList(compiler, signature);
|
||||
consume(compiler, TOKEN_NAME, "Expect constructor name after 'this'.");
|
||||
|
||||
// Capture the name.
|
||||
signatureFromToken(compiler, signature);
|
||||
|
||||
if (match(compiler, TOKEN_EQ))
|
||||
{
|
||||
error(compiler, "A constructor cannot be a setter.");
|
||||
}
|
||||
|
||||
if (!match(compiler, TOKEN_LEFT_PAREN))
|
||||
{
|
||||
error(compiler, "A constructor cannot be a getter.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow an empty parameter list.
|
||||
if (match(compiler, TOKEN_RIGHT_PAREN)) return;
|
||||
|
||||
finishParameterList(compiler, signature);
|
||||
consume(compiler, TOKEN_RIGHT_PAREN, "Expect ')' after parameters.");
|
||||
}
|
||||
|
||||
// This table defines all of the parsing rules for the prefix and infix
|
||||
@ -2468,12 +2496,11 @@ GrammarRule rules[] =
|
||||
/* TOKEN_IMPORT */ UNUSED,
|
||||
/* TOKEN_IN */ UNUSED,
|
||||
/* TOKEN_IS */ INFIX_OPERATOR(PREC_IS, "is"),
|
||||
/* TOKEN_NEW */ { new_, NULL, constructorSignature, PREC_NONE, NULL },
|
||||
/* TOKEN_NULL */ PREFIX(null),
|
||||
/* TOKEN_RETURN */ UNUSED,
|
||||
/* TOKEN_STATIC */ UNUSED,
|
||||
/* TOKEN_SUPER */ PREFIX(super_),
|
||||
/* TOKEN_THIS */ PREFIX(this_),
|
||||
/* TOKEN_THIS */ { this_, NULL, constructorSignature, PREC_NONE, NULL },
|
||||
/* TOKEN_TRUE */ PREFIX(boolean),
|
||||
/* TOKEN_VAR */ UNUSED,
|
||||
/* TOKEN_WHILE */ UNUSED,
|
||||
@ -2570,6 +2597,7 @@ static int getNumArguments(const uint8_t* bytecode, const Value* constants,
|
||||
case CODE_LOAD_LOCAL_6:
|
||||
case CODE_LOAD_LOCAL_7:
|
||||
case CODE_LOAD_LOCAL_8:
|
||||
case CODE_CONSTRUCT:
|
||||
return 0;
|
||||
|
||||
case CODE_LOAD_LOCAL:
|
||||
@ -2904,17 +2932,91 @@ void statement(Compiler* compiler)
|
||||
emit(compiler, CODE_POP);
|
||||
}
|
||||
|
||||
// Compiles a method definition inside a class body. Returns the symbol in the
|
||||
// method table for the new method.
|
||||
static int method(Compiler* compiler, ClassCompiler* classCompiler,
|
||||
bool isConstructor, bool isForeign, SignatureFn signatureFn)
|
||||
// Creates a matching constructor method for an initializer with [signature]
|
||||
// and [initializerSymbol].
|
||||
//
|
||||
// Construction is a two-stage process in Wren that involves two separate
|
||||
// methods. There is a static method that allocates a new instance of the class.
|
||||
// It then invokes an initializer method on the new instance, forwarding all of
|
||||
// the constructor arguments to it.
|
||||
//
|
||||
// The allocator method always has a fixed implementation:
|
||||
//
|
||||
// CODE_CONSTRUCT - Replace the class in slot 0 with a new instance of it.
|
||||
// CODE_CALL - Invoke the initializer on the new instance.
|
||||
//
|
||||
// This creates that method and calls the initializer with [initializerSymbol].
|
||||
static void createConstructor(Compiler* compiler, Signature* signature,
|
||||
int initializerSymbol)
|
||||
{
|
||||
// Build the method signature.
|
||||
Signature signature;
|
||||
signatureFromToken(compiler, &signature);
|
||||
Compiler methodCompiler;
|
||||
initCompiler(&methodCompiler, compiler->parser, compiler, false);
|
||||
|
||||
classCompiler->methodName = signature.name;
|
||||
classCompiler->methodLength = signature.length;
|
||||
// Allocate the instance.
|
||||
emit(&methodCompiler, CODE_CONSTRUCT);
|
||||
|
||||
// Run its initializer.
|
||||
emitShortArg(&methodCompiler, CODE_CALL_0 + signature->arity,
|
||||
initializerSymbol);
|
||||
|
||||
// Return the instance.
|
||||
emit(&methodCompiler, CODE_RETURN);
|
||||
|
||||
endCompiler(&methodCompiler, "", 0);
|
||||
}
|
||||
|
||||
// Loads the enclosing class onto the stack and then binds the function already
|
||||
// on the stack as a method on that class.
|
||||
static void defineMethod(Compiler* compiler, int classSlot, bool isStatic,
|
||||
int methodSymbol)
|
||||
{
|
||||
// Load the class. We have to do this for each method because we can't
|
||||
// keep the class on top of the stack. If there are static fields, they
|
||||
// will be locals above the initial variable slot for the class on the
|
||||
// stack. To skip past those, we just load the class each time right before
|
||||
// defining a method.
|
||||
if (compiler->scopeDepth == 0)
|
||||
{
|
||||
// The class is at the top level (scope depth is 0, not -1 to account for
|
||||
// the static variable scope surrounding the class itself), so load it from
|
||||
// there.
|
||||
emitShortArg(compiler, CODE_LOAD_MODULE_VAR, classSlot);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadLocal(compiler, classSlot);
|
||||
}
|
||||
|
||||
// Define the method.
|
||||
Code instruction = isStatic ? CODE_METHOD_STATIC : CODE_METHOD_INSTANCE;
|
||||
emitShortArg(compiler, instruction, methodSymbol);
|
||||
}
|
||||
|
||||
// Compiles a method definition inside a class body.
|
||||
//
|
||||
// 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)
|
||||
{
|
||||
Signature signature;
|
||||
classCompiler->signature = &signature;
|
||||
|
||||
// TODO: What about foreign constructors?
|
||||
bool isForeign = match(compiler, TOKEN_FOREIGN);
|
||||
classCompiler->inStatic = match(compiler, TOKEN_STATIC);
|
||||
|
||||
SignatureFn signatureFn = rules[compiler->parser->current.type].method;
|
||||
nextToken(compiler->parser);
|
||||
|
||||
if (signatureFn == NULL)
|
||||
{
|
||||
error(compiler, "Expect method definition.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build the method signature.
|
||||
signatureFromToken(compiler, &signature);
|
||||
|
||||
Compiler methodCompiler;
|
||||
initCompiler(&methodCompiler, compiler->parser, compiler, false);
|
||||
@ -2922,6 +3024,11 @@ static int method(Compiler* compiler, ClassCompiler* classCompiler,
|
||||
// Compile the method signature.
|
||||
signatureFn(&methodCompiler, &signature);
|
||||
|
||||
if (classCompiler->inStatic && signature.type == SIG_INITIALIZER)
|
||||
{
|
||||
error(compiler, "A constructor cannot be static.");
|
||||
}
|
||||
|
||||
// Include the full signature in debug messages in stack traces.
|
||||
char fullSignature[MAX_METHOD_SIGNATURE];
|
||||
int length;
|
||||
@ -2941,12 +3048,52 @@ static int method(Compiler* compiler, ClassCompiler* classCompiler,
|
||||
else
|
||||
{
|
||||
consume(compiler, TOKEN_LEFT_BRACE, "Expect '{' to begin method body.");
|
||||
finishBody(&methodCompiler, isConstructor);
|
||||
finishBody(&methodCompiler, signature.type == SIG_INITIALIZER);
|
||||
|
||||
endCompiler(&methodCompiler, fullSignature, length);
|
||||
}
|
||||
|
||||
return signatureSymbol(compiler, &signature);
|
||||
// Define the method. For a constructor, this defines the instance
|
||||
// initializer method.
|
||||
int methodSymbol = signatureSymbol(compiler, &signature);
|
||||
defineMethod(compiler, classSlot, classCompiler->inStatic, methodSymbol);
|
||||
|
||||
if (signature.type == SIG_INITIALIZER)
|
||||
{
|
||||
// Also define a matching constructor method on the metaclass.
|
||||
signature.type = SIG_METHOD;
|
||||
int constructorSymbol = signatureSymbol(compiler, &signature);
|
||||
|
||||
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 "this 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;
|
||||
signature.name = "new";
|
||||
signature.length = 3;
|
||||
signature.type = SIG_INITIALIZER;
|
||||
signature.arity = 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
|
||||
@ -2955,7 +3102,6 @@ static void classDefinition(Compiler* compiler)
|
||||
{
|
||||
// Create a variable to store the class in.
|
||||
int slot = declareNamedVariable(compiler);
|
||||
bool isModule = compiler->scopeDepth == -1;
|
||||
|
||||
// Make a string constant for the name.
|
||||
int nameConstant = addConstant(compiler, wrenNewString(compiler->parser->vm,
|
||||
@ -3004,54 +3150,11 @@ static void classDefinition(Compiler* compiler)
|
||||
consume(compiler, TOKEN_LEFT_BRACE, "Expect '{' after class declaration.");
|
||||
matchLine(compiler);
|
||||
|
||||
bool hasConstructor = false;
|
||||
|
||||
while (!match(compiler, TOKEN_RIGHT_BRACE))
|
||||
{
|
||||
Code instruction = CODE_METHOD_INSTANCE;
|
||||
bool isConstructor = false;
|
||||
// TODO: What about foreign constructors?
|
||||
bool isForeign = match(compiler, TOKEN_FOREIGN);
|
||||
|
||||
classCompiler.isStaticMethod = false;
|
||||
|
||||
if (match(compiler, TOKEN_STATIC))
|
||||
{
|
||||
instruction = CODE_METHOD_STATIC;
|
||||
classCompiler.isStaticMethod = true;
|
||||
}
|
||||
else if (peek(compiler) == TOKEN_NEW)
|
||||
{
|
||||
// If the method name is "new", it's a constructor.
|
||||
isConstructor = true;
|
||||
}
|
||||
|
||||
SignatureFn signature = rules[compiler->parser->current.type].method;
|
||||
nextToken(compiler->parser);
|
||||
|
||||
if (signature == NULL)
|
||||
{
|
||||
error(compiler, "Expect method definition.");
|
||||
break;
|
||||
}
|
||||
|
||||
int methodSymbol = method(compiler, &classCompiler, isConstructor,
|
||||
isForeign, signature);
|
||||
|
||||
// Load the class. We have to do this for each method because we can't
|
||||
// keep the class on top of the stack. If there are static fields, they
|
||||
// will be locals above the initial variable slot for the class on the
|
||||
// stack. To skip past those, we just load the class each time right before
|
||||
// defining a method.
|
||||
if (isModule)
|
||||
{
|
||||
emitShortArg(compiler, CODE_LOAD_MODULE_VAR, slot);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadLocal(compiler, slot);
|
||||
}
|
||||
|
||||
// Define the method.
|
||||
emitShortArg(compiler, instruction, methodSymbol);
|
||||
if (!method(compiler, &classCompiler, slot, &hasConstructor)) break;
|
||||
|
||||
// Don't require a newline after the last definition.
|
||||
if (match(compiler, TOKEN_RIGHT_BRACE)) break;
|
||||
@ -3059,6 +3162,12 @@ static void classDefinition(Compiler* compiler)
|
||||
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.
|
||||
compiler->bytecode.data[numFieldsInstruction] = (uint8_t)fields.count;
|
||||
wrenSymbolTableClear(compiler->parser->vm, &fields);
|
||||
|
||||
@ -68,9 +68,9 @@ static const char* coreLibSource =
|
||||
"\n"
|
||||
" isEmpty { iterate(null) ? false : true }\n"
|
||||
"\n"
|
||||
" map(transformation) { new MapSequence(this, transformation) }\n"
|
||||
" map(transformation) { MapSequence.new(this, transformation) }\n"
|
||||
"\n"
|
||||
" where(predicate) { new WhereSequence(this, predicate) }\n"
|
||||
" where(predicate) { WhereSequence.new(this, predicate) }\n"
|
||||
"\n"
|
||||
" reduce(acc, f) {\n"
|
||||
" for (element in this) {\n"
|
||||
@ -108,7 +108,7 @@ static const char* coreLibSource =
|
||||
" }\n"
|
||||
"\n"
|
||||
" toList {\n"
|
||||
" var result = new List\n"
|
||||
" var result = List.new()\n"
|
||||
" for (element in this) {\n"
|
||||
" result.add(element)\n"
|
||||
" }\n"
|
||||
@ -117,7 +117,7 @@ static const char* coreLibSource =
|
||||
"}\n"
|
||||
"\n"
|
||||
"class MapSequence is Sequence {\n"
|
||||
" new(sequence, fn) {\n"
|
||||
" this new(sequence, fn) {\n"
|
||||
" _sequence = sequence\n"
|
||||
" _fn = fn\n"
|
||||
" }\n"
|
||||
@ -127,7 +127,7 @@ static const char* coreLibSource =
|
||||
"}\n"
|
||||
"\n"
|
||||
"class WhereSequence is Sequence {\n"
|
||||
" new(sequence, fn) {\n"
|
||||
" this new(sequence, fn) {\n"
|
||||
" _sequence = sequence\n"
|
||||
" _fn = fn\n"
|
||||
" }\n"
|
||||
@ -143,11 +143,11 @@ static const char* coreLibSource =
|
||||
"}\n"
|
||||
"\n"
|
||||
"class String is Sequence {\n"
|
||||
" bytes { new StringByteSequence(this) }\n"
|
||||
" bytes { StringByteSequence.new(this) }\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"class StringByteSequence is Sequence {\n"
|
||||
" new(string) {\n"
|
||||
" this new(string) {\n"
|
||||
" _string = string\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
@ -176,8 +176,8 @@ static const char* coreLibSource =
|
||||
"}\n"
|
||||
"\n"
|
||||
"class Map {\n"
|
||||
" keys { new MapKeySequence(this) }\n"
|
||||
" values { new MapValueSequence(this) }\n"
|
||||
" keys { MapKeySequence.new(this) }\n"
|
||||
" values { MapValueSequence.new(this) }\n"
|
||||
"\n"
|
||||
" toString {\n"
|
||||
" var first = true\n"
|
||||
@ -194,7 +194,7 @@ static const char* coreLibSource =
|
||||
"}\n"
|
||||
"\n"
|
||||
"class MapKeySequence is Sequence {\n"
|
||||
" new(map) {\n"
|
||||
" this new(map) {\n"
|
||||
" _map = map\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
@ -203,7 +203,7 @@ static const char* coreLibSource =
|
||||
"}\n"
|
||||
"\n"
|
||||
"class MapValueSequence is Sequence {\n"
|
||||
" new(map) {\n"
|
||||
" this new(map) {\n"
|
||||
" _map = map\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
@ -215,13 +215,9 @@ static const char* coreLibSource =
|
||||
|
||||
// A simple primitive that just returns "this". Used in a few different places:
|
||||
//
|
||||
// * The default constructor on Object needs no initialization so just uses
|
||||
// this.
|
||||
// * The default new() initializer on Object needs no initialization so just
|
||||
// uses this.
|
||||
// * String's toString method obviously can use this.
|
||||
// * Fiber's instantiate method just returns the Fiber class. The new() method
|
||||
// is responsible for creating the new fiber.
|
||||
// * Fn's "constructor" is given the actual function as an argument, so the
|
||||
// instantiate method just returns the Fn class.
|
||||
DEF_PRIMITIVE(return_this)
|
||||
{
|
||||
RETURN_VAL(args[0]);
|
||||
@ -244,11 +240,6 @@ DEF_PRIMITIVE(bool_toString)
|
||||
}
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(class_instantiate)
|
||||
{
|
||||
RETURN_VAL(wrenNewInstance(vm, AS_CLASS(args[0])));
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(class_name)
|
||||
{
|
||||
RETURN_OBJ(AS_CLASS(args[0])->name);
|
||||
@ -532,7 +523,7 @@ DEF_PRIMITIVE(fn_toString)
|
||||
RETURN_VAL(CONST_STRING(vm, "<fn>"));
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(list_instantiate)
|
||||
DEF_PRIMITIVE(list_new)
|
||||
{
|
||||
RETURN_OBJ(wrenNewList(vm, 0));
|
||||
}
|
||||
@ -649,7 +640,7 @@ DEF_PRIMITIVE(list_subscriptSetter)
|
||||
RETURN_VAL(args[2]);
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(map_instantiate)
|
||||
DEF_PRIMITIVE(map_new)
|
||||
{
|
||||
RETURN_OBJ(wrenNewMap(vm));
|
||||
}
|
||||
@ -998,11 +989,6 @@ DEF_PRIMITIVE(object_type)
|
||||
RETURN_OBJ(wrenGetClass(vm, args[0]));
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(object_instantiate)
|
||||
{
|
||||
RETURN_ERROR("Must provide a class to 'new' to construct.");
|
||||
}
|
||||
|
||||
DEF_PRIMITIVE(range_from)
|
||||
{
|
||||
RETURN_NUM(AS_RANGE(args[0])->from);
|
||||
@ -1303,16 +1289,14 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
PRIMITIVE(vm->objectClass, "!", object_not);
|
||||
PRIMITIVE(vm->objectClass, "==(_)", object_eqeq);
|
||||
PRIMITIVE(vm->objectClass, "!=(_)", object_bangeq);
|
||||
PRIMITIVE(vm->objectClass, "new", return_this);
|
||||
PRIMITIVE(vm->objectClass, "this new()", return_this);
|
||||
PRIMITIVE(vm->objectClass, "is(_)", object_is);
|
||||
PRIMITIVE(vm->objectClass, "toString", object_toString);
|
||||
PRIMITIVE(vm->objectClass, "type", object_type);
|
||||
PRIMITIVE(vm->objectClass, "<instantiate>", object_instantiate);
|
||||
|
||||
// Now we can define Class, which is a subclass of Object.
|
||||
vm->classClass = defineClass(vm, "Class");
|
||||
wrenBindSuperclass(vm, vm->classClass, vm->objectClass);
|
||||
PRIMITIVE(vm->classClass, "<instantiate>", class_instantiate);
|
||||
PRIMITIVE(vm->classClass, "name", class_name);
|
||||
PRIMITIVE(vm->classClass, "supertype", class_supertype);
|
||||
PRIMITIVE(vm->classClass, "toString", class_toString);
|
||||
@ -1361,7 +1345,6 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
PRIMITIVE(vm->boolClass, "!", bool_not);
|
||||
|
||||
vm->fiberClass = AS_CLASS(wrenFindVariable(vm, "Fiber"));
|
||||
PRIMITIVE(vm->fiberClass->obj.classObj, "<instantiate>", return_this);
|
||||
PRIMITIVE(vm->fiberClass->obj.classObj, "new(_)", fiber_new);
|
||||
PRIMITIVE(vm->fiberClass->obj.classObj, "abort(_)", fiber_abort);
|
||||
PRIMITIVE(vm->fiberClass->obj.classObj, "current", fiber_current);
|
||||
@ -1376,7 +1359,6 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
PRIMITIVE(vm->fiberClass, "try()", fiber_try);
|
||||
|
||||
vm->fnClass = AS_CLASS(wrenFindVariable(vm, "Fn"));
|
||||
PRIMITIVE(vm->fnClass->obj.classObj, "<instantiate>", return_this);
|
||||
PRIMITIVE(vm->fnClass->obj.classObj, "new(_)", fn_new);
|
||||
|
||||
PRIMITIVE(vm->fnClass, "arity", fn_arity);
|
||||
@ -1463,7 +1445,7 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
PRIMITIVE(vm->stringClass, "toString", return_this);
|
||||
|
||||
vm->listClass = AS_CLASS(wrenFindVariable(vm, "List"));
|
||||
PRIMITIVE(vm->listClass->obj.classObj, "<instantiate>", list_instantiate);
|
||||
PRIMITIVE(vm->listClass->obj.classObj, "new()", list_new);
|
||||
PRIMITIVE(vm->listClass, "[_]", list_subscript);
|
||||
PRIMITIVE(vm->listClass, "[_]=(_)", list_subscriptSetter);
|
||||
PRIMITIVE(vm->listClass, "add(_)", list_add);
|
||||
@ -1475,7 +1457,7 @@ void wrenInitializeCore(WrenVM* vm)
|
||||
PRIMITIVE(vm->listClass, "removeAt(_)", list_removeAt);
|
||||
|
||||
vm->mapClass = AS_CLASS(wrenFindVariable(vm, "Map"));
|
||||
PRIMITIVE(vm->mapClass->obj.classObj, "<instantiate>", map_instantiate);
|
||||
PRIMITIVE(vm->mapClass->obj.classObj, "new()", map_new);
|
||||
PRIMITIVE(vm->mapClass, "[_]", map_subscript);
|
||||
PRIMITIVE(vm->mapClass, "[_]=(_)", map_subscriptSetter);
|
||||
PRIMITIVE(vm->mapClass, "clear()", map_clear);
|
||||
|
||||
@ -277,6 +277,8 @@ static int dumpInstruction(WrenVM* vm, ObjFn* fn, int i, int* lastLine)
|
||||
break;
|
||||
}
|
||||
|
||||
case CODE_CONSTRUCT: printf("CODE_CONSTRUCT\n"); break;
|
||||
|
||||
case CODE_CLASS:
|
||||
{
|
||||
int numFields = READ_BYTE();
|
||||
|
||||
@ -150,6 +150,13 @@ OPCODE(RETURN)
|
||||
// Pushes the created closure.
|
||||
OPCODE(CLOSURE)
|
||||
|
||||
// Creates a new instance of a class.
|
||||
//
|
||||
// Assumes the class object is in slot zero, and replaces it with the new
|
||||
// uninitialized instance of that class. This opcode is only emitted by the
|
||||
// compiler-generated constructor metaclass methods.
|
||||
OPCODE(CONSTRUCT)
|
||||
|
||||
// Creates a class. Top of stack is the superclass, or `null` if the class
|
||||
// inherits Object. Below that is a string for the name of the class. Byte
|
||||
// [arg] is the number of fields in the class.
|
||||
|
||||
@ -1023,6 +1023,11 @@ static WrenInterpretResult runInterpreter(WrenVM* vm, register ObjFiber* fiber)
|
||||
DISPATCH();
|
||||
}
|
||||
|
||||
CASE_CODE(CONSTRUCT):
|
||||
ASSERT(IS_CLASS(stackStart[0]), "'this' should be a class.");
|
||||
stackStart[0] = wrenNewInstance(vm, AS_CLASS(stackStart[0]));
|
||||
DISPATCH();
|
||||
|
||||
CASE_CODE(CLOSURE):
|
||||
{
|
||||
ObjFn* prototype = AS_FN(fn->constants[READ_SHORT()]);
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
// Ported from the Python version.
|
||||
|
||||
class Tree {
|
||||
new(item, depth) {
|
||||
this new(item, depth) {
|
||||
_item = item
|
||||
if (depth > 0) {
|
||||
var item2 = item + item
|
||||
depth = depth - 1
|
||||
_left = new Tree(item2 - 1, depth)
|
||||
_right = new Tree(item2, depth)
|
||||
_left = Tree.new(item2 - 1, depth)
|
||||
_right = Tree.new(item2, depth)
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,9 +27,9 @@ var stretchDepth = maxDepth + 1
|
||||
var start = IO.clock
|
||||
|
||||
IO.print("stretch tree of depth ", stretchDepth, " check: ",
|
||||
new Tree(0, stretchDepth).check)
|
||||
Tree.new(0, stretchDepth).check)
|
||||
|
||||
var longLivedTree = new Tree(0, maxDepth)
|
||||
var longLivedTree = Tree.new(0, maxDepth)
|
||||
|
||||
// iterations = 2 ** maxDepth
|
||||
var iterations = 1
|
||||
@ -41,7 +41,7 @@ var depth = minDepth
|
||||
while (depth < stretchDepth) {
|
||||
var check = 0
|
||||
for (i in 1..iterations) {
|
||||
check = check + new Tree(i, depth).check + new Tree(-i, depth).check
|
||||
check = check + Tree.new(i, depth).check + Tree.new(-i, depth).check
|
||||
}
|
||||
|
||||
IO.print((iterations * 2), " trees of depth ", depth, " check: ", check)
|
||||
|
||||
@ -50,7 +50,7 @@ var ORDERED = null
|
||||
// disrupting current constraints. Strengths cannot be created outside
|
||||
// this class, so == can be used for value comparison.
|
||||
class Strength {
|
||||
new(value, name) {
|
||||
this new(value, name) {
|
||||
_value = value
|
||||
_name = name
|
||||
}
|
||||
@ -67,13 +67,13 @@ class Strength {
|
||||
}
|
||||
|
||||
// Compile time computed constants.
|
||||
REQUIRED = new Strength(0, "required")
|
||||
STRONG_REFERRED = new Strength(1, "strongPreferred")
|
||||
PREFERRED = new Strength(2, "preferred")
|
||||
STRONG_DEFAULT = new Strength(3, "strongDefault")
|
||||
NORMAL = new Strength(4, "normal")
|
||||
WEAK_DEFAULT = new Strength(5, "weakDefault")
|
||||
WEAKEST = new Strength(6, "weakest")
|
||||
REQUIRED = Strength.new(0, "required")
|
||||
STRONG_REFERRED = Strength.new(1, "strongPreferred")
|
||||
PREFERRED = Strength.new(2, "preferred")
|
||||
STRONG_DEFAULT = Strength.new(3, "strongDefault")
|
||||
NORMAL = Strength.new(4, "normal")
|
||||
WEAK_DEFAULT = Strength.new(5, "weakDefault")
|
||||
WEAKEST = Strength.new(6, "weakest")
|
||||
|
||||
ORDERED = [
|
||||
WEAKEST, WEAK_DEFAULT, NORMAL, STRONG_DEFAULT, PREFERRED, STRONG_REFERRED
|
||||
@ -82,7 +82,7 @@ ORDERED = [
|
||||
var ThePlanner
|
||||
|
||||
class Constraint {
|
||||
new(strength) {
|
||||
this new(strength) {
|
||||
_strength = strength
|
||||
}
|
||||
|
||||
@ -131,7 +131,7 @@ class Constraint {
|
||||
|
||||
// Abstract superclass for constraints having a single possible output variable.
|
||||
class UnaryConstraint is Constraint {
|
||||
new(myOutput, strength) {
|
||||
this new(myOutput, strength) {
|
||||
super(strength)
|
||||
_satisfied = false
|
||||
_myOutput = myOutput
|
||||
@ -187,7 +187,7 @@ class UnaryConstraint is Constraint {
|
||||
// change their output during plan execution. This is called "stay
|
||||
// optimization".
|
||||
class StayConstraint is UnaryConstraint {
|
||||
new(variable, strength) {
|
||||
this new(variable, strength) {
|
||||
super(variable, strength)
|
||||
}
|
||||
|
||||
@ -199,7 +199,7 @@ class StayConstraint is UnaryConstraint {
|
||||
// A unary input constraint used to mark a variable that the client
|
||||
// wishes to change.
|
||||
class EditConstraint is UnaryConstraint {
|
||||
EditConstraint(variable, strength) {
|
||||
this new(variable, strength) {
|
||||
super(variable, strength)
|
||||
}
|
||||
|
||||
@ -219,7 +219,7 @@ var BACKWARD = 0
|
||||
// Abstract superclass for constraints having two possible output
|
||||
// variables.
|
||||
class BinaryConstraint is Constraint {
|
||||
new(v1, v2, strength) {
|
||||
this new(v1, v2, strength) {
|
||||
super(strength)
|
||||
_v1 = v1
|
||||
_v2 = v2
|
||||
@ -328,7 +328,7 @@ class BinaryConstraint is Constraint {
|
||||
// this relationship but the scale factor and offset are considered
|
||||
// read-only.
|
||||
class ScaleConstraint is BinaryConstraint {
|
||||
new(src, scale, offset, dest, strength) {
|
||||
this new(src, scale, offset, dest, strength) {
|
||||
_scale = scale
|
||||
_offset = offset
|
||||
super(src, dest, strength)
|
||||
@ -376,7 +376,7 @@ class ScaleConstraint is BinaryConstraint {
|
||||
|
||||
// Constrains two variables to have the same value.
|
||||
class EqualityConstraint is BinaryConstraint {
|
||||
new(v1, v2, strength) {
|
||||
this new(v1, v2, strength) {
|
||||
super(v1, v2, strength)
|
||||
}
|
||||
|
||||
@ -391,7 +391,7 @@ class EqualityConstraint is BinaryConstraint {
|
||||
// various parameters of interest to the DeltaBlue incremental
|
||||
// constraint solver.
|
||||
class Variable {
|
||||
new(name, value) {
|
||||
this new(name, value) {
|
||||
_constraints = []
|
||||
_determinedBy = null
|
||||
_mark = 0
|
||||
@ -430,7 +430,7 @@ class Variable {
|
||||
// to resatisfy all currently satisfiable constraints in the face of
|
||||
// one or more changing inputs.
|
||||
class Plan {
|
||||
new {
|
||||
this new() {
|
||||
_list = []
|
||||
}
|
||||
|
||||
@ -448,7 +448,7 @@ class Plan {
|
||||
}
|
||||
|
||||
class Planner {
|
||||
new {
|
||||
this new() {
|
||||
_currentMark = 0
|
||||
}
|
||||
|
||||
@ -521,7 +521,7 @@ class Planner {
|
||||
// Assume: [sources] are all satisfied.
|
||||
makePlan(sources) {
|
||||
var mark = newMark
|
||||
var plan = new Plan
|
||||
var plan = Plan.new()
|
||||
var todo = sources
|
||||
while (todo.count > 0) {
|
||||
var constraint = todo.removeAt(-1)
|
||||
@ -622,23 +622,23 @@ var total = 0
|
||||
// constraint so it cannot be accomodated. The cost in this case is,
|
||||
// of course, very low. Typical situations lie somewhere between these
|
||||
// two extremes.
|
||||
var chainTest = new Fn {|n|
|
||||
ThePlanner = new Planner
|
||||
var chainTest = Fn.new {|n|
|
||||
ThePlanner = Planner.new()
|
||||
var prev = null
|
||||
var first = null
|
||||
var last = null
|
||||
|
||||
// Build chain of n equality constraints.
|
||||
for (i in 0..n) {
|
||||
var v = new Variable("v", 0)
|
||||
if (prev != null) new EqualityConstraint(prev, v, REQUIRED)
|
||||
var v = Variable.new("v", 0)
|
||||
if (prev != null) EqualityConstraint.new(prev, v, REQUIRED)
|
||||
if (i == 0) first = v
|
||||
if (i == n) last = v
|
||||
prev = v
|
||||
}
|
||||
|
||||
new StayConstraint(last, STRONG_DEFAULT)
|
||||
var edit = new EditConstraint(first, PREFERRED)
|
||||
StayConstraint.new(last, STRONG_DEFAULT)
|
||||
var edit = EditConstraint.new(first, PREFERRED)
|
||||
var plan = ThePlanner.extractPlanFromConstraints([edit])
|
||||
for (i in 0...100) {
|
||||
first.value = i
|
||||
@ -647,8 +647,8 @@ var chainTest = new Fn {|n|
|
||||
}
|
||||
}
|
||||
|
||||
var change = new Fn {|v, newValue|
|
||||
var edit = new EditConstraint(v, PREFERRED)
|
||||
var change = Fn.new {|v, newValue|
|
||||
var edit = EditConstraint.new(v, PREFERRED)
|
||||
var plan = ThePlanner.extractPlanFromConstraints([edit])
|
||||
for (i in 0...10) {
|
||||
v.value = newValue
|
||||
@ -662,20 +662,20 @@ var change = new Fn {|v, newValue|
|
||||
// other by a simple linear transformation (scale and offset). The
|
||||
// time is measured to change a variable on either side of the
|
||||
// mapping and to change the scale and offset factors.
|
||||
var projectionTest = new Fn {|n|
|
||||
ThePlanner = new Planner
|
||||
var scale = new Variable("scale", 10)
|
||||
var offset = new Variable("offset", 1000)
|
||||
var projectionTest = Fn.new {|n|
|
||||
ThePlanner = Planner.new()
|
||||
var scale = Variable.new("scale", 10)
|
||||
var offset = Variable.new("offset", 1000)
|
||||
var src = null
|
||||
var dst = null
|
||||
|
||||
var dests = []
|
||||
for (i in 0...n) {
|
||||
src = new Variable("src", i)
|
||||
dst = new Variable("dst", i)
|
||||
src = Variable.new("src", i)
|
||||
dst = Variable.new("dst", i)
|
||||
dests.add(dst)
|
||||
new StayConstraint(src, NORMAL)
|
||||
new ScaleConstraint(src, scale, offset, dst, REQUIRED)
|
||||
StayConstraint.new(src, NORMAL)
|
||||
ScaleConstraint.new(src, scale, offset, dst, REQUIRED)
|
||||
}
|
||||
|
||||
change.call(src, 17)
|
||||
|
||||
@ -5,7 +5,7 @@ var sum = 0
|
||||
var start = IO.clock
|
||||
|
||||
for (i in 0...100000) {
|
||||
fibers.add(new Fiber {
|
||||
fibers.add(Fiber.new {
|
||||
sum = sum + i
|
||||
if (i < 99999) fibers[i + 1].call()
|
||||
})
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
class Toggle {
|
||||
new(startState) {
|
||||
this new(startState) {
|
||||
_state = startState
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ class Toggle {
|
||||
}
|
||||
|
||||
class NthToggle is Toggle {
|
||||
new(startState, maxCounter) {
|
||||
this new(startState, maxCounter) {
|
||||
super(startState)
|
||||
_countMax = maxCounter
|
||||
_count = 0
|
||||
@ -31,7 +31,7 @@ class NthToggle is Toggle {
|
||||
var start = IO.clock
|
||||
var n = 100000
|
||||
var val = true
|
||||
var toggle = new Toggle(val)
|
||||
var toggle = Toggle.new(val)
|
||||
|
||||
for (i in 0...n) {
|
||||
val = toggle.activate.value
|
||||
@ -49,7 +49,7 @@ for (i in 0...n) {
|
||||
IO.print(toggle.value)
|
||||
|
||||
val = true
|
||||
var ntoggle = new NthToggle(val, 3)
|
||||
var ntoggle = NthToggle.new(val, 3)
|
||||
|
||||
for (i in 0...n) {
|
||||
val = ntoggle.activate.value
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
Fiber.abort("Error message.")
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("fiber")
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
var fiber
|
||||
|
||||
fiber = new Fiber {
|
||||
fiber = Fiber.new {
|
||||
fiber.call() // expect runtime error: Fiber has already been called.
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
var a
|
||||
var b
|
||||
|
||||
a = new Fiber {
|
||||
a = Fiber.new {
|
||||
b.call() // expect runtime error: Fiber has already been called.
|
||||
}
|
||||
|
||||
b = new Fiber {
|
||||
b = Fiber.new {
|
||||
a.call()
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("fiber")
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("fiber")
|
||||
return "result"
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("call")
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("fiber")
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
var fiber
|
||||
|
||||
fiber = new Fiber {
|
||||
fiber = Fiber.new {
|
||||
fiber.call(2) // expect runtime error: Fiber has already been called.
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
var a
|
||||
var b
|
||||
|
||||
a = new Fiber {
|
||||
a = Fiber.new {
|
||||
b.call(3) // expect runtime error: Fiber has already been called.
|
||||
}
|
||||
|
||||
b = new Fiber {
|
||||
b = Fiber.new {
|
||||
a.call(2)
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("call")
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
"s".unknown
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("1")
|
||||
Fiber.yield()
|
||||
IO.print("2")
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
"s".unknown
|
||||
}
|
||||
|
||||
|
||||
@ -1 +1 @@
|
||||
var fiber = new Fiber("not fn") // expect runtime error: Argument must be a function.
|
||||
var fiber = Fiber.new("not fn") // expect runtime error: Argument must be a function.
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
var b = new Fiber {
|
||||
var b = Fiber.new {
|
||||
IO.print("fiber b")
|
||||
}
|
||||
|
||||
var a = new Fiber {
|
||||
var a = Fiber.new {
|
||||
IO.print("begin fiber a")
|
||||
b.call()
|
||||
IO.print("end fiber a")
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("fiber")
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
var fiber
|
||||
|
||||
fiber = new Fiber {
|
||||
fiber = Fiber.new {
|
||||
IO.print(1) // expect: 1
|
||||
fiber.run()
|
||||
IO.print(2) // expect: 2
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
var a
|
||||
var b
|
||||
|
||||
a = new Fiber {
|
||||
a = Fiber.new {
|
||||
IO.print(2)
|
||||
b.run()
|
||||
IO.print("nope")
|
||||
}
|
||||
|
||||
b = new Fiber {
|
||||
b = Fiber.new {
|
||||
IO.print(1)
|
||||
a.run()
|
||||
IO.print(3)
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
var a = new Fiber {
|
||||
var a = Fiber.new {
|
||||
IO.print("run")
|
||||
}
|
||||
|
||||
// Run a through an intermediate fiber since it will get discarded and we need
|
||||
// to return to the main one after a completes.
|
||||
var b = new Fiber {
|
||||
var b = Fiber.new {
|
||||
a.run()
|
||||
IO.print("nope")
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("fiber")
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
var fiber
|
||||
|
||||
fiber = new Fiber {
|
||||
fiber = Fiber.new {
|
||||
IO.print(1) // expect: 1
|
||||
fiber.run("ignored")
|
||||
IO.print(2) // expect: 2
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
var a
|
||||
var b
|
||||
|
||||
a = new Fiber {
|
||||
a = Fiber.new {
|
||||
IO.print(2)
|
||||
b.run("ignored")
|
||||
IO.print("nope")
|
||||
}
|
||||
|
||||
b = new Fiber {
|
||||
b = Fiber.new {
|
||||
IO.print(1)
|
||||
a.run("ignored")
|
||||
IO.print(3)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("before")
|
||||
true.unknownMethod
|
||||
IO.print("after")
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
var fiber
|
||||
|
||||
fiber = new Fiber {
|
||||
fiber = Fiber.new {
|
||||
fiber.try() // expect runtime error: Fiber has already been called.
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
var a
|
||||
var b
|
||||
|
||||
a = new Fiber {
|
||||
a = Fiber.new {
|
||||
b.try() // expect runtime error: Fiber has already been called.
|
||||
}
|
||||
|
||||
b = new Fiber {
|
||||
b = Fiber.new {
|
||||
a.call()
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("try")
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("fiber")
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {}
|
||||
var fiber = Fiber.new {}
|
||||
IO.print(fiber is Fiber) // expect: true
|
||||
IO.print(fiber is Object) // expect: true
|
||||
IO.print(fiber is Bool) // expect: false
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("fiber 1")
|
||||
Fiber.yield()
|
||||
IO.print("fiber 2")
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("fiber 1")
|
||||
var result = Fiber.yield()
|
||||
IO.print(result)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("fiber")
|
||||
var result = Fiber.yield()
|
||||
IO.print(result)
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
var a = new Fiber {
|
||||
var a = Fiber.new {
|
||||
IO.print("before") // expect: before
|
||||
Fiber.yield()
|
||||
}
|
||||
|
||||
// Run a chain of fibers. Since none of them are called, they all get discarded
|
||||
// and there is no remaining caller.
|
||||
var b = new Fiber { a.run() }
|
||||
var c = new Fiber { b.run() }
|
||||
var b = Fiber.new { a.run() }
|
||||
var c = Fiber.new { b.run() }
|
||||
c.run()
|
||||
IO.print("not reached")
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {
|
||||
var fiber = Fiber.new {
|
||||
IO.print("fiber 1")
|
||||
Fiber.yield("yield 1")
|
||||
IO.print("fiber 2")
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
var a = new Fiber {
|
||||
var a = Fiber.new {
|
||||
IO.print("before") // expect: before
|
||||
Fiber.yield(1)
|
||||
}
|
||||
|
||||
// Run a chain of fibers. Since none of them are called, they all get discarded
|
||||
// and there is no remaining caller.
|
||||
var b = new Fiber { a.run() }
|
||||
var c = new Fiber { b.run() }
|
||||
var b = Fiber.new { a.run() }
|
||||
var c = Fiber.new { b.run() }
|
||||
c.run()
|
||||
IO.print("not reached")
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
IO.print(new Fn {}.arity) // expect: 0
|
||||
IO.print(new Fn {|a| a}.arity) // expect: 1
|
||||
IO.print(new Fn {|a, b| a}.arity) // expect: 2
|
||||
IO.print(new Fn {|a, b, c| a}.arity) // expect: 3
|
||||
IO.print(new Fn {|a, b, c, d| a}.arity) // expect: 4
|
||||
IO.print(Fn.new {}.arity) // expect: 0
|
||||
IO.print(Fn.new {|a| a}.arity) // expect: 1
|
||||
IO.print(Fn.new {|a, b| a}.arity) // expect: 2
|
||||
IO.print(Fn.new {|a, b, c| a}.arity) // expect: 3
|
||||
IO.print(Fn.new {|a, b, c, d| a}.arity) // expect: 4
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
var f0 = new Fn { IO.print("zero") }
|
||||
var f1 = new Fn {|a| IO.print("one ", a) }
|
||||
var f2 = new Fn {|a, b| IO.print("two ", a, " ", b) }
|
||||
var f3 = new Fn {|a, b, c| IO.print("three ", a, " ", b, " ", c) }
|
||||
var f0 = Fn.new { IO.print("zero") }
|
||||
var f1 = Fn.new {|a| IO.print("one ", a) }
|
||||
var f2 = Fn.new {|a, b| IO.print("two ", a, " ", b) }
|
||||
var f3 = Fn.new {|a, b, c| IO.print("three ", a, " ", b, " ", c) }
|
||||
|
||||
f0.call("a") // expect: zero
|
||||
f0.call("a", "b") // expect: zero
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
var f2 = new Fn {|a, b| IO.print(a, b) }
|
||||
var f2 = Fn.new {|a, b| IO.print(a, b) }
|
||||
f2.call("a") // expect runtime error: Function expects more arguments.
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
// Not structurally equal.
|
||||
IO.print(new Fn { 123 } == new Fn { 123 }) // expect: false
|
||||
IO.print(new Fn { 123 } != new Fn { 123 }) // expect: true
|
||||
IO.print(Fn.new { 123 } == Fn.new { 123 }) // expect: false
|
||||
IO.print(Fn.new { 123 } != Fn.new { 123 }) // expect: true
|
||||
|
||||
// Not equal to other types.
|
||||
IO.print(new Fn { 123 } == 1) // expect: false
|
||||
IO.print(new Fn { 123 } == false) // expect: false
|
||||
IO.print(new Fn { 123 } == "fn 123") // expect: false
|
||||
IO.print(new Fn { 123 } != 1) // expect: true
|
||||
IO.print(new Fn { 123 } != false) // expect: true
|
||||
IO.print(new Fn { 123 } != "fn 123") // expect: true
|
||||
IO.print(Fn.new { 123 } == 1) // expect: false
|
||||
IO.print(Fn.new { 123 } == false) // expect: false
|
||||
IO.print(Fn.new { 123 } == "fn 123") // expect: false
|
||||
IO.print(Fn.new { 123 } != 1) // expect: true
|
||||
IO.print(Fn.new { 123 } != false) // expect: true
|
||||
IO.print(Fn.new { 123 } != "fn 123") // expect: true
|
||||
|
||||
// Equal by identity.
|
||||
var f = new Fn { 123 }
|
||||
var f = Fn.new { 123 }
|
||||
IO.print(f == f) // expect: true
|
||||
IO.print(f != f) // expect: false
|
||||
|
||||
@ -1 +1 @@
|
||||
new Fn(3) // expect runtime error: Argument must be a function.
|
||||
Fn.new(3) // expect runtime error: Argument must be a function.
|
||||
|
||||
@ -1 +1 @@
|
||||
IO.print(new Fn {}) // expect: <fn>
|
||||
IO.print(Fn.new {}) // expect: <fn>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
IO.print(new Fn { 0 } is Fn) // expect: true
|
||||
IO.print(new Fn { 0 } is Object) // expect: true
|
||||
IO.print(new Fn { 0 } is String) // expect: false
|
||||
IO.print(new Fn { 0 }.type == Fn) // expect: true
|
||||
IO.print(Fn.new { 0 } is Fn) // expect: true
|
||||
IO.print(Fn.new { 0 } is Object) // expect: true
|
||||
IO.print(Fn.new { 0 } is String) // expect: false
|
||||
IO.print(Fn.new { 0 }.type == Fn) // expect: true
|
||||
|
||||
@ -18,6 +18,6 @@ class Foo {
|
||||
toString { "Foo.toString" }
|
||||
}
|
||||
|
||||
IO.print([1, new Foo, 2].join(", ")) // expect: 1, Foo.toString, 2
|
||||
IO.print([1, Foo.new(), 2].join(", ")) // expect: 1, Foo.toString, 2
|
||||
|
||||
// TODO: Handle lists that contain themselves.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var list = new List
|
||||
var list = List.new()
|
||||
|
||||
IO.print(list.count) // expect: 0
|
||||
IO.print(list) // expect: []
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
var a = [1, 4, 2, 1, 5]
|
||||
var b = ["W", "o", "r", "l", "d"]
|
||||
var max = new Fn {|a, b| a > b ? a : b }
|
||||
var sum = new Fn {|a, b| a + b }
|
||||
var max = Fn.new {|a, b| a > b ? a : b }
|
||||
var sum = Fn.new {|a, b| a + b }
|
||||
|
||||
IO.print(a.reduce(max)) // expect: 5
|
||||
IO.print(a.reduce(10, max)) // expect: 10
|
||||
|
||||
@ -12,6 +12,6 @@ class Foo {
|
||||
toString { "Foo.toString" }
|
||||
}
|
||||
|
||||
IO.print([1, new Foo, 2]) // expect: [1, Foo.toString, 2]
|
||||
IO.print([1, Foo.new(), 2]) // expect: [1, Foo.toString, 2]
|
||||
|
||||
// TODO: Handle lists that contain themselves.
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var fiber = new Fiber {}
|
||||
var fiber = Fiber.new {}
|
||||
|
||||
var map = {
|
||||
null: "null value",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
var map = new Map
|
||||
var map = Map.new()
|
||||
|
||||
IO.print(map.count) // expect: 0
|
||||
IO.print(map) // expect: {}
|
||||
|
||||
@ -12,7 +12,7 @@ class Foo {
|
||||
toString { "Foo.toString" }
|
||||
}
|
||||
|
||||
IO.print({1: new Foo}) // expect: {1: Foo.toString}
|
||||
IO.print({1: Foo.new()}) // expect: {1: Foo.toString}
|
||||
|
||||
// Since iteration order is unspecified, we don't know what order the results
|
||||
// will be.
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
class Foo {}
|
||||
IO.print(!(new Foo)) // expect: false
|
||||
IO.print(!Foo.new()) // expect: false
|
||||
|
||||
@ -26,15 +26,15 @@ IO.print(Object.same(Bool, Bool)) // expect: true
|
||||
// Other types compare by identity.
|
||||
class Foo {}
|
||||
|
||||
var foo = new Foo
|
||||
var foo = Foo.new()
|
||||
IO.print(Object.same(foo, foo)) // expect: true
|
||||
IO.print(Object.same(foo, new Foo)) // expect: false
|
||||
IO.print(Object.same(foo, Foo.new())) // expect: false
|
||||
|
||||
// Ignores == operators.
|
||||
class Bar {
|
||||
==(other) { true }
|
||||
}
|
||||
|
||||
var bar = new Bar
|
||||
var bar = Bar.new()
|
||||
IO.print(Object.same(bar, bar)) // expect: true
|
||||
IO.print(Object.same(bar, new Bar)) // expect: false
|
||||
IO.print(Object.same(bar, Bar.new())) // expect: false
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
class Foo {}
|
||||
IO.print((new Foo).toString == "instance of Foo") // expect: true
|
||||
IO.print(Foo.new().toString == "instance of Foo") // expect: true
|
||||
|
||||
@ -8,4 +8,4 @@ class TestSequence is Sequence {
|
||||
iteratorValue(iterator) { iterator }
|
||||
}
|
||||
|
||||
IO.print((new TestSequence).count) // expect: 10
|
||||
IO.print(TestSequence.new().count) // expect: 10
|
||||
|
||||
@ -7,4 +7,4 @@ class InfiniteSequence is Sequence {
|
||||
}
|
||||
|
||||
// Should not try to iterate the whole sequence.
|
||||
IO.print((new InfiniteSequence).isEmpty) // expect: false
|
||||
IO.print(InfiniteSequence.new().isEmpty) // expect: false
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// Infinite iterator demonstrating that Sequence.map is not eager
|
||||
class FibIterator {
|
||||
new {
|
||||
this new() {
|
||||
_current = 0
|
||||
_next = 1
|
||||
}
|
||||
@ -16,7 +16,7 @@ class FibIterator {
|
||||
|
||||
class Fib is Sequence {
|
||||
iterate(iterator) {
|
||||
if (iterator == null) return new FibIterator
|
||||
if (iterator == null) return FibIterator.new()
|
||||
iterator.iterate
|
||||
return iterator
|
||||
}
|
||||
@ -24,7 +24,7 @@ class Fib is Sequence {
|
||||
iteratorValue(iterator) { iterator.value }
|
||||
}
|
||||
|
||||
var squareFib = (new Fib).map {|fib| fib * fib }
|
||||
var squareFib = Fib.new().map {|fib| fib * fib }
|
||||
var iterator = null
|
||||
|
||||
IO.print(squareFib is Sequence) // expect: true
|
||||
|
||||
@ -8,4 +8,4 @@ class TestSequence is Sequence {
|
||||
iteratorValue(iterator) { iterator }
|
||||
}
|
||||
|
||||
IO.print((new TestSequence).toList) // expect: [1, 2, 3]
|
||||
IO.print(TestSequence.new().toList) // expect: [1, 2, 3]
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// Infinite iterator demonstrating that Sequence.where is not eager
|
||||
class FibIterator {
|
||||
new {
|
||||
this new() {
|
||||
_current = 0
|
||||
_next = 1
|
||||
}
|
||||
@ -16,7 +16,7 @@ class FibIterator {
|
||||
|
||||
class Fib is Sequence {
|
||||
iterate(iterator) {
|
||||
if (iterator == null) return new FibIterator
|
||||
if (iterator == null) return FibIterator.new()
|
||||
iterator.iterate
|
||||
return iterator
|
||||
}
|
||||
@ -24,7 +24,7 @@ class Fib is Sequence {
|
||||
iteratorValue(iterator) { iterator.value }
|
||||
}
|
||||
|
||||
var largeFibs = (new Fib).where {|fib| fib > 100 }
|
||||
var largeFibs = Fib.new().where {|fib| fib > 100 }
|
||||
var iterator = null
|
||||
|
||||
IO.print(largeFibs is Sequence) // expect: true
|
||||
|
||||
@ -3,7 +3,7 @@ class Foo {
|
||||
}
|
||||
|
||||
// Calls toString on argument.
|
||||
IO.print(new Foo) // expect: Foo.toString
|
||||
IO.print(Foo.new()) // expect: Foo.toString
|
||||
|
||||
// With one argument, returns the argument.
|
||||
IO.print(IO.print(1) == 1) // expect: 1
|
||||
|
||||
@ -2,4 +2,4 @@ class BadToString {
|
||||
toString { 3 }
|
||||
}
|
||||
|
||||
IO.print(new BadToString) // expect: [invalid toString]
|
||||
IO.print(BadToString.new()) // expect: [invalid toString]
|
||||
|
||||
@ -2,5 +2,5 @@ class BadToString {
|
||||
toString { 3 }
|
||||
}
|
||||
|
||||
IO.write(new BadToString) // expect: [invalid toString]
|
||||
IO.write(BadToString.new()) // expect: [invalid toString]
|
||||
IO.print
|
||||
@ -1,4 +1,4 @@
|
||||
new Fn {
|
||||
Fn.new {
|
||||
var a = "before"
|
||||
IO.print(a) // expect: before
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
var f
|
||||
for (i in [1, 2, 3]) {
|
||||
var j = 4
|
||||
f = new Fn { IO.print(i + j) }
|
||||
f = Fn.new { IO.print(i + j) }
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
var f
|
||||
while (true) {
|
||||
var i = "i"
|
||||
f = new Fn { IO.print(i) }
|
||||
f = Fn.new { IO.print(i) }
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
var done = false
|
||||
while (!done) {
|
||||
new Fn {
|
||||
Fn.new {
|
||||
break // expect error
|
||||
}
|
||||
done = true
|
||||
|
||||
@ -12,4 +12,4 @@ class foo {
|
||||
}
|
||||
|
||||
foo.callFoo // expect: static foo method
|
||||
(new foo).callFoo // expect: instance foo method
|
||||
foo.new().callFoo // expect: instance foo method
|
||||
|
||||
@ -11,4 +11,4 @@ class Foo {
|
||||
}
|
||||
|
||||
Foo.sayName // expect: Foo!
|
||||
(new Foo).sayName // expect: Foo!
|
||||
Foo.new().sayName // expect: Foo!
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
class Foo {}
|
||||
|
||||
var foo = new Foo
|
||||
var foo = Foo.new()
|
||||
IO.print(foo is Foo) // expect: true
|
||||
|
||||
// TODO: Test precedence and grammar of what follows "new".
|
||||
|
||||
@ -3,13 +3,13 @@ var g = null
|
||||
|
||||
{
|
||||
var local = "local"
|
||||
f = new Fn {
|
||||
f = Fn.new {
|
||||
IO.print(local)
|
||||
local = "after f"
|
||||
IO.print(local)
|
||||
}
|
||||
|
||||
g = new Fn {
|
||||
g = Fn.new {
|
||||
IO.print(local)
|
||||
local = "after g"
|
||||
IO.print(local)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
var f = null
|
||||
|
||||
new Fn {|param|
|
||||
f = new Fn {
|
||||
Fn.new {|param|
|
||||
f = Fn.new {
|
||||
IO.print(param)
|
||||
}
|
||||
}.call("param")
|
||||
|
||||
@ -3,10 +3,10 @@
|
||||
// would crash because it walked to the end of the upvalue list (correct), but
|
||||
// then didn't handle not finding the variable.
|
||||
|
||||
new Fn {
|
||||
Fn.new {
|
||||
var a = "a"
|
||||
var b = "b"
|
||||
new Fn {
|
||||
Fn.new {
|
||||
IO.print(b) // expect: b
|
||||
IO.print(a) // expect: a
|
||||
}.call()
|
||||
|
||||
@ -2,11 +2,11 @@ var F = null
|
||||
|
||||
class Foo {
|
||||
method(param) {
|
||||
F = new Fn {
|
||||
F = Fn.new {
|
||||
IO.print(param)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(new Foo).method("param")
|
||||
Foo.new().method("param")
|
||||
F.call() // expect: param
|
||||
|
||||
@ -2,7 +2,7 @@ var f = null
|
||||
|
||||
{
|
||||
var local = "local"
|
||||
f = new Fn {
|
||||
f = Fn.new {
|
||||
IO.print(local)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
// TODO: Is this right? Shouldn't it resolve to this.local?
|
||||
var foo = null
|
||||
|
||||
{
|
||||
@ -8,7 +9,7 @@ var foo = null
|
||||
}
|
||||
}
|
||||
|
||||
foo = new Foo
|
||||
foo = Foo.new()
|
||||
}
|
||||
|
||||
foo.method // expect: local
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
var f = null
|
||||
|
||||
new Fn {
|
||||
Fn.new {
|
||||
var a = "a"
|
||||
new Fn {
|
||||
Fn.new {
|
||||
var b = "b"
|
||||
new Fn {
|
||||
Fn.new {
|
||||
var c = "c"
|
||||
f = new Fn {
|
||||
f = Fn.new {
|
||||
IO.print(a)
|
||||
IO.print(b)
|
||||
IO.print(c)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
var local = "local"
|
||||
new Fn {
|
||||
Fn.new {
|
||||
IO.print(local) // expect: local
|
||||
}.call()
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user