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:
Bob Nystrom
2013-12-04 15:43:50 +00:00
parent 7e7a508096
commit 5b05c477c6
32 changed files with 1063 additions and 416 deletions
+25
View File
@@ -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
+21
View File
@@ -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
}
+10
View File
@@ -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
+19
View File
@@ -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).
+6
View File
@@ -0,0 +1,6 @@
var global = "global"
// TODO(bob): Forward reference to global declared after use.
fn {
io.write(global) // expect: global
}.call
+15
View File
@@ -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