feat: implement closures with upvalue support and statement/expression separation
Add closure objects that wrap functions with captured upvalues, enabling first-class closures that close over local variables from enclosing scopes. Introduce new bytecode instructions (CODE_CLOSURE, CODE_LOAD_UPVALUE, CODE_STORE_UPVALUE, CODE_CLOSE_UPVALUE, CODE_RETURN) and a CompilerUpvalue tracking system with a MAX_UPVALUES limit of 256. Refactor the grammar to strictly separate statements from expressions, fixing a bug where block expressions incorrectly popped locals while temporaries remained on the stack. Rename internal functions (wrenCallFunction, wrenDebugDumpInstruction, wrenDebugDumpStack) and add Upvalue allocation, closure creation, and debug dump support for the new instructions.
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