mirror of
https://github.com/lua/lua.git
synced 2025-01-28 06:03:00 +08:00
002beeebe7
Instead of using 'alimit' for keeping the size of the array and at the same time being a hint for '#t', a table now keeps these two values separate. The Table structure has a field 'asize' with the size of the array, while the length hint is kept in the array itself. That way, tables with no array part waste no space with that field. Moreover, the space for the hint may have zero cost for small arrays, if the array of tags plus the hint still fits in a single word.
942 lines
22 KiB
Lua
942 lines
22 KiB
Lua
-- $Id: testes/nextvar.lua $
|
|
-- See Copyright Notice in file all.lua
|
|
|
|
print('testing tables, next, and for')
|
|
|
|
local function checkerror (msg, f, ...)
|
|
local s, err = pcall(f, ...)
|
|
assert(not s and string.find(err, msg))
|
|
end
|
|
|
|
|
|
|
|
----------------------------------------------------------------
|
|
local function printTable (t)
|
|
local a, h = T.querytab(t)
|
|
print("array:")
|
|
for i = 1, a do
|
|
print("", T.querytab(t, i - 1))
|
|
end
|
|
print("hash:")
|
|
for i = 1, h do
|
|
print("", T.querytab(t, a + i - 1))
|
|
end
|
|
end
|
|
----------------------------------------------------------------
|
|
local function countentries (t)
|
|
local e = 0
|
|
for _ in pairs(t) do e = e + 1 end
|
|
return e
|
|
end
|
|
----------------------------------------------------------------
|
|
|
|
|
|
local function check (t, na, nh)
|
|
if not T then return end
|
|
local a, h = T.querytab(t)
|
|
if a ~= na or h ~= nh then
|
|
print(na, nh, a, h)
|
|
assert(nil)
|
|
end
|
|
end
|
|
|
|
|
|
local a = {}
|
|
|
|
-- make sure table has lots of space in hash part
|
|
for i=1,100 do a[i.."+"] = true end
|
|
for i=1,100 do a[i.."+"] = undef end
|
|
-- fill hash part with numeric indices testing size operator
|
|
for i=1,100 do
|
|
a[i] = true
|
|
assert(#a == i)
|
|
end
|
|
|
|
|
|
do -- rehash moving elements from array to hash
|
|
local a = {}
|
|
for i = 1, 100 do a[i] = i end
|
|
check(a, 128, 0)
|
|
|
|
for i = 5, 95 do a[i] = nil end
|
|
check(a, 128, 0)
|
|
|
|
a[129] = 1 -- force a re-hash
|
|
check(a, 4, 8) -- keys larger than 4 go to the hash part
|
|
|
|
for i = 1, 4 do assert(a[i] == i) end
|
|
for i = 5, 95 do assert(a[i] == nil) end
|
|
for i = 96, 100 do assert(a[i] == i) end
|
|
assert(a[129] == 1)
|
|
end
|
|
|
|
|
|
do -- growing hash part keeping array part
|
|
local a = table.create(1000)
|
|
check(a, 1000, 0)
|
|
a.x = 10
|
|
check(a, 1000, 1) -- array part keeps its elements
|
|
end
|
|
|
|
|
|
do -- "growing" length of a prebuilt table
|
|
local N = 100
|
|
local a = table.create(N)
|
|
for i = 1, N do
|
|
a[#a + 1] = true
|
|
assert(#a == i)
|
|
end
|
|
check(a, N, 0)
|
|
end
|
|
|
|
|
|
-- testing ipairs
|
|
local x = 0
|
|
for k,v in ipairs{10,20,30;x=12} do
|
|
x = x + 1
|
|
assert(k == x and v == x * 10)
|
|
end
|
|
|
|
for _ in ipairs{x=12, y=24} do assert(nil) end
|
|
|
|
-- test for 'false' x ipair
|
|
x = false
|
|
local i = 0
|
|
for k,v in ipairs{true,false,true,false} do
|
|
i = i + 1
|
|
x = not x
|
|
assert(x == v)
|
|
end
|
|
assert(i == 4)
|
|
|
|
-- iterator function is always the same
|
|
assert(type(ipairs{}) == 'function' and ipairs{} == ipairs{})
|
|
|
|
|
|
do -- overflow (must wrap-around)
|
|
local f = ipairs{}
|
|
local k, v = f({[math.mininteger] = 10}, math.maxinteger)
|
|
assert(k == math.mininteger and v == 10)
|
|
k, v = f({[math.mininteger] = 10}, k)
|
|
assert(k == nil)
|
|
end
|
|
|
|
|
|
do
|
|
-- alternate insertions and deletions in an almost full hash.
|
|
-- In versions pre-5.5, that causes constant rehashings and
|
|
-- takes a long time to complete.
|
|
local a = {}
|
|
for i = 1, 2^11 - 1 do
|
|
a[i .. ""] = true
|
|
end
|
|
|
|
for i = 1, 1e5 do
|
|
local key = i .. "."
|
|
a[key] = true
|
|
a[key] = nil
|
|
end
|
|
assert(countentries(a) == 2^11 - 1)
|
|
end
|
|
|
|
if not T then
|
|
(Message or print)
|
|
('\n >>> testC not active: skipping tests for table sizes <<<\n')
|
|
else --[
|
|
-- testing table sizes
|
|
|
|
|
|
-- minimum power of 2 (or zero) >= n
|
|
local function mp2 (n)
|
|
local mp = 2^math.ceil(math.log(n, 2))
|
|
assert((mp == 0 or mp/2 < n) and n <= mp)
|
|
return mp
|
|
end
|
|
|
|
|
|
-- testing C library sizes
|
|
do
|
|
local s = 0
|
|
for _ in pairs(math) do s = s + 1 end
|
|
check(math, 0, mp2(s))
|
|
end
|
|
|
|
|
|
-- testing constructor sizes
|
|
local sizes = {0, 1, 2, 3, 4, 5, 7, 8, 9, 15, 16, 17,
|
|
30, 31, 32, 33, 34, 254, 255, 256, 257, 500, 1001}
|
|
|
|
for _, sa in ipairs(sizes) do -- 'sa' is size of the array part
|
|
local arr = {"return {"}
|
|
for i = 1, sa do arr[1 + i] = "1," end -- build array part
|
|
for _, sh in ipairs(sizes) do -- 'sh' is size of the hash part
|
|
for j = 1, sh do -- build hash part
|
|
arr[1 + sa + j] = string.format('k%x=%d,', j, j)
|
|
end
|
|
arr[1 + sa + sh + 1] = "}"
|
|
local prog = table.concat(arr)
|
|
local f = assert(load(prog))
|
|
collectgarbage("stop")
|
|
f() -- call once to ensure stack space
|
|
-- make sure table is not resized after being created
|
|
if sa == 0 or sh == 0 then
|
|
T.alloccount(2); -- header + array or hash part
|
|
else
|
|
T.alloccount(3); -- header + array part + hash part
|
|
end
|
|
local t = f()
|
|
T.alloccount();
|
|
collectgarbage("restart")
|
|
assert(#t == sa)
|
|
check(t, sa, mp2(sh))
|
|
end
|
|
end
|
|
|
|
|
|
-- tests with unknown number of elements
|
|
local a = {}
|
|
for i=1,sizes[#sizes] do a[i] = i end -- build auxiliary table
|
|
for k in ipairs(sizes) do
|
|
local t = {table.unpack(a,1,k)}
|
|
assert(#t == k)
|
|
check(t, k, 0)
|
|
t = {1,2,3,table.unpack(a,1,k)}
|
|
check(t, k+3, 0)
|
|
assert(#t == k + 3)
|
|
end
|
|
|
|
|
|
-- testing tables dynamically built
|
|
local lim = 130
|
|
local a = {}; a[2] = 1; check(a, 2, 0)
|
|
a = {}; a[0] = 1; check(a, 0, 1);
|
|
a[2] = 1; check(a, 2, 1)
|
|
a = {}; a[0] = 1; a[1] = 1; check(a, 1, 1)
|
|
a = {}
|
|
for i = 1,lim do
|
|
a[i] = 1
|
|
assert(#a == i)
|
|
check(a, mp2(i), 0)
|
|
end
|
|
|
|
a = {}
|
|
for i = 1,lim do
|
|
a['a'..i] = 1
|
|
assert(#a == 0)
|
|
check(a, 0, mp2(i))
|
|
end
|
|
|
|
|
|
-- insert and delete elements until a rehash occurr. Caller must ensure
|
|
-- that a rehash will change the shape of the table. Must repeat because
|
|
-- the insertion may collide with the deleted element, and then there is
|
|
-- no rehash.
|
|
local function forcerehash (t)
|
|
local na, nh = T.querytab(t)
|
|
local i = 10000
|
|
repeat
|
|
i = i + 1
|
|
t[i] = true
|
|
t[i] = undef
|
|
local nna, nnh = T.querytab(t)
|
|
until nna ~= na or nnh ~= nh
|
|
end
|
|
|
|
|
|
do
|
|
local a = {}
|
|
for i=1,16 do a[i] = i end
|
|
check(a, 16, 0)
|
|
for i=1,11 do a[i] = undef end
|
|
check(a, 16, 0)
|
|
a[30] = true -- force a rehash
|
|
a[30] = undef
|
|
check(a, 0, 8) -- 5 elements in the hash part: [12]-[16]
|
|
a[10] = 1
|
|
forcerehash(a)
|
|
check(a, 16, 1)
|
|
for i=1,14 do a[i] = true; a[i] = undef end
|
|
check(a, 16, 1) -- no rehash...
|
|
a[31] = true; a[32] = true -- force a rehash
|
|
check(a, 0, 4) -- [15], [16], [31], [32]
|
|
end
|
|
|
|
-- reverse filling
|
|
do
|
|
local N = 2^10
|
|
local a = {}
|
|
for i = N, 1, -1 do a[i] = i end -- fill in reverse
|
|
check(a, mp2(N), 0)
|
|
end
|
|
|
|
|
|
do -- "almost sparse" arrays
|
|
-- create table with holes in 1/3 of its entries; all its
|
|
-- elements are always in the array part
|
|
local a = {}
|
|
for i = 1, 257 do
|
|
if i % 3 ~= 1 then
|
|
a[i] = true
|
|
check(a, mp2(i), 0)
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
do
|
|
-- alternate insertions and deletions should give some extra
|
|
-- space for the hash part. Otherwise, a mix of insertions/deletions
|
|
-- could cause too many rehashes. (See the other test for "alternate
|
|
-- insertions and deletions" in this file.)
|
|
local a = {}
|
|
for i = 1, 256 do
|
|
a[i .. ""] = true
|
|
end
|
|
check(a, 0, 256) -- hash part is full
|
|
a["256"] = nil -- delete a key
|
|
forcerehash(a)
|
|
-- table has only 255 elements, but it got some extra space;
|
|
-- otherwise, almost each delete-insert would rehash the table again.
|
|
assert(countentries(a) == 255)
|
|
check(a, 0, 512)
|
|
end
|
|
|
|
|
|
-- size tests for vararg
|
|
lim = 35
|
|
local function foo (n, ...)
|
|
local arg = {...}
|
|
check(arg, n, 0)
|
|
assert(select('#', ...) == n)
|
|
arg[n+1] = true
|
|
check(arg, mp2(n+1), 0)
|
|
arg.x = true
|
|
check(arg, mp2(n+1), 1)
|
|
end
|
|
local a = {}
|
|
for i=1,lim do a[i] = true; foo(i, table.unpack(a)) end
|
|
|
|
end --]
|
|
|
|
|
|
-- test size operation on tables with nils
|
|
assert(#{} == 0)
|
|
assert(#{nil} == 0)
|
|
assert(#{nil, nil} == 0)
|
|
assert(#{nil, nil, nil} == 0)
|
|
assert(#{nil, nil, nil, nil} == 0)
|
|
assert(#{1, 2, 3, nil, nil} == 3)
|
|
print'+'
|
|
|
|
|
|
do
|
|
local s1, s2 = math.randomseed()
|
|
print(string.format(
|
|
"testing length for some random tables (seeds 0X%x:%x)", s1, s2))
|
|
local N = 130
|
|
for i = 1, 1e3 do -- create that many random tables
|
|
local a = table.create(math.random(N)) -- initiate with random size
|
|
for j = 1, math.random(N) do -- add random number of random entries
|
|
a[math.random(N)] = true
|
|
end
|
|
assert(#a == 0 or a[#a] and not a[#a + 1])
|
|
end
|
|
end
|
|
|
|
local nofind = {}
|
|
|
|
a,b,c = 1,2,3
|
|
a,b,c = nil
|
|
|
|
|
|
-- next uses always the same iteraction function
|
|
assert(next{} == next{})
|
|
|
|
local function find (name)
|
|
local n,v
|
|
while 1 do
|
|
n,v = next(_G, n)
|
|
if not n then return nofind end
|
|
assert(_G[n] ~= undef)
|
|
if n == name then return v end
|
|
end
|
|
end
|
|
|
|
local function find1 (name)
|
|
for n,v in pairs(_G) do
|
|
if n==name then return v end
|
|
end
|
|
return nil -- not found
|
|
end
|
|
|
|
|
|
assert(print==find("print") and print == find1("print"))
|
|
assert(_G["print"]==find("print"))
|
|
assert(assert==find1("assert"))
|
|
assert(nofind==find("return"))
|
|
assert(not find1("return"))
|
|
_G["ret" .. "urn"] = undef
|
|
assert(nofind==find("return"))
|
|
_G["xxx"] = 1
|
|
assert(xxx==find("xxx"))
|
|
|
|
-- invalid key to 'next'
|
|
checkerror("invalid key", next, {10,20}, 3)
|
|
|
|
-- both 'pairs' and 'ipairs' need an argument
|
|
checkerror("bad argument", pairs)
|
|
checkerror("bad argument", ipairs)
|
|
|
|
print('+')
|
|
|
|
a = {}
|
|
for i=0,10000 do
|
|
if math.fmod(i,10) ~= 0 then
|
|
a['x'..i] = i
|
|
end
|
|
end
|
|
|
|
n = {n=0}
|
|
for i,v in pairs(a) do
|
|
n.n = n.n+1
|
|
assert(i and v and a[i] == v)
|
|
end
|
|
assert(n.n == 9000)
|
|
a = nil
|
|
|
|
do -- clear global table
|
|
local a = {}
|
|
for n,v in pairs(_G) do a[n]=v end
|
|
for n,v in pairs(a) do
|
|
if not package.loaded[n] and type(v) ~= "function" and
|
|
not string.find(n, "^[%u_]") then
|
|
_G[n] = undef
|
|
end
|
|
collectgarbage()
|
|
end
|
|
end
|
|
|
|
|
|
--
|
|
|
|
local function checknext (a)
|
|
local b = {}
|
|
do local k,v = next(a); while k do b[k] = v; k,v = next(a,k) end end
|
|
for k,v in pairs(b) do assert(a[k] == v) end
|
|
for k,v in pairs(a) do assert(b[k] == v) end
|
|
end
|
|
|
|
checknext{1,x=1,y=2,z=3}
|
|
checknext{1,2,x=1,y=2,z=3}
|
|
checknext{1,2,3,x=1,y=2,z=3}
|
|
checknext{1,2,3,4,x=1,y=2,z=3}
|
|
checknext{1,2,3,4,5,x=1,y=2,z=3}
|
|
|
|
assert(#{} == 0)
|
|
assert(#{[-1] = 2} == 0)
|
|
for i=0,40 do
|
|
local a = {}
|
|
for j=1,i do a[j]=j end
|
|
assert(#a == i)
|
|
end
|
|
|
|
-- 'maxn' is now deprecated, but it is easily defined in Lua
|
|
function table.maxn (t)
|
|
local max = 0
|
|
for k in pairs(t) do
|
|
max = (type(k) == 'number') and math.max(max, k) or max
|
|
end
|
|
return max
|
|
end
|
|
|
|
assert(table.maxn{} == 0)
|
|
assert(table.maxn{["1000"] = true} == 0)
|
|
assert(table.maxn{["1000"] = true, [24.5] = 3} == 24.5)
|
|
assert(table.maxn{[1000] = true} == 1000)
|
|
assert(table.maxn{[10] = true, [100*math.pi] = print} == 100*math.pi)
|
|
|
|
table.maxn = nil
|
|
|
|
-- int overflow
|
|
a = {}
|
|
for i=0,50 do a[2^i] = true end
|
|
assert(a[#a])
|
|
|
|
print('+')
|
|
|
|
|
|
do -- testing 'next' with all kinds of keys
|
|
local a = {
|
|
[1] = 1, -- integer
|
|
[1.1] = 2, -- float
|
|
['x'] = 3, -- short string
|
|
[string.rep('x', 1000)] = 4, -- long string
|
|
[print] = 5, -- C function
|
|
[checkerror] = 6, -- Lua function
|
|
[coroutine.running()] = 7, -- thread
|
|
[true] = 8, -- boolean
|
|
[io.stdin] = 9, -- userdata
|
|
[{}] = 10, -- table
|
|
}
|
|
local b = {}; for i = 1, 10 do b[i] = true end
|
|
for k, v in pairs(a) do
|
|
assert(b[v]); b[v] = undef
|
|
end
|
|
assert(next(b) == nil) -- 'b' now is empty
|
|
end
|
|
|
|
|
|
-- erasing values
|
|
local t = {[{1}] = 1, [{2}] = 2, [string.rep("x ", 4)] = 3,
|
|
[100.3] = 4, [4] = 5}
|
|
|
|
local n = 0
|
|
for k, v in pairs( t ) do
|
|
n = n+1
|
|
assert(t[k] == v)
|
|
t[k] = undef
|
|
collectgarbage()
|
|
assert(t[k] == undef)
|
|
end
|
|
assert(n == 5)
|
|
|
|
|
|
do
|
|
print("testing next x GC of deleted keys")
|
|
-- bug in 5.4.1
|
|
local co = coroutine.wrap(function (t)
|
|
for k, v in pairs(t) do
|
|
local k1 = next(t) -- all previous keys were deleted
|
|
assert(k == k1) -- current key is the first in the table
|
|
t[k] = nil
|
|
local expected = (type(k) == "table" and k[1] or
|
|
type(k) == "function" and k() or
|
|
string.sub(k, 1, 1))
|
|
assert(expected == v)
|
|
coroutine.yield(v)
|
|
end
|
|
end)
|
|
local t = {}
|
|
t[{1}] = 1 -- add several unanchored, collectable keys
|
|
t[{2}] = 2
|
|
t[string.rep("a", 50)] = "a" -- long string
|
|
t[string.rep("b", 50)] = "b"
|
|
t[{3}] = 3
|
|
t[string.rep("c", 10)] = "c" -- short string
|
|
t[function () return 10 end] = 10
|
|
local count = 7
|
|
while co(t) do
|
|
collectgarbage("collect") -- collect dead keys
|
|
count = count - 1
|
|
end
|
|
assert(count == 0 and next(t) == nil) -- traversed the whole table
|
|
end
|
|
|
|
|
|
local function test (a)
|
|
assert(not pcall(table.insert, a, 2, 20));
|
|
table.insert(a, 10); table.insert(a, 2, 20);
|
|
table.insert(a, 1, -1); table.insert(a, 40);
|
|
table.insert(a, #a+1, 50)
|
|
table.insert(a, 2, -2)
|
|
assert(a[2] ~= undef)
|
|
assert(a["2"] == undef)
|
|
assert(not pcall(table.insert, a, 0, 20));
|
|
assert(not pcall(table.insert, a, #a + 2, 20));
|
|
assert(table.remove(a,1) == -1)
|
|
assert(table.remove(a,1) == -2)
|
|
assert(table.remove(a,1) == 10)
|
|
assert(table.remove(a,1) == 20)
|
|
assert(table.remove(a,1) == 40)
|
|
assert(table.remove(a,1) == 50)
|
|
assert(table.remove(a,1) == nil)
|
|
assert(table.remove(a) == nil)
|
|
assert(table.remove(a, #a) == nil)
|
|
end
|
|
|
|
a = {n=0, [-7] = "ban"}
|
|
test(a)
|
|
assert(a.n == 0 and a[-7] == "ban")
|
|
|
|
a = {[-7] = "ban"};
|
|
test(a)
|
|
assert(a.n == nil and #a == 0 and a[-7] == "ban")
|
|
|
|
a = {[-1] = "ban"}
|
|
test(a)
|
|
assert(#a == 0 and table.remove(a) == nil and a[-1] == "ban")
|
|
|
|
a = {[0] = "ban"}
|
|
assert(#a == 0 and table.remove(a) == "ban" and a[0] == undef)
|
|
|
|
table.insert(a, 1, 10); table.insert(a, 1, 20); table.insert(a, 1, -1)
|
|
assert(table.remove(a) == 10)
|
|
assert(table.remove(a) == 20)
|
|
assert(table.remove(a) == -1)
|
|
assert(table.remove(a) == nil)
|
|
|
|
a = {'c', 'd'}
|
|
table.insert(a, 3, 'a')
|
|
table.insert(a, 'b')
|
|
assert(table.remove(a, 1) == 'c')
|
|
assert(table.remove(a, 1) == 'd')
|
|
assert(table.remove(a, 1) == 'a')
|
|
assert(table.remove(a, 1) == 'b')
|
|
assert(table.remove(a, 1) == nil)
|
|
assert(#a == 0 and a.n == nil)
|
|
|
|
a = {10,20,30,40}
|
|
assert(table.remove(a, #a + 1) == nil)
|
|
assert(not pcall(table.remove, a, 0))
|
|
assert(a[#a] == 40)
|
|
assert(table.remove(a, #a) == 40)
|
|
assert(a[#a] == 30)
|
|
assert(table.remove(a, 2) == 20)
|
|
assert(a[#a] == 30 and #a == 2)
|
|
|
|
do -- testing table library with metamethods
|
|
local function test (proxy, t)
|
|
for i = 1, 10 do
|
|
table.insert(proxy, 1, i)
|
|
end
|
|
assert(#proxy == 10 and #t == 10 and proxy[1] ~= undef)
|
|
for i = 1, 10 do
|
|
assert(t[i] == 11 - i)
|
|
end
|
|
table.sort(proxy)
|
|
for i = 1, 10 do
|
|
assert(t[i] == i and proxy[i] == i)
|
|
end
|
|
assert(table.concat(proxy, ",") == "1,2,3,4,5,6,7,8,9,10")
|
|
for i = 1, 8 do
|
|
assert(table.remove(proxy, 1) == i)
|
|
end
|
|
assert(#proxy == 2 and #t == 2)
|
|
local a, b, c = table.unpack(proxy)
|
|
assert(a == 9 and b == 10 and c == nil)
|
|
end
|
|
|
|
-- all virtual
|
|
local t = {}
|
|
local proxy = setmetatable({}, {
|
|
__len = function () return #t end,
|
|
__index = t,
|
|
__newindex = t,
|
|
})
|
|
test(proxy, t)
|
|
|
|
-- only __newindex
|
|
local count = 0
|
|
t = setmetatable({}, {
|
|
__newindex = function (t,k,v) count = count + 1; rawset(t,k,v) end})
|
|
test(t, t)
|
|
assert(count == 10) -- after first 10, all other sets are not new
|
|
|
|
-- no __newindex
|
|
t = setmetatable({}, {
|
|
__index = function (_,k) return k + 1 end,
|
|
__len = function (_) return 5 end})
|
|
assert(table.concat(t, ";") == "2;3;4;5;6")
|
|
|
|
end
|
|
|
|
|
|
do -- testing overflow in table.insert (must wrap-around)
|
|
|
|
local t = setmetatable({},
|
|
{__len = function () return math.maxinteger end})
|
|
table.insert(t, 20)
|
|
local k, v = next(t)
|
|
assert(k == math.mininteger and v == 20)
|
|
end
|
|
|
|
if not T then
|
|
(Message or print)
|
|
('\n >>> testC not active: skipping tests for table library on non-tables <<<\n')
|
|
else --[
|
|
local debug = require'debug'
|
|
local tab = {10, 20, 30}
|
|
local mt = {}
|
|
local u = T.newuserdata(0)
|
|
checkerror("table expected", table.insert, u, 40)
|
|
checkerror("table expected", table.remove, u)
|
|
debug.setmetatable(u, mt)
|
|
checkerror("table expected", table.insert, u, 40)
|
|
checkerror("table expected", table.remove, u)
|
|
mt.__index = tab
|
|
checkerror("table expected", table.insert, u, 40)
|
|
checkerror("table expected", table.remove, u)
|
|
mt.__newindex = tab
|
|
checkerror("table expected", table.insert, u, 40)
|
|
checkerror("table expected", table.remove, u)
|
|
mt.__len = function () return #tab end
|
|
table.insert(u, 40)
|
|
assert(#u == 4 and #tab == 4 and u[4] == 40 and tab[4] == 40)
|
|
assert(table.remove(u) == 40)
|
|
table.insert(u, 1, 50)
|
|
assert(#u == 4 and #tab == 4 and u[4] == 30 and tab[1] == 50)
|
|
|
|
mt.__newindex = nil
|
|
mt.__len = nil
|
|
local tab2 = {}
|
|
local u2 = T.newuserdata(0)
|
|
debug.setmetatable(u2, {__newindex = function (_, k, v) tab2[k] = v end})
|
|
table.move(u, 1, 4, 1, u2)
|
|
assert(#tab2 == 4 and tab2[1] == tab[1] and tab2[4] == tab[4])
|
|
|
|
end -- ]
|
|
|
|
print('+')
|
|
|
|
a = {}
|
|
for i=1,1000 do
|
|
a[i] = i; a[i - 1] = undef
|
|
end
|
|
assert(next(a,nil) == 1000 and next(a,1000) == nil)
|
|
|
|
assert(next({}) == nil)
|
|
assert(next({}, nil) == nil)
|
|
|
|
for a,b in pairs{} do error"not here" end
|
|
for i=1,0 do error'not here' end
|
|
for i=0,1,-1 do error'not here' end
|
|
a = nil; for i=1,1 do assert(not a); a=1 end; assert(a)
|
|
a = nil; for i=1,1,-1 do assert(not a); a=1 end; assert(a)
|
|
|
|
do
|
|
print("testing floats in numeric for")
|
|
local a
|
|
-- integer count
|
|
a = 0; for i=1, 1, 1 do a=a+1 end; assert(a==1)
|
|
a = 0; for i=10000, 1e4, -1 do a=a+1 end; assert(a==1)
|
|
a = 0; for i=1, 0.99999, 1 do a=a+1 end; assert(a==0)
|
|
a = 0; for i=9999, 1e4, -1 do a=a+1 end; assert(a==0)
|
|
a = 0; for i=1, 0.99999, -1 do a=a+1 end; assert(a==1)
|
|
|
|
-- float count
|
|
a = 0; for i=0, 0.999999999, 0.1 do a=a+1 end; assert(a==10)
|
|
a = 0; for i=1.0, 1, 1 do a=a+1 end; assert(a==1)
|
|
a = 0; for i=-1.5, -1.5, 1 do a=a+1 end; assert(a==1)
|
|
a = 0; for i=1e6, 1e6, -1 do a=a+1 end; assert(a==1)
|
|
a = 0; for i=1.0, 0.99999, 1 do a=a+1 end; assert(a==0)
|
|
a = 0; for i=99999, 1e5, -1.0 do a=a+1 end; assert(a==0)
|
|
a = 0; for i=1.0, 0.99999, -1 do a=a+1 end; assert(a==1)
|
|
end
|
|
|
|
do -- attempt to change the control variable
|
|
local st, msg = load "for i = 1, 10 do i = 10 end"
|
|
assert(not st and string.find(msg, "assign to const variable 'i'"))
|
|
|
|
local st, msg = load "for v, k in pairs{} do v = 10 end"
|
|
assert(not st and string.find(msg, "assign to const variable 'v'"))
|
|
end
|
|
|
|
-- conversion
|
|
a = 0; for i="10","1","-2" do a=a+1 end; assert(a==5)
|
|
|
|
do -- checking types
|
|
local c
|
|
local function checkfloat (i)
|
|
assert(math.type(i) == "float")
|
|
c = c + 1
|
|
end
|
|
|
|
c = 0; for i = 1.0, 10 do checkfloat(i) end
|
|
assert(c == 10)
|
|
|
|
c = 0; for i = -1, -10, -1.0 do checkfloat(i) end
|
|
assert(c == 10)
|
|
|
|
local function checkint (i)
|
|
assert(math.type(i) == "integer")
|
|
c = c + 1
|
|
end
|
|
|
|
local m = math.maxinteger
|
|
c = 0; for i = m, m - 10, -1 do checkint(i) end
|
|
assert(c == 11)
|
|
|
|
c = 0; for i = 1, 10.9 do checkint(i) end
|
|
assert(c == 10)
|
|
|
|
c = 0; for i = 10, 0.001, -1 do checkint(i) end
|
|
assert(c == 10)
|
|
|
|
c = 0; for i = 1, "10.8" do checkint(i) end
|
|
assert(c == 10)
|
|
|
|
c = 0; for i = 9, "3.4", -1 do checkint(i) end
|
|
assert(c == 6)
|
|
|
|
c = 0; for i = 0, " -3.4 ", -1 do checkint(i) end
|
|
assert(c == 4)
|
|
|
|
c = 0; for i = 100, "96.3", -2 do checkint(i) end
|
|
assert(c == 2)
|
|
|
|
c = 0; for i = 1, math.huge do if i > 10 then break end; checkint(i) end
|
|
assert(c == 10)
|
|
|
|
c = 0; for i = -1, -math.huge, -1 do
|
|
if i < -10 then break end; checkint(i)
|
|
end
|
|
assert(c == 10)
|
|
|
|
|
|
for i = math.mininteger, -10e100 do assert(false) end
|
|
for i = math.maxinteger, 10e100, -1 do assert(false) end
|
|
|
|
end
|
|
|
|
|
|
do -- testing other strange cases for numeric 'for'
|
|
|
|
local function checkfor (from, to, step, t)
|
|
local c = 0
|
|
for i = from, to, step do
|
|
c = c + 1
|
|
assert(i == t[c])
|
|
end
|
|
assert(c == #t)
|
|
end
|
|
|
|
local maxi = math.maxinteger
|
|
local mini = math.mininteger
|
|
|
|
checkfor(mini, maxi, maxi, {mini, -1, maxi - 1})
|
|
|
|
checkfor(mini, math.huge, maxi, {mini, -1, maxi - 1})
|
|
|
|
checkfor(maxi, mini, mini, {maxi, -1})
|
|
|
|
checkfor(maxi, mini, -maxi, {maxi, 0, -maxi})
|
|
|
|
checkfor(maxi, -math.huge, mini, {maxi, -1})
|
|
|
|
checkfor(maxi, mini, 1, {})
|
|
checkfor(mini, maxi, -1, {})
|
|
|
|
checkfor(maxi - 6, maxi, 3, {maxi - 6, maxi - 3, maxi})
|
|
checkfor(mini + 4, mini, -2, {mini + 4, mini + 2, mini})
|
|
|
|
local step = maxi // 10
|
|
local c = mini
|
|
for i = mini, maxi, step do
|
|
assert(i == c)
|
|
c = c + step
|
|
end
|
|
|
|
c = maxi
|
|
for i = maxi, mini, -step do
|
|
assert(i == c)
|
|
c = c - step
|
|
end
|
|
|
|
checkfor(maxi, maxi, maxi, {maxi})
|
|
checkfor(maxi, maxi, mini, {maxi})
|
|
checkfor(mini, mini, maxi, {mini})
|
|
checkfor(mini, mini, mini, {mini})
|
|
end
|
|
|
|
|
|
checkerror("'for' step is zero", function ()
|
|
for i = 1, 10, 0 do end
|
|
end)
|
|
|
|
checkerror("'for' step is zero", function ()
|
|
for i = 1, -10, 0 do end
|
|
end)
|
|
|
|
checkerror("'for' step is zero", function ()
|
|
for i = 1.0, -10, 0.0 do end
|
|
end)
|
|
|
|
collectgarbage()
|
|
|
|
|
|
-- testing generic 'for'
|
|
|
|
local function f (n, p)
|
|
local t = {}; for i=1,p do t[i] = i*10 end
|
|
return function (_, n, ...)
|
|
assert(select("#", ...) == 0) -- no extra arguments
|
|
if n > 0 then
|
|
n = n-1
|
|
return n, table.unpack(t)
|
|
end
|
|
end, nil, n
|
|
end
|
|
|
|
local x = 0
|
|
for n,a,b,c,d in f(5,3) do
|
|
x = x+1
|
|
assert(a == 10 and b == 20 and c == 30 and d == nil)
|
|
end
|
|
assert(x == 5)
|
|
|
|
|
|
|
|
-- testing __pairs and __ipairs metamethod
|
|
a = {}
|
|
do
|
|
local x,y,z = pairs(a)
|
|
assert(type(x) == 'function' and y == a and z == nil)
|
|
end
|
|
|
|
local function foo (e,i)
|
|
assert(e == a)
|
|
if i <= 10 then return i+1, i+2 end
|
|
end
|
|
|
|
local function foo1 (e,i)
|
|
i = i + 1
|
|
assert(e == a)
|
|
if i <= e.n then return i,a[i] end
|
|
end
|
|
|
|
setmetatable(a, {__pairs = function (x) return foo, x, 0 end})
|
|
|
|
local i = 0
|
|
for k,v in pairs(a) do
|
|
i = i + 1
|
|
assert(k == i and v == k+1)
|
|
end
|
|
|
|
a.n = 5
|
|
a[3] = 30
|
|
|
|
-- testing ipairs with metamethods
|
|
a = {n=10}
|
|
setmetatable(a, { __index = function (t,k)
|
|
if k <= t.n then return k * 10 end
|
|
end})
|
|
i = 0
|
|
for k,v in ipairs(a) do
|
|
i = i + 1
|
|
assert(k == i and v == i * 10)
|
|
end
|
|
assert(i == a.n)
|
|
|
|
|
|
-- testing yield inside __pairs
|
|
do
|
|
local t = setmetatable({10, 20, 30}, {__pairs = function (t)
|
|
local inc = coroutine.yield()
|
|
return function (t, i)
|
|
if i > 1 then return i - inc, t[i - inc] else return nil end
|
|
end, t, #t + 1
|
|
end})
|
|
|
|
local res = {}
|
|
local co = coroutine.wrap(function ()
|
|
for i,p in pairs(t) do res[#res + 1] = p end
|
|
end)
|
|
|
|
co() -- start coroutine
|
|
co(1) -- continue after yield
|
|
assert(res[1] == 30 and res[2] == 20 and res[3] == 10 and #res == 3)
|
|
|
|
end
|
|
|
|
print"OK"
|