Make "this" the implicit receiver!

This commit is contained in:
Bob Nystrom
2014-02-12 17:33:35 -08:00
parent 92971d1cfa
commit 756fe0d920
13 changed files with 217 additions and 51 deletions

View File

@ -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

View File

@ -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
}

View File

@ -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);
}

View File

@ -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"

View 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

View 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.

View 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

View 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

View 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

View 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

View File

@ -1,7 +1,7 @@
class Foo {
getClosure {
return fn {
return this.toString
return toString
}
}

View File

@ -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" }
}

View File

@ -3,7 +3,7 @@ class Foo {
return fn {
return fn {
return fn {
return this.toString
return toString
}
}
}