Get closures working!
In the process, I had to change the grammar. There is now a strong separation between statements and expressions. The code was just wrong before when it popped locals at the end of a block scope because there could be temporaries on the stack if the block was in expression position. This fixes that. Still need to implement closing over `this`.
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
var f = null
|
||||
var g = null
|
||||
|
||||
{
|
||||
var local = "local"
|
||||
f = fn {
|
||||
io.write(local)
|
||||
local = "after f"
|
||||
io.write(local)
|
||||
}
|
||||
|
||||
g = fn {
|
||||
io.write(local)
|
||||
local = "after g"
|
||||
io.write(local)
|
||||
}
|
||||
}
|
||||
|
||||
f.call
|
||||
// expect: local
|
||||
// expect: after f
|
||||
|
||||
g.call
|
||||
// expect: after f
|
||||
// expect: after g
|
||||
@@ -0,0 +1,9 @@
|
||||
var f = null
|
||||
|
||||
fn(param) {
|
||||
f = fn {
|
||||
io.write(param)
|
||||
}
|
||||
}.call("param")
|
||||
|
||||
f.call // expect: param
|
||||
@@ -0,0 +1,12 @@
|
||||
var f = null
|
||||
|
||||
class Foo {
|
||||
method(param) {
|
||||
f = fn {
|
||||
io.write(param)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Foo.new.method("param")
|
||||
f.call // expect: param
|
||||
@@ -0,0 +1,10 @@
|
||||
var f = null
|
||||
|
||||
{
|
||||
var local = "local"
|
||||
f = fn {
|
||||
io.write(local)
|
||||
}
|
||||
}
|
||||
|
||||
f.call // expect: local
|
||||
@@ -0,0 +1,14 @@
|
||||
var foo = null
|
||||
|
||||
{
|
||||
var local = "local"
|
||||
class Foo {
|
||||
method {
|
||||
io.write(local)
|
||||
}
|
||||
}
|
||||
|
||||
foo = Foo.new
|
||||
}
|
||||
|
||||
foo.method // expect: local
|
||||
@@ -0,0 +1,21 @@
|
||||
var f = null
|
||||
|
||||
fn {
|
||||
var a = "a"
|
||||
fn {
|
||||
var b = "b"
|
||||
fn {
|
||||
var c = "c"
|
||||
f = fn {
|
||||
io.write(a)
|
||||
io.write(b)
|
||||
io.write(c)
|
||||
}
|
||||
}.call
|
||||
}.call
|
||||
}.call
|
||||
|
||||
f.call
|
||||
// expect: a
|
||||
// expect: b
|
||||
// expect: c
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
var local = "local"
|
||||
fn {
|
||||
io.write(local) // expect: local
|
||||
}.call
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
var local = "local"
|
||||
class Foo {
|
||||
method {
|
||||
io.write(local)
|
||||
}
|
||||
}
|
||||
|
||||
Foo.new.method // expect: local
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
var f = null
|
||||
|
||||
{
|
||||
var a = "a"
|
||||
f = fn {
|
||||
io.write(a)
|
||||
io.write(a)
|
||||
}
|
||||
}
|
||||
|
||||
f.call
|
||||
// expect: a
|
||||
// expect: a
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
var f = null
|
||||
|
||||
{
|
||||
var a = "a"
|
||||
f = fn io.write(a)
|
||||
}
|
||||
|
||||
{
|
||||
// Since a is out of scope, the local slot will be reused by b. Make sure
|
||||
// that f still closes over a.
|
||||
var b = "b"
|
||||
f.call // expect: a
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(bob): Closing over this.
|
||||
// TODO(bob): Close over fn/method parameter.
|
||||
// TODO(bob): Maximum number of closed-over variables (directly and/or indirect).
|
||||
@@ -0,0 +1,6 @@
|
||||
var global = "global"
|
||||
// TODO(bob): Forward reference to global declared after use.
|
||||
|
||||
fn {
|
||||
io.write(global) // expect: global
|
||||
}.call
|
||||
@@ -0,0 +1,15 @@
|
||||
var global = "global"
|
||||
// TODO(bob): Forward reference to global declared after use.
|
||||
|
||||
class Foo {
|
||||
method {
|
||||
io.write(global)
|
||||
}
|
||||
|
||||
static classMethod {
|
||||
io.write(global)
|
||||
}
|
||||
}
|
||||
|
||||
Foo.new.method // expect: global
|
||||
Foo.classMethod // expect: global
|
||||
@@ -3,7 +3,7 @@ class Foo {
|
||||
this baz { io.write("this baz") }
|
||||
this bar(arg) { io.write("this bar " + arg) }
|
||||
|
||||
toString { "Foo" }
|
||||
toString { return "Foo" }
|
||||
}
|
||||
|
||||
// Different names.
|
||||
|
||||
@@ -4,7 +4,7 @@ class Foo {
|
||||
this new(a) { io.write(a) }
|
||||
this new(a, b) { io.write(a + b) }
|
||||
|
||||
toString { "Foo" }
|
||||
toString { return "Foo" }
|
||||
}
|
||||
|
||||
// Can overload by arity.
|
||||
|
||||
@@ -27,3 +27,4 @@ foo.write
|
||||
// TODO(bob): Inherited fields.
|
||||
// TODO(bob): Trying to get or set a field outside of a class.
|
||||
// TODO(bob): Fields in nested classes.
|
||||
// TODO(bob): Closing over fields.
|
||||
|
||||
+2
-25
@@ -6,27 +6,12 @@ if (false) io.write("bad")
|
||||
if (true) io.write("good") else io.write("bad") // expect: good
|
||||
if (false) io.write("bad") else io.write("good") // expect: good
|
||||
|
||||
// Allow statements for then branch.
|
||||
// Allow blocks for branches.
|
||||
if (true) { io.write("block") } // expect: block
|
||||
if (true) if (true) io.write("double") // expect: double
|
||||
|
||||
// Allow statements for else branch.
|
||||
if (false) null else { io.write("block") } // expect: block
|
||||
if (false) null else if (true) io.write("double") // expect: double
|
||||
|
||||
// Return the 'then' expression if the condition is true.
|
||||
var a = if (true) "good"
|
||||
io.write(a) // expect: good
|
||||
|
||||
// Return null if the condition is false and there is no else.
|
||||
var b = if (false) "bad"
|
||||
io.write(b) // expect: null
|
||||
|
||||
// Return the 'else' expression if the condition is false.
|
||||
var c = if (false) "bad" else "good"
|
||||
io.write(c) // expect: good
|
||||
|
||||
// Assignment in if condition.
|
||||
var a = false
|
||||
if (a = true) io.write(a) // expect: true
|
||||
|
||||
// Newline after "if".
|
||||
@@ -37,14 +22,6 @@ if
|
||||
if (false) io.write("bad") else
|
||||
io.write("good") // expect: good
|
||||
|
||||
// Definition in then arm.
|
||||
if (true) var a = io.write("ok") // expect: ok
|
||||
if (true) class Foo {} // no error
|
||||
|
||||
// Definition in else arm.
|
||||
if (false) null else var a = io.write("ok") // expect: ok
|
||||
if (true) null else class Foo {} // no error
|
||||
|
||||
// Only false is falsy.
|
||||
if (0) io.write(0) // expect: 0
|
||||
if (null) io.write(null) // expect: null
|
||||
|
||||
+17
-17
@@ -1,21 +1,21 @@
|
||||
class Foo {
|
||||
method { 0 }
|
||||
method(a) { a }
|
||||
method(a, b) { a + b }
|
||||
method(a, b, c) { a + b + c }
|
||||
method(a, b, c, d) { a + b + c + d }
|
||||
method(a, b, c, d, e) { a + b + c + d + e }
|
||||
method(a, b, c, d, e, f) { a + b + c + d + e + f }
|
||||
method(a, b, c, d, e, f, g) { a + b + c + d + e + f + g }
|
||||
method(a, b, c, d, e, f, g, h) { a + b + c + d + e + f + g + h }
|
||||
method(a, b, c, d, e, f, g, h, i) { a + b + c + d + e + f + g + h + i }
|
||||
method(a, b, c, d, e, f, g, h, i, j) { a + b + c + d + e + f + g + h + i + j }
|
||||
method(a, b, c, d, e, f, g, h, i, j, k) { a + b + c + d + e + f + g + h + i + j + k}
|
||||
method(a, b, c, d, e, f, g, h, i, j, k, l) { a + b + c + d + e + f + g + h + i + j + k + l}
|
||||
method(a, b, c, d, e, f, g, h, i, j, k, l, m) { a + b + c + d + e + f + g + h + i + j + k + l + m}
|
||||
method(a, b, c, d, e, f, g, h, i, j, k, l, m, n) { a + b + c + d + e + f + g + h + i + j + k + l + m + n}
|
||||
method(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) { a + b + c + d + e + f + g + h + i + j + k + l + m + n + o}
|
||||
method(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) { a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p}
|
||||
method { return 0 }
|
||||
method(a) { return a }
|
||||
method(a, b) { return a + b }
|
||||
method(a, b, c) { return a + b + c }
|
||||
method(a, b, c, d) { return a + b + c + d }
|
||||
method(a, b, c, d, e) { return a + b + c + d + e }
|
||||
method(a, b, c, d, e, f) { return a + b + c + d + e + f }
|
||||
method(a, b, c, d, e, f, g) { return a + b + c + d + e + f + g }
|
||||
method(a, b, c, d, e, f, g, h) { return a + b + c + d + e + f + g + h }
|
||||
method(a, b, c, d, e, f, g, h, i) { return a + b + c + d + e + f + g + h + i }
|
||||
method(a, b, c, d, e, f, g, h, i, j) { return a + b + c + d + e + f + g + h + i + j }
|
||||
method(a, b, c, d, e, f, g, h, i, j, k) { return a + b + c + d + e + f + g + h + i + j + k}
|
||||
method(a, b, c, d, e, f, g, h, i, j, k, l) { return a + b + c + d + e + f + g + h + i + j + k + l}
|
||||
method(a, b, c, d, e, f, g, h, i, j, k, l, m) { return a + b + c + d + e + f + g + h + i + j + k + l + m}
|
||||
method(a, b, c, d, e, f, g, h, i, j, k, l, m, n) { return a + b + c + d + e + f + g + h + i + j + k + l + m + n}
|
||||
method(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) { return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o}
|
||||
method(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) { return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p}
|
||||
}
|
||||
|
||||
var foo = Foo.new
|
||||
|
||||
+13
-13
@@ -1,18 +1,18 @@
|
||||
class Foo {
|
||||
+ other { "infix + " + other }
|
||||
- other { "infix - " + other }
|
||||
* other { "infix * " + other }
|
||||
/ other { "infix / " + other }
|
||||
% other { "infix % " + other }
|
||||
< other { "infix < " + other }
|
||||
> other { "infix > " + other }
|
||||
<= other { "infix <= " + other }
|
||||
>= other { "infix >= " + other }
|
||||
== other { "infix == " + other }
|
||||
!= other { "infix != " + other }
|
||||
+ other { return "infix + " + other }
|
||||
- other { return "infix - " + other }
|
||||
* other { return "infix * " + other }
|
||||
/ other { return "infix / " + other }
|
||||
% other { return "infix % " + other }
|
||||
< other { return "infix < " + other }
|
||||
> other { return "infix > " + other }
|
||||
<= other { return "infix <= " + other }
|
||||
>= other { return "infix >= " + other }
|
||||
== other { return "infix == " + other }
|
||||
!= other { return "infix != " + other }
|
||||
|
||||
! { "prefix !" }
|
||||
- { "prefix -" }
|
||||
! { return "prefix !" }
|
||||
- { return "prefix -" }
|
||||
}
|
||||
|
||||
var foo = Foo.new
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
class Foo {
|
||||
bar { "on instance" }
|
||||
static bar { "on metaclass" }
|
||||
bar { return "on instance" }
|
||||
static bar { return "on metaclass" }
|
||||
|
||||
bar(arg) { "on instance " + arg }
|
||||
static bar(arg) { "on metaclass " + arg }
|
||||
bar(arg) { return "on instance " + arg }
|
||||
static bar(arg) { return "on metaclass " + arg }
|
||||
}
|
||||
|
||||
io.write("on metaclass " + "arg") // expect: on metaclass arg
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class Foo {
|
||||
bar { this }
|
||||
baz { "baz" }
|
||||
bar { return this }
|
||||
baz { return "baz" }
|
||||
}
|
||||
|
||||
Foo.new.bar.baz
|
||||
io.write(Foo.new.bar.baz) // expect: baz
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
// Create a local scope for the 'then' expression.
|
||||
var a = "out"
|
||||
if (true) var a = "in"
|
||||
if (true) {
|
||||
var a = "in"
|
||||
}
|
||||
io.write(a) // expect: out
|
||||
|
||||
// Create a local scope for the 'else' expression.
|
||||
var b = "out"
|
||||
if (false) "dummy" else var b = "in"
|
||||
if (false) "dummy" else {
|
||||
var b = "in"
|
||||
}
|
||||
io.write(b) // expect: out
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Body has its own scope.
|
||||
var a = "outer"
|
||||
var i = 0
|
||||
while ((i = i + 1) <= 1) var a = "inner"
|
||||
while ((i = i + 1) <= 1) {
|
||||
var a = "inner"
|
||||
}
|
||||
io.write(a) // expect: outer
|
||||
|
||||
// TODO(bob): What about condition?
|
||||
|
||||
+2
-13
@@ -8,8 +8,8 @@ while (c < 3) io.write(c = c + 1)
|
||||
// Block body.
|
||||
var a = 0
|
||||
while (a < 3) {
|
||||
io.write(a)
|
||||
a = a + 1
|
||||
io.write(a)
|
||||
a = a + 1
|
||||
}
|
||||
// expect: 0
|
||||
// expect: 1
|
||||
@@ -22,14 +22,3 @@ while
|
||||
// expect: 1
|
||||
// expect: 2
|
||||
// expect: 3
|
||||
|
||||
// Result is null.
|
||||
var e = 0
|
||||
var f = while (e < 3) {
|
||||
e = e + 1
|
||||
}
|
||||
io.write(f) // expect: null
|
||||
|
||||
// Definition body.
|
||||
while (false) var a = "ok" // no error
|
||||
while (false) class Foo {} // no error
|
||||
|
||||
Reference in New Issue
Block a user