mirror of
https://github.com/wren-lang/wren.git
synced 2026-01-11 22:28:45 +01:00
Make "this" the implicit receiver!
This commit is contained in:
@ -101,7 +101,7 @@ class Constraint {
|
||||
|
||||
// Activate this constraint and attempt to satisfy it.
|
||||
addConstraint {
|
||||
this.addToGraph
|
||||
addToGraph
|
||||
planner.incrementalAdd(this)
|
||||
}
|
||||
|
||||
@ -111,16 +111,16 @@ class Constraint {
|
||||
// there is one, or nil, if there isn't.
|
||||
// Assume: I am not already satisfied.
|
||||
satisfy(mark) {
|
||||
this.chooseMethod(mark)
|
||||
if (!this.isSatisfied) {
|
||||
chooseMethod(mark)
|
||||
if (!isSatisfied) {
|
||||
if (_strength == REQUIRED) {
|
||||
IO.print("Could not satisfy a required constraint!")
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
this.markInputs(mark)
|
||||
var out = this.output
|
||||
markInputs(mark)
|
||||
var out = output
|
||||
var overridden = out.determinedBy
|
||||
if (overridden != null) overridden.markUnsatisfied
|
||||
out.determinedBy = this
|
||||
@ -130,8 +130,8 @@ class Constraint {
|
||||
}
|
||||
|
||||
destroyConstraint {
|
||||
if (this.isSatisfied) planner.incrementalRemove(this)
|
||||
this.removeFromGraph
|
||||
if (isSatisfied) planner.incrementalRemove(this)
|
||||
removeFromGraph
|
||||
}
|
||||
|
||||
// Normal constraints are not input constraints. An input constraint
|
||||
@ -146,7 +146,7 @@ class UnaryConstraint is Constraint {
|
||||
super(strength)
|
||||
_satisfied = false
|
||||
_myOutput = myOutput
|
||||
this.addConstraint
|
||||
addConstraint
|
||||
}
|
||||
|
||||
// Adds this constraint to the constraint graph.
|
||||
@ -158,7 +158,7 @@ class UnaryConstraint is Constraint {
|
||||
// Decides if this constraint can be satisfied and records that decision.
|
||||
chooseMethod(mark) {
|
||||
_satisfied = (_myOutput.mark != mark) &&
|
||||
Strength.stronger(this.strength, _myOutput.walkStrength)
|
||||
Strength.stronger(strength, _myOutput.walkStrength)
|
||||
}
|
||||
|
||||
// Returns true if this constraint is satisfied in the current solution.
|
||||
@ -175,9 +175,9 @@ class UnaryConstraint is Constraint {
|
||||
// 'stay', the value for the current output of this constraint. Assume
|
||||
// this constraint is satisfied.
|
||||
recalculate {
|
||||
_myOutput.walkStrength = this.strength
|
||||
_myOutput.stay = !this.isInput
|
||||
if (_myOutput.stay) this.execute // Stay optimization.
|
||||
_myOutput.walkStrength = strength
|
||||
_myOutput.stay = !isInput
|
||||
if (_myOutput.stay) execute // Stay optimization.
|
||||
}
|
||||
|
||||
// Records that this constraint is unsatisfied.
|
||||
@ -235,7 +235,7 @@ class BinaryConstraint is Constraint {
|
||||
_v1 = v1
|
||||
_v2 = v2
|
||||
_direction = NONE
|
||||
this.addConstraint
|
||||
addConstraint
|
||||
}
|
||||
|
||||
direction { return _direction }
|
||||
@ -248,7 +248,7 @@ class BinaryConstraint is Constraint {
|
||||
chooseMethod(mark) {
|
||||
if (_v1.mark == mark) {
|
||||
if (_v2.mark != mark &&
|
||||
Strength.stronger(this.strength, _v2.walkStrength)) {
|
||||
Strength.stronger(strength, _v2.walkStrength)) {
|
||||
_direction = FORWARD
|
||||
} else {
|
||||
_direction = NONE
|
||||
@ -257,7 +257,7 @@ class BinaryConstraint is Constraint {
|
||||
|
||||
if (_v2.mark == mark) {
|
||||
if (_v1.mark != mark &&
|
||||
Strength.stronger(this.strength, _v1.walkStrength)) {
|
||||
Strength.stronger(strength, _v1.walkStrength)) {
|
||||
_direction = BACKWARD
|
||||
} else {
|
||||
_direction = NONE
|
||||
@ -265,13 +265,13 @@ class BinaryConstraint is Constraint {
|
||||
}
|
||||
|
||||
if (Strength.weaker(_v1.walkStrength, _v2.walkStrength)) {
|
||||
if (Strength.stronger(this.strength, _v1.walkStrength)) {
|
||||
if (Strength.stronger(strength, _v1.walkStrength)) {
|
||||
_direction = BACKWARD
|
||||
} else {
|
||||
_direction = NONE
|
||||
}
|
||||
} else {
|
||||
if (Strength.stronger(this.strength, _v2.walkStrength)) {
|
||||
if (Strength.stronger(strength, _v2.walkStrength)) {
|
||||
_direction = FORWARD
|
||||
} else {
|
||||
_direction = BACKWARD
|
||||
@ -291,7 +291,7 @@ class BinaryConstraint is Constraint {
|
||||
|
||||
// Mark the input variable with the given mark.
|
||||
markInputs(mark) {
|
||||
this.input.mark = mark
|
||||
input.mark = mark
|
||||
}
|
||||
|
||||
// Returns the current input variable
|
||||
@ -310,11 +310,11 @@ class BinaryConstraint is Constraint {
|
||||
// 'stay', the value for the current output of this
|
||||
// constraint. Assume this constraint is satisfied.
|
||||
recalculate {
|
||||
var ihn = this.input
|
||||
var out = this.output
|
||||
out.walkStrength = Strength.weakest(this.strength, ihn.walkStrength)
|
||||
var ihn = input
|
||||
var out = output
|
||||
out.walkStrength = Strength.weakest(strength, ihn.walkStrength)
|
||||
out.stay = ihn.stay
|
||||
if (out.stay) this.execute
|
||||
if (out.stay) execute
|
||||
}
|
||||
|
||||
// Record the fact that this constraint is unsatisfied.
|
||||
@ -323,7 +323,7 @@ class BinaryConstraint is Constraint {
|
||||
}
|
||||
|
||||
inputsKnown(mark) {
|
||||
var i = this.input
|
||||
var i = input
|
||||
return i.mark == mark || i.stay || i.determinedBy == null
|
||||
}
|
||||
|
||||
@ -365,11 +365,11 @@ class ScaleConstraint is BinaryConstraint {
|
||||
|
||||
// Enforce this constraint. Assume that it is satisfied.
|
||||
execute {
|
||||
if (this.direction == FORWARD) {
|
||||
this.v2.value = this.v1.value * _scale.value + _offset.value;
|
||||
if (direction == FORWARD) {
|
||||
v2.value = v1.value * _scale.value + _offset.value;
|
||||
} else {
|
||||
// TODO: Is this the same semantics as ~/?
|
||||
this.v1.value = ((this.v2.value - _offset.value) / _scale.value).floor;
|
||||
v1.value = ((v2.value - _offset.value) / _scale.value).floor;
|
||||
}
|
||||
}
|
||||
|
||||
@ -377,11 +377,11 @@ class ScaleConstraint is BinaryConstraint {
|
||||
// 'stay', the value for the current output of this constraint. Assume
|
||||
// this constraint is satisfied.
|
||||
recalculate {
|
||||
var ihn = this.input
|
||||
var out = this.output
|
||||
out.walkStrength = Strength.weakest(this.strength, ihn.walkStrength)
|
||||
var ihn = input
|
||||
var out = output
|
||||
out.walkStrength = Strength.weakest(strength, ihn.walkStrength)
|
||||
out.stay = ihn.stay && _scale.stay && _offset.stay
|
||||
if (out.stay) this.execute
|
||||
if (out.stay) execute
|
||||
}
|
||||
}
|
||||
|
||||
@ -393,7 +393,7 @@ class EqualityConstraint is BinaryConstraint {
|
||||
|
||||
// Enforce this constraint. Assume that it is satisfied.
|
||||
execute {
|
||||
this.output.value = this.input.value
|
||||
output.value = input.value
|
||||
}
|
||||
}
|
||||
|
||||
@ -484,7 +484,7 @@ class Planner {
|
||||
// the algorithm to avoid getting into an infinite loop even if the
|
||||
// constraint graph has an inadvertent cycle.
|
||||
incrementalAdd(constraint) {
|
||||
var mark = this.newMark
|
||||
var mark = newMark
|
||||
var overridden = constraint.satisfy(mark)
|
||||
while (overridden != null) {
|
||||
overridden = overridden.satisfy(mark)
|
||||
@ -504,12 +504,12 @@ class Planner {
|
||||
var out = constraint.output
|
||||
constraint.markUnsatisfied
|
||||
constraint.removeFromGraph
|
||||
var unsatisfied = this.removePropagateFrom(out)
|
||||
var unsatisfied = removePropagateFrom(out)
|
||||
var strength = REQUIRED
|
||||
while (true) {
|
||||
for (i in 0...unsatisfied.count) {
|
||||
var u = unsatisfied[i]
|
||||
if (u.strength == strength) this.incrementalAdd(u)
|
||||
if (u.strength == strength) incrementalAdd(u)
|
||||
}
|
||||
strength = strength.nextWeaker
|
||||
if (strength == WEAKEST) break
|
||||
@ -540,7 +540,7 @@ class Planner {
|
||||
// any constraint.
|
||||
// Assume: [sources] are all satisfied.
|
||||
makePlan(sources) {
|
||||
var mark = this.newMark
|
||||
var mark = newMark
|
||||
var plan = new Plan
|
||||
var todo = sources
|
||||
while (todo.count > 0) {
|
||||
@ -548,7 +548,7 @@ class Planner {
|
||||
if (constraint.output.mark != mark && constraint.inputsKnown(mark)) {
|
||||
plan.addConstraint(constraint)
|
||||
constraint.output.mark = mark
|
||||
this.addConstraintsConsumingTo(constraint.output, todo)
|
||||
addConstraintsConsumingTo(constraint.output, todo)
|
||||
}
|
||||
}
|
||||
return plan
|
||||
@ -563,7 +563,7 @@ class Planner {
|
||||
// if not in plan already and eligible for inclusion.
|
||||
if (constraint.isInput && constraint.isSatisfied) sources.add(constraint)
|
||||
}
|
||||
return this.makePlan(sources)
|
||||
return makePlan(sources)
|
||||
}
|
||||
|
||||
// Recompute the walkabout strengths and stay flags of all variables
|
||||
@ -582,12 +582,12 @@ class Planner {
|
||||
while (todo.count > 0) {
|
||||
var d = todo.removeAt(-1)
|
||||
if (d.output.mark == mark) {
|
||||
this.incrementalRemove(constraint)
|
||||
incrementalRemove(constraint)
|
||||
return false
|
||||
}
|
||||
|
||||
d.recalculate
|
||||
this.addConstraintsConsumingTo(d.output, todo)
|
||||
addConstraintsConsumingTo(d.output, todo)
|
||||
}
|
||||
|
||||
return true
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
class List {
|
||||
toString {
|
||||
var result = "["
|
||||
for (i in 0...this.count) {
|
||||
for (i in 0...count) {
|
||||
if (i > 0) result = result + ", "
|
||||
result = result + this[i].toString
|
||||
}
|
||||
|
||||
@ -1434,13 +1434,12 @@ static void methodCall(Compiler* compiler, Code instruction,
|
||||
methodSymbol(compiler, name, length));
|
||||
}
|
||||
|
||||
// Compiles an expression that starts with ".name". That includes getters,
|
||||
// method calls with arguments, and setter calls.
|
||||
// Compiles a call whose name is the previously consumed token. This includes
|
||||
// getters, method calls with arguments, and setter calls.
|
||||
static void namedCall(Compiler* compiler, bool allowAssignment,
|
||||
Code instruction)
|
||||
{
|
||||
// Build the method name.
|
||||
consume(compiler, TOKEN_NAME, "Expect method name after '.'.");
|
||||
char name[MAX_METHOD_SIGNATURE];
|
||||
int length = copyName(compiler, name);
|
||||
|
||||
@ -1703,9 +1702,23 @@ static void name(Compiler* compiler, bool allowAssignment)
|
||||
Code loadInstruction;
|
||||
int index = resolveName(compiler, token->start, token->length,
|
||||
&loadInstruction);
|
||||
if (index == -1) error(compiler, "Undefined variable.");
|
||||
if (index != -1)
|
||||
{
|
||||
variable(compiler, allowAssignment, index, loadInstruction);
|
||||
return;
|
||||
}
|
||||
|
||||
variable(compiler, allowAssignment, index, loadInstruction);
|
||||
// Otherwise, if we are inside a class, it's a getter call with an implicit
|
||||
// receiver.
|
||||
ClassCompiler* classCompiler = getEnclosingClass(compiler);
|
||||
if (classCompiler == NULL)
|
||||
{
|
||||
error(compiler, "Undefined variable.");
|
||||
return;
|
||||
}
|
||||
|
||||
loadThis(compiler);
|
||||
namedCall(compiler, allowAssignment, CODE_CALL_0);
|
||||
}
|
||||
|
||||
static void null(Compiler* compiler, bool allowAssignment)
|
||||
@ -1781,6 +1794,7 @@ static void super_(Compiler* compiler, bool allowAssignment)
|
||||
if (match(compiler, TOKEN_DOT))
|
||||
{
|
||||
// Compile the superclass call.
|
||||
consume(compiler, TOKEN_NAME, "Expect method name after 'super.'.");
|
||||
namedCall(compiler, allowAssignment, CODE_SUPER_0);
|
||||
}
|
||||
else
|
||||
@ -1851,6 +1865,7 @@ static void subscript(Compiler* compiler, bool allowAssignment)
|
||||
|
||||
void call(Compiler* compiler, bool allowAssignment)
|
||||
{
|
||||
consume(compiler, TOKEN_NAME, "Expect method name after '.'.");
|
||||
namedCall(compiler, allowAssignment, CODE_CALL_0);
|
||||
}
|
||||
|
||||
|
||||
@ -44,7 +44,7 @@ static const char* libSource =
|
||||
"class List {\n"
|
||||
" toString {\n"
|
||||
" var result = \"[\"\n"
|
||||
" for (i in 0...this.count) {\n"
|
||||
" for (i in 0...count) {\n"
|
||||
" if (i > 0) result = result + \", \"\n"
|
||||
" result = result + this[i].toString\n"
|
||||
" }\n"
|
||||
|
||||
23
test/implicit_receiver/inherited_methods.wren
Normal file
23
test/implicit_receiver/inherited_methods.wren
Normal file
@ -0,0 +1,23 @@
|
||||
class Foo {
|
||||
getter {
|
||||
IO.print("getter")
|
||||
}
|
||||
|
||||
setter = value {
|
||||
IO.print("setter")
|
||||
}
|
||||
|
||||
method(a) {
|
||||
IO.print("method")
|
||||
}
|
||||
}
|
||||
|
||||
class Bar is Foo {
|
||||
test {
|
||||
getter // expect: getter
|
||||
setter = "value" // expect: setter
|
||||
method("arg") // expect: method
|
||||
}
|
||||
}
|
||||
|
||||
(new Bar).test
|
||||
23
test/implicit_receiver/instance_methods.wren
Normal file
23
test/implicit_receiver/instance_methods.wren
Normal file
@ -0,0 +1,23 @@
|
||||
class Foo {
|
||||
getter {
|
||||
IO.print("getter")
|
||||
}
|
||||
|
||||
setter = value {
|
||||
IO.print("setter")
|
||||
}
|
||||
|
||||
method(a) {
|
||||
IO.print("method")
|
||||
}
|
||||
|
||||
test {
|
||||
getter // expect: getter
|
||||
setter = "value" // expect: setter
|
||||
method("arg") // expect: method
|
||||
}
|
||||
}
|
||||
|
||||
(new Foo).test
|
||||
|
||||
// TODO: Need to decide how these interact with globals.
|
||||
17
test/implicit_receiver/locals_shadow_getter.wren
Normal file
17
test/implicit_receiver/locals_shadow_getter.wren
Normal file
@ -0,0 +1,17 @@
|
||||
class Foo {
|
||||
bar { return "getter" }
|
||||
|
||||
test {
|
||||
IO.print(bar) // expect: getter
|
||||
|
||||
{
|
||||
IO.print(bar) // expect: getter
|
||||
var bar = "local"
|
||||
IO.print(bar) // expect: local
|
||||
}
|
||||
|
||||
IO.print(bar) // expect: getter
|
||||
}
|
||||
}
|
||||
|
||||
(new Foo).test
|
||||
20
test/implicit_receiver/locals_shadow_setter.wren
Normal file
20
test/implicit_receiver/locals_shadow_setter.wren
Normal file
@ -0,0 +1,20 @@
|
||||
class Foo {
|
||||
bar = value {
|
||||
IO.print("setter")
|
||||
return value
|
||||
}
|
||||
|
||||
test {
|
||||
bar = "value" // expect: setter
|
||||
|
||||
{
|
||||
bar = "value" // expect: setter
|
||||
var bar = "local"
|
||||
bar = "value" // no expectation
|
||||
}
|
||||
|
||||
bar = "value" // expect: setter
|
||||
}
|
||||
}
|
||||
|
||||
(new Foo).test
|
||||
47
test/implicit_receiver/nested_class.wren
Normal file
47
test/implicit_receiver/nested_class.wren
Normal file
@ -0,0 +1,47 @@
|
||||
class Outer {
|
||||
getter {
|
||||
IO.print("outer getter")
|
||||
}
|
||||
|
||||
setter = value {
|
||||
IO.print("outer setter")
|
||||
}
|
||||
|
||||
method(a) {
|
||||
IO.print("outer method")
|
||||
}
|
||||
|
||||
test {
|
||||
getter // expect: outer getter
|
||||
setter = "value" // expect: outer setter
|
||||
method("arg") // expect: outer method
|
||||
|
||||
class Inner {
|
||||
getter {
|
||||
IO.print("inner getter")
|
||||
}
|
||||
|
||||
setter = value {
|
||||
IO.print("inner setter")
|
||||
}
|
||||
|
||||
method(a) {
|
||||
IO.print("inner method")
|
||||
}
|
||||
|
||||
test {
|
||||
getter // expect: inner getter
|
||||
setter = "value" // expect: inner setter
|
||||
method("arg") // expect: inner method
|
||||
}
|
||||
}
|
||||
|
||||
(new Inner).test
|
||||
|
||||
getter // expect: outer getter
|
||||
setter = "value" // expect: outer setter
|
||||
method("arg") // expect: outer method
|
||||
}
|
||||
}
|
||||
|
||||
(new Outer).test
|
||||
21
test/implicit_receiver/static_methods.wren
Normal file
21
test/implicit_receiver/static_methods.wren
Normal file
@ -0,0 +1,21 @@
|
||||
class Foo {
|
||||
static getter {
|
||||
IO.print("getter")
|
||||
}
|
||||
|
||||
static setter = value {
|
||||
IO.print("setter")
|
||||
}
|
||||
|
||||
static method(a) {
|
||||
IO.print("method")
|
||||
}
|
||||
|
||||
static test {
|
||||
getter // expect: getter
|
||||
setter = "value" // expect: setter
|
||||
method("arg") // expect: method
|
||||
}
|
||||
}
|
||||
|
||||
Foo.test
|
||||
@ -1,7 +1,7 @@
|
||||
class Foo {
|
||||
getClosure {
|
||||
return fn {
|
||||
return this.toString
|
||||
return toString
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
class Outer {
|
||||
method {
|
||||
IO.print(this.toString) // expect: Outer
|
||||
IO.print(this) // expect: Outer
|
||||
|
||||
fn {
|
||||
IO.print(this.toString) // expect: Outer
|
||||
IO.print(this) // expect: Outer
|
||||
|
||||
class Inner {
|
||||
method {
|
||||
IO.print(this.toString) // expect: Inner
|
||||
IO.print(this) // expect: Inner
|
||||
}
|
||||
toString { return "Inner" }
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ class Foo {
|
||||
return fn {
|
||||
return fn {
|
||||
return fn {
|
||||
return this.toString
|
||||
return toString
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user