Skip to content

Instantly share code, notes, and snippets.

@MikePall
Last active July 8, 2026 09:00
Show Gist options
  • Select an option

  • Save MikePall/a8372d92cb2e6380cf56e78f69ee70a4 to your computer and use it in GitHub Desktop.

Select an option

Save MikePall/a8372d92cb2e6380cf56e78f69ee70a4 to your computer and use it in GitHub Desktop.
LuaJIT 3.0 Syntax Extensions Test Cases
local function expect_err(code, expect)
local f, err = loadstring(code, "=")
if f ~= nil then
error("unexpected success", 2)
elseif not err:match(expect) then
error('expected "'..expect..'", but got "'..err:gsub("^:1: ", "")..'"', 2)
end
end
local N = setmetatable({}, { __index = function(_, x) return x end })
-- Bit operators: syntax.
do
assert(~ N[0b1010] == 0xfffffff5|0)
assert(N[0xffffffff] | 0 == -1)
assert(N[0b1010] & N[0b1100] == 0b1000)
assert(N[0b1010] | N[0b1100] == 0b1110)
assert(N[0b1010] ~ N[0b1100] == 0b0110)
assert(N[0x55aa55aa] << N[4] == 0x5aa55aa0)
assert(N[0x55aa55aa] >> N[4] == 0x055aa55a)
assert(N[0x55aa55aa] ~>> N[4] == 0x055aa55a)
assert(N[0xaa55aa55] >> N[4] == 0x0aa55aa5)
assert(N[0xaa55aa55] ~>> N[4] == 0xfaa55aa5|0)
end
-- Bit operators: constant folding.
do
assert(~ 0b1010 == 0xfffffff5|0)
assert(0xffffffff | 0 == -1)
assert(0b1010 & 0b1100 == 0b1000)
assert(0b1010 | 0b1100 == 0b1110)
assert(0b1010 ~ 0b1100 == 0b0110)
assert(0x55aa55aa << 4 == 0x5aa55aa0)
assert(0x55aa55aa >> 4 == 0x055aa55a)
assert(0x55aa55aa ~>> 4 == 0x055aa55a)
assert(0xaa55aa55 >> 4 == 0x0aa55aa5)
assert(0xaa55aa55 ~>> 4 == 0xfaa55aa5|0)
end
-- Bit operators: precedence.
do
assert(~ N[0b1010] & N[0b1100] == 0b0100)
assert((~ N[0b1010]) & N[0b1100] == 0b0100)
assert(~ (N[0b1010] & N[0b1100]) == -9)
assert(~ N[0b1010] | N[0b1100] == -3)
assert((~ N[0b1010]) | N[0b1100] == -3)
assert(~ (N[0b1010] | N[0b1100]) == -15)
assert(~ N[0b1010] ~ N[0b1100] == -7)
assert((~ N[0b1010]) ~ N[0b1100] == -7)
assert(~ (N[0b1010] ~ N[0b1100]) == -7)
assert(~ N[0x55aa55aa] << N[4] == 0xa55aa550|0)
assert((~ N[0x55aa55aa]) << N[4] == 0xa55aa550|0)
assert(~ (N[0x55aa55aa] << N[4]) == 0xa55aa55f|0)
assert(~ N[0x55aa55aa] >> N[4] == 0x0aa55aa5)
assert((~ N[0x55aa55aa]) >> N[4] == 0x0aa55aa5)
assert(~ (N[0x55aa55aa] >> N[4]) == 0xfaa55aa5|0)
assert(~ N[0x55aa55aa] ~>> N[4] == 0xfaa55aa5|0)
assert((~ N[0x55aa55aa]) ~>> N[4] == 0xfaa55aa5|0)
assert(~ (N[0x55aa55aa] ~>> N[4]) == 0xfaa55aa5|0)
assert(N[0b1010] & N[0b1100] ~ N[0b1101] == 0b0101)
assert((N[0b1010] & N[0b1100]) ~ N[0b1101] == 0b0101)
assert(N[0b1010] & (N[0b1100] ~ N[0b1101]) == 0b0000)
assert(N[0b1010] | N[0b1100] & N[0b1101] == 0b1110)
assert((N[0b1010] | N[0b1100]) & N[0b1101] == 0b1100)
assert(N[0b1010] | (N[0b1100] & N[0b1101]) == 0b1110)
assert(N[0b1010] | N[0b1100] ~ N[0b1101] == 0b1011)
assert((N[0b1010] | N[0b1100]) ~ N[0b1101] == 0b0011)
assert(N[0b1010] | (N[0b1100] ~ N[0b1101]) == 0b1011)
assert(N[0x55aa55aa] >> N[3] & N[1] == 0x00000001)
assert((N[0x55aa55aa] >> N[3]) & N[1] == 0x00000001)
assert(N[0x55aa55aa] >> (N[3] & N[1]) == 0x2ad52ad5)
assert(N[0b1010] | N[0b1100] + N[0b0101] == 0b11011)
assert((N[0b1010] | N[0b1100]) + N[0b0101] == 0b10011)
assert(N[0b1010] | (N[0b1100] + N[0b0101]) == 0b11011)
end
-- Bit operators: associativity.
do
assert(N[0x55aa55aa] << N[4] << N[2] == 0x6a956a80)
assert((N[0x55aa55aa] << N[4]) << N[2] == 0x6a956a80)
assert(N[0x55aa55aa] << (N[4] << N[2]) == 0x55aa0000)
assert(N[0x55aa55aa] >> N[4] >> N[2] == 0x0156a956)
assert((N[0x55aa55aa] >> N[4]) >> N[2] == 0x0156a956)
assert(N[0x55aa55aa] >> (N[4] >> N[2]) == 0x2ad52ad5)
assert(N[0xaa55aa55] ~>> N[4] ~>> N[2] == 0xfea956a9|0)
assert((N[0xaa55aa55] ~>> N[4]) ~>> N[2] == 0xfea956a9|0)
assert(N[0xaa55aa55] ~>> (N[4] ~>> N[2]) == 0xd52ad52a|0)
end
-- Bit operators: shift width masking.
do
assert(N[0x55aa55aa] << N[32] == 0x55aa55aa)
assert(N[0x55aa55aa] >> N[32] == 0x55aa55aa)
assert(N[0x55aa55aa] ~>> N[32] == 0x55aa55aa)
assert(N[0xaa55aa55] ~>> N[32] == 0xaa55aa55|0)
assert(N[0x55aa55aa] << N[36] == 0x5aa55aa0)
assert(N[0x55aa55aa] >> N[36] == 0x055aa55a)
assert(N[0x55aa55aa] ~>> N[36] == 0x055aa55a)
assert(N[0xaa55aa55] ~>> N[36] == 0xfaa55aa5|0)
assert(N[0xaa55aa55] << N[-1] == 0x80000000|0)
assert(N[0xaa55aa55] >> N[-1] == 0x00000001)
assert(N[0xaa55aa55] ~>> N[-1] == 0xffffffff|0)
end
-- Bit operators: metamethods.
if jit and jit.version_num >= 30000 then -- NOT backported to v2.1.
local mt = {}
for _,name in ipairs({"bnot", "band", "bor", "bxor", "shl", "shr", "sar"}) do
mt["__"..name] = function() return name end
end
local a = setmetatable({}, mt)
local b = setmetatable({}, mt)
assert(~ a == "bnot")
assert(a & 1 == "band" and 1 & b == "band" and a & b == "band")
assert(a | 1 == "bor" and 1 | b == "bor" and a | b == "bor")
assert(a ~ 1 == "bxor" and 1 ~ b == "bxor" and a ~ b == "bxor")
assert(a << 1 == "shl" and 1 << b == "shl" and a << b == "shl")
assert(a >> 1 == "shr" and 1 >> b == "shr" and a >> b == "shr")
assert(a ~>> 1 == "sar" and 1 ~>> b == "sar" and a ~>> b == "sar")
end
-- Bit operators: syntax errors.
do
expect_err("~", "near.*~")
expect_err("~ a", "near.*~")
expect_err("~ a = 1", "near.*~")
expect_err("~ a += 1", "near.*~")
expect_err("~ a()", "near.*~")
expect_err("&", "near.*&")
expect_err("a &", "near.*&")
expect_err("a & b", "near.*&")
expect_err("a & b = 1", "near.*&")
expect_err("a & b += 1", "near.*&")
expect_err("a() & b", "near.*&")
expect_err("a & b()", "near.*&")
expect_err("|", "near.*|")
expect_err("a |", "near.*|")
expect_err("a | b", "near.*|")
expect_err("a | b = 1", "near.*|")
expect_err("a | b += 1", "near.*|")
expect_err("a() | b", "near.*|")
expect_err("a | b()", "near.*|")
expect_err("~", "near.*~")
expect_err("a ~", "near.*~")
expect_err("a ~ b", "near.*~")
expect_err("a ~ b = 1", "near.*~")
expect_err("a ~ b += 1", "near.*~")
expect_err("a() ~ b", "near.*~")
expect_err("a ~ b()", "near.*~")
expect_err("<<", "near.*<<")
expect_err("a <<", "near.*<<")
expect_err("a << b", "near.*<<")
expect_err("a << b = 1", "near.*<<")
expect_err("a << b += 1", "near.*<<")
expect_err("a() << b", "near.*<<")
expect_err("a << b()", "near.*<<")
expect_err(">>", "near.*>>")
expect_err("a >>", "near.*>>")
expect_err("a >> b", "near.*>>")
expect_err("a >> b = 1", "near.*>>")
expect_err("a >> b += 1", "near.*>>")
expect_err("a() >> b", "near.*>>")
expect_err("a >> b()", "near.*>>")
expect_err("~>>", "near.*~>>")
expect_err("a ~>>", "near.*~>>")
expect_err("a ~>> b", "near.*~>>")
expect_err("a ~>> b = 1", "near.*~>>")
expect_err("a ~>> b += 1", "near.*~>>")
expect_err("a() ~>> b", "near.*~>>")
expect_err("a ~>> b()", "near.*~>>")
end
-- Adapted from bit operations library tests.
local xbit = {
bnot = function(a) return ~a end,
band = function(a, b) return a & b end,
bor = function(a, b) return a | b end,
bxor = function(a, b) return a ~ b end,
lshift = function(a, b) return a << b end,
rshift = function(a, b) return a >> b end,
arshift = function(a, b) return a ~>> b end,
}
local vb = {
0, 1, -1, 2, -2, 0x12345678, 0x87654321,
0x33333333, 0x77777777, 0x55aa55aa, 0xaa55aa55,
0x7fffffff, 0x80000000, 0xffffffff
}
local function cksum(name, s, r)
local z = 0
for i=1,#s do z = (z + string.byte(s, i)*i) % 2147483629 end
if z ~= r then
error("bit."..name.." test failed (got "..z..", expected "..r..")", 0)
end
end
local function check_unop(name, r)
local f = xbit[name]
local s = ""
if pcall(f) or pcall(f, "z") or pcall(f, true) then
error("bit."..name.." fails to detect argument errors", 0)
end
for _,x in ipairs(vb) do s = s..","..tostring(f(x)) end
cksum(name, s, r)
end
local function check_binop(name, r)
local f = xbit[name]
local s = ""
if pcall(f) or pcall(f, "z") or pcall(f, true) then
error("bit."..name.." fails to detect argument errors", 0)
end
for _,x in ipairs(vb) do
for _,y in ipairs(vb) do s = s..","..tostring(f(x, y)) end
end
cksum(name, s, r)
end
local function check_binop_range(name, r, yb, ye)
local f = xbit[name]
local s = ""
if pcall(f) or pcall(f, "z") or pcall(f, true) or pcall(f, 1, true) then
error("bit."..name.." fails to detect argument errors", 0)
end
for _,x in ipairs(vb) do
for y=yb,ye do s = s..","..tostring(f(x, y)) end
end
cksum(name, s, r)
end
local function check_shift(name, r)
check_binop_range(name, r, 0, 31)
end
-- Bit operators: minimal sanity checks.
do
assert(0x7fffffff == 2147483647, "broken hex literals")
assert(0xffffffff == -1 or 0xffffffff == 2^32-1, "broken hex literals")
assert(tostring(-1) == "-1", "broken tostring()")
assert(tostring(0xffffffff) == "-1" or tostring(0xffffffff) == "4294967295", "broken tostring()")
end
-- Bit operators: basic argument processing.
do
assert(xbit.bxor(1,2) == 3)
end
-- Bit operators: computed value.
do
check_unop("bnot", 287870)
check_binop("band", 41206764)
check_binop("bor", 51253663)
check_binop("bxor", 79322427)
check_shift("lshift", 325260344)
check_shift("rshift", 139061800)
check_shift("arshift", 111364720)
end
local function expect_err(code, expect)
local f, err = loadstring(code, "=")
if f ~= nil then
error("unexpected success", 2)
elseif not err:match(expect) then
error('expected "'..expect..'", but got "'..err:gsub("^:1: ", "")..'"', 2)
end
end
local ok, ffi = pcall(require, "ffi")
if ok then
NULL = ffi.new("void *")
else
NULL = nil
end
NIL = nil
FALSE = false
TRUE = true
NUMBER1 = 1
NUMBER2 = 2
collectgarbage() -- Prevent (simple) global store-to-load forwarding.
-- nil-coalescing operator: syntax.
do
assert((NIL ?? NIL) == nil)
assert((NIL ?? NUMBER1) == 1)
assert((NULL ?? NUMBER1) == 1)
assert((FALSE ?? NUMBER1) == false)
assert((TRUE ?? NUMBER1) == true)
assert((NUMBER1 ?? NUMBER2) == 1)
assert((NIL == FALSE ?? NUMBER1) == false)
assert((FALSE == FALSE ?? NUMBER1) == true)
assert((NUMBER1 < NUMBER2 ?? NUMBER1) == true)
assert((NUMBER1 > NUMBER2 ?? NUMBER1) == false)
assert((NIL ?? NUMBER1 + NUMBER2) == 3)
assert((NULL ?? NUMBER1 + NUMBER2) == 3)
assert((FALSE ?? NUMBER1 + NUMBER2) == false)
assert((TRUE ?? NUMBER1 + NUMBER2) == true)
assert((NIL ?? NIL ?? NIL) == nil)
assert((NIL ?? NIL ?? NUMBER1) == 1)
assert((NIL ?? NULL ?? NUMBER1) == 1)
assert((NULL ?? NULL ?? NUMBER1) == 1)
assert((NIL ?? FALSE ?? NIL) == false)
assert((NIL ?? FALSE ?? NUMBER2) == false)
assert((NIL ?? NUMBER1 ?? NIL) == 1)
assert((NIL ?? NUMBER1 ?? NUMBER2) == 1)
end
-- nil-coalescing operator: constant folding.
do
assert((nil ?? nil) == nil)
assert((nil ?? 1) == 1)
assert((false ?? 1) == false)
assert((true ?? 1) == true)
assert((1 ?? 2) == 1)
assert((nil == false ?? 1) == false)
assert((false == false ?? 1) == true)
assert((1 < 2 ?? 1) == true)
assert((1 > 2 ?? 1) == false)
assert((nil ?? 1 + 2) == 3)
assert((false ?? 1 + 2) == false)
assert((true ?? 1 + 2) == true)
assert((nil ?? nil ?? nil) == nil)
assert((nil ?? nil ?? 1) == 1)
assert((nil ?? false ?? nil) == false)
assert((nil ?? false ?? 1) == false)
assert((nil ?? 1 ?? nil) == 1)
assert((nil ?? 1 ?? 2) == 1)
end
-- nil-coalescing operator: mixed with not/and/or.
do
assert((not (NIL ?? NUMBER1)) == false)
assert((not (FALSE ?? NUMBER1)) == true)
assert((not (TRUE ?? NUMBER1)) == false)
assert(((not NIL) ?? NUMBER1) == true)
assert(((not FALSE) ?? NUMBER1) == true)
assert(((not TRUE) ?? NUMBER1) == false)
assert(((NIL ?? NUMBER1) and NUMBER2) == 2)
assert((NIL ?? (NUMBER1 and NUMBER2)) == 2)
assert(((NUMBER1 and NUMBER2) ?? NIL) == 2)
assert((NUMBER1 and (NIL ?? NUMBER2)) == 2)
assert(((NIL ?? NUMBER1) or NUMBER2) == 1)
assert((NIL ?? (NUMBER1 or NUMBER2)) == 1)
assert((NIL or NUMBER1 ?? NUMBER2) == 1)
assert(((NIL or NUMBER1) ?? NUMBER2) == 1)
assert((NIL or (NUMBER1 ?? NUMBER2)) == 1)
assert(((NUMBER1 or NUMBER2) ?? NIL) == 1)
assert((NUMBER1 or (NIL ?? NUMBER2)) == 1)
end
-- nil-coalescing operator: precedence.
do
assert((NIL ?? NUMBER1 + NUMBER2) == 3)
assert((NUMBER1 + NUMBER2 ?? 4 + 8) == 3)
assert((FALSE ?? NIL ? 2 : 3) == 3)
assert(((FALSE ?? NIL) ? 2 : 3) == 3)
assert((FALSE ?? (NIL ? 2 : 3)) == false)
assert((not NIL ?? NUMBER1) == true)
assert((NIL ?? NUMBER1 and NUMBER2) == 2)
assert((NIL ?? NUMBER1 or NUMBER2) == 1)
end
-- nil-coalescing operator: short-circuiting.
do
local function bad() error("call short-circuiting failed", 2) end
assert((NUMBER1 ?? bad()) == 1)
assert((NUMBER1 ?? bad() ?? bad()) == 1)
local t = setmetatable({}, {
__index = function() error("access short-circuiting failed", 2) end,
})
assert((NUMBER1 ?? t.x) == 1)
assert((NUMBER1 ?? t.x ?? t.y) == 1)
local ok, err = pcall(function() return NIL ?? bad() ?? t.x end)
assert(err:match("call"))
end
-- nil-coalescing operator: syntax errors.
do
expect_err("??", "near.*%?%?")
expect_err("a ??", "near.*%?%?")
expect_err("a ?? b", "near.*%?%?")
expect_err("a ?? b = 1", "near.*%?%?")
expect_err("a ?? b += 1", "near.*%?%?")
expect_err("a() ?? b", "near.*%?%?")
expect_err("a ?? b()", "near.*%?%?")
end
NULL = nil
FALSE = nil
TRUE = nil
NUMBER1 = nil
NUMBER2 = nil
local function expect_err(code, expect)
local f, err = loadstring(code, "=")
if f ~= nil then
error("unexpected success", 2)
elseif not err:match(expect) then
error('expected "'..expect..'", but got "'..err:gsub("^:1: ", "")..'"', 2)
end
end
local ok, ffi = pcall(require, "ffi")
if ok then
NULL = ffi.new("void *")
else
NULL = "FALLBACK-ANY-NON-NIL-OR-FALSE-OBJECT" -- Only here!
end
NIL = nil
FALSE = false
TRUE = true
NUMBER1 = 1
NUMBER2 = 2
collectgarbage() -- Prevent (simple) global store-to-load forwarding.
-- Conditional operator: syntax.
do
assert((NIL ? 1 : 2) == 2)
assert((FALSE ? 1 : 2) == 2)
assert((TRUE ? 1 : 2) == 1)
assert((NULL ? 1 : 2) == 1)
assert((9 ? 1 : 2) == 1)
assert(("" ? 1 : 2) == 1)
assert((NIL?1:2) == 2)
assert((FALSE?1:2) == 2)
assert((TRUE?1:2) == 1)
assert((NIL ? NIL : FALSE) == false)
assert((NIL ? FALSE : NIL) == nil)
assert((FALSE ? NIL : FALSE) == false)
assert((FALSE ? FALSE : NIL) == nil)
assert((TRUE ? NIL : FALSE) == nil)
assert((TRUE ? FALSE : NIL) == false)
assert((NIL == FALSE ? 9 : 11) == 11)
assert((FALSE == FALSE ? 9 : 11) == 9)
assert((NUMBER1 < NUMBER2 ? 9 : 11) == 9)
assert((NUMBER1 > NUMBER2 ? 9 : 11) == 11)
assert((NIL ? NUMBER1 + NUMBER2 : NUMBER1 - NUMBER2) == -1)
assert((FALSE ? NUMBER1 + NUMBER2 : NUMBER1 - NUMBER2) == -1)
assert((TRUE ? NUMBER1 + NUMBER2 : NUMBER1 - NUMBER2) == 3)
assert((NUMBER1 < NUMBER2 ? NUMBER1 + NUMBER2 : NUMBER1 - NUMBER2) == 3)
assert((NUMBER1 > NUMBER2 ? NUMBER1 + NUMBER2 : NUMBER1 - NUMBER2) == -1)
end
-- Conditional operator: constant folding.
do
assert((nil ? 1 : 2) == 2)
assert((false ? 1 : 2) == 2)
assert((true ? 1 : 2) == 1)
assert((9 ? 1 : 2) == 1)
assert(("" ? 1 : 2) == 1)
assert((nil ? nil : false) == false)
assert((nil ? false : nil) == nil)
assert((false ? nil : false) == false)
assert((false ? false : nil) == nil)
assert((true ? nil : false) == nil)
assert((true ? false : nil) == false)
assert((nil == false ? 9 : 11) == 11)
assert((false == false ? 9 : 11) == 9)
assert((1 < 2 ? 9 : 11) == 9)
assert((1 > 2 ? 9 : 11) == 11)
assert((nil ? 1 + 2 : 1 - 2) == -1)
assert((false ? 1 + 2 : 1 - 2) == -1)
assert((true ? 1 + 2 : 1 - 2) == 3)
assert((1 < 2 ? 1 + 2 : 1 - 2) == 3)
assert((1 > 2 ? 1 + 2 : 1 - 2) == -1)
end
-- Conditional operator: mixed with not/and/or.
do
assert((not (NIL ? NUMBER1 : FALSE)) == true)
assert((not (FALSE ? NUMBER1 : FALSE)) == true)
assert((not (TRUE ? NUMBER1 : FALSE)) == false)
assert(((not NIL) ? NUMBER1 : FALSE) == 1)
assert(((not FALSE) ? NUMBER1 : FALSE) == 1)
assert(((not TRUE) ? NUMBER1 : FALSE) == false)
assert(((NIL ? NUMBER1 : FALSE) and NUMBER2) == false)
assert((NIL ? (NUMBER1 and NUMBER2) : FALSE) == false)
assert(((NUMBER1 and NUMBER2) ? NIL : FALSE) == nil)
assert((NUMBER1 and (NIL ? NUMBER2 : FALSE)) == false)
assert(((NIL ? NUMBER1 : FALSE) or NUMBER2) == 2)
assert((NIL ? (NUMBER1 or NUMBER2) : FALSE) == false)
assert((NIL or NUMBER1 ? NUMBER2 : FALSE) == 2)
assert(((NIL or NUMBER1) ? NUMBER2 : FALSE) == 2)
assert((NIL or (NUMBER1 ? NUMBER2 : FALSE)) == 2)
assert(((NUMBER1 or NUMBER2) ? NIL : FALSE) == nil)
assert((NUMBER1 or (NIL ? NUMBER2 : FALSE)) == 1)
end
-- Conditional operator: associativity.
do
assert((FALSE ? NUMBER1 : FALSE ? NUMBER2 : NUMBER1 + NUMBER2) == 3)
assert((TRUE ? NUMBER1 : FALSE ? NUMBER2 : NUMBER1 + NUMBER2) == 1)
assert((FALSE ? NUMBER1 : TRUE ? NUMBER2 : NUMBER1 + NUMBER2) == 2)
assert((TRUE ? NUMBER1 : TRUE ? NUMBER2 : NUMBER1 + NUMBER2) == 1)
assert((FALSE ? NUMBER1 : (FALSE ? NUMBER2 : NUMBER1 + NUMBER2)) == 3)
assert((TRUE ? NUMBER1 : (FALSE ? NUMBER2 : NUMBER1 + NUMBER2)) == 1)
assert((FALSE ? NUMBER1 : (TRUE ? NUMBER2 : NUMBER1 + NUMBER2)) == 2)
assert((TRUE ? NUMBER1 : (TRUE ? NUMBER2 : NUMBER1 + NUMBER2)) == 1)
assert(((FALSE ? NUMBER1 : FALSE) ? NUMBER2 : NUMBER1 + NUMBER2) == 3)
assert(((TRUE ? NUMBER1 : FALSE) ? NUMBER2 : NUMBER1 + NUMBER2) == 2)
assert(((FALSE ? NUMBER1 : TRUE) ? NUMBER2 : NUMBER1 + NUMBER2) == 2)
assert(((TRUE ? NUMBER1 : TRUE) ? NUMBER2 : NUMBER1 + NUMBER2) == 2)
end
-- Conditional operator: short-circuiting.
do
local function bad() error("call short-circuiting failed", 2) end
assert((FALSE ? bad() : NUMBER1) == 1)
assert((TRUE ? NUMBER1 : bad()) == 1)
local t = setmetatable({}, {
__index = function() error("access short-circuiting failed", 2) end,
})
assert((FALSE ? t.x : NUMBER1) == 1)
assert((TRUE ? NUMBER1 : t.x) == 1)
end
-- Conditional operator: colon parsing.
do
local a = {
x = 1,
b = function(obj1)
return {
x = 2,
c = function(obj2)
return obj1.x + obj2.x + 10
end,
}
end,
}
local b = function()
return {
x = 3,
c = function(obj3)
return obj3.x + 20
end,
}
end
local c = function() return 15 end
assert((TRUE ? a : b():c() + 30).x == 1)
assert((FALSE ? a : b():c() + 30) == 53)
assert((TRUE ? a : 0 + b():c() + 30).x == 1)
assert((FALSE ? a : 0 + b():c() + 30) == 53)
assert((TRUE ? (a:b()) : c() + 30).x == 2)
assert((FALSE ? (a:b()) : c() + 30) == 45)
end
-- Conditional operator: forced single return value.
do
local function f() return 1, 2 end
local function g() return 3, 4 end
local a, b = FALSE ? f() : 9
assert(a == 9 and b == nil)
local a, b = TRUE ? f() : 9
assert(a == 1 and b == nil)
local a, b = FALSE ? 9 : f()
assert(a == 1 and b == nil)
local a, b = TRUE ? 9 : f()
assert(a == 9 and b == nil)
local a, b = FALSE ? f() : g()
assert(a == 3 and b == nil)
local a, b = TRUE ? f() : g()
assert(a == 1 and b == nil)
end
-- Conditional operator: syntax errors.
do
expect_err("?:", "near.*%?")
expect_err("a?:", "near.*%?")
expect_err("a?b:", "near.*%?")
expect_err("a?:c", "near.*%?")
expect_err("a?b:c", "near.*%?")
expect_err("a?b:c = 1", "near.*%?")
expect_err("(a?b:c) = 1", "near.*=")
expect_err("local x = a ?", "near.*eof")
expect_err("local x = a ? :", "near.*:")
expect_err("local x = a ? b :", "near.*eof")
expect_err("local x = a ? : c", "near.*:")
expect_err("local x = a ? obj:method() : c", "near.*eof")
end
NULL = nil
FALSE = nil
TRUE = nil
NUMBER1 = nil
NUMBER2 = nil
NIL = nil
FALSE = false
TRUE = true
NUMBER1 = 1
NUMBER2 = 2
collectgarbage() -- Prevent (simple) global store-to-load forwarding.
-- Customary operators: syntax.
do
assert((! NIL) == true)
assert((! FALSE) == true)
assert((! TRUE) == false)
assert((! NUMBER1) == false)
assert((NIL && NUMBER1) == nil)
assert((FALSE && NUMBER1) == false)
assert((TRUE && NUMBER1) == 1)
assert((NIL || NUMBER1) == 1)
assert((FALSE || NUMBER1) == 1)
assert((TRUE || NUMBER1) == true)
end
-- Customary operators: constant folding.
do
assert((! nil) == true)
assert((! false) == true)
assert((! true) == false)
assert((! 1) == false)
assert((nil && 1) == nil)
assert((false && 1) == false)
assert((true && 1) == 1)
assert((nil || 1) == 1)
assert((false || 1) == 1)
assert((true || 1) == true)
end
-- Customary operators: precedence.
do
assert((! NIL && NUMBER1) == 1)
assert(((! NIL) && NUMBER1) == 1)
assert((! (NIL && NUMBER1)) == true)
assert((! NIL || NUMBER1) == true)
assert(((! NIL) || NUMBER1) == true)
assert((! (NIL || NUMBER1)) == false)
assert((NIL && TRUE || NUMBER1) == 1)
assert(((NIL && TRUE) || NUMBER1) == 1)
assert((NIL && (TRUE || NUMBER1)) == nil)
assert((NIL && FALSE || NUMBER1) == 1)
assert(((NIL && FALSE) || NUMBER1) == 1)
assert((NIL && (FALSE || NUMBER1)) == nil)
assert((TRUE && FALSE || NUMBER1) == 1)
assert(((TRUE && FALSE) || NUMBER1) == 1)
assert((TRUE && (FALSE || NUMBER1)) == 1) -- OK. Classic pitfall, use ?:
debug.setmetatable(false, {
__add = function(a, b)
if a == false then return b+10 else return b+20 end
end,
})
assert((! NUMBER1 + NUMBER2) == 12)
assert(((! NUMBER1) + NUMBER2) == 12)
assert((! (NUMBER1 + NUMBER2)) == false)
assert((TRUE && NUMBER1 + NUMBER2) == 3)
assert((FALSE && NUMBER1 + NUMBER2) == false)
assert((FALSE && (NUMBER1 + NUMBER2)) == false)
assert(((FALSE && NUMBER1) + NUMBER2) == 12)
assert((FALSE || NUMBER1 + NUMBER2) == 3)
assert((TRUE || NUMBER1 + NUMBER2) == true)
assert((TRUE || (NUMBER1 + NUMBER2)) == true)
assert(((TRUE || NUMBER1) + NUMBER2) == 22)
debug.setmetatable(false, nil)
end
-- Customary operators: associativity.
do
assert((TRUE && NUMBER1 && NUMBER2) == 2)
assert(((TRUE && NUMBER1) && NUMBER2) == 2)
assert((TRUE && (NUMBER1 && NUMBER2)) == 2)
assert((FALSE && NUMBER1 && NUMBER2) == false)
assert(((FALSE && NUMBER1) && NUMBER2) == false)
assert((FALSE && (NUMBER1 && NUMBER2)) == false)
assert((TRUE || NUMBER1 || NUMBER2) == true)
assert(((TRUE || NUMBER1) || NUMBER2) == true)
assert((TRUE || (NUMBER1 || NUMBER2)) == true)
assert((FALSE || NUMBER1 || NUMBER2) == 1)
assert(((FALSE || NUMBER1) || NUMBER2) == 1)
assert((FALSE || (NUMBER1 || NUMBER2)) == 1)
end
-- Customary operators: short-circuiting.
do
local function bad() error("call short-circuiting failed", 2) end
assert((FALSE && bad()) == false)
assert((TRUE || bad()) == true)
local t = setmetatable({}, {
__index = function() error("access short-circuiting failed", 2) end,
})
assert((FALSE && t.x) == false)
assert((TRUE || t.x) == true)
end
NULL = nil
FALSE = nil
TRUE = nil
NUMBER1 = nil
NUMBER2 = nil
local function expect_err(code, expect)
local f, err = loadstring(code, "=")
if f ~= nil then
error("unexpected success", 2)
elseif not err:match(expect) then
error('expected "'..expect..'", but got "'..err:gsub("^:1: ", "")..'"', 2)
end
end
local ok, ffi = pcall(require, "ffi")
if ok then
NULL = ffi.new("void *")
else
NULL = nil
end
NIL = nil
FALSE = false
STRX = "x"
TAB = {
x = 1,
y = { z = 2 },
v = "y",
f = function(x) return x + 2 end,
m = function(self, y) return self.x + y + 4 end,
9,
}
collectgarbage() -- Prevent (simple) global store-to-load forwarding.
-- Safe navigation operator: indexing syntax.
do
local local_nil = NIL
local local_null = NULL
local local_tab = { x = 1 }
assert((nil)?.x == nil)
assert(local_nil?.x == nil)
assert(local_null?.x == nil)
assert(tostring(local_null?.x) == "nil")
assert(local_tab?.x == 1)
local uv_nil = NIL
local uv_null = NULL
local uv_tab = { x = 1 }
assert((function() return uv_nil?.x end)() == nil)
assert((function() return uv_null?.x end)() == nil)
assert((function() return uv_tab?.x end)() == 1)
local uvcf1, uvcf2, uvcf3
do
local uvc_nil = NIL
local uvc_null = NULL
local uvc_tab = { x = 1 }
uvcf1 = function() return uvc_nil?.x end
uvcf2 = function() return uvc_null?.x end
uvcf3 = function() return uvc_tab?.x end
end
assert(uvcf1() == nil)
assert(uvcf2() == nil)
assert(uvcf3() == 1)
assert(NIL?.x == nil)
assert(NULL?.x == nil)
assert(TAB?.x == 1)
assert(TAB ?. x == 1)
assert(NIL?.["x"] == nil)
assert(NULL?.["x"] == nil)
assert(TAB?.["x"] == 1)
assert(NIL?.[STRX] == nil)
assert(NULL?.[STRX] == nil)
assert(TAB?.[STRX] == 1)
assert(NIL?.[1] == nil)
assert(NULL?.[1] == nil)
assert(TAB?.[1] == 9)
assert(TAB ?. [1] == 9)
local function fnil() return NIL end
assert(fnil()?.x == nil)
assert(fnil()?.[STRX] == nil)
local function ftab() return TAB end
assert(ftab()?.x == 1)
assert(ftab()?.[STRX] == 1)
end
-- Safe navigation operator: false is not nil.
do
debug.setmetatable(false, {
__index = function(t, k) if t == false then return k end end,
})
assert((false)?.x == "x")
local local_false = FALSE
assert(local_false?.x == "x")
local uv_false = FALSE
assert((function() return uv_false?.x end)() == "x")
local uvcf
do
local uvc_false = FALSE
uvcf = function() return uvc_false?.x end
end
assert(uvcf() == "x")
assert(FALSE?.x == "x")
debug.setmetatable(false, nil)
end
-- Safe navigation operator: chaining.
do
local local_tab = { y = { z = 2 }, v = "y" }
assert(local_tab?.y.z == 2)
assert(local_tab.y?.z == 2)
assert(local_tab?.y?.z == 2)
assert(local_tab?.[local_tab?.v]?.z == 2)
assert(local_tab.NOKEY?.z == nil)
assert(local_tab?.NOKEY?.z == nil)
assert(TAB?.y.z == 2)
assert(TAB.y?.z == 2)
assert(TAB?.y?.z == 2)
assert(TAB?.[TAB?.v]?.z == 2)
assert(TAB.NOKEY?.z == nil)
assert(TAB?.NOKEY?.z == nil)
end
-- Safe navigation operator: call syntax.
do
assert(NIL?.() == nil)
assert(NULL?.() == nil)
assert(NIL?.x() == nil)
assert(NIL?.x?.() == nil)
local f = TAB.f
assert(f?.(10) == 12)
assert((TAB.f)?.(10) == 12)
assert(TAB.f?.(10) == 12)
assert(TAB?.f?.(10) == 12)
assert(TAB.NOKEY?.(10) == nil)
assert(TAB?.NOKEY?.(10) == nil)
assert(TAB ?. f ?. (10) == 12)
assert(TAB:m?.(10) == 15)
assert(TAB?.:m(10) == 15)
assert(TAB?.:m?.(10) == 15)
assert(TAB:NOKEY?.(10) == nil)
assert(TAB?.:NOKEY?.(10) == nil)
assert(TAB ?. : m ?. (10) == 15)
assert(NIL?."" == nil)
local fs = function(s) return s.."y" end
assert(fs?."x" == "xy")
assert(NIL?.{} == nil)
local ft = function(t) return t.x + 2 end
assert(ft?.{ x = 10 } == 12)
assert(("abcd")?.:sub(2, 3) == "bc")
end
-- Safe navigation operator: assignment.
do
NIL?.v = 9
NULL?.v = 9
NIL?.v += 9
NULL?.v += 9
TAB.v = 5
TAB?.v = 9
assert(TAB.v == 9)
TAB?.v += 3
assert(TAB.v == 12)
TAB.v = nil
end
-- Safe navigation operator: short-circuiting.
do
local function bad() error("call short-circuiting failed", 2) end
assert(NIL?.[bad()] == nil)
assert(NIL?.f(bad()) == nil)
NIL?.[bad()] = 1
NIL?.x = bad()
assert(TAB?.NOKEY?.[bad()] == nil)
assert(TAB?.NOKEY?.(bad()) == nil)
local t = setmetatable({}, {
__index = function() error("access short-circuiting failed", 2) end,
})
assert(NIL?.[t.x] == nil)
assert(NIL?.f(t.x) == nil)
local ok, err = pcall(function() return FALSE?.[bad()] end)
assert(err:match("call"))
end
-- Safe navigation operator: multiple return values.
do
local function f() return 42, 99 end
local a, b = f?.()
assert(a == 42 and b == 99)
local y, a, b = 11, f?.()
assert(y == 11 and a == 42 and b == 99)
do local a, b, c, d, e = 1, 2, 3, 4, 5 end
local a, b = NIL?.()
assert(a == nil and b == nil)
local y, a, b = 11, NIL?.()
assert(y == 11 and a == nil and b == nil)
local obj = { method = function(self) return 42, 99 end }
local a, b = obj?.:method()
assert(a == 42 and b == 99)
local y, a, b = 11, obj?.:method()
assert(y == 11 and a == 42 and b == 99)
do local a, b, c, d, e = 1, 2, 3, 4, 5 end
local a, b = obj?.:NOMETHOD?.()
assert(a == nil and b == nil)
local y, a, b = 11, obj?.:NOMETHOD?.()
assert(y == 11 and a == nil and b == nil)
end
-- Safe navigation operator: forced single return value.
do
local function f() return 1, 2 end
local function g(...) return ... end
local a, b = g(f?.())
assert(a == 1 and b == nil)
local t = { f?.() }
assert(#t == 1 and t[1] == 1 and t[2] == nil)
end
-- Safe navigation operator: syntax errors.
do
expect_err("?.", "near.*%?%.")
expect_err("a?.", "near.*eof")
expect_err("a?.=", "near.*=")
expect_err("a?.b", "near.*eof")
expect_err("a?.b=", "near.*eof")
expect_err("a()?.b", "near.*eof")
expect_err("a()?.b=", "near.*eof")
expect_err("a?.[", "near.*eof")
expect_err("a?.(", "near.*eof")
expect_err("a?.\"", "near.*eof")
expect_err("a?.{", "near.*eof")
expect_err("a?.b, c = 1, 2", "near.*,")
expect_err("a, b?.c = 1, 2", "near.*%?%.")
expect_err("y = a?.5:1", "name.*expected")
assert((NIL ? .5 : 1) == 1)
end
NULL = nil
FALSE = nil
STRX = nil
TAB = nil
local function expect_err(code, expect)
local f, err = loadstring(code, "=")
if f ~= nil then
error("unexpected success", 2)
elseif not err:match(expect) then
error('expected "'..expect..'", but got "'..err:gsub("^:1: ", "")..'"', 2)
end
end
-- Short functions: expression syntax.
do
local f0 = ||->11
assert(f0() == 11)
local f1a = x->x+1
assert(f1a(10) == 11)
local f1b = |x|->x+1
assert(f1b(10) == 11)
local f2 = |x, y| -> x + y
assert(f2(10, 2) == 12)
end
-- Short functions: statement syntax.
do
local f00 = ||->do end
assert(f00() == nil)
assert(select("#", f00()) == 0)
local f0 = ||->do return 11 end
assert(f0() == 11)
local f1a = x->do return x+1 end
assert(f1a(10) == 11)
local f1b = |x|->do return x+1 end
assert(f1b(10) == 11)
local f2 = |x, y| -> do return x + y end
assert(f2(10, 2) == 12)
end
-- Short functions: vararg functions.
do
local fv1a = |...| -> ...
local a, b = fv1a(1, 2)
assert(a == 1 and b == 2)
local fv1b = |...| -> do return ... end
local c, d = fv1b(1, 2)
assert(c == 1 and d == 2)
local fv3 = |a, b, ...| -> a + b + ...
assert(fv3(1, 2, 8, 100) == 11)
end
-- Short functions: multiple results.
do
local gg = ||->do return 1, 2 end
local a, b = gg()
assert(a == 1 and b == 2)
local ff = ||->gg()
local c, d = ff()
assert(c == 1 and d == 2)
end
-- Short functions: parse single expression.
do
local a, b = x->x, 1
assert(type(a) == "function" and b == 1)
local c, d = a(2)
assert(c == 2 and d == nil)
local e, f = |x|->x, 1
assert(f == 1)
local g, h = ||->a, 1
assert(h == 1)
local t = { 1, x->x+1, 3, f = x->x+2, g = "g", 4 }
assert(t[1] == 1 and t[2](10) == 11 and t[3] == 3)
assert(t.f(10) == 12 and t.g == "g" and t[4] == 4)
end
-- Short functions: syntax errors.
do
expect_err("local f = -> 1", "unexpected.*%->")
expect_err("local f = || 1", "%->.*expected")
expect_err("local f = |x| 1", "%->.*expected")
expect_err("local f = || ->", "eof")
expect_err("local f = |x| ->", "eof")
expect_err("local f = || -> do", "end.*expected")
expect_err("local f = |x| -> do", "end.*expected")
expect_err("local f = || -> ||", "%->.*expected")
expect_err("local f = |x| -> ||", "%->.*expected")
expect_err("local f = || -> |y|", "%->.*expected")
expect_err("local f = |x| -> |y|", "%->.*expected")
expect_err("local f = || -> ->", "unexpected.*%->")
expect_err("local f = |x| -> ->", "unexpected.*%->")
expect_err("local f = a || ->", "unexpected.*%->")
expect_err("local f = a |x| ->", "unexpected.*%->")
expect_err("|| -> 1", "unexpected.*'||'")
expect_err("|.| -> 1", "unexpected.*'|'")
expect_err("x -> x = 1", "expected.*%->")
expect_err("local f = |x+y| -> a", "'|'.*expected")
expect_err("local f = |x=y| -> a", "'|'.*expected")
expect_err("local f = |x:y| -> a", "'|'.*expected")
expect_err("local f = a ? ||->a:b() : c", "arguments.*expected")
end
-- Short functions: closures.
do
local a = 1
local fc1 = x -> x + a
assert(fc1(10) == 11)
local b = 1
(x -> do b = x end)(20)
assert(b == 20)
local fc2
do
local c = 1
fc2 = x -> x + c
end
collectgarbage()
assert(fc2(10) == 11)
local fc3g, fc3s
do
local d = 1
fc3g, fc3s = || -> d, x -> do d = x end
end
collectgarbage()
assert(fc3g() == 1)
assert(fc3s(2) == nil)
assert(fc3g() == 2)
local inv = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }
local t = { 3, 7, 2, 6 }
table.sort(t, |a, b| -> inv[a] < inv[b])
assert(t[1] == 7 and t[2] == 6 and t[3] == 3 and t[4] == 2)
end
-- Short functions: precedence.
do
local f = x -> y -> x + y
assert(f(10)(1) == 11)
debug.setmetatable(f, {
__pow = |a, b| -> b(a),
__add = |a, b| -> b(a),
})
local a = 2 ^ x -> x + 3
assert(a == 5)
local b = 1 + x -> do return x + 3 end + 8
assert(b == 12)
debug.setmetatable(f, nil)
end
local N = setmetatable({}, { __index = function(_, x) return x end })
-- FFI bit operators: syntax.
do
assert(~ N[0b1010LL] == 0xfffffffffffffff5LL)
assert(~ N[0b1010ULL] == 0xfffffffffffffff5ULL)
assert(N[0x1122334455667788LL] & N[0x5a5a5a5a5a5a5a5aLL] == 0x1002124050425208LL)
assert(N[0x1122334455667788LL] | N[0x5a5a5a5a5a5a5a5aLL] == 0x5b7a7b5e5f7e7fdaLL)
assert(N[0x1122334455667788LL] ~ N[0x5a5a5a5a5a5a5a5aLL] == 0x4b78691e0f3c2dd2LL)
assert(N[0x55aa55aa55aa55aaLL] << N[4] == 0x5aa55aa55aa55aa0LL)
assert(N[0x55aa55aa55aa55aaLL] >> N[4] == 0x055aa55aa55aa55aLL)
assert(N[0x55aa55aa55aa55aaLL] ~>> N[4] == 0x055aa55aa55aa55aLL)
assert(N[0xaa55aa55aa55aa55LL] >> N[4] == 0x0aa55aa55aa55aa5LL)
assert(N[0xaa55aa55aa55aa55LL] ~>> N[4] == 0xfaa55aa55aa55aa5LL)
assert(N[0x55aa55aa55aa55aaLL] << N[36] == 0x5aa55aa000000000LL)
assert(N[0x55aa55aa55aa55aaLL] >> N[36] == 0x00000000055aa55aLL)
assert(N[0x55aa55aa55aa55aaLL] ~>> N[36] == 0x00000000055aa55aLL)
assert(N[0xaa55aa55aa55aa55LL] >> N[36] == 0x000000000aa55aa5LL)
assert(N[0xaa55aa55aa55aa55LL] ~>> N[36] == 0xfffffffffaa55aa5LL)
end
-- FFI bit operators: constant folding.
do
assert(~ 0b1010LL == 0xfffffffffffffff5LL)
assert(~ 0b1010ULL == 0xfffffffffffffff5ULL)
assert(0x1122334455667788LL & 0x5a5a5a5a5a5a5a5aLL == 0x1002124050425208LL)
assert(0x1122334455667788LL | 0x5a5a5a5a5a5a5a5aLL == 0x5b7a7b5e5f7e7fdaLL)
assert(0x1122334455667788LL ~ 0x5a5a5a5a5a5a5a5aLL == 0x4b78691e0f3c2dd2LL)
assert(0x55aa55aa55aa55aaLL << 4 == 0x5aa55aa55aa55aa0LL)
assert(0x55aa55aa55aa55aaLL >> 4 == 0x055aa55aa55aa55aLL)
assert(0x55aa55aa55aa55aaLL ~>> 4 == 0x055aa55aa55aa55aLL)
assert(0xaa55aa55aa55aa55LL >> 4 == 0x0aa55aa55aa55aa5LL)
assert(0xaa55aa55aa55aa55LL ~>> 4 == 0xfaa55aa55aa55aa5LL)
assert(0x55aa55aa55aa55aaLL << 36 == 0x5aa55aa000000000LL)
assert(0x55aa55aa55aa55aaLL >> 36 == 0x00000000055aa55aLL)
assert(0x55aa55aa55aa55aaLL ~>> 36 == 0x00000000055aa55aLL)
assert(0xaa55aa55aa55aa55LL >> 36 == 0x000000000aa55aa5LL)
assert(0xaa55aa55aa55aa55LL ~>> 36 == 0xfffffffffaa55aa5LL)
end
-- FFI bit operators: coercion.
do
assert(N[0xffffffff] | N[0LL] == 0xffffffffLL)
assert(N[0LL] | N[0xffffffff] == 0xffffffffLL)
assert(N[0xffffffff] | N[0ULL] == 0xffffffffULL)
assert(N[0ULL] | N[0xffffffff] == 0xffffffffULL)
assert(N[0x7ffffffffffffc00] | N[0LL] == 0x7ffffffffffffc00LL)
assert(N[-0x8000000000000000] | N[0LL] == 0x8000000000000000LL)
assert(N[0xfffffffffffff800] | N[0ULL] == 0xfffffffffffff800ULL)
assert((N[0x55aa55aa] | N[0LL]) << N[4] == 0x000000055aa55aa0LL)
assert(N[0x55aa55aa] << N[4LL] == 0x5aa55aa0)
assert((N[-1LL] | N[0ULL]) > 0LL)
assert((N[-1LL] << N[4ULL]) < 0LL)
end
-- FFI bit operators: shift width masking.
do
assert(N[0x55aa55aa55aa55aaLL] << N[64] == 0x55aa55aa55aa55aaLL)
assert(N[0x55aa55aa55aa55aaLL] >> N[64] == 0x55aa55aa55aa55aaLL)
assert(N[0x55aa55aa55aa55aaLL] ~>> N[64] == 0x55aa55aa55aa55aaLL)
assert(N[0xaa55aa55aa55aa55LL] ~>> N[64] == 0xaa55aa55aa55aa55LL)
assert(N[0x55aa55aa55aa55aaLL] << N[68] == 0x5aa55aa55aa55aa0LL)
assert(N[0x55aa55aa55aa55aaLL] >> N[68] == 0x055aa55aa55aa55aLL)
assert(N[0x55aa55aa55aa55aaLL] ~>> N[68] == 0x055aa55aa55aa55aLL)
assert(N[0xaa55aa55aa55aa55LL] ~>> N[68] == 0xfaa55aa55aa55aa5LL)
assert(N[0xaa55aa55aa55aa55LL] << N[-1] == 0x8000000000000000LL)
assert(N[0xaa55aa55aa55aa55LL] >> N[-1] == 0x0000000000000001LL)
assert(N[0xaa55aa55aa55aa55LL] ~>> N[-1] == 0xffffffffffffffffLL)
end
-- FFI bit operators: JIT logical ops.
do
local a = 0x123456789abcdef0LL
local y1, y2, y3, y4, y5, y6
for i=1,100 do
y1 = a & 0x000000005a5a5a5aLL
y2 = a & 0x5a5a5a5a00000000LL
y3 = a & 0xffffffff5a5a5a5aLL
y4 = a & 0x5a5a5a5affffffffLL
y5 = a & 0xffffffff00000000LL
y6 = a & 0x00000000ffffffffLL
end
assert(y1 == 0x000000001a185a50LL)
assert(y2 == 0x1210525800000000LL)
assert(y3 == 0x123456781a185a50LL)
assert(y4 == 0x121052589abcdef0LL)
assert(y5 == 0x1234567800000000LL)
assert(y6 == 0x000000009abcdef0LL)
for i=1,100 do
y1 = a | 0x000000005a5a5a5aLL
y2 = a | 0x5a5a5a5a00000000LL
y3 = a | 0xffffffff5a5a5a5aLL
y4 = a | 0x5a5a5a5affffffffLL
y5 = a | 0xffffffff00000000LL
y6 = a | 0x00000000ffffffffLL
end
assert(y1 == 0x12345678dafedefaLL)
assert(y2 == 0x5a7e5e7a9abcdef0LL)
assert(y3 == 0xffffffffdafedefaLL)
assert(y4 == 0x5a7e5e7affffffffLL)
assert(y5 == 0xffffffff9abcdef0LL)
assert(y6 == 0x12345678ffffffffLL)
for i=1,100 do
y1 = a ~ 0x000000005a5a5a5aLL
y2 = a ~ 0x5a5a5a5a00000000LL
y3 = a ~ 0xffffffff5a5a5a5aLL
y4 = a ~ 0x5a5a5a5affffffffLL
y5 = a ~ 0xffffffff00000000LL
y6 = a ~ 0x00000000ffffffffLL
end
assert(y1 == 0x12345678c0e684aaLL)
assert(y2 == 0x486e0c229abcdef0LL)
assert(y3 == 0xedcba987c0e684aaLL)
assert(y4 == 0x486e0c226543210fLL)
assert(y5 == 0xedcba9879abcdef0LL)
assert(y6 == 0x123456786543210fLL)
end
-- FFI bit operators: JIT shift and logical ops.
do
local a, b = 0x123456789abcdef0LL, 0x31415926535898LL
for i=1,200 do
a = a ~ b; b = (b ~>> 14) + (b << 50)
a = a - b; b = (b << 5) + (b ~>> 59)
b = a ~ b; b = b - (b << 13) - (b >> 51)
end
assert(b == -7993764627526027113LL)
end
-- Adapted from bit operations library tests.
local xbit = {
bnot = function(a) return ~a end,
band = function(a, b) return a & b end,
bor = function(a, b) return a | b end,
bxor = function(a, b) return a ~ b end,
lshift = function(a, b) return a << b end,
rshift = function(a, b) return a >> b end,
arshift = function(a, b) return a ~>> b end,
}
local vb = {
0LL, 1LL, -1LL, 2LL, -2LL,
0x123456789abcdef0LL, 0x0fedcba987654321LL,
0x33333333LL, 0x77777777LL, 0x55aa55aaLL, 0xaa55aa55LL,
0x7fffffffLL, 0x80000000LL, 0xffffffffLL,
0x3333333333333333LL, 0x7777777777777777LL,
0x55aa55aa55aa55aaLL, 0xaa55aa55aa55aa55LL,
0x7fffffffffffffffLL, 0x8000000000000000LL, 0xffffffffffffffffLL,
}
local function cksum(name, s, r)
local z = 0
for i=1,#s do z = (z + string.byte(s, i)*i) % 2147483629 end
if z ~= r then
error("bit."..name.." test failed (got "..z..", expected "..r..")", 0)
end
end
local function check_unop(name, r)
local f = xbit[name]
local s = ""
if pcall(f) or pcall(f, "z") or pcall(f, true) then
error("bit."..name.." fails to detect argument errors", 0)
end
for _,x in ipairs(vb) do s = s..","..tostring(f(x)) end
cksum(name, s, r)
end
local function check_binop(name, r)
local f = xbit[name]
local s = ""
if pcall(f) or pcall(f, "z") or pcall(f, true) then
error("bit."..name.." fails to detect argument errors", 0)
end
for _,x in ipairs(vb) do
for _,y in ipairs(vb) do s = s..","..tostring(f(x, y)) end
end
cksum(name, s, r)
end
local function check_binop_range(name, r, yb, ye)
local f = xbit[name]
local s = ""
if pcall(f) or pcall(f, "z") or pcall(f, true) or pcall(f, 1, true) then
error("bit."..name.." fails to detect argument errors", 0)
end
for _,x in ipairs(vb) do
for y=yb,ye do s = s..","..tostring(f(x, y)) end
end
cksum(name, s, r)
end
local function check_shift(name, r)
check_binop_range(name, r, 0, 31)
end
-- FFI bit operators: computed value.
do
check_unop("bnot", 2562401)
check_binop("band", 691178007)
check_binop("bor", 1218205008)
check_binop("bxor", 1616063830)
check_shift("lshift", 722026261)
check_shift("rshift", 1938929707)
check_shift("arshift", 1443445799)
end
-- Numbers with underscores: parser.
do
assert(1_234 == 1234)
assert(1_ == 1)
assert(0_ == 0)
assert(0_1 == 1)
assert(1_2_3__4__ == 1234)
assert(0x1_2 == 18)
assert(0__x__1__2__ == 18)
assert(0__b__1__0__ == 2)
end
-- Numbers with underscores: rejected by tonumber.
do
assert(not tonumber("1_2"))
assert(not tonumber("0_x2"))
assert(not tonumber("0x_2"))
assert(not tonumber("0x2_"))
end
local function expect_err(code, expect)
local f, err = loadstring(code, "=")
if f ~= nil then
error("unexpected success", 2)
elseif not err:match(expect) then
error('expected "'..expect..'", but got "'..err:gsub("^:1: ", "")..'"', 2)
end
end
NUMBER1 = 1
NUMBER2 = 2
NUMBER3 = 3
STRX = "x"
STRY = "y"
collectgarbage() -- Prevent (simple) global store-to-load forwarding.
-- Compound assignment: syntax.
do
do local a = NUMBER3; a += NUMBER2; assert(a == 5) end
do local a = NUMBER3; a -= NUMBER2; assert(a == 1) end
do local a = NUMBER3; a *= NUMBER2; assert(a == 6) end
do local a = NUMBER3; a /= NUMBER2; assert(a == 1.5) end
do local a = NUMBER3; a %= NUMBER2; assert(a == 1) end
do local a = NUMBER3; a &= NUMBER2; assert(a == 2) end
do local a = NUMBER1; a |= NUMBER2; assert(a == 3) end
do local a = NUMBER3; a ~= NUMBER2; assert(a == 1) end
do local a = NUMBER3; a <<= NUMBER2; assert(a == 12) end
do local a = NUMBER3; a >>= NUMBER1; assert(a == 1) end
do local a = NUMBER3; a ~>>= NUMBER1; assert(a == 1) end
do local a = -NUMBER3; a ~>>= NUMBER1; assert(a == -2) end
do local a = STRX; a ..= STRY; assert(a == "xy") end
do local t = { x = NUMBER2 }; t.x += NUMBER3; assert(t.x == 5) end
do local t = { NUMBER2 }; t[1] += NUMBER3; assert(t[1] == 5) end
end
-- Compound assignment: constant folding.
do
do local a = 3; a += 2; assert(a == 5) end
do local a = 3; a -= 2; assert(a == 1) end
do local a = 3; a *= 2; assert(a == 6) end
do local a = 3; a /= 2; assert(a == 1.5) end
do local a = 3; a %= 2; assert(a == 1) end
do local a = 3; a &= 2; assert(a == 2) end
do local a = 1; a |= 2; assert(a == 3) end
do local a = 3; a ~= 2; assert(a == 1) end
do local a = 3; a <<= 2; assert(a == 12) end
do local a = 3; a >>= 1; assert(a == 1) end
do local a = 3; a ~>>= 1; assert(a == 1) end
do local a = -3; a ~>>= 1; assert(a == -2) end
do local a = "x"; a ..= "y"; assert(a == "xy") end
end
-- Compound assignment: indexing metamethods.
do
local mt = {
__index = function(t, k) t.get += 1; return rawget(t, "_"..k) end,
__newindex = function(t, k, v) t.set += 1; rawset(t, "_"..k, v) end,
}
local function proxy()
return setmetatable({ get = 0, set = 0, }, mt)
end
do
local t = proxy()
t.x = NUMBER2
assert(t.get == 0 and t.set == 1)
t.x += NUMBER3
assert(t.get == 1 and t.set == 2)
assert(t.x == 5)
assert(t.get == 2 and t.set == 2)
end
do
local t = proxy()
rawset(t, "x", NUMBER2)
assert(t.get == 0 and t.set == 0)
t.x += NUMBER3
assert(t.get == 0 and t.set == 0)
assert(t.x == 5)
end
end
-- Compound assignment: extra metamethod argument.
if jit and jit.version_num >= 30000 then -- NOT backported to v2.1.
local mt = { __add = function(a, b, c) b.c = c; return a.x + b.x end }
local function adder(x)
return setmetatable({ c = "BAD", x = x }, mt)
end
do
local a = adder(NUMBER2)
local b = adder(NUMBER3)
local y = a + b
assert(y == 5 and b.c == nil)
end
do
local a = adder(NUMBER2)
local b = adder(NUMBER3)
a += b
assert(a == 5 and b.c == true)
end
end
-- Compound assignment: syntax error.
do
expect_err("x + = 1", "near.*%+")
expect_err("+=", "near.*%+")
expect_err("x +=", "near.*eof")
expect_err("+= 1", "near.*%+")
expect_err("(x) += 1", "near.*%+")
expect_err("f() += 1", "near.*%+")
expect_err("x, y += 1", "near.*%+") -- No parallel assignment.
expect_err("x, y += 1, 2", "near.*%+") -- No parallel assignment.
expect_err("x ^= 1", "near.*^") -- No power compound assignment.
end
NUMBER1 = nil
NUMBER2 = nil
NUMBER3 = nil
STRX = nil
STRY = nil
local function expect_err(code, expect)
local f, err = loadstring(code, "=")
if f ~= nil then
error("unexpected success", 2)
elseif not err:match(expect) then
error('expected "'..expect..'", but got "'..err:gsub("^:1: ", "")..'"', 2)
end
end
-- const: declaration.
do
const x = 1
assert(x == 1)
const y, z = 2, 3
assert(y == 2 and z == 3)
local function f() const u = 2; return x + u end
local u = 10
assert(f() == 3)
end
-- const: lexical scoping.
do
do const x = 1 end
const x = 2
do const y = 3 end
local y = 4
assert(x == 2 and y == 4)
end
-- const: re-assignment error.
do
expect_err("const x = 1; x = 1", "assign to const")
expect_err("const x = 1; x += 1", "assign to const")
expect_err("const x, y = 1, 2; y = 2", "assign to const")
expect_err("const x = 1; function x() end", "assign to const")
expect_err("const x = 1; local function f() x = 1 end", "assign to const")
expect_err("const x = 1; local function f() return function() return function() x = 1 end end end", "assign to const")
-- Check line number of error.
expect_err("const x = 1\na,\nx,\ny = 1, 2, 3\nreturn", ":3:")
expect_err("const x = 1\nfunction x()\nend\nreturn", ":2:")
end
-- const: local re-declaration error.
do
expect_err("const x = 1; const x = 1", "declare const")
expect_err("const x = 1; local x = 1", "declare const")
expect_err("const x = 1; local y, x = 1, 1", "declare const")
expect_err("const x, x = 1, 1", "declare const")
expect_err("const x = 1; local function x() end", "declare const")
expect_err("const x = 1; for x=1,100 do end", "declare const")
expect_err("const x = 1; for x in pairs(_G) do end", "declare const")
end
-- const: upvalue re-declaration error.
do
expect_err("const x = 1; local function f() const x = 1 end", "declare const")
expect_err("const x = 1; local function f() local x = 1 end", "declare const")
expect_err("const x = 1; local function f() return function() return function() const x = 1 end end end", "declare const")
end
-- const: parameter re-declaration error.
do
expect_err("const x = 1; local function f(x) end", "declare const")
expect_err("const x = 1; local f = |x| -> x", "declare const")
end
-- const: soft keyword.
do
do
local const = 1
assert(const == 1)
const = 2
assert(const == 2)
const = const
assert(const == 2)
end
do
const const = 1
assert(const == 1)
end
do
local x = 1
goto const
x = 2
::const::
assert(x == 1)
end
do
local t = { const = 1 }
assert(t.const == 1)
end
do
local function const() return 1 end
assert(const() == 1)
end
do
local function f(const) return const + 1 end
assert(f(1) == 2)
end
do
local f = const -> const + 1
assert(f(1) == 2)
assert((const -> const + 1)(1) == 2)
end
end
local function expect_err(code, expect)
local f, err = loadstring(code, "=")
if f ~= nil then
error("unexpected success", 2)
elseif not err:match(expect) then
error('expected "'..expect..'", but got "'..err:gsub("^:1: ", "")..'"', 2)
end
end
-- continue: while loop.
do
local sum = 0
local i = 0
while i < 100 do
i = i + 1
if (i & 4) == 0 then continue end
if i >= 70 and i <= 80 then continue end
if i == 95 then break end
sum = sum + i
end
assert(sum == 1830)
end
-- continue: repeat loop.
do
local sum = 0
local i = 0
repeat
i = i + 1
if (i & 4) == 0 then continue end
if i >= 70 and i <= 80 then continue end
if i == 95 then break end
sum = sum + i
until i >= 100
assert(sum == 1830)
end
-- continue: numeric for loop.
do
local sum = 0
for i=1,100 do
if (i & 4) == 0 then continue end
if i >= 70 and i <= 80 then continue end
if i == 95 then break end
sum = sum + i
end
assert(sum == 1830)
end
-- continue: generic for loop.
do
local t = {}
for i=1,100 do t[i] = i end
local sum = 0
for _,i in ipairs(t) do
if (i & 4) == 0 then continue end
if i >= 70 and i <= 80 then continue end
if i == 95 then break end
sum = sum + i
end
assert(sum == 1830)
end
-- continue: nested continue.
do
local sum = 0
for j=1,10 do
if j == 8 then continue end
for i=1,100 do
if (i & 4) == 0 then continue end
if i >= 70 and i <= 80 then continue end
if i == 95 then break end
sum = sum + i
end
end
assert(sum == 1830*9)
end
-- continue: closures.
do
local t = {}
for i=1,100 do
t[i] = function() return i+2 end
if i >= 70 then
continue
end
end
for i=1,100 do
assert(t[i]() == i+2)
end
local u = {}
local j = 0
repeat
j = j + 1
local k = j
if j >= 70 then
continue
end
until (function() u[j] = function() return k+2 end; return k >= 100 end)()
for i=1,100 do
assert(u[i]() == i+2)
end
end
-- continue: syntax errors and semantic errors.
do
expect_err("continue", "loop.*continue")
expect_err("do continue end", "loop.*continue")
expect_err("function f() continue end", "loop.*continue")
expect_err("repeat continue; x = 1 until false", "expected")
expect_err("repeat if x then continue end; local a = a; until not a", "continue.*scope.*'a'")
expect_err("repeat if x then continue end; local a = a; if y then continue end until not a", "continue.*scope.*'a'")
expect_err("repeat if true then local a; continue end; local a = a; until not a", "continue.*scope.*'a'")
end
-- continue: soft keyword.
do
do
local continue = 1
assert(continue == 1)
continue = 2
assert(continue == 2)
continue = continue
assert(continue == 2)
end
do
const continue = 1
assert(continue == 1)
end
do
local x = 1
goto continue
x = 2
::continue::
assert(x == 1)
end
do
local t = { continue = 1 }
assert(t.continue == 1)
end
do
local function continue() return 1 end
assert(continue() == 1)
end
do
local function f(continue) return continue + 1 end
assert(f(1) == 2)
end
do
local f = continue -> continue + 1
assert(f(1) == 2)
assert((continue -> continue + 1)(1) == 2)
end
end

