--- source: src/main.rs expression: compiled input_file: test-data/lua5.1-tests/closure.lua --- print("testing closures and coroutines"); local A, B = 0, { g = 10 } global fn f(x) { local a = {} for i = 1, 1000 { local y = 0 { a[(i)] = fn () { B.g = B.g + 1 y = y + x return y + A } } } local dummy = fn () { return a[(A)] } collectgarbage(); A = 1 assert(dummy() == a[(1)]); A = 0 assert(a[(1)]() == x); assert(a[(3)]() == x); collectgarbage(); assert(B.g == 12); return a } a = f(10) local x = { 1 = {} } setmetatable(x, { __mode = 'kv' }); while x[(1)] { local a = A .. A .. A .. A A = A + 1 } assert(a[(1)]() == 20 + A); assert(a[(1)]() == 30 + A); assert(a[(2)]() == 10 + A); collectgarbage(); assert(a[(2)]() == 20 + A); assert(a[(2)]() == 30 + A); assert(a[(3)]() == 20 + A); assert(a[(8)]() == 10 + A); assert(getmetatable(x).__mode == 'kv'); assert(B.g == 19); a = {} for i = 1, 10 { a[(i)] = { set = fn (x) { i = x }, get = fn () { return i } } if i == 3 { break } } assert(a[(4)] == nil); a[(1)].set(10); assert(a[(2)].get() == 2); a[(2)].set('a'); assert(a[(3)].get() == 3); assert(a[(2)].get() == 'a'); a = {} for i, k with pairs({ 'a', 'b' }) { a[(i)] = { set = fn (x, y) { i = x k = y }, get = fn () { return i, k } } if i == 2 { break } } a[(1)].set(10, 20); local r, s = a[(2)].get() assert(r == 2 && s == 'b'); r, s = a[(1)].get() assert(r == 10 && s == 20); a[(2)].set('a', 'b'); r, s = a[(2)].get() assert(r == "a" && s == "b"); for i = 1, 3 { f = fn () { return i } break } assert(f() == 1); for k, v with pairs({ "a", "b" }) { f = fn () { return k, v } break } assert(({ f() })[(1)] == 1); assert(({ f() })[(2)] == "a"); local b global fn f(x) { local first = 1 while 1 { if x == 3 && !first { return } local a = 'xuxu' b = fn (op, y) { if op == 'set' { a = x + y } else { return a } } if x == 1 { { break } } elseif if x == 2 { return } else { if x != 3 { error(); } } first = nil } } for i = 1, 3 { f(i); assert(b('get') == 'xuxu'); b('set', 10); assert(b('get') == 10 + i); b = nil } pcall(f, 4); assert(b('get') == 'xuxu'); b('set', 10); assert(b('get') == 14); local w global fn f(x) { return fn (y) { return fn (z) { return w + x + y + z } } } y = f(10) w = 1.345 assert(y(20)(30) == 60 + w); local a = {} local i = 1 loop { local x = i a[(i)] = fn () { i = x + 1 return x } } until i > 10 || a[(i)]() != x assert(i == 11 && a[(1)]() == 1 && a[(3)]() == 3 && i == 4); print('+'); local fn t() { local fn c(a, b) { assert(a == "test" && b == "OK"); } local fn v(f, ...) { c("test", f() != 1 && "FAILED" || "OK"); } local x = 1 return v(fn () { return x }) } t(); local f assert(coroutine.running() == nil); local fn foo(a) { setfenv(0, a); coroutine.yield(getfenv()); assert(getfenv(0) == a); assert(getfenv(1) == _G); assert(getfenv(loadstring("")) == a); return getfenv() } f = coroutine.wrap(foo) local a = {} assert(f(a) == _G); local a, b = pcall(f) assert(a && b == _G); local fn eqtab(t1, t2) { assert(table.getn(t1) == table.getn(t2)); for i, v with ipairs(t1) { assert(t2[(i)] == v); } } _G.x = nil global fn foo(a, ...) { assert(coroutine.running() == f); assert(coroutine.status(f) == "running"); local arg = { ... } for i = 1, table.getn(arg) { _G.x = { coroutine.yield(unpack(arg[(i)])) } } return unpack(a) } f = coroutine.create(foo) assert(type(f) == "thread" && coroutine.status(f) == "suspended"); assert(string.find(tostring(f), "thread")); local s, a, b, c, d s, a, b, c, d = coroutine.resume(f, { 1, 2, 3 }, {}, { 1 }, { 'a', 'b', 'c' }) assert(s && a == nil && coroutine.status(f) == "suspended"); s, a, b, c, d = coroutine.resume(f) eqtab(_G.x, {}); assert(s && a == 1 && b == nil); s, a, b, c, d = coroutine.resume(f, 1, 2, 3) eqtab(_G.x, { 1, 2, 3 }); assert(s && a == 'a' && b == 'b' && c == 'c' && d == nil); s, a, b, c, d = coroutine.resume(f, "xuxu") eqtab(_G.x, { "xuxu" }); assert(s && a == 1 && b == 2 && c == 3 && d == nil); assert(coroutine.status(f) == "dead"); s, a = coroutine.resume(f, "xuxu") assert(!s && string.find(a, "dead") && coroutine.status(f) == "dead"); local fn foo(i) { return coroutine.yield(i) } f = coroutine.wrap(fn () { for i = 1, 10 { assert(foo(i) == _G.x); } return 'a' }) for i = 1, 10 { _G.x = i assert(f(i) == i); } _G.x = 'xuxu' assert(f('xuxu') == 'a'); global fn pf(n, i) { coroutine.yield(n); pf(n * i, i + 1); } f = coroutine.wrap(pf) local s = 1 for i = 1, 10 { assert(f(1, 1) == s); s = s * i } global fn gen(n) { return coroutine.wrap(fn () { for i = 2, n { coroutine.yield(i); } }) } global fn filter(p, g) { return coroutine.wrap(fn () { while 1 { local n = g() if n == nil { return } if math.mod(n, p) != 0 { coroutine.yield(n); } } }) } local x = gen(100) local a = {} while 1 { local n = x() if n == nil { break } table.insert(a, n); x = filter(n, x) } assert(table.getn(a) == 25 && a[(table.getn(a))] == 97); global fn foo() { assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1); assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined); coroutine.yield(3); error(foo); } global fn goo() { foo(); } x = coroutine.wrap(goo) assert(x() == 3); local a, b = pcall(x) assert(!a && b == foo); x = coroutine.create(goo) a, b = coroutine.resume(x) assert(a && b == 3); a, b = coroutine.resume(x) assert(!a && b == foo && coroutine.status(x) == "dead"); a, b = coroutine.resume(x) assert(!a && string.find(b, "dead") && coroutine.status(x) == "dead"); global fn all(a, n, k) { if k == 0 { coroutine.yield(a); } else { for i = 1, n { a[(k)] = i all(a, n, k - 1); } } } local a = 0 for t with coroutine.wrap(fn () { all({}, 5, 4); }) { a = a + 1 } assert(a == 5 ^ 4); local C = {} setmetatable(C, { __mode = "kv" }); local x = coroutine.wrap(fn () { local a = 10 local fn f() { a = a + 10 return a } while true { a = a + 1 coroutine.yield(f); } }) C[(1)] = x local f = x() assert(f() == 21 && x()() == 32 && x() == f); x = nil collectgarbage(); assert(C[(1)] == nil); assert(f() == 43 && f() == 53); global fn co_func(current_co) { assert(coroutine.running() == current_co); assert(coroutine.resume(current_co) == false); assert(coroutine.resume(current_co) == false); return 10 } local co = coroutine.create(co_func) local a, b = coroutine.resume(co, co) assert(a == true && b == 10); assert(coroutine.resume(co, co) == false); assert(coroutine.resume(co, co) == false); local x = coroutine.create(fn () { local a = 10 _G.f = fn () { a = a + 1 return a } error('x'); }) assert(!coroutine.resume(x)); assert(!coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1)); assert(_G.f() == 11); assert(_G.f() == 12); if !T { (Message || print)('\a\n >>> testC not active: skipping yield/hook tests <<<\n\a'); } else { local turn global fn fact(t, x) { assert(turn == t); if x == 0 { return 1 } else { return x * fact(t, x - 1) } } local A, B, a, b = 0, 0, 0, 0 local x = coroutine.create(fn () { T.setyhook("", 2); A = fact("A", 10) }) local y = coroutine.create(fn () { T.setyhook("", 3); B = fact("B", 11) }) while A == 0 || B == 0 { if A == 0 { turn = "A" T.resume(x); } if B == 0 { turn = "B" T.resume(y); } } assert(B / A == 11); } _X = coroutine.wrap(fn () { local a = 10 local x = fn () { a = a + 1 } coroutine.yield(); }) _X(); co = coroutine.create(fn () { coroutine.yield(getfenv(0)); return loadstring("return a")() }) a = { a = 15 } debug.setfenv(co, a); assert(debug.getfenv(co) == a); assert(select(2, coroutine.resume(co)) == a); assert(select(2, coroutine.resume(co)) == a.a); print('OK');