LuaJIT 3.0 Syntax Extensions Test Cases

The files with v3_ in front of it only work in 3.0, not in the LuaJIT 2.1 backport.

local function expect_err(code, expect)
local f, err = loadstring(code, "=")
if f ~= nil then
error("unexpected success", 2)
elseif not err:match(expect) then
error('expected "'..expect..'", but got "'..err:gsub("^:1: ", "")..'"', 2)
end
end
local N = setmetatable({}, { __index = function(_, x) return x end })
-- Floor division: syntax.
do
assert((N[6] // N[2]) == 3)
assert((N[7] // N[2]) == 3)
assert((N[-7] // N[2]) == -4)
end
-- Floor division: constant folding.
do
assert((6 // 2) == 3)
assert((7 // 2) == 3)
assert((-7 // 2) == -4)
end
-- Floor division: compound assignment.
do
local a = N[7]
a //= N[2]
assert(a == 3)
end
-- Floor division: precedence.
do
assert((N[2] + N[7] // N[3] + N[1]) == 5)
assert((N[2] * N[7] // N[3] * N[1]) == 4)
assert((N[2] ^ N[7] // N[3] ^ N[1]) == 42)
end
-- Floor division: associativity.
do
assert((N[9] // N[3] // N[2]) == 1)
assert(((N[9] // N[3]) // N[2]) == 1)
assert((N[9] // (N[3] // N[2])) == 9)
end
-- Floor division: metamethod.
do
local mt = { __idiv = function() return "idiv" end }
local a = setmetatable({}, mt)
local b = setmetatable({}, mt)
assert(a // 1 == "idiv" and 1 // a == "idiv" and a // b == "idiv")
end
-- Floor division: syntax errors.
do
expect_err("//", "near.*//")
expect_err("a //", "near.*//")
expect_err("a // b", "near.*//")
expect_err("local y //", "near.*//")
expect_err("local y = //", "near.*//")
expect_err("local y = a //", "near.*eof")
expect_err("local y = a // //", "near.*//")
end
local N = setmetatable({}, { __index = function(_, x) return x end })
-- FFI floor division: syntax.
do
assert((N[6LL] // N[2LL]) == 3)
assert((N[7LL] // N[2LL]) == 3)
assert((N[-7LL] // N[2LL]) == -4)
assert((N[6ULL] // N[2ULL]) == 3)
assert((N[7ULL] // N[2ULL]) == 3)
end
-- FFI floor division: constant folding.
do
assert((6LL // 2LL) == 3)
assert((7LL // 2LL) == 3)
assert((-7LL // 2LL) == -4)
assert((6ULL // 2ULL) == 3)
assert((7ULL // 2ULL) == 3)
end
-- FFI floor division: compound assignment.
do
local a = N[7LL]
a //= N[2LL]
assert(a == 3)
end
-- FFI floor division: coercion.
do
assert((N[0x1ffffffffLL] // N[2]) % 1 == 0)
assert((N[0x1ffffffff] // N[2LL]) % 1 == 0)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